authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-01-21 20:51:56-05:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-01-22 03:44:13-05:00
logd4b6a53327352dc239644dab0ec2abbe3a1b1be9
tree80135ba47ac387e8b1c51fc6b1b8cdfb42227267
parent9b7b240c8e4757bf0119db70da30722aa97fd6ae

x86_64: implement error return traces


6 files changed, 128 insertions(+), 45 deletions(-)

src/arch/x86_64/CodeGen.zig+123-40
......@@ -33,6 +33,8 @@ const FrameIndex = bits.FrameIndex;
3333
3434const InnerError = codegen.CodeGenError || error{OutOfRegisters};
3535
36const err_ret_trace_index: Air.Inst.Index = @enumFromInt(std.math.maxInt(u32));
37
3638gpa: Allocator,
3739pt: Zcu.PerThread,
3840air: Air,
......@@ -55,6 +57,7 @@ va_info: union {
5557 win64: struct {},
5658},
5759ret_mcv: InstTracking,
60err_ret_trace_reg: Register,
5861fn_type: Type,
5962src_loc: Zcu.LazySrcLoc,
6063
......@@ -626,6 +629,7 @@ const InstTracking = struct {
626629 switch (self.long) {
627630 .none => self.long = try cg.allocRegOrMem(inst, false),
628631 .load_frame => {},
632 .lea_frame => return,
629633 .reserved_frame => |index| self.long = .{ .load_frame = .{ .index = index } },
630634 else => unreachable,
631635 }
......@@ -887,6 +891,7 @@ pub fn generate(
887891 .args = undefined, // populated after `resolveCallingConventionValues`
888892 .va_info = undefined, // populated after `resolveCallingConventionValues`
889893 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
894 .err_ret_trace_reg = undefined, // populated after `resolveCallingConventionValues`
890895 .fn_type = fn_type,
891896 .src_loc = src_loc,
892897 .end_di_line = func.rbrace_line,
......@@ -935,6 +940,7 @@ pub fn generate(
935940
936941 function.args = call_info.args;
937942 function.ret_mcv = call_info.return_value;
943 function.err_ret_trace_reg = call_info.err_ret_trace_reg;
938944 function.frame_allocs.set(@intFromEnum(FrameIndex.ret_addr), .init(.{
939945 .size = Type.usize.abiSize(zcu),
940946 .alignment = Type.usize.abiAlignment(zcu).min(call_info.stack_align),
......@@ -962,6 +968,14 @@ pub fn generate(
962968 } },
963969 .x86_64_win => .{ .win64 = .{} },
964970 };
971 if (call_info.err_ret_trace_reg != .none) {
972 function.register_manager.getRegAssumeFree(call_info.err_ret_trace_reg, err_ret_trace_index);
973 try function.inst_tracking.putNoClobber(
974 gpa,
975 err_ret_trace_index,
976 .init(.{ .register = call_info.err_ret_trace_reg }),
977 );
978 }
965979
966980 function.gen() catch |err| switch (err) {
967981 error.CodegenFail => return error.CodegenFail,
......@@ -1042,6 +1056,7 @@ pub fn generateLazy(
10421056 .args = undefined,
10431057 .va_info = undefined,
10441058 .ret_mcv = undefined,
1059 .err_ret_trace_reg = undefined,
10451060 .fn_type = undefined,
10461061 .src_loc = src_loc,
10471062 .end_di_line = undefined, // no debug info yet
......@@ -2503,9 +2518,6 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
25032518 .optional_payload => try cg.airOptionalPayload(inst),
25042519 .unwrap_errunion_err => try cg.airUnwrapErrUnionErr(inst),
25052520 .unwrap_errunion_payload => try cg.airUnwrapErrUnionPayload(inst),
2506 .err_return_trace => try cg.airErrReturnTrace(inst),
2507 .set_err_return_trace => try cg.airSetErrReturnTrace(inst),
2508 .save_err_return_trace_index=> try cg.airSaveErrReturnTraceIndex(inst),
25092521
25102522 .wrap_optional => try cg.airWrapOptional(inst),
25112523 .wrap_errunion_payload => try cg.airWrapErrUnionPayload(inst),
......@@ -11236,12 +11248,46 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
1123611248 .wasm_memory_size => unreachable,
1123711249 .wasm_memory_grow => unreachable,
1123811250
11251 .err_return_trace => {
11252 const ert: Temp = .{ .index = err_ret_trace_index };
11253 try ert.moveTo(inst, cg);
11254 },
11255 .set_err_return_trace => {
11256 const un_op = air_datas[@intFromEnum(inst)].un_op;
11257 var ops = try cg.tempsFromOperands(inst, .{un_op});
11258 switch (ops[0].unwrap(cg)) {
11259 .ref => {
11260 const result = try cg.allocRegOrMem(err_ret_trace_index, true);
11261 try cg.genCopy(.usize, result, ops[0].tracking(cg).short, .{});
11262 tracking_log.debug("{} => {} (birth)", .{ err_ret_trace_index, result });
11263 cg.inst_tracking.putAssumeCapacityNoClobber(err_ret_trace_index, .init(result));
11264 },
11265 .temp => |temp_index| {
11266 const temp_tracking = temp_index.tracking(cg);
11267 tracking_log.debug("{} => {} (birth)", .{ err_ret_trace_index, temp_tracking.short });
11268 cg.inst_tracking.putAssumeCapacityNoClobber(err_ret_trace_index, temp_tracking.*);
11269 assert(cg.reuseTemp(err_ret_trace_index, temp_index.toIndex(), temp_tracking));
11270 },
11271 .err_ret_trace => unreachable,
11272 }
11273 },
11274
1123911275 .addrspace_cast => {
1124011276 const ty_op = air_datas[@intFromEnum(inst)].ty_op;
1124111277 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand});
1124211278 try ops[0].moveTo(inst, cg);
1124311279 },
1124411280
11281 .save_err_return_trace_index => {
11282 const ty_pl = air_datas[@intFromEnum(inst)].ty_pl;
11283 const agg_ty = ty_pl.ty.toType();
11284 assert(agg_ty.containerLayout(zcu) != .@"packed");
11285 var ert: Temp = .{ .index = err_ret_trace_index };
11286 var res = try ert.load(.usize, .{ .disp = @intCast(agg_ty.structFieldOffset(ty_pl.payload, zcu)) }, cg);
11287 try ert.die(cg);
11288 try res.moveTo(inst, cg);
11289 },
11290
1124511291 .vector_store_elem => return cg.fail("TODO implement vector_store_elem", .{}),
1124611292
1124711293 .c_va_arg => try cg.airVaArg(inst),
......@@ -11697,7 +11743,7 @@ fn restoreState(self: *CodeGen, state: State, deaths: []const Air.Inst.Index, co
1169711743 const target_maybe_inst = if (state.free_registers.isSet(reg_index)) null else target_slot;
1169811744 if (std.debug.runtime_safety) if (target_maybe_inst) |target_inst|
1169911745 assert(self.inst_tracking.getIndex(target_inst).? < state.inst_tracking_len);
11700 if (opts.emit_instructions) {
11746 if (opts.emit_instructions and current_maybe_inst != target_maybe_inst) {
1170111747 if (current_maybe_inst) |current_inst|
1170211748 try self.inst_tracking.getPtr(current_inst).?.spill(self, current_inst);
1170311749 if (target_maybe_inst) |target_inst|
......@@ -11709,7 +11755,7 @@ fn restoreState(self: *CodeGen, state: State, deaths: []const Air.Inst.Index, co
1170911755 self.register_manager.freeRegIndex(reg_index);
1171011756 }
1171111757 if (target_maybe_inst) |target_inst| {
11712 self.register_manager.getRegIndexAssumeFree(reg_index, target_maybe_inst);
11758 self.register_manager.getRegIndexAssumeFree(reg_index, target_inst);
1171311759 self.inst_tracking.getPtr(target_inst).?.trackMaterialize(target_inst, reg_tracking);
1171411760 }
1171511761 } else if (target_maybe_inst) |_|
......@@ -11750,9 +11796,10 @@ pub fn spillEflagsIfOccupied(self: *CodeGen) !void {
1175011796 }
1175111797}
1175211798
11753pub fn spillCallerPreservedRegs(self: *CodeGen, cc: std.builtin.CallingConvention.Tag) !void {
11799pub fn spillCallerPreservedRegs(self: *CodeGen, cc: std.builtin.CallingConvention.Tag, ignore_reg: Register) !void {
1175411800 switch (cc) {
11755 inline .auto, .x86_64_sysv, .x86_64_win => |tag| try self.spillRegisters(abi.getCallerPreservedRegs(tag)),
11801 inline .auto, .x86_64_sysv, .x86_64_win => |tag| inline for (comptime abi.getCallerPreservedRegs(tag)) |reg|
11802 if (reg != ignore_reg) try self.register_manager.getKnownReg(reg, null),
1175611803 else => unreachable,
1175711804 }
1175811805}
......@@ -14406,22 +14453,6 @@ fn genUnwrapErrUnionPayloadPtrMir(
1440614453 return result;
1440714454}
1440814455
14409fn airErrReturnTrace(self: *CodeGen, inst: Air.Inst.Index) !void {
14410 _ = inst;
14411 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch});
14412 //return self.finishAir(inst, result, .{ .none, .none, .none });
14413}
14414
14415fn airSetErrReturnTrace(self: *CodeGen, inst: Air.Inst.Index) !void {
14416 _ = inst;
14417 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
14418}
14419
14420fn airSaveErrReturnTraceIndex(self: *CodeGen, inst: Air.Inst.Index) !void {
14421 _ = inst;
14422 return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch});
14423}
14424
1442514456fn airWrapOptional(self: *CodeGen, inst: Air.Inst.Index) !void {
1442614457 const pt = self.pt;
1442714458 const zcu = pt.zcu;
......@@ -21188,7 +21219,7 @@ fn genCall(self: *CodeGen, info: union(enum) {
2118821219 }
2118921220
2119021221 try self.spillEflagsIfOccupied();
21191 try self.spillCallerPreservedRegs(fn_info.cc);
21222 try self.spillCallerPreservedRegs(fn_info.cc, call_info.err_ret_trace_reg);
2119221223
2119321224 // set stack arguments first because this can clobber registers
2119421225 // also clobber spill arguments as we go
......@@ -21273,6 +21304,24 @@ fn genCall(self: *CodeGen, info: union(enum) {
2127321304 else => unreachable,
2127421305 };
2127521306
21307 if (call_info.err_ret_trace_reg != .none) {
21308 if (self.inst_tracking.getPtr(err_ret_trace_index)) |err_ret_trace| {
21309 if (switch (err_ret_trace.short) {
21310 .register => |reg| call_info.err_ret_trace_reg != reg,
21311 else => true,
21312 }) {
21313 try self.register_manager.getReg(call_info.err_ret_trace_reg, err_ret_trace_index);
21314 try reg_locks.append(self.register_manager.lockReg(call_info.err_ret_trace_reg));
21315
21316 try self.genSetReg(call_info.err_ret_trace_reg, .usize, err_ret_trace.short, .{});
21317 err_ret_trace.trackMaterialize(err_ret_trace_index, .{
21318 .long = err_ret_trace.long,
21319 .short = .{ .register = call_info.err_ret_trace_reg },
21320 });
21321 }
21322 }
21323 }
21324
2127621325 // now we are free to set register arguments
2127721326 switch (call_info.return_value.long) {
2127821327 .none, .unreach => {},
......@@ -21447,6 +21496,17 @@ fn airRet(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void {
2144721496 else => unreachable,
2144821497 }
2144921498 self.ret_mcv.liveOut(self, inst);
21499
21500 if (self.err_ret_trace_reg != .none) {
21501 if (self.inst_tracking.getPtr(err_ret_trace_index)) |err_ret_trace| {
21502 if (switch (err_ret_trace.short) {
21503 .register => |reg| self.err_ret_trace_reg != reg,
21504 else => true,
21505 }) try self.genSetReg(self.err_ret_trace_reg, .usize, err_ret_trace.short, .{});
21506 err_ret_trace.liveOut(self, err_ret_trace_index);
21507 }
21508 }
21509
2145021510 try self.finishAir(inst, .unreach, .{ un_op, .none, .none });
2145121511
2145221512 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
......@@ -21467,6 +21527,17 @@ fn airRetLoad(self: *CodeGen, inst: Air.Inst.Index) !void {
2146721527 else => unreachable,
2146821528 }
2146921529 self.ret_mcv.liveOut(self, inst);
21530
21531 if (self.err_ret_trace_reg != .none) {
21532 if (self.inst_tracking.getPtr(err_ret_trace_index)) |err_ret_trace| {
21533 if (switch (err_ret_trace.short) {
21534 .register => |reg| self.err_ret_trace_reg != reg,
21535 else => true,
21536 }) try self.genSetReg(self.err_ret_trace_reg, .usize, err_ret_trace.short, .{});
21537 err_ret_trace.liveOut(self, err_ret_trace_index);
21538 }
21539 }
21540
2147021541 try self.finishAir(inst, .unreach, .{ un_op, .none, .none });
2147121542
2147221543 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
......@@ -26098,8 +26169,13 @@ fn airTagName(self: *CodeGen, inst: Air.Inst.Index) !void {
2609826169 stack_frame_align.* = stack_frame_align.max(needed_call_frame.abi_align);
2609926170 }
2610026171
26172 const err_ret_trace_reg = if (zcu.comp.config.any_error_tracing) err_ret_trace_reg: {
26173 const param_gpr = abi.getCAbiIntParamRegs(.auto);
26174 break :err_ret_trace_reg param_gpr[param_gpr.len - 1];
26175 } else .none;
26176
2610126177 try self.spillEflagsIfOccupied();
26102 try self.spillCallerPreservedRegs(.auto);
26178 try self.spillCallerPreservedRegs(.auto, err_ret_trace_reg);
2610326179
2610426180 const param_regs = abi.getCAbiIntParamRegs(.auto);
2610526181
......@@ -28564,6 +28640,7 @@ const CallMCValues = struct {
2856428640 stack_align: InternPool.Alignment,
2856528641 gp_count: u32,
2856628642 fp_count: u32,
28643 err_ret_trace_reg: Register,
2856728644
2856828645 fn deinit(self: *CallMCValues, func: *CodeGen) void {
2856928646 func.gpa.free(self.args);
......@@ -28598,6 +28675,7 @@ fn resolveCallingConventionValues(
2859828675 .stack_align = undefined,
2859928676 .gp_count = 0,
2860028677 .fp_count = 0,
28678 .err_ret_trace_reg = .none,
2860128679 };
2860228680 errdefer self.gpa.free(result.args);
2860328681
......@@ -28842,6 +28920,11 @@ fn resolveCallingConventionValues(
2884228920 var param_x87 = abi.getCAbiX87ParamRegs(cc);
2884328921 var param_sse = abi.getCAbiSseParamRegs(cc, self.target);
2884428922
28923 if (zcu.comp.config.any_error_tracing) {
28924 result.err_ret_trace_reg = param_gpr[param_gpr.len - 1];
28925 param_gpr = param_gpr[0 .. param_gpr.len - 1];
28926 }
28927
2884528928 // Return values
2884628929 result.return_value = if (ret_ty.isNoReturn(zcu))
2884728930 .init(.unreach)
......@@ -29159,16 +29242,8 @@ fn typeOf(self: *CodeGen, inst: Air.Inst.Ref) Type {
2915929242}
2916029243
2916129244fn typeOfIndex(self: *CodeGen, inst: Air.Inst.Index) Type {
29162 const pt = self.pt;
29163 const zcu = pt.zcu;
2916429245 const temp: Temp = .{ .index = inst };
29165 return switch (temp.unwrap(self)) {
29166 .ref => switch (self.air.instructions.items(.tag)[@intFromEnum(inst)]) {
29167 .loop_switch_br => self.typeOf(self.air.unwrapSwitch(inst).operand),
29168 else => self.air.typeOfIndex(inst, &zcu.intern_pool),
29169 },
29170 .temp => temp.typeOf(self),
29171 };
29246 return temp.typeOf(self);
2917229247}
2917329248
2917429249fn intCompilerRtAbiName(int_bits: u32) u8 {
......@@ -29336,10 +29411,12 @@ const Temp = struct {
2933629411 fn unwrap(temp: Temp, cg: *CodeGen) union(enum) {
2933729412 ref: Air.Inst.Ref,
2933829413 temp: Index,
29414 err_ret_trace,
2933929415 } {
2934029416 switch (temp.index.unwrap()) {
2934129417 .ref => |ref| return .{ .ref = ref },
2934229418 .target => |target_index| {
29419 if (temp.index == err_ret_trace_index) return .err_ret_trace;
2934329420 const temp_index: Index = @enumFromInt(target_index);
2934429421 assert(temp_index.isValid(cg));
2934529422 return .{ .temp = temp_index };
......@@ -29349,14 +29426,18 @@ const Temp = struct {
2934929426
2935029427 fn typeOf(temp: Temp, cg: *CodeGen) Type {
2935129428 return switch (temp.unwrap(cg)) {
29352 .ref => |ref| cg.typeOf(ref),
29429 .ref => switch (cg.air.instructions.items(.tag)[@intFromEnum(temp.index)]) {
29430 .loop_switch_br => cg.typeOf(cg.air.unwrapSwitch(temp.index).operand),
29431 else => cg.air.typeOfIndex(temp.index, &cg.pt.zcu.intern_pool),
29432 },
2935329433 .temp => |temp_index| temp_index.typeOf(cg),
29434 .err_ret_trace => .usize,
2935429435 };
2935529436 }
2935629437
2935729438 fn isMut(temp: Temp, cg: *CodeGen) bool {
2935829439 return switch (temp.unwrap(cg)) {
29359 .ref => false,
29440 .ref, .err_ret_trace => false,
2936029441 .temp => |temp_index| switch (temp_index.tracking(cg).short) {
2936129442 .none,
2936229443 .unreach,
......@@ -29456,7 +29537,7 @@ const Temp = struct {
2945629537 fn toOffset(temp: *Temp, off: i32, cg: *CodeGen) !void {
2945729538 if (off == 0) return;
2945829539 switch (temp.unwrap(cg)) {
29459 .ref => {},
29540 .ref, .err_ret_trace => {},
2946029541 .temp => |temp_index| {
2946129542 const temp_tracking = temp_index.tracking(cg);
2946229543 switch (temp_tracking.short) {
......@@ -29617,6 +29698,7 @@ const Temp = struct {
2961729698 },
2961829699 }
2961929700 },
29701 .err_ret_trace => unreachable,
2962029702 }
2962129703 const new_temp = try temp.getLimb(limb_ty, limb_index, cg);
2962229704 try temp.die(cg);
......@@ -29633,7 +29715,7 @@ const Temp = struct {
2963329715 }
2963429716
2963529717 fn toReg(temp: *Temp, new_reg: Register, cg: *CodeGen) !bool {
29636 const val, const ty = val_ty: switch (temp.unwrap(cg)) {
29718 const val, const ty: Type = val_ty: switch (temp.unwrap(cg)) {
2963729719 .ref => |ref| .{ temp.tracking(cg).short, cg.typeOf(ref) },
2963829720 .temp => |temp_index| {
2963929721 const temp_tracking = temp_index.tracking(cg);
......@@ -29641,6 +29723,7 @@ const Temp = struct {
2964129723 temp_tracking.short.register == new_reg) return false;
2964229724 break :val_ty .{ temp_tracking.short, temp_index.typeOf(cg) };
2964329725 },
29726 .err_ret_trace => .{ temp.tracking(cg).short, .usize },
2964429727 };
2964529728 const new_temp_index = cg.next_temp_index;
2964629729 try cg.register_manager.getReg(new_reg, new_temp_index.toIndex());
......@@ -30167,7 +30250,7 @@ const Temp = struct {
3016730250
3016830251 fn moveTo(temp: Temp, inst: Air.Inst.Index, cg: *CodeGen) !void {
3016930252 if (cg.liveness.isUnused(inst)) try temp.die(cg) else switch (temp.unwrap(cg)) {
30170 .ref => {
30253 .ref, .err_ret_trace => {
3017130254 const result = try cg.allocRegOrMem(inst, true);
3017230255 try cg.genCopy(cg.typeOfIndex(inst), result, temp.tracking(cg).short, .{});
3017330256 tracking_log.debug("{} => {} (birth)", .{ inst, result });
......@@ -30184,7 +30267,7 @@ const Temp = struct {
3018430267
3018530268 fn die(temp: Temp, cg: *CodeGen) !void {
3018630269 switch (temp.unwrap(cg)) {
30187 .ref => {},
30270 .ref, .err_ret_trace => {},
3018830271 .temp => |temp_index| try temp_index.tracking(cg).die(cg, temp_index.toIndex()),
3018930272 }
3019030273 }
src/target.zig+1-1
......@@ -708,7 +708,7 @@ pub inline fn backendSupportsFeature(backend: std.builtin.CompilerBackend, compt
708708 else => false,
709709 },
710710 .error_return_trace => switch (backend) {
711 .stage2_llvm => true,
711 .stage2_llvm, .stage2_x86_64 => true,
712712 else => false,
713713 },
714714 .is_named_enum_value => switch (backend) {
test/cases/compile_errors/anytype_param_requires_comptime.zig+1-1
......@@ -15,6 +15,6 @@ pub export fn entry() void {
1515// error
1616//
1717// :7:25: error: unable to resolve comptime value
18// :7:25: note: initializer of comptime-only struct 'tmp.S.foo__anon_165.C' must be comptime-known
18// :7:25: note: initializer of comptime-only struct 'tmp.S.foo__anon_441.C' must be comptime-known
1919// :4:16: note: struct requires comptime because of this field
2020// :4:16: note: types are not available at runtime
test/cases/compile_errors/bogus_method_call_on_slice.zig+1-1
......@@ -16,5 +16,5 @@ pub export fn entry2() void {
1616//
1717// :3:6: error: no field or member function named 'copy' in '[]const u8'
1818// :9:8: error: no field or member function named 'bar' in '@TypeOf(.{})'
19// :12:18: error: no field or member function named 'bar' in 'tmp.entry2__struct_169'
19// :12:18: error: no field or member function named 'bar' in 'tmp.entry2__struct_445'
2020// :12:6: note: struct declared here
test/cases/compile_errors/coerce_anon_struct.zig+1-1
......@@ -6,6 +6,6 @@ export fn foo() void {
66
77// error
88//
9// :4:16: error: expected type 'tmp.T', found 'tmp.foo__struct_158'
9// :4:16: error: expected type 'tmp.T', found 'tmp.foo__struct_434'
1010// :3:16: note: struct declared here
1111// :1:11: note: struct declared here
test/cases/compile_errors/implicit_cast_of_error_set_not_a_subset.zig+1-1
......@@ -12,5 +12,5 @@ fn foo(set1: Set1) void {
1212// backend=stage2
1313// target=native
1414//
15// :7:21: error: expected type 'error{A,C}', found 'error{A,B}'
15// :7:21: error: expected type 'error{C,A}', found 'error{A,B}'
1616// :7:21: note: 'error.B' not a member of destination error set