Commit 3ba7098a17

Andrew Kelley <andrew@ziglang.org>
2022-07-28 02:54:38
LLVM: fix returning extern union with C callconv
1 parent 6a4df27
Changed files (2)
src
codegen
test
behavior
src/codegen/llvm.zig
@@ -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);
test/behavior/union.zig
@@ -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);
+}