Commit 9e7293619f

Veikka Tuominen <git@vexu.eu>
2022-11-20 12:23:08
llvm: aarch64 C ABI: pass byref params as mutable pointers
Closes #13597
1 parent 08a00f0
Changed files (3)
src
codegen
test
src/codegen/llvm.zig
@@ -988,6 +988,25 @@ pub const Object = struct {
                         args.appendAssumeCapacity(load_inst);
                     }
                 },
+                .byref_mut => {
+                    const param_ty = fn_info.param_types[it.zig_index - 1];
+                    const param_llvm_ty = try dg.lowerType(param_ty);
+                    const param = llvm_func.getParam(llvm_arg_i);
+                    const alignment = param_ty.abiAlignment(target);
+
+                    dg.addArgAttr(llvm_func, llvm_arg_i, "noundef");
+                    llvm_arg_i += 1;
+
+                    try args.ensureUnusedCapacity(1);
+
+                    if (isByRef(param_ty)) {
+                        args.appendAssumeCapacity(param);
+                    } else {
+                        const load_inst = builder.buildLoad(param_llvm_ty, param, "");
+                        load_inst.setAlignment(alignment);
+                        args.appendAssumeCapacity(load_inst);
+                    }
+                },
                 .abi_sized_int => {
                     assert(!it.byval_attr);
                     const param_ty = fn_info.param_types[it.zig_index - 1];
@@ -2583,6 +2602,9 @@ pub const DeclGen = struct {
                     const alignment = param_ty.abiAlignment(target);
                     dg.addByRefParamAttrs(llvm_fn, it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty);
                 },
+                .byref_mut => {
+                    dg.addArgAttr(llvm_fn, it.llvm_index - 1, "noundef");
+                },
                 // No attributes needed for these.
                 .no_bits,
                 .abi_sized_int,
@@ -3101,7 +3123,7 @@ pub const DeclGen = struct {
                 const param_ty = fn_info.param_types[it.zig_index - 1];
                 try llvm_params.append(try dg.lowerType(param_ty));
             },
-            .byref => {
+            .byref, .byref_mut => {
                 const param_ty = fn_info.param_types[it.zig_index - 1];
                 const raw_llvm_ty = try dg.lowerType(param_ty);
                 try llvm_params.append(raw_llvm_ty.pointerType(0));
@@ -4726,6 +4748,27 @@ pub const FuncGen = struct {
                     try llvm_args.append(arg_ptr);
                 }
             },
+            .byref_mut => {
+                const arg = args[it.zig_index - 1];
+                const param_ty = self.air.typeOf(arg);
+                const llvm_arg = try self.resolveInst(arg);
+
+                const alignment = param_ty.abiAlignment(target);
+                const param_llvm_ty = try self.dg.lowerType(param_ty);
+                const arg_ptr = self.buildAlloca(param_llvm_ty, alignment);
+                if (isByRef(param_ty)) {
+                    const load_inst = self.builder.buildLoad(param_llvm_ty, llvm_arg, "");
+                    load_inst.setAlignment(alignment);
+
+                    const store_inst = self.builder.buildStore(load_inst, arg_ptr);
+                    store_inst.setAlignment(alignment);
+                    try llvm_args.append(arg_ptr);
+                } else {
+                    const store_inst = self.builder.buildStore(llvm_arg, arg_ptr);
+                    store_inst.setAlignment(alignment);
+                    try llvm_args.append(arg_ptr);
+                }
+            },
             .abi_sized_int => {
                 const arg = args[it.zig_index - 1];
                 const param_ty = self.air.typeOf(arg);
@@ -10384,6 +10427,7 @@ const ParamTypeIterator = struct {
         no_bits,
         byval,
         byref,
+        byref_mut,
         abi_sized_int,
         multiple_llvm_types,
         slice,
@@ -10547,7 +10591,7 @@ const ParamTypeIterator = struct {
                         it.zig_index += 1;
                         it.llvm_index += 1;
                         switch (aarch64_c_abi.classifyType(ty, it.target)) {
-                            .memory => return .byref,
+                            .memory => return .byref_mut,
                             .float_array => |len| return Lowering{ .float_array = len },
                             .byval => return .byval,
                             .integer => {
@@ -10578,9 +10622,7 @@ const ParamTypeIterator = struct {
                             return .as_u16;
                         }
                         switch (riscv_c_abi.classifyType(ty, it.target)) {
-                            .memory => {
-                                return .byref;
-                            },
+                            .memory => return .byref_mut,
                             .byval => return .byval,
                             .integer => return .abi_sized_int,
                             .double_integer => return Lowering{ .i64_array = 2 },
test/c_abi/cfuncs.c
@@ -833,3 +833,12 @@ struct PD zig_ret_PD();
 int c_assert_ret_PD(){
     return c_assert_PD(zig_ret_PD());
 }
+
+struct ByRef {
+    int val;
+    int arr[15];
+};
+struct ByRef c_modify_by_ref_param(struct ByRef in) {
+    in.val = 42;
+    return in;
+}
test/c_abi/main.zig
@@ -988,3 +988,16 @@ pub export fn zig_assert_PD(lv: PD) c_int {
     if (err != 0) std.debug.print("Received {}", .{lv});
     return err;
 }
+
+const ByRef = extern struct {
+    val: c_int,
+    arr: [15]c_int,
+};
+extern fn c_modify_by_ref_param(ByRef) ByRef;
+
+test "C function modifies by ref param" {
+    if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
+
+    const res = c_modify_by_ref_param(.{ .val = 1, .arr = undefined });
+    try expect(res.val == 42);
+}