authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-04 22:57:57-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-04 22:57:57-07:00
log44252f4d352d53afd86d678c0b0a40b3f681c7eb
tree91dddc4c240b1aa429ea6d80835640b4a85e2c23
parent17fc44dd1287163c25832c40f7e81cd5532e52bd

LLVM: fix C ABI for windows

* sret logic needed a check for hasRuntimeBits() * lower f128 on windows targets with the "sse" class rather than "memory". For reference, clang emits a compile error when __float128 is used with the MSVC ABI, saying that this type is not supported. The docs for the x64 calling convention have both of these sentences: - "Any argument that doesn't fit in 8 bytes, or isn't 1, 2, 4, or 8 bytes, must be passed by reference." - "All floating point operations are done using the 16 XMM registers." * For i128, however, it is clear that the Windows calling convention wants such an object to be passed by reference. I fixed the LLVM lowering for function parameters to make this work.

2 files changed, 35 insertions(+), 9 deletions(-)

src/arch/x86_64/abi.zig+9-8
...@@ -12,13 +12,10 @@ pub fn classifyWindows(ty: Type, target: Target) Class {...@@ -12,13 +12,10 @@ pub fn classifyWindows(ty: Type, target: Target) Class {
12 // and the registers used for those arguments. Any argument that doesn't fit in 812 // and the registers used for those arguments. Any argument that doesn't fit in 8
13 // bytes, or isn't 1, 2, 4, or 8 bytes, must be passed by reference. A single argument13 // bytes, or isn't 1, 2, 4, or 8 bytes, must be passed by reference. A single argument
14 // is never spread across multiple registers."14 // is never spread across multiple registers."
15 // "All floating point operations are done using the 16 XMM registers."
15 // "Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed16 // "Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed
16 // as if they were integers of the same size."17 // as if they were integers of the same size."
17 switch (ty.abiSize(target)) {18 switch (ty.zigTypeTag()) {
18 1, 2, 4, 8 => {},
19 else => return .memory,
20 }
21 return switch (ty.zigTypeTag()) {
22 .Pointer,19 .Pointer,
23 .Int,20 .Int,
24 .Bool,21 .Bool,
...@@ -33,9 +30,13 @@ pub fn classifyWindows(ty: Type, target: Target) Class {...@@ -33,9 +30,13 @@ pub fn classifyWindows(ty: Type, target: Target) Class {
33 .ErrorUnion,30 .ErrorUnion,
34 .AnyFrame,31 .AnyFrame,
35 .Frame,32 .Frame,
36 => .integer,33 => switch (ty.abiSize(target)) {
34 0 => unreachable,
35 1, 2, 4, 8 => return .integer,
36 else => return .memory,
37 },
3738
38 .Float, .Vector => .sse,39 .Float, .Vector => return .sse,
3940
40 .Type,41 .Type,
41 .ComptimeFloat,42 .ComptimeFloat,
...@@ -47,7 +48,7 @@ pub fn classifyWindows(ty: Type, target: Target) Class {...@@ -47,7 +48,7 @@ pub fn classifyWindows(ty: Type, target: Target) Class {
47 .Opaque,48 .Opaque,
48 .EnumLiteral,49 .EnumLiteral,
49 => unreachable,50 => unreachable,
50 };51 }
51}52}
5253
53/// There are a maximum of 8 possible return slots. Returned values are in54/// There are a maximum of 8 possible return slots. Returned values are in
src/codegen/llvm.zig+26-1
...@@ -644,7 +644,17 @@ pub const Object = struct {...@@ -644,7 +644,17 @@ pub const Object = struct {
644 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;644 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
645645
646 const llvm_arg_i = @intCast(c_uint, args.items.len) + param_offset;646 const llvm_arg_i = @intCast(c_uint, args.items.len) + param_offset;
647 try args.append(llvm_func.getParam(llvm_arg_i));647 const param = llvm_func.getParam(llvm_arg_i);
648 // It is possible for the calling convention to make the argument's by-reference nature
649 // disagree with our canonical value for it, in which case we must dereference here.
650 const need_deref = !param_ty.isPtrAtRuntime() and !isByRef(param_ty) and
651 (param.typeOf().getTypeKind() == .Pointer);
652 const loaded_param = if (!need_deref) param else l: {
653 const load_inst = builder.buildLoad(param, "");
654 load_inst.setAlignment(param_ty.abiAlignment(target));
655 break :l load_inst;
656 };
657 try args.append(loaded_param);
648 }658 }
649659
650 var di_file: ?*llvm.DIFile = null;660 var di_file: ?*llvm.DIFile = null;
...@@ -3743,6 +3753,19 @@ pub const FuncGen = struct {...@@ -3743,6 +3753,19 @@ pub const FuncGen = struct {
3743 arg_ptr.setAlignment(alignment);3753 arg_ptr.setAlignment(alignment);
3744 const store_inst = self.builder.buildStore(llvm_arg, arg_ptr);3754 const store_inst = self.builder.buildStore(llvm_arg, arg_ptr);
3745 store_inst.setAlignment(alignment);3755 store_inst.setAlignment(alignment);
3756
3757 if (abi_llvm_ty.getTypeKind() == .Pointer) {
3758 // In this case, the calling convention wants a pointer, but
3759 // we have a value.
3760 if (arg_ptr.typeOf() == abi_llvm_ty) {
3761 try llvm_args.append(arg_ptr);
3762 continue;
3763 }
3764 const casted_ptr = self.builder.buildBitCast(arg_ptr, abi_llvm_ty, "");
3765 try llvm_args.append(casted_ptr);
3766 continue;
3767 }
3768
3746 break :p self.builder.buildBitCast(arg_ptr, ptr_abi_ty, "");3769 break :p self.builder.buildBitCast(arg_ptr, ptr_abi_ty, "");
3747 };3770 };
37483771
...@@ -7931,6 +7954,8 @@ fn llvmFieldIndex(...@@ -7931,6 +7954,8 @@ fn llvmFieldIndex(
7931}7954}
79327955
7933fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool {7956fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool {
7957 if (!fn_info.return_type.hasRuntimeBitsIgnoreComptime()) return false;
7958
7934 switch (fn_info.cc) {7959 switch (fn_info.cc) {
7935 .Unspecified, .Inline => return isByRef(fn_info.return_type),7960 .Unspecified, .Inline => return isByRef(fn_info.return_type),
7936 .C => switch (target.cpu.arch) {7961 .C => switch (target.cpu.arch) {