forked from M-Labs/nac3
core: Fix passing structure arguments to extern functions
All parameters with a structure type in extern functions are marked as `byref` instead of `byval`, as most ABIs require the first several arguments to be passed in registers before spilling into the stack. `byval` breaks this contract by explicitly requiring all arguments to be passed in the stack, breaking interop with libraries written in other languages.
This commit is contained in:
parent
176f250bdb
commit
cb39f61e79
@ -365,6 +365,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||||||
let mut return_slot = None;
|
let mut return_slot = None;
|
||||||
if fun.count_params() > 0 {
|
if fun.count_params() > 0 {
|
||||||
let sret_id = Attribute::get_named_enum_kind_id("sret");
|
let sret_id = Attribute::get_named_enum_kind_id("sret");
|
||||||
|
let byref_id = Attribute::get_named_enum_kind_id("byref");
|
||||||
let byval_id = Attribute::get_named_enum_kind_id("byval");
|
let byval_id = Attribute::get_named_enum_kind_id("byval");
|
||||||
|
|
||||||
let offset = if fun.get_enum_attribute(AttributeLoc::Param(0), sret_id).is_some() {
|
let offset = if fun.get_enum_attribute(AttributeLoc::Param(0), sret_id).is_some() {
|
||||||
@ -376,7 +377,8 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||||||
0
|
0
|
||||||
};
|
};
|
||||||
for (i, param) in params.iter().enumerate() {
|
for (i, param) in params.iter().enumerate() {
|
||||||
if fun.get_enum_attribute(AttributeLoc::Param((i + offset) as u32), byval_id).is_some() {
|
let loc = AttributeLoc::Param((i + offset) as u32);
|
||||||
|
if fun.get_enum_attribute(loc, byref_id).is_some() || fun.get_enum_attribute(loc, byval_id).is_some() {
|
||||||
// lazy update
|
// lazy update
|
||||||
if loc_params.is_empty() {
|
if loc_params.is_empty() {
|
||||||
loc_params.extend(params[0..i+offset].iter().copied());
|
loc_params.extend(params[0..i+offset].iter().copied());
|
||||||
@ -745,11 +747,11 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||||||
Some(ctx.get_llvm_abi_type(generator, fun.0.ret))
|
Some(ctx.get_llvm_abi_type(generator, fun.0.ret))
|
||||||
};
|
};
|
||||||
let has_sret = ret_type.map_or(false, |ret_type| need_sret(ctx.ctx, ret_type));
|
let has_sret = ret_type.map_or(false, |ret_type| need_sret(ctx.ctx, ret_type));
|
||||||
let mut byvals = Vec::new();
|
let mut byrefs = Vec::new();
|
||||||
let mut params = args.iter().enumerate()
|
let mut params = args.iter().enumerate()
|
||||||
.map(|(i, arg)| match ctx.get_llvm_abi_type(generator, arg.ty) {
|
.map(|(i, arg)| match ctx.get_llvm_abi_type(generator, arg.ty) {
|
||||||
BasicTypeEnum::StructType(ty) if is_extern => {
|
BasicTypeEnum::StructType(ty) if is_extern => {
|
||||||
byvals.push((i, ty));
|
byrefs.push((i, ty));
|
||||||
ty.ptr_type(AddressSpace::default()).into()
|
ty.ptr_type(AddressSpace::default()).into()
|
||||||
},
|
},
|
||||||
x => x
|
x => x
|
||||||
@ -770,9 +772,19 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
for (i, ty) in byvals {
|
|
||||||
fun_val.add_attribute(AttributeLoc::Param((i as u32) + offset),
|
// The attribute ID used to mark arguments of a structure type.
|
||||||
ctx.ctx.create_type_attribute(Attribute::get_named_enum_kind_id("byval"), ty.as_any_type_enum()));
|
// Structure-Typed parameters of extern functions must **not** be marked as `byval`, as
|
||||||
|
// `byval` explicitly specifies that the argument is to be passed on the stack, which breaks
|
||||||
|
// on most ABIs where the first several arguments are expected to be passed in registers.
|
||||||
|
let passing_attr_id = Attribute::get_named_enum_kind_id(
|
||||||
|
if is_extern { "byref" } else { "byval" }
|
||||||
|
);
|
||||||
|
for (i, ty) in byrefs {
|
||||||
|
fun_val.add_attribute(
|
||||||
|
AttributeLoc::Param((i as u32) + offset),
|
||||||
|
ctx.ctx.create_type_attribute(passing_attr_id, ty.as_any_type_enum())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
fun_val
|
fun_val
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user