authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-11-17 01:54:49+01:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-11-18 11:10:52+01:00
log891f1870320000205291940ba2e276c1fa043cd0
tree336c97a163394c7795e76794fd2b3811342d7076
parent73f863a6fb1e844ec312345b3160e3987fe6586d
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

cbe: fix big-endian unnatural integer bitcast

Integers with padding bits on big-endian targets cannot quite be bitcast with a trivial memcpy, because the padding bits (which are zext or sext) are the most-significant, so are at the *lowest* addresses. So to bitcast to something which doesn't have padding bits, we need to offset past the padding. The logic I've added here definitely doesn't handle all possibilities correctly; I think that would actually be quite complicated. However, it handles a common case, and so prevents the Zig compiler itself from being miscompiled on big-endian targets (hence fixing a bootstrapping problem on big-endian).

1 files changed, 26 insertions(+), 10 deletions(-)

src/codegen/c.zig+26-10
......@@ -5084,16 +5084,32 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal
50845084 } else operand;
50855085
50865086 const local = try f.allocLocal(null, dest_ty);
5087 try w.writeAll("memcpy(&");
5088 try f.writeCValue(w, local, .Other);
5089 try w.writeAll(", &");
5090 try f.writeCValue(w, operand_lval, .Other);
5091 try w.writeAll(", sizeof(");
5092 try f.renderType(
5093 w,
5094 if (dest_ty.abiSize(zcu) <= operand_ty.abiSize(zcu)) dest_ty else operand_ty,
5095 );
5096 try w.writeAll("));");
5087 // On big-endian targets, copying ABI integers with padding bits is awkward, because the padding bits are at the low bytes of the value.
5088 // We need to offset the source or destination pointer appropriately and copy the right number of bytes.
5089 if (target.cpu.arch.endian() == .big and dest_ty.isAbiInt(zcu) and !operand_ty.isAbiInt(zcu)) {
5090 // e.g. [10]u8 -> u80. We need to offset the destination so that we copy to the least significant bits of the integer.
5091 const offset = dest_ty.abiSize(zcu) - operand_ty.abiSize(zcu);
5092 try w.writeAll("memcpy((char *)&");
5093 try f.writeCValue(w, local, .Other);
5094 try w.print(" + {d}, &", .{offset});
5095 try f.writeCValue(w, operand_lval, .Other);
5096 try w.print(", {d});", .{operand_ty.abiSize(zcu)});
5097 } else if (target.cpu.arch.endian() == .big and operand_ty.isAbiInt(zcu) and !dest_ty.isAbiInt(zcu)) {
5098 // e.g. u80 -> [10]u8. We need to offset the source so that we copy from the least significant bits of the integer.
5099 const offset = operand_ty.abiSize(zcu) - dest_ty.abiSize(zcu);
5100 try w.writeAll("memcpy(&");
5101 try f.writeCValue(w, local, .Other);
5102 try w.writeAll(", (const char *)&");
5103 try f.writeCValue(w, operand_lval, .Other);
5104 try w.print(" + {d}, {d});", .{ offset, dest_ty.abiSize(zcu) });
5105 } else {
5106 try w.writeAll("memcpy(&");
5107 try f.writeCValue(w, local, .Other);
5108 try w.writeAll(", &");
5109 try f.writeCValue(w, operand_lval, .Other);
5110 try w.print(", {d});", .{@min(dest_ty.abiSize(zcu), operand_ty.abiSize(zcu))});
5111 }
5112
50975113 try f.object.newline();
50985114
50995115 // Ensure padding bits have the expected value.