Commit 44252f4d35

Andrew Kelley <andrew@ziglang.org>
2022-05-05 07:57:57
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.
1 parent 17fc44d
Changed files (2)
src
arch
x86_64
codegen
src/arch/x86_64/abi.zig
@@ -12,13 +12,10 @@ pub fn classifyWindows(ty: Type, target: Target) Class {
     // and the registers used for those arguments. Any argument that doesn't fit in 8
     // bytes, or isn't 1, 2, 4, or 8 bytes, must be passed by reference. A single argument
     // is never spread across multiple registers."
+    // "All floating point operations are done using the 16 XMM registers."
     // "Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed
     // as if they were integers of the same size."
-    switch (ty.abiSize(target)) {
-        1, 2, 4, 8 => {},
-        else => return .memory,
-    }
-    return switch (ty.zigTypeTag()) {
+    switch (ty.zigTypeTag()) {
         .Pointer,
         .Int,
         .Bool,
@@ -33,9 +30,13 @@ pub fn classifyWindows(ty: Type, target: Target) Class {
         .ErrorUnion,
         .AnyFrame,
         .Frame,
-        => .integer,
+        => switch (ty.abiSize(target)) {
+            0 => unreachable,
+            1, 2, 4, 8 => return .integer,
+            else => return .memory,
+        },
 
-        .Float, .Vector => .sse,
+        .Float, .Vector => return .sse,
 
         .Type,
         .ComptimeFloat,
@@ -47,7 +48,7 @@ pub fn classifyWindows(ty: Type, target: Target) Class {
         .Opaque,
         .EnumLiteral,
         => unreachable,
-    };
+    }
 }
 
 /// There are a maximum of 8 possible return slots. Returned values are in
src/codegen/llvm.zig
@@ -644,7 +644,17 @@ pub const Object = struct {
             if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
 
             const llvm_arg_i = @intCast(c_uint, args.items.len) + param_offset;
-            try args.append(llvm_func.getParam(llvm_arg_i));
+            const param = llvm_func.getParam(llvm_arg_i);
+            // It is possible for the calling convention to make the argument's by-reference nature
+            // disagree with our canonical value for it, in which case we must dereference here.
+            const need_deref = !param_ty.isPtrAtRuntime() and !isByRef(param_ty) and
+                (param.typeOf().getTypeKind() == .Pointer);
+            const loaded_param = if (!need_deref) param else l: {
+                const load_inst = builder.buildLoad(param, "");
+                load_inst.setAlignment(param_ty.abiAlignment(target));
+                break :l load_inst;
+            };
+            try args.append(loaded_param);
         }
 
         var di_file: ?*llvm.DIFile = null;
@@ -3743,6 +3753,19 @@ pub const FuncGen = struct {
                 arg_ptr.setAlignment(alignment);
                 const store_inst = self.builder.buildStore(llvm_arg, arg_ptr);
                 store_inst.setAlignment(alignment);
+
+                if (abi_llvm_ty.getTypeKind() == .Pointer) {
+                    // In this case, the calling convention wants a pointer, but
+                    // we have a value.
+                    if (arg_ptr.typeOf() == abi_llvm_ty) {
+                        try llvm_args.append(arg_ptr);
+                        continue;
+                    }
+                    const casted_ptr = self.builder.buildBitCast(arg_ptr, abi_llvm_ty, "");
+                    try llvm_args.append(casted_ptr);
+                    continue;
+                }
+
                 break :p self.builder.buildBitCast(arg_ptr, ptr_abi_ty, "");
             };
 
@@ -7931,6 +7954,8 @@ fn llvmFieldIndex(
 }
 
 fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool {
+    if (!fn_info.return_type.hasRuntimeBitsIgnoreComptime()) return false;
+
     switch (fn_info.cc) {
         .Unspecified, .Inline => return isByRef(fn_info.return_type),
         .C => switch (target.cpu.arch) {