Hi fellow Rustaceans,
TL;DR: Is there some way to specify that a function (either on the callee or caller side) cannot use SIMD (SSE) XMM
registers in its calling convention?
As part of an unusual research OS project, I have two crates that run in the same address space:
-
lib_reg
: a crate that is compiled without SIMD support (soft_float
target feature enabled), and exposes several public functions. -
lib_sse
: a crate that is compiled with SIMD support (sse2
target feature enabled).
I want to call lib_reg
's functions from the lib_sse
crate.
However, the caller site in the lib_sse
crate places arguments inside the XMM
registers, but the callee function in the lib_reg
crate expects those arguments to be placed in RDI
, RSI
, etc, so linking the two doesn't work when they are compiled separately. Additionally, the XMM registers are sometimes used to pass the return value back from the callee function, which the lib_sse
crate expects to happen, but the lib_reg
crate cannot do.
A typical disassembly of the caller code in lib_sse
looks like this:
movsd -0x40(%rbp),%xmm0
movsd -0x38(%rbp),%xmm1
movsd -0x30(%rbp),%xmm2
movsd -0x28(%rbp),%xmm3
...
callq *%rcx # calls a function in lib_reg
One potential solution to this would be to declare the lib_reg
functions with a calling convention that stipulates that only normal registers (and stack space) could be used, and disallows using XMM registers for passing arguments. This could look something like:
extern "no_sse_regs" fn my_public_func(...)
However, no such calling convention exists, and when I compile the lib_sse
crate with target_features = +sse2
, the compiler is allowed to use SIMD registers for every single function in that crate. It'd be nice if I could specify which functions are allowed to use SIMD registers in their calling convention, and which ones aren't.
I looked into the #[target_feature(enable = ...)]
feature, but it doesn't quite do what I need it to.
One way to circumvent this would be to use unsafe inline assembly blocks to, which is kind of what system calls require, i.e., manually placing arguments into specific registers, but that loses the language-level info like types, borrowing/lifetimes, mutability, etc. It's also intractable to manually specify argument placement for hundreds of functions.
Any ideas on how to realize this? Thanks!