LLVM: fix returning extern union with C callconv

This commit is contained in:
Andrew Kelley 2022-07-27 17:54:38 -07:00
parent 6a4df2778e
commit 3ba7098a17
2 changed files with 45 additions and 3 deletions

View File

@ -4491,17 +4491,29 @@ pub const FuncGen = struct {
}
return null;
}
const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info);
const ptr_abi_ty = abi_ret_ty.pointerType(0);
const operand = try self.resolveInst(un_op);
const target = self.dg.module.getTarget();
const alignment = ret_ty.abiAlignment(target);
if (isByRef(ret_ty)) {
// operand is a pointer however self.ret_ptr is null so that means
// we need to return a value.
const casted_ptr = self.builder.buildBitCast(operand, ptr_abi_ty, "");
const load_inst = self.builder.buildLoad(casted_ptr, "");
load_inst.setAlignment(alignment);
_ = self.builder.buildRet(load_inst);
return null;
}
const llvm_ret_ty = operand.typeOf();
if (abi_ret_ty == llvm_ret_ty) {
_ = self.builder.buildRet(operand);
return null;
}
const target = self.dg.module.getTarget();
const alignment = ret_ty.abiAlignment(target);
const ptr_abi_ty = abi_ret_ty.pointerType(0);
const rp = self.buildAlloca(llvm_ret_ty);
rp.setAlignment(alignment);
const store_inst = self.builder.buildStore(operand, rp);

View File

@ -1226,3 +1226,33 @@ test "extern union most-aligned field is smaller" {
var a: ?U = .{ .un = [_]u8{0} ** 110 };
try expect(a != null);
}
test "return an extern union from C calling convention" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
const namespace = struct {
const S = extern struct {
x: c_int,
};
const U = extern union {
l: c_long,
d: f64,
s: S,
};
fn bar(arg_u: U) callconv(.C) U {
var u = arg_u;
return u;
}
};
var u: namespace.U = namespace.U{
.l = @as(c_long, 42),
};
u = namespace.bar(namespace.U{
.d = 4.0,
});
try expect(u.d == 4.0);
}