Commit 36df2a83fc

Andrew Kelley <andrew@ziglang.org>
2023-04-27 02:53:25
C backend: fix memset for structs and arrays
1 parent 9295355
Changed files (1)
src
codegen
src/codegen/c.zig
@@ -4419,51 +4419,94 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
     const dest_ty = f.air.typeOfIndex(inst);
 
     const operand = try f.resolveInst(ty_op.operand);
-    try reap(f, inst, &.{ty_op.operand});
     const operand_ty = f.air.typeOf(ty_op.operand);
-    const target = f.object.dg.module.getTarget();
-    const writer = f.object.writer();
 
-    const local = try f.allocLocal(inst, dest_ty);
+    const bitcasted = try bitcast(f, dest_ty, operand, operand_ty);
+    try reap(f, inst, &.{ty_op.operand});
+    return bitcasted.move(f, inst, dest_ty);
+}
+
+const LocalResult = struct {
+    c_value: CValue,
+    need_free: bool,
+
+    fn move(lr: LocalResult, f: *Function, inst: Air.Inst.Index, dest_ty: Type) !CValue {
+        if (lr.need_free) {
+            // Move the freshly allocated local to be owned by this instruction,
+            // by returning it here instead of freeing it.
+            return lr.c_value;
+        }
+
+        const local = try f.allocLocal(inst, dest_ty);
+        try lr.free(f);
+        const writer = f.object.writer();
+        try f.writeCValue(writer, local, .Other);
+        if (dest_ty.isAbiInt()) {
+            try writer.writeAll(" = ");
+        } else {
+            try writer.writeAll(" = (");
+            try f.renderType(writer, dest_ty);
+            try writer.writeByte(')');
+        }
+        try f.writeCValue(writer, lr.c_value, .Initializer);
+        try writer.writeAll(";\n");
+        return local;
+    }
+
+    fn free(lr: LocalResult, f: *Function) !void {
+        if (lr.need_free) {
+            try freeLocal(f, 0, lr.c_value.new_local, 0);
+        }
+    }
+};
 
-    // If the assignment looks like 'x = x', we don't need it
-    const can_elide = operand == .local and operand.local == local.new_local;
+fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !LocalResult {
+    const target = f.object.dg.module.getTarget();
+    const writer = f.object.writer();
 
     if (operand_ty.isAbiInt() and dest_ty.isAbiInt()) {
-        if (can_elide) return local;
         const src_info = dest_ty.intInfo(target);
         const dest_info = operand_ty.intInfo(target);
         if (src_info.signedness == dest_info.signedness and
             src_info.bits == dest_info.bits)
         {
-            try f.writeCValue(writer, local, .Other);
-            try writer.writeAll(" = ");
-            try f.writeCValue(writer, operand, .Initializer);
-            try writer.writeAll(";\n");
-            return local;
+            return .{
+                .c_value = operand,
+                .need_free = false,
+            };
         }
     }
 
     if (dest_ty.isPtrAtRuntime() and operand_ty.isPtrAtRuntime()) {
-        if (can_elide) return local;
+        const local = try f.allocLocal(0, dest_ty);
         try f.writeCValue(writer, local, .Other);
         try writer.writeAll(" = (");
         try f.renderType(writer, dest_ty);
         try writer.writeByte(')');
         try f.writeCValue(writer, operand, .Other);
         try writer.writeAll(";\n");
-        return local;
+        return .{
+            .c_value = local,
+            .need_free = true,
+        };
     }
 
     const operand_lval = if (operand == .constant) blk: {
-        const operand_local = try f.allocLocal(inst, operand_ty);
+        const operand_local = try f.allocLocal(0, operand_ty);
         try f.writeCValue(writer, operand_local, .Other);
-        try writer.writeAll(" = ");
+        if (operand_ty.isAbiInt()) {
+            try writer.writeAll(" = ");
+        } else {
+            try writer.writeAll(" = (");
+            try f.renderType(writer, operand_ty);
+            try writer.writeByte(')');
+        }
         try f.writeCValue(writer, operand, .Initializer);
         try writer.writeAll(";\n");
         break :blk operand_local;
     } else operand;
 
+    const local = try f.allocLocal(0, dest_ty);
     try writer.writeAll("memcpy(&");
     try f.writeCValue(writer, local, .Other);
     try writer.writeAll(", &");
@@ -4528,10 +4571,13 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
     }
 
     if (operand == .constant) {
-        try freeLocal(f, inst, operand_lval.new_local, 0);
+        try freeLocal(f, 0, operand_lval.new_local, 0);
     }
 
-    return local;
+    return .{
+        .c_value = local,
+        .need_free = true,
+    };
 }
 
 fn airTrap(writer: anytype) !CValue {
@@ -6288,15 +6334,27 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
         }
         try writer.writeAll("; ++");
         try f.writeCValue(writer, index, .Other);
-        try writer.writeAll(") ((");
-        try f.renderType(writer, elem_ptr_ty);
-        try writer.writeByte(')');
-        try writeSliceOrPtr(f, writer, dest_slice, dest_ty);
-        try writer.writeAll(")[");
-        try f.writeCValue(writer, index, .Other);
-        try writer.writeAll("] = ");
-        try f.writeCValue(writer, value, .FunctionArgument);
-        try writer.writeAll(";\n");
+        try writer.writeAll(") ");
+        if (lowersToArray(elem_ty, target)) {
+            // Arrays are not assignable, so we use memcpy here.
+            try writer.writeAll("memcpy(");
+            try writeSliceOrPtr(f, writer, dest_slice, dest_ty);
+            try writer.writeAll("[");
+            try f.writeCValue(writer, index, .Other);
+            try writer.writeAll("], ");
+            try f.writeCValue(writer, value, .FunctionArgument);
+            try writer.print(", {d});\n", .{elem_abi_size});
+        } else {
+            try writer.writeAll("((");
+            try f.renderType(writer, elem_ptr_ty);
+            try writer.writeByte(')');
+            try writeSliceOrPtr(f, writer, dest_slice, dest_ty);
+            try writer.writeAll(")[");
+            try f.writeCValue(writer, index, .Other);
+            try writer.writeAll("] = ");
+            try f.writeCValue(writer, value, .FunctionArgument);
+            try writer.writeAll(";\n");
+        }
 
         try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
         try freeLocal(f, inst, index.new_local, 0);
@@ -6304,12 +6362,14 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
         return .none;
     }
 
+    const bitcasted = try bitcast(f, Type.u8, value, elem_ty);
+
     try writer.writeAll("memset(");
     switch (dest_ty.ptrSize()) {
         .Slice => {
             try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" });
             try writer.writeAll(", ");
-            try f.writeCValue(writer, value, .FunctionArgument);
+            try f.writeCValue(writer, bitcasted.c_value, .FunctionArgument);
             try writer.writeAll(", ");
             try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" });
             try writer.writeAll(");\n");
@@ -6320,11 +6380,12 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
 
             try f.writeCValue(writer, dest_slice, .FunctionArgument);
             try writer.writeAll(", ");
-            try f.writeCValue(writer, value, .FunctionArgument);
+            try f.writeCValue(writer, bitcasted.c_value, .FunctionArgument);
             try writer.print(", {d});\n", .{len});
         },
         .Many, .C => unreachable,
     }
+    try bitcasted.free(f);
     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
     return .none;
 }