[Canary] Build and test for RISC-V64 libco backend - #12243
Conversation
Signed-off-by: Hiroshi Hatake <hiroshi@chronosphere.io>
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
edsiper/flb_libco and monkey/monkey PRs have been merged |
Thanks a lot! This is because some of the compilers need to follow C11 precisely and |
THIS IS CANARY PR. DO NOT MERGE.
Currently, we didn't have riscv64 backend on flb_libco library and bundled library as in monkey HTTP server.
This PR fills the gap of riscv64 coroutine implementation for RISC-V64 Linux environment.
Without this patch, runtime tests of filter_wasm were failed.
After applying this patch, we get on the actual RISC-V64 Linux environment:
Plus, the implemented coroutine function can be used as:
Background of RISC-V64 implementation
The backend works because it saves exactly the state that the RISC-V ELF ABI says must survive an ordinary C function call. From each coroutine’s perspective,
co_switch()behaves like a function that pauses and later returns normally.The register classification comes from the RISC-V ELF psABI calling convention.
How a context switch happens
The C wrapper passes two context pointers to the assembly routine:
By the normal RISC-V calling convention:
The assembly first saves the current coroutine:
It then loads the target coroutine:
The final
retjumps to theraloaded from the target context.For a suspended coroutine, that
rapoints immediately after its earlier call toco_switch_riscv64(). Execution therefore continues as thoughco_switch()had just returned.For a new coroutine, [co_create() (line 173)](C:/Users/cosmo/Documents/GitHub/fluent-bit/lib/flb_libco/riscv64.c:173) initializes:
Its first
retenters the trampoline on the newly allocated stack. The trampoline invokes the actual coroutine entry function and aborts if that function unexpectedly returns.Why caller-saved registers are omitted
The backend does not need to save
a0–a7ort0–t6. They are caller-saved registers. When the compiler generates a call toco_switch(), it already spills any live caller-saved values that will be needed afterward.Likewise:
gpis fixed by the ABI and must not be modified.tpidentifies the current OS thread. Fluent Bit coroutines remain on the same pthread, so it must remain unchanged.co_switch().This backend therefore assumes that a coroutine is never migrated to another pthread, matching Fluent Bit’s current coroutine model.
Floating-point ABI handling
The compiler exposes the selected ABI through predefined macros:
__riscv_float_abi_single__riscv_float_abi_doubleLP64D uses 64-bit operations:
LP64F uses 32-bit operations:
Soft-float LP64 does not preserve floating-point registers because that ABI does not classify them as callee-saved program state.
On a typical DC ROMA II LP64D environment, GCC should report:
Expected relevant definitions include:
These macros select riscv64.c and enable its double-precision register-save path.
Stack correctness
RISC-V requires the stack pointer to remain 16-byte aligned. Both the requested stack size and context header are rounded to multiples of 16. Linux RV64
malloc()supplies suitably aligned memory, so the calculated top of the allocation remains 16-byte aligned.The stack grows downward from that address, while the saved context resides at the bottom of the allocation:
How the focused test proves preservation
The test commit does more than verify that switching does not crash:
s0–s11,fs0–fs11, andfcsr.Correct return flow also implicitly verifies
raandsp: an incorrect value for either normally returns to the wrong instruction or accesses the wrong stack and crashes.RV64E and LP64Q are excluded because their register sets or floating-point widths require different layouts. They continue to the generic backend instead of silently using an incompatible context representation.
The backend works because it saves exactly the state that the RISC-V ELF ABI says must survive an ordinary C function call. From each coroutine’s perspective,
co_switch()behaves like a function that pauses and later returns normally.rasps0–s11fs0–fs11fcsrThe register classification comes from the [RISC-V ELF psABI calling convention](https://riscv-non-isa.github.io/riscv-elf-psabi-doc/).
How a context switch happens
The C wrapper passes two context pointers to the assembly routine:
By the normal RISC-V calling convention:
The assembly first saves the current coroutine:
It then loads the target coroutine:
The final
retjumps to theraloaded from the target context.For a suspended coroutine, that
rapoints immediately after its earlier call toco_switch_riscv64(). Execution therefore continues as thoughco_switch()had just returned.For a new coroutine, co_create() initializes:
Its first
retenters the trampoline on the newly allocated stack. The trampoline invokes the actual coroutine entry function and aborts if that function unexpectedly returns.Why caller-saved registers are omitted
The backend does not need to save
a0–a7ort0–t6. They are caller-saved registers. When the compiler generates a call toco_switch(), it already spills any live caller-saved values that will be needed afterward.Likewise:
gpis fixed by the ABI and must not be modified.tpidentifies the current OS thread. Fluent Bit coroutines remain on the same pthread, so it must remain unchanged.co_switch().This backend therefore assumes that a coroutine is never migrated to another pthread, matching Fluent Bit’s current coroutine model.
Floating-point ABI handling
The compiler exposes the selected ABI through predefined macros:
__riscv_float_abi_single__riscv_float_abi_doubleLP64D uses 64-bit operations:
LP64F uses 32-bit operations:
Soft-float LP64 does not preserve floating-point registers because that ABI does not classify them as callee-saved program state.
On a typical DC ROMA II LP64D environment, GCC should report:
Expected relevant definitions include:
These macros select riscv64.c and enable its double-precision register-save path.
Stack correctness
RISC-V requires the stack pointer to remain 16-byte aligned. Both the requested stack size and context header are rounded to multiples of 16. Linux RV64
malloc()supplies suitably aligned memory, so the calculated top of the allocation remains 16-byte aligned.The stack grows downward from that address, while the saved context resides at the bottom of the allocation:
How the focused test proves preservation
The test commit does more than verify that switching does not crash:
s0–s11,fs0–fs11, andfcsr.Correct return flow also implicitly verifies
raandsp: an incorrect value for either normally returns to the wrong instruction or accesses the wrong stack and crashes.RV64E and LP64Q are excluded because their register sets or floating-point widths require different layouts. They continue to the generic backend instead of silently using an incompatible context representation.
Enter
[N/A]in the box, if an item is not applicable to your change.Testing
Before we can approve your change; please submit the following in a comment:
If this is a change to packaging of containers or native binaries then please confirm it works for all targets.
ok-package-testlabel to test for all targets (requires maintainer to do).Documentation
Backporting
Fluent Bit is licensed under Apache 2.0, by submitting this pull request I understand that this code will be released under the terms of that license.