Skip to content

Commit d4b6a53

Browse files
committed
x86_64: implement error return traces
1 parent 9b7b240 commit d4b6a53

File tree

6 files changed

+128
-45
lines changed

6 files changed

+128
-45
lines changed

src/arch/x86_64/CodeGen.zig

Lines changed: 123 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ const FrameIndex = bits.FrameIndex;
3333

3434
const InnerError = codegen.CodeGenError || error{OutOfRegisters};
3535

36+
const err_ret_trace_index: Air.Inst.Index = @enumFromInt(std.math.maxInt(u32));
37+
3638
gpa: Allocator,
3739
pt: Zcu.PerThread,
3840
air: Air,
@@ -55,6 +57,7 @@ va_info: union {
5557
win64: struct {},
5658
},
5759
ret_mcv: InstTracking,
60+
err_ret_trace_reg: Register,
5861
fn_type: Type,
5962
src_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

11753-
pub fn spillCallerPreservedRegs(self: *CodeGen, cc: std.builtin.CallingConvention.Tag) !void {
11799+
pub 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

14409-
fn 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-
14415-
fn airSetErrReturnTrace(self: *CodeGen, inst: Air.Inst.Index) !void {
14416-
_ = inst;
14417-
return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch});
14418-
}
14419-
14420-
fn airSaveErrReturnTraceIndex(self: *CodeGen, inst: Air.Inst.Index) !void {
14421-
_ = inst;
14422-
return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch});
14423-
}
14424-
1442514456
fn 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

2916129244
fn 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

2917429249
fn 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,14 +29715,15 @@ 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);
2964029722
if (temp_tracking.short == .register and
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
}

0 commit comments

Comments
 (0)