Stream: general

Topic: Assertion fails when calling rust function taking StructArgu


view this post on Zulip SapphireAmoeba5 (May 07 2024 at 19:41):

I am trying to call a Rust function that takes in a struct from within cranelift, but after I finish generating the IR and setting everything up calling finalize_definitions my program panics with the message assertion failed: (diff >> 26 == -1) || (diff >> 26 == 0

The program only panics when running on my M1 Mac Laptop, when I run it on my x86_64 linux machine everything is as I expect it to be.

The struct

#[repr(C)]
struct MyS {
    x: i64,
    y: i64,
    z: i64,
    w: i64,
}

Here is the rust function

extern "C" fn pass_struct(x: MyS) {
    println!("Called from cranelift: {x:?}")
}

Here is the cranelift function signature

let mut sig_pass_struct = module.make_signature();

sig_pass_struct.params.push(AbiParam::special(
   module.target_config().pointer_type(),
    ArgumentPurpose::StructArgument(mem::size_of::<MyS>() as u32),
 ));

 let func_pass_struct = module
     .declare_function("pass_struct", Linkage::Import, &sig_pass_struct)
     .unwrap();

And the code used to generate cranelift IR

let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);

// ...

let param_0 = bcx.block_params(block)[0]; // i64
let param_1 = bcx.block_params(block)[1]; // i64

let sum = bcx.ins().iadd(param_0, param_1);
let diff = bcx.ins().isub(param_0, param_1);
let product = bcx.ins().imul(param_0, param_1);
let square = bcx.ins().imul(param_0, param_0);

let stack_slot = bcx.create_sized_stack_slot(StackSlotData::new(
    StackSlotKind::ExplicitSlot,
    mem::size_of::<MyS>() as u32,
));

bcx.ins().stack_store(sum, stack_slot, 0);
bcx.ins().stack_store(diff, stack_slot, 8);
bcx.ins().stack_store(product, stack_slot, 16);
bcx.ins().stack_store(square, stack_slot, 24);

let addr = bcx.ins().stack_addr(types::I64, stack_slot, 0);

let function = module.declare_func_in_func(func_pass_struct, &mut bcx.func);
bcx.ins().call(function, &[addr]);

When I change the cranelift signature for the Rust function from taking in an AbiParam::special(ArgumentPurpose::StructArgument(...)) to taking in a AbiParam::new(types::I64) it doesn't panic and runs exactly as I expect.

view this post on Zulip Chris Fallin (May 07 2024 at 19:44):

The assertion is almost certainly something to do with aarch64's 26-bit limit on various kinds of offsets. I don't remember exactly how StructArgument lowers but IIRC it was for a special kind of compiler-managed memcpy; if you're allocating structs and managing pointers to them in your compiled code, you might as well pass a raw pointer (an I64, as you switched to) as well. Specifically, StructArgument doesn't mean "this is a struct" or something like that; Cranelift doesn't care what is behind the I64, or even that it's a pointer.

view this post on Zulip bjorn3 (May 08 2024 at 12:04):

StructArgument is passed at a fixed position on the stack to the callee. It is only required for compatibility with existing C code. If you write your own code I suggest that you explicitly pass your structs as pointers on both the caller and callee side. So extern "C" fn pass_struct(x: &MyS) in your case.


Last updated: Dec 23 2024 at 12:05 UTC