figure out CSlice/&CSlice inconsistency #76
Labels
No Milestone
No Assignees
4 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: M-Labs/artiq-zynq#76
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
DMA and cache functions use
CSlice
:But the RPC functions need
&CSlice
(only on Zynq, and not in the original runtime):Why?
I don't see why it would be passed by value on OR1k:
Not sure whether that is what is going on here, but depending on the ABI, structs are implicitly passed on the stack, which will be compatible with references on some architectures.
Should the compiler be changed so that RPC consistently uses
CSlice
everywhere then?Those patches work for Zynq but break OR1K:
This crashes on OR1K with the original compiler:
So it seems that the compiler's (not Rust) behavior is different between OR1K and ARM, and is arguably incorrect on OR1K.
I think the compiler's behaviour behind the scenes is the same – it always passes structs as a pointer to stack memory (
_prepare_ffi_call
). It is, however, incorrect on ARM/EABI, as (IIRC) composite types are opportunistically passed in registers there.LLVM unfortunately doesn't provide support for ABI details out of the box, instead requiring each frontend to implement the necessary conversions.
@dpn But why would there be a difference between
rpc_send
anddma_record_start
/get
?The former is actually declared as a pointer to a slice in the LLVM IR generator. As such, they are always drumroll passed as a pointer to a slice. On OR1k, this apparently matched the Rust (C) ABI for
CSlice
. On ARM, it doesn't, because the slice members are opportunistically passed in registers.The argument to the latter is passed by pointer, but marked with the LLVM
byval
attribute, the meaning of which is annoyingly backend-specific, but it is used to capture cases where the ABI dictates that memory for storing aggregate arguments that are passed by value should be allocated by the caller in a special fashion (e.g. adjacent to other parameters) and discarded after the call.The best (imho only) way to debug this would be to settle on a clear C ABI for each function, and make sure
llvm_ir_generator
emits exactly the same signatures as Clang (or I supposerustc
) does for each target we support. From experience (porting LDC to various platforms and OSes), it is nearly impossible to get this right by just reading the LLVM docs.pca006132: "Regarding this, we now use &CSlice consistently for lists. For names, we use CSlice."
@pca006132 your comment seems true on Zynq, but on RISC-V we have
&CSlice
everywhere. Why?Some platform ABI will require passing structs by register under certain condition (size of the composite type, available registers, etc.). This is done by the frontend and requires platform specific implementation. The way we are doing this is by trial and error to see if the output matches. A simpler way would be to use pointers for all FFI calls.