authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-01-08 13:55:34-05:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-01-08 19:33:45-05:00
log3f95003d4c57650f9b4779f55c8d7368b137337c
tree2fbeaef5ca4774b3224d57e0b06e9607d8802248
parent5b5c60f433394c2b703fb8bfb8603395e66b0c5a

cbe: fix miscomps of x86_64 backend


3 files changed, 83 insertions(+), 19 deletions(-)

src/codegen/c.zig+67-19
...@@ -105,7 +105,7 @@ pub const CValue = union(enum) {...@@ -105,7 +105,7 @@ pub const CValue = union(enum) {
105};105};
106106
107const BlockData = struct {107const BlockData = struct {
108 block_id: usize,108 block_id: u32,
109 result: CValue,109 result: CValue,
110};110};
111111
...@@ -359,8 +359,8 @@ pub const Function = struct {...@@ -359,8 +359,8 @@ pub const Function = struct {
359 liveness: Liveness,359 liveness: Liveness,
360 value_map: CValueMap,360 value_map: CValueMap,
361 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty,361 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty,
362 next_arg_index: usize = 0,362 next_arg_index: u32 = 0,
363 next_block_index: usize = 0,363 next_block_index: u32 = 0,
364 object: Object,364 object: Object,
365 lazy_fns: LazyFnMap,365 lazy_fns: LazyFnMap,
366 func_index: InternPool.Index,366 func_index: InternPool.Index,
...@@ -663,6 +663,7 @@ pub const DeclGen = struct {...@@ -663,6 +663,7 @@ pub const DeclGen = struct {
663 mod: *Module,663 mod: *Module,
664 pass: Pass,664 pass: Pass,
665 is_naked_fn: bool,665 is_naked_fn: bool,
666 expected_block: ?u32,
666 /// This is a borrowed reference from `link.C`.667 /// This is a borrowed reference from `link.C`.
667 fwd_decl: std.ArrayList(u8),668 fwd_decl: std.ArrayList(u8),
668 error_msg: ?*Zcu.ErrorMsg,669 error_msg: ?*Zcu.ErrorMsg,
...@@ -1399,12 +1400,24 @@ pub const DeclGen = struct {...@@ -1399,12 +1400,24 @@ pub const DeclGen = struct {
1399 .repeated_elem => |elem| elem,1400 .repeated_elem => |elem| elem,
1400 };1401 };
14011402
1403 const field_int_info: std.builtin.Type.Int = if (field_ty.isAbiInt(zcu))
1404 field_ty.intInfo(zcu)
1405 else
1406 .{ .signedness = .unsigned, .bits = undefined };
1407 switch (field_int_info.signedness) {
1408 .signed => {
1409 try writer.writeByte('(');
1410 try dg.renderValue(writer, Value.fromInterned(field_val), .Other);
1411 try writer.writeAll(" & ");
1412 const field_uint_ty = try pt.intType(.unsigned, field_int_info.bits);
1413 try dg.renderValue(writer, try field_uint_ty.maxIntScalar(pt, field_uint_ty), .Other);
1414 try writer.writeByte(')');
1415 },
1416 .unsigned => try dg.renderValue(writer, Value.fromInterned(field_val), .Other),
1417 }
1402 if (bit_offset != 0) {1418 if (bit_offset != 0) {
1403 try dg.renderValue(writer, Value.fromInterned(field_val), .Other);
1404 try writer.writeAll(" << ");1419 try writer.writeAll(" << ");
1405 try dg.renderValue(writer, try pt.intValue(bit_offset_ty, bit_offset), .FunctionArgument);1420 try dg.renderValue(writer, try pt.intValue(bit_offset_ty, bit_offset), .FunctionArgument);
1406 } else {
1407 try dg.renderValue(writer, Value.fromInterned(field_val), .Other);
1408 }1421 }
14091422
1410 bit_offset += field_ty.bitSize(zcu);1423 bit_offset += field_ty.bitSize(zcu);
...@@ -2899,6 +2912,8 @@ pub fn genFunc(f: *Function) !void {...@@ -2899,6 +2912,8 @@ pub fn genFunc(f: *Function) !void {
2899 const main_body = f.air.getMainBody();2912 const main_body = f.air.getMainBody();
2900 try genBodyResolveState(f, undefined, &.{}, main_body, false);2913 try genBodyResolveState(f, undefined, &.{}, main_body, false);
2901 try o.indent_writer.insertNewline();2914 try o.indent_writer.insertNewline();
2915 if (o.dg.expected_block) |_|
2916 return f.fail("runtime code not allowed in naked function", .{});
29022917
2903 // Take advantage of the free_locals map to bucket locals per type. All2918 // Take advantage of the free_locals map to bucket locals per type. All
2904 // locals corresponding to AIR instructions should be in there due to2919 // locals corresponding to AIR instructions should be in there due to
...@@ -3189,6 +3204,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,...@@ -3189,6 +3204,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
3189 const air_datas = f.air.instructions.items(.data);3204 const air_datas = f.air.instructions.items(.data);
31903205
3191 for (body) |inst| {3206 for (body) |inst| {
3207 if (f.object.dg.expected_block) |_|
3208 return f.fail("runtime code not allowed in naked function", .{});
3192 if (f.liveness.isUnused(inst) and !f.air.mustLower(inst, ip))3209 if (f.liveness.isUnused(inst) and !f.air.mustLower(inst, ip))
3193 continue;3210 continue;
31943211
...@@ -4517,6 +4534,7 @@ fn airCall(...@@ -4517,6 +4534,7 @@ fn airCall(
4517) !CValue {4534) !CValue {
4518 const pt = f.object.dg.pt;4535 const pt = f.object.dg.pt;
4519 const zcu = pt.zcu;4536 const zcu = pt.zcu;
4537 const ip = &zcu.intern_pool;
4520 // Not even allowed to call panic in a naked function.4538 // Not even allowed to call panic in a naked function.
4521 if (f.object.dg.is_naked_fn) return .none;4539 if (f.object.dg.is_naked_fn) return .none;
45224540
...@@ -4562,11 +4580,12 @@ fn airCall(...@@ -4562,11 +4580,12 @@ fn airCall(
4562 }4580 }
45634581
4564 const callee_ty = f.typeOf(pl_op.operand);4582 const callee_ty = f.typeOf(pl_op.operand);
4565 const fn_info = zcu.typeToFunc(switch (callee_ty.zigTypeTag(zcu)) {4583 const callee_is_ptr = switch (callee_ty.zigTypeTag(zcu)) {
4566 .@"fn" => callee_ty,4584 .@"fn" => false,
4567 .pointer => callee_ty.childType(zcu),4585 .pointer => true,
4568 else => unreachable,4586 else => unreachable,
4569 }).?;4587 };
4588 const fn_info = zcu.typeToFunc(if (callee_is_ptr) callee_ty.childType(zcu) else callee_ty).?;
4570 const ret_ty = Type.fromInterned(fn_info.return_type);4589 const ret_ty = Type.fromInterned(fn_info.return_type);
4571 const ret_ctype: CType = if (ret_ty.isNoReturn(zcu))4590 const ret_ctype: CType = if (ret_ty.isNoReturn(zcu))
4572 CType.void4591 CType.void
...@@ -4598,20 +4617,29 @@ fn airCall(...@@ -4598,20 +4617,29 @@ fn airCall(
4598 callee: {4617 callee: {
4599 known: {4618 known: {
4600 const callee_val = (try f.air.value(pl_op.operand, pt)) orelse break :known;4619 const callee_val = (try f.air.value(pl_op.operand, pt)) orelse break :known;
4601 const fn_nav = switch (zcu.intern_pool.indexToKey(callee_val.toIntern())) {4620 const fn_nav, const need_cast = switch (ip.indexToKey(callee_val.toIntern())) {
4602 .@"extern" => |@"extern"| @"extern".owner_nav,4621 .@"extern" => |@"extern"| .{ @"extern".owner_nav, false },
4603 .func => |func| func.owner_nav,4622 .func => |func| .{ func.owner_nav, Type.fromInterned(func.ty).fnCallingConvention(zcu) != .naked and
4623 Type.fromInterned(func.uncoerced_ty).fnCallingConvention(zcu) == .naked },
4604 .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) {4624 .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) {
4605 .nav => |nav| nav,4625 .nav => |nav| .{ nav, Type.fromInterned(ptr.ty).childType(zcu).fnCallingConvention(zcu) != .naked and
4626 zcu.navValue(nav).typeOf(zcu).fnCallingConvention(zcu) == .naked },
4606 else => break :known,4627 else => break :known,
4607 } else break :known,4628 } else break :known,
4608 else => break :known,4629 else => break :known,
4609 };4630 };
4631 if (need_cast) {
4632 try writer.writeAll("((");
4633 try f.renderType(writer, if (callee_is_ptr) callee_ty else try pt.singleConstPtrType(callee_ty));
4634 try writer.writeByte(')');
4635 if (!callee_is_ptr) try writer.writeByte('&');
4636 }
4610 switch (modifier) {4637 switch (modifier) {
4611 .auto, .always_tail => try f.object.dg.renderNavName(writer, fn_nav),4638 .auto, .always_tail => try f.object.dg.renderNavName(writer, fn_nav),
4612 inline .never_tail, .never_inline => |m| try writer.writeAll(try f.getLazyFnName(@unionInit(LazyFnKey, @tagName(m), fn_nav))),4639 inline .never_tail, .never_inline => |m| try writer.writeAll(try f.getLazyFnName(@unionInit(LazyFnKey, @tagName(m), fn_nav))),
4613 else => unreachable,4640 else => unreachable,
4614 }4641 }
4642 if (need_cast) try writer.writeByte(')');
4615 break :callee;4643 break :callee;
4616 }4644 }
4617 switch (modifier) {4645 switch (modifier) {
...@@ -4712,7 +4740,7 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)...@@ -4712,7 +4740,7 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)
4712 const zcu = pt.zcu;4740 const zcu = pt.zcu;
4713 const liveness_block = f.liveness.getBlock(inst);4741 const liveness_block = f.liveness.getBlock(inst);
47144742
4715 const block_id: usize = f.next_block_index;4743 const block_id = f.next_block_index;
4716 f.next_block_index += 1;4744 f.next_block_index += 1;
4717 const writer = f.object.writer();4745 const writer = f.object.writer();
47184746
...@@ -4739,7 +4767,13 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)...@@ -4739,7 +4767,13 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)
4739 try f.object.indent_writer.insertNewline();4767 try f.object.indent_writer.insertNewline();
47404768
4741 // noreturn blocks have no `br` instructions reaching them, so we don't want a label4769 // noreturn blocks have no `br` instructions reaching them, so we don't want a label
4742 if (!f.typeOfIndex(inst).isNoReturn(zcu)) {4770 if (f.object.dg.is_naked_fn) {
4771 if (f.object.dg.expected_block) |expected_block| {
4772 if (block_id != expected_block)
4773 return f.fail("runtime code not allowed in naked function", .{});
4774 f.object.dg.expected_block = null;
4775 }
4776 } else if (!f.typeOfIndex(inst).isNoReturn(zcu)) {
4743 // label must be followed by an expression, include an empty one.4777 // label must be followed by an expression, include an empty one.
4744 try writer.print("zig_block_{d}:;\n", .{block_id});4778 try writer.print("zig_block_{d}:;\n", .{block_id});
4745 }4779 }
...@@ -4803,6 +4837,8 @@ fn lowerTry(...@@ -4803,6 +4837,8 @@ fn lowerTry(
48034837
4804 try genBodyResolveState(f, inst, liveness_condbr.else_deaths, body, false);4838 try genBodyResolveState(f, inst, liveness_condbr.else_deaths, body, false);
4805 try f.object.indent_writer.insertNewline();4839 try f.object.indent_writer.insertNewline();
4840 if (f.object.dg.expected_block) |_|
4841 return f.fail("runtime code not allowed in naked function", .{});
4806 }4842 }
48074843
4808 // Now we have the "then branch" (in terms of the liveness data); process any deaths.4844 // Now we have the "then branch" (in terms of the liveness data); process any deaths.
...@@ -4820,9 +4856,7 @@ fn lowerTry(...@@ -4820,9 +4856,7 @@ fn lowerTry(
48204856
4821 try reap(f, inst, &.{operand});4857 try reap(f, inst, &.{operand});
48224858
4823 if (f.liveness.isUnused(inst)) {4859 if (f.liveness.isUnused(inst)) return .none;
4824 return .none;
4825 }
48264860
4827 const local = try f.allocLocal(inst, inst_ty);4861 const local = try f.allocLocal(inst, inst_ty);
4828 const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete));4862 const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete));
...@@ -4842,6 +4876,12 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void {...@@ -4842,6 +4876,12 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void {
4842 const result = block.result;4876 const result = block.result;
4843 const writer = f.object.writer();4877 const writer = f.object.writer();
48444878
4879 if (f.object.dg.is_naked_fn) {
4880 if (result != .none) return f.fail("runtime code not allowed in naked function", .{});
4881 f.object.dg.expected_block = block.block_id;
4882 return;
4883 }
4884
4845 // If result is .none then the value of the block is unused.4885 // If result is .none then the value of the block is unused.
4846 if (result != .none) {4886 if (result != .none) {
4847 const operand_ty = f.typeOf(branch.operand);4887 const operand_ty = f.typeOf(branch.operand);
...@@ -5096,6 +5136,8 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !void {...@@ -5096,6 +5136,8 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !void {
50965136
5097 try genBodyResolveState(f, inst, liveness_condbr.then_deaths, then_body, false);5137 try genBodyResolveState(f, inst, liveness_condbr.then_deaths, then_body, false);
5098 try writer.writeByte('\n');5138 try writer.writeByte('\n');
5139 if (else_body.len > 0) if (f.object.dg.expected_block) |_|
5140 return f.fail("runtime code not allowed in naked function", .{});
50995141
5100 // We don't need to use `genBodyResolveState` for the else block, because this instruction is5142 // We don't need to use `genBodyResolveState` for the else block, because this instruction is
5101 // noreturn so must terminate a body, therefore we don't need to leave `value_map` or5143 // noreturn so must terminate a body, therefore we don't need to leave `value_map` or
...@@ -5193,6 +5235,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void...@@ -5193,6 +5235,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
5193 try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);5235 try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);
5194 f.object.indent_writer.popIndent();5236 f.object.indent_writer.popIndent();
5195 try writer.writeByte('}');5237 try writer.writeByte('}');
5238 if (f.object.dg.expected_block) |_|
5239 return f.fail("runtime code not allowed in naked function", .{});
51965240
5197 // The case body must be noreturn so we don't need to insert a break.5241 // The case body must be noreturn so we don't need to insert a break.
5198 }5242 }
...@@ -5236,6 +5280,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void...@@ -5236,6 +5280,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
5236 try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);5280 try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);
5237 f.object.indent_writer.popIndent();5281 f.object.indent_writer.popIndent();
5238 try writer.writeByte('}');5282 try writer.writeByte('}');
5283 if (f.object.dg.expected_block) |_|
5284 return f.fail("runtime code not allowed in naked function", .{});
5239 }5285 }
5240 }5286 }
5241 if (is_dispatch_loop) {5287 if (is_dispatch_loop) {
...@@ -5248,6 +5294,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void...@@ -5248,6 +5294,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
5248 try die(f, inst, death.toRef());5294 try die(f, inst, death.toRef());
5249 }5295 }
5250 try genBody(f, else_body);5296 try genBody(f, else_body);
5297 if (f.object.dg.expected_block) |_|
5298 return f.fail("runtime code not allowed in naked function", .{});
5251 } else {5299 } else {
5252 try writer.writeAll("zig_unreachable();");5300 try writer.writeAll("zig_unreachable();");
5253 }5301 }
src/link/C.zig+6
...@@ -218,6 +218,7 @@ pub fn updateFunc(...@@ -218,6 +218,7 @@ pub fn updateFunc(
218 .error_msg = null,218 .error_msg = null,
219 .pass = .{ .nav = func.owner_nav },219 .pass = .{ .nav = func.owner_nav },
220 .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked,220 .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked,
221 .expected_block = null,
221 .fwd_decl = fwd_decl.toManaged(gpa),222 .fwd_decl = fwd_decl.toManaged(gpa),
222 .ctype_pool = ctype_pool.*,223 .ctype_pool = ctype_pool.*,
223 .scratch = .{},224 .scratch = .{},
...@@ -272,6 +273,7 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) !void {...@@ -272,6 +273,7 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) !void {
272 .error_msg = null,273 .error_msg = null,
273 .pass = .{ .uav = uav },274 .pass = .{ .uav = uav },
274 .is_naked_fn = false,275 .is_naked_fn = false,
276 .expected_block = null,
275 .fwd_decl = fwd_decl.toManaged(gpa),277 .fwd_decl = fwd_decl.toManaged(gpa),
276 .ctype_pool = codegen.CType.Pool.empty,278 .ctype_pool = codegen.CType.Pool.empty,
277 .scratch = .{},279 .scratch = .{},
...@@ -347,6 +349,7 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !...@@ -347,6 +349,7 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !
347 .error_msg = null,349 .error_msg = null,
348 .pass = .{ .nav = nav_index },350 .pass = .{ .nav = nav_index },
349 .is_naked_fn = false,351 .is_naked_fn = false,
352 .expected_block = null,
350 .fwd_decl = fwd_decl.toManaged(gpa),353 .fwd_decl = fwd_decl.toManaged(gpa),
351 .ctype_pool = ctype_pool.*,354 .ctype_pool = ctype_pool.*,
352 .scratch = .{},355 .scratch = .{},
...@@ -675,6 +678,7 @@ fn flushErrDecls(self: *C, pt: Zcu.PerThread, ctype_pool: *codegen.CType.Pool) F...@@ -675,6 +678,7 @@ fn flushErrDecls(self: *C, pt: Zcu.PerThread, ctype_pool: *codegen.CType.Pool) F
675 .error_msg = null,678 .error_msg = null,
676 .pass = .flush,679 .pass = .flush,
677 .is_naked_fn = false,680 .is_naked_fn = false,
681 .expected_block = null,
678 .fwd_decl = fwd_decl.toManaged(gpa),682 .fwd_decl = fwd_decl.toManaged(gpa),
679 .ctype_pool = ctype_pool.*,683 .ctype_pool = ctype_pool.*,
680 .scratch = .{},684 .scratch = .{},
...@@ -722,6 +726,7 @@ fn flushLazyFn(...@@ -722,6 +726,7 @@ fn flushLazyFn(
722 .error_msg = null,726 .error_msg = null,
723 .pass = .flush,727 .pass = .flush,
724 .is_naked_fn = false,728 .is_naked_fn = false,
729 .expected_block = null,
725 .fwd_decl = fwd_decl.toManaged(gpa),730 .fwd_decl = fwd_decl.toManaged(gpa),
726 .ctype_pool = ctype_pool.*,731 .ctype_pool = ctype_pool.*,
727 .scratch = .{},732 .scratch = .{},
...@@ -868,6 +873,7 @@ pub fn updateExports(...@@ -868,6 +873,7 @@ pub fn updateExports(
868 .error_msg = null,873 .error_msg = null,
869 .pass = pass,874 .pass = pass,
870 .is_naked_fn = false,875 .is_naked_fn = false,
876 .expected_block = null,
871 .fwd_decl = fwd_decl.toManaged(gpa),877 .fwd_decl = fwd_decl.toManaged(gpa),
872 .ctype_pool = decl_block.ctype_pool,878 .ctype_pool = decl_block.ctype_pool,
873 .scratch = .{},879 .scratch = .{},
test/behavior/packed-struct.zig+10
...@@ -1317,3 +1317,13 @@ test "packed struct equality" {...@@ -1317,3 +1317,13 @@ test "packed struct equality" {
1317 try S.doTest(x, y);1317 try S.doTest(x, y);
1318 comptime try S.doTest(x, y);1318 comptime try S.doTest(x, y);
1319}1319}
1320
1321test "packed struct with signed field" {
1322 var s: packed struct {
1323 a: i2,
1324 b: u6,
1325 } = .{ .a = -1, .b = 42 };
1326 s = s;
1327 try expect(s.a == -1);
1328 try expect(s.b == 42);
1329}