stage2: implement error return traces

This commit is contained in:
Veikka Tuominen
2022-04-22 21:30:54 +03:00
committed by Andrew Kelley
parent 5888446c03
commit eee8fffec7
15 changed files with 318 additions and 10 deletions

View File

@@ -1911,6 +1911,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
.wrap_errunion_payload => try airWrapErrUnionPay(f, inst),
.wrap_errunion_err => try airWrapErrUnionErr(f, inst),
.errunion_payload_ptr_set => try airErrUnionPayloadPtrSet(f, inst),
.err_return_trace => try airErrReturnTrace(f, inst),
.set_err_return_trace => try airSetErrReturnTrace(f, inst),
.wasm_memory_size => try airWasmMemorySize(f, inst),
.wasm_memory_grow => try airWasmMemoryGrow(f, inst),
@@ -3447,6 +3449,38 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
return local;
}
fn airErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue {
if (f.liveness.isUnused(inst)) return CValue.none;
const un_op = f.air.instructions.items(.data)[inst].un_op;
const writer = f.object.writer();
const inst_ty = f.air.typeOfIndex(inst);
const operand = try f.resolveInst(un_op);
const local = try f.allocLocal(inst_ty, .Const);
try writer.writeAll(" = ");
_ = operand;
_ = local;
return f.fail("TODO: C backend: implement airErrReturnTrace", .{});
}
fn airSetErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue {
if (f.liveness.isUnused(inst)) return CValue.none;
const un_op = f.air.instructions.items(.data)[inst].un_op;
const writer = f.object.writer();
const inst_ty = f.air.typeOfIndex(inst);
const operand = try f.resolveInst(un_op);
const local = try f.allocLocal(inst_ty, .Const);
try writer.writeAll(" = ");
_ = operand;
_ = local;
return f.fail("TODO: C backend: implement airSetErrReturnTrace", .{});
}
fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
if (f.liveness.isUnused(inst))
return CValue.none;

View File

@@ -636,10 +636,18 @@ pub const Object = struct {
const ret_ptr = if (sret) llvm_func.getParam(0) else null;
const gpa = dg.gpa;
const err_return_tracing = fn_info.return_type.isError() and
dg.module.comp.bin_file.options.error_return_tracing;
const err_ret_trace = if (err_return_tracing)
llvm_func.getParam(@boolToInt(ret_ptr != null))
else
null;
var args = std.ArrayList(*const llvm.Value).init(gpa);
defer args.deinit();
const param_offset: c_uint = @boolToInt(ret_ptr != null);
const param_offset = @as(c_uint, @boolToInt(ret_ptr != null)) + @boolToInt(err_return_tracing);
for (fn_info.param_types) |param_ty| {
if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
@@ -711,6 +719,7 @@ pub const Object = struct {
.base_line = dg.decl.src_line,
.prev_dbg_line = 0,
.prev_dbg_column = 0,
.err_ret_trace = err_ret_trace,
};
defer fg.deinit();
@@ -1755,6 +1764,17 @@ pub const Object = struct {
try param_di_types.append(try o.lowerDebugType(Type.void, .full));
}
if (fn_info.return_type.isError() and
o.module.comp.bin_file.options.error_return_tracing)
{
var ptr_ty_payload: Type.Payload.ElemType = .{
.base = .{ .tag = .single_mut_pointer },
.data = o.getStackTraceType(),
};
const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
try param_di_types.append(try o.lowerDebugType(ptr_ty, .full));
}
for (fn_info.param_types) |param_ty| {
if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
@@ -1824,6 +1844,27 @@ pub const Object = struct {
"", // unique id
);
}
fn getStackTraceType(o: *Object) Type {
const mod = o.module;
const std_pkg = mod.main_pkg.table.get("std").?;
const std_file = (mod.importPkg(std_pkg) catch unreachable).file;
const builtin_str: []const u8 = "builtin";
const std_namespace = mod.declPtr(std_file.root_decl.unwrap().?).src_namespace;
const builtin_decl = std_namespace.decls
.getKeyAdapted(builtin_str, Module.DeclAdapter{ .mod = mod }).?;
const stack_trace_str: []const u8 = "StackTrace";
// buffer is only used for int_type, `builtin` is a struct.
const builtin_ty = mod.declPtr(builtin_decl).val.toType(undefined);
const builtin_namespace = builtin_ty.getNamespace().?;
const stack_trace_decl = builtin_namespace.decls
.getKeyAdapted(stack_trace_str, Module.DeclAdapter{ .mod = mod }).?;
return mod.declPtr(stack_trace_decl).val.toType(undefined);
}
};
pub const DeclGen = struct {
@@ -1976,8 +2017,15 @@ pub const DeclGen = struct {
llvm_fn.addSretAttr(0, raw_llvm_ret_ty);
}
const err_return_tracing = fn_info.return_type.isError() and
dg.module.comp.bin_file.options.error_return_tracing;
if (err_return_tracing) {
dg.addArgAttr(llvm_fn, @boolToInt(sret), "nonnull");
}
// Set parameter attributes.
var llvm_param_i: c_uint = @boolToInt(sret);
var llvm_param_i: c_uint = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing);
for (fn_info.param_types) |param_ty| {
if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
@@ -2435,6 +2483,17 @@ pub const DeclGen = struct {
try llvm_params.append(llvm_sret_ty.pointerType(0));
}
if (fn_info.return_type.isError() and
dg.module.comp.bin_file.options.error_return_tracing)
{
var ptr_ty_payload: Type.Payload.ElemType = .{
.base = .{ .tag = .single_mut_pointer },
.data = dg.object.getStackTraceType(),
};
const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
try llvm_params.append(try lowerFnParamTy(dg, fn_info.cc, ptr_ty));
}
for (fn_info.param_types) |param_ty| {
if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
@@ -3449,6 +3508,8 @@ pub const FuncGen = struct {
llvm_func: *const llvm.Value,
err_ret_trace: ?*const llvm.Value = null,
/// This data structure is used to implement breaking to blocks.
blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, struct {
parent_bb: *const llvm.BasicBlock,
@@ -3678,6 +3739,8 @@ pub const FuncGen = struct {
.unwrap_errunion_err => try self.airErrUnionErr(inst, false),
.unwrap_errunion_err_ptr => try self.airErrUnionErr(inst, true),
.errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
.err_return_trace => try self.airErrReturnTrace(inst),
.set_err_return_trace => try self.airSetErrReturnTrace(inst),
.wrap_optional => try self.airWrapOptional(inst),
.wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
@@ -3732,6 +3795,12 @@ pub const FuncGen = struct {
break :blk ret_ptr;
};
if (fn_info.return_type.isError() and
self.dg.module.comp.bin_file.options.error_return_tracing)
{
try llvm_args.append(self.err_ret_trace.?);
}
for (args) |arg| {
const param_ty = self.air.typeOf(arg);
if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
@@ -5149,6 +5218,17 @@ pub const FuncGen = struct {
return self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
}
fn airErrReturnTrace(self: *FuncGen, _: Air.Inst.Index) !?*const llvm.Value {
return self.err_ret_trace.?;
}
fn airSetErrReturnTrace(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
const un_op = self.air.instructions.items(.data)[inst].un_op;
const operand = try self.resolveInst(un_op);
self.err_ret_trace = operand;
return null;
}
fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
if (self.liveness.isUnused(inst)) return null;