mirror of
https://github.com/ziglang/zig.git
synced 2024-11-15 08:33:06 +00:00
stage2: add debug info for locals in the LLVM backend
Adds 2 new AIR instructions: * dbg_var_ptr * dbg_var_val Sema no longer emits dbg_stmt AIR instructions when strip=true. LLVM backend: fixed lowerPtrToVoid when calling ptrAlignment on the element type is problematic. LLVM backend: fixed alloca instructions improperly getting debug location annotated, causing chaotic debug info behavior. zig_llvm.cpp: fixed incorrect bindings for a function that should use unsigned integers for line and column. A bunch of C test cases regressed because the new dbg_var AIR instructions caused their operands to be alive, exposing latent bugs. Mostly it's just a problem that the C backend lowers mutable and const slices to the same C type, so we need to represent that in the C backend instead of printing two duplicate typedefs.
This commit is contained in:
parent
7ec2261dbf
commit
0bc9635490
24
src/Air.zig
24
src/Air.zig
@ -327,6 +327,15 @@ pub const Inst = struct {
|
||||
/// Result type is always void.
|
||||
/// Uses the `dbg_stmt` field.
|
||||
dbg_stmt,
|
||||
/// Marks the beginning of a local variable. The operand is a pointer pointing
|
||||
/// to the storage for the variable. The local may be a const or a var.
|
||||
/// Result type is always void.
|
||||
/// Uses `pl_op`. The payload index is the variable name. It points to the extra
|
||||
/// array, reinterpreting the bytes there as a null-terminated string.
|
||||
dbg_var_ptr,
|
||||
/// Same as `dbg_var_ptr` except the local is a const, not a var, and the
|
||||
/// operand is the local's value.
|
||||
dbg_var_val,
|
||||
/// ?T => bool
|
||||
/// Result type is always bool.
|
||||
/// Uses the `un_op` field.
|
||||
@ -962,6 +971,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
|
||||
|
||||
.breakpoint,
|
||||
.dbg_stmt,
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
.store,
|
||||
.fence,
|
||||
.atomic_store_unordered,
|
||||
@ -972,13 +983,13 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
|
||||
.memcpy,
|
||||
.set_union_tag,
|
||||
.prefetch,
|
||||
=> return Type.initTag(.void),
|
||||
=> return Type.void,
|
||||
|
||||
.ptrtoint,
|
||||
.slice_len,
|
||||
.ret_addr,
|
||||
.frame_addr,
|
||||
=> return Type.initTag(.usize),
|
||||
=> return Type.usize,
|
||||
|
||||
.wasm_memory_grow => return Type.i32,
|
||||
.wasm_memory_size => return Type.u32,
|
||||
@ -1089,3 +1100,12 @@ pub fn value(air: Air, inst: Air.Inst.Ref) ?Value {
|
||||
else => return air.typeOfIndex(inst_index).onePossibleValue(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nullTerminatedString(air: Air, index: usize) [:0]const u8 {
|
||||
const bytes = std.mem.sliceAsBytes(air.extra[index..]);
|
||||
var end: usize = 0;
|
||||
while (bytes[end] != 0) {
|
||||
end += 1;
|
||||
}
|
||||
return bytes[0..end :0];
|
||||
}
|
||||
|
@ -2389,6 +2389,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
|
||||
.breakpoint,
|
||||
.fence,
|
||||
.dbg_stmt,
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
.ensure_result_used,
|
||||
.ensure_result_non_error,
|
||||
.@"export",
|
||||
@ -2666,6 +2668,15 @@ fn varDecl(
|
||||
} else .none;
|
||||
const init_inst = try reachableExpr(gz, scope, result_loc, var_decl.ast.init_node, node);
|
||||
|
||||
if (!gz.force_comptime) {
|
||||
_ = try gz.add(.{ .tag = .dbg_var_val, .data = .{
|
||||
.str_op = .{
|
||||
.str = ident_name,
|
||||
.operand = init_inst,
|
||||
},
|
||||
} });
|
||||
}
|
||||
|
||||
const sub_scope = try block_arena.create(Scope.LocalVal);
|
||||
sub_scope.* = .{
|
||||
.parent = scope,
|
||||
@ -2751,6 +2762,15 @@ fn varDecl(
|
||||
}
|
||||
gz.instructions.items.len = dst;
|
||||
|
||||
if (!gz.force_comptime) {
|
||||
_ = try gz.add(.{ .tag = .dbg_var_val, .data = .{
|
||||
.str_op = .{
|
||||
.str = ident_name,
|
||||
.operand = init_inst,
|
||||
},
|
||||
} });
|
||||
}
|
||||
|
||||
const sub_scope = try block_arena.create(Scope.LocalVal);
|
||||
sub_scope.* = .{
|
||||
.parent = scope,
|
||||
@ -2785,6 +2805,16 @@ fn varDecl(
|
||||
_ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
|
||||
}
|
||||
const const_ptr = try gz.addUnNode(.make_ptr_const, init_scope.rl_ptr, node);
|
||||
|
||||
if (!gz.force_comptime) {
|
||||
_ = try gz.add(.{ .tag = .dbg_var_ptr, .data = .{
|
||||
.str_op = .{
|
||||
.str = ident_name,
|
||||
.operand = const_ptr,
|
||||
},
|
||||
} });
|
||||
}
|
||||
|
||||
const sub_scope = try block_arena.create(Scope.LocalPtr);
|
||||
sub_scope.* = .{
|
||||
.parent = scope,
|
||||
@ -2848,6 +2878,16 @@ fn varDecl(
|
||||
if (resolve_inferred_alloc != .none) {
|
||||
_ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
|
||||
}
|
||||
|
||||
if (!gz.force_comptime) {
|
||||
_ = try gz.add(.{ .tag = .dbg_var_ptr, .data = .{
|
||||
.str_op = .{
|
||||
.str = ident_name,
|
||||
.operand = var_data.alloc,
|
||||
},
|
||||
} });
|
||||
}
|
||||
|
||||
const sub_scope = try block_arena.create(Scope.LocalPtr);
|
||||
sub_scope.* = .{
|
||||
.parent = scope,
|
||||
|
@ -394,6 +394,13 @@ fn analyzeInst(
|
||||
return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
|
||||
},
|
||||
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
=> {
|
||||
const operand = inst_datas[inst].pl_op.operand;
|
||||
return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
|
||||
},
|
||||
|
||||
.prefetch => {
|
||||
const prefetch = inst_datas[inst].prefetch;
|
||||
return trackOperands(a, new_set, inst, main_tomb, .{ prefetch.ptr, .none, .none });
|
||||
|
47
src/Sema.zig
47
src/Sema.zig
@ -872,6 +872,16 @@ fn analyzeBodyInner(
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.dbg_var_ptr => {
|
||||
try sema.zirDbgVar(block, inst, .dbg_var_ptr);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.dbg_var_val => {
|
||||
try sema.zirDbgVar(block, inst, .dbg_var_val);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.ensure_err_payload_void => {
|
||||
try sema.zirEnsureErrPayloadVoid(block, inst);
|
||||
i += 1;
|
||||
@ -4158,14 +4168,11 @@ fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError
|
||||
}
|
||||
|
||||
fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
// We do not set sema.src here because dbg_stmt instructions are only emitted for
|
||||
// ZIR code that possibly will need to generate runtime code. So error messages
|
||||
// and other source locations must not rely on sema.src being set from dbg_stmt
|
||||
// instructions.
|
||||
if (block.is_comptime) return;
|
||||
if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return;
|
||||
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].dbg_stmt;
|
||||
_ = try block.addInst(.{
|
||||
@ -4177,6 +4184,38 @@ fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!voi
|
||||
});
|
||||
}
|
||||
|
||||
fn zirDbgVar(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
inst: Zir.Inst.Index,
|
||||
air_tag: Air.Inst.Tag,
|
||||
) CompileError!void {
|
||||
if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return;
|
||||
|
||||
const str_op = sema.code.instructions.items(.data)[inst].str_op;
|
||||
const operand = sema.resolveInst(str_op.operand);
|
||||
const operand_ty = sema.typeOf(operand);
|
||||
if (!(try sema.typeHasRuntimeBits(block, sema.src, operand_ty))) return;
|
||||
const name = str_op.getStr(sema.code);
|
||||
|
||||
// Add the name to the AIR.
|
||||
const name_extra_index = @intCast(u32, sema.air_extra.items.len);
|
||||
const elements_used = name.len / 4 + 1;
|
||||
try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements_used);
|
||||
const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
|
||||
mem.copy(u8, buffer, name);
|
||||
buffer[name.len] = 0;
|
||||
sema.air_extra.items.len += elements_used;
|
||||
|
||||
_ = try block.addInst(.{
|
||||
.tag = air_tag,
|
||||
.data = .{ .pl_op = .{
|
||||
.payload = name_extra_index,
|
||||
.operand = operand,
|
||||
} },
|
||||
});
|
||||
}
|
||||
|
||||
fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
|
||||
const src = inst_data.src();
|
||||
|
22
src/Zir.zig
22
src/Zir.zig
@ -322,6 +322,14 @@ pub const Inst = struct {
|
||||
/// Uses the `dbg_stmt` union field. The line and column are offset
|
||||
/// from the parent declaration.
|
||||
dbg_stmt,
|
||||
/// Marks a variable declaration. Used for debug info.
|
||||
/// Uses the `str_op` union field. The string is the local variable name,
|
||||
/// and the operand is the pointer to the variable's location. The local
|
||||
/// may be a const or a var.
|
||||
dbg_var_ptr,
|
||||
/// Same as `dbg_var_ptr` but the local is always a const and the operand
|
||||
/// is the local's value.
|
||||
dbg_var_val,
|
||||
/// Uses a name to identify a Decl and takes a pointer to it.
|
||||
/// Uses the `str_tok` union field.
|
||||
decl_ref,
|
||||
@ -1032,6 +1040,8 @@ pub const Inst = struct {
|
||||
.error_set_decl_anon,
|
||||
.error_set_decl_func,
|
||||
.dbg_stmt,
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
.decl_ref,
|
||||
.decl_val,
|
||||
.load,
|
||||
@ -1297,6 +1307,8 @@ pub const Inst = struct {
|
||||
.error_set_decl_anon = .pl_node,
|
||||
.error_set_decl_func = .pl_node,
|
||||
.dbg_stmt = .dbg_stmt,
|
||||
.dbg_var_ptr = .str_op,
|
||||
.dbg_var_val = .str_op,
|
||||
.decl_ref = .str_tok,
|
||||
.decl_val = .str_tok,
|
||||
.load = .un_node,
|
||||
@ -2232,6 +2244,15 @@ pub const Inst = struct {
|
||||
return .{ .node_offset = self.src_node };
|
||||
}
|
||||
},
|
||||
str_op: struct {
|
||||
/// Offset into `string_bytes`. Null-terminated.
|
||||
str: u32,
|
||||
operand: Ref,
|
||||
|
||||
pub fn getStr(self: @This(), zir: Zir) [:0]const u8 {
|
||||
return zir.nullTerminatedString(self.str);
|
||||
}
|
||||
},
|
||||
|
||||
// Make sure we don't accidentally add a field to make this union
|
||||
// bigger than expected. Note that in Debug builds, Zig is allowed
|
||||
@ -2268,6 +2289,7 @@ pub const Inst = struct {
|
||||
switch_capture,
|
||||
dbg_stmt,
|
||||
inst_node,
|
||||
str_op,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -643,6 +643,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.prefetch => try self.airPrefetch(inst),
|
||||
.mul_add => try self.airMulAdd(inst),
|
||||
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
=> try self.airDbgVar(inst),
|
||||
|
||||
.call => try self.airCall(inst, .auto),
|
||||
.call_always_tail => try self.airCall(inst, .always_tail),
|
||||
.call_never_tail => try self.airCall(inst, .never_tail),
|
||||
@ -2650,6 +2654,15 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAirBookkeeping();
|
||||
}
|
||||
|
||||
fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const name = self.air.nullTerminatedString(pl_op.payload);
|
||||
const operand = pl_op.operand;
|
||||
// TODO emit debug info for this variable
|
||||
_ = name;
|
||||
return self.finishAir(inst, .dead, .{ operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const cond = try self.resolveInst(pl_op.operand);
|
||||
|
@ -642,6 +642,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.prefetch => try self.airPrefetch(inst),
|
||||
.mul_add => try self.airMulAdd(inst),
|
||||
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
=> try self.airDbgVar(inst),
|
||||
|
||||
.call => try self.airCall(inst, .auto),
|
||||
.call_always_tail => try self.airCall(inst, .always_tail),
|
||||
.call_never_tail => try self.airCall(inst, .never_tail),
|
||||
@ -2831,6 +2835,15 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAirBookkeeping();
|
||||
}
|
||||
|
||||
fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const name = self.air.nullTerminatedString(pl_op.payload);
|
||||
const operand = pl_op.operand;
|
||||
// TODO emit debug info for this variable
|
||||
_ = name;
|
||||
return self.finishAir(inst, .dead, .{ operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const cond = try self.resolveInst(pl_op.operand);
|
||||
|
@ -609,6 +609,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.prefetch => try self.airPrefetch(inst),
|
||||
.mul_add => try self.airMulAdd(inst),
|
||||
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
=> try self.airDbgVar(inst),
|
||||
|
||||
.call => try self.airCall(inst, .auto),
|
||||
.call_always_tail => try self.airCall(inst, .always_tail),
|
||||
.call_never_tail => try self.airCall(inst, .never_tail),
|
||||
@ -1636,6 +1640,15 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAirBookkeeping();
|
||||
}
|
||||
|
||||
fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const name = self.air.nullTerminatedString(pl_op.payload);
|
||||
const operand = pl_op.operand;
|
||||
// TODO emit debug info for this variable
|
||||
_ = name;
|
||||
return self.finishAir(inst, .dead, .{ operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
_ = inst;
|
||||
|
||||
|
@ -1219,13 +1219,18 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
|
||||
.br => self.airBr(inst),
|
||||
.bool_to_int => self.airBoolToInt(inst),
|
||||
.cond_br => self.airCondBr(inst),
|
||||
.dbg_stmt => WValue.none,
|
||||
.intcast => self.airIntcast(inst),
|
||||
.fptrunc => self.airFptrunc(inst),
|
||||
.fpext => self.airFpext(inst),
|
||||
.float_to_int => self.airFloatToInt(inst),
|
||||
.get_union_tag => self.airGetUnionTag(inst),
|
||||
|
||||
// TODO
|
||||
.dbg_stmt,
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
=> WValue.none,
|
||||
|
||||
.call => self.airCall(inst, .auto),
|
||||
.call_always_tail => self.airCall(inst, .always_tail),
|
||||
.call_never_tail => self.airCall(inst, .never_tail),
|
||||
|
@ -726,6 +726,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.prefetch => try self.airPrefetch(inst),
|
||||
.mul_add => try self.airMulAdd(inst),
|
||||
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
=> try self.airDbgVar(inst),
|
||||
|
||||
.call => try self.airCall(inst, .auto),
|
||||
.call_always_tail => try self.airCall(inst, .always_tail),
|
||||
.call_never_tail => try self.airCall(inst, .never_tail),
|
||||
@ -3666,6 +3670,15 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAirBookkeeping();
|
||||
}
|
||||
|
||||
fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const name = self.air.nullTerminatedString(pl_op.payload);
|
||||
const operand = pl_op.operand;
|
||||
// TODO emit debug info for this variable
|
||||
_ = name;
|
||||
return self.finishAir(inst, .dead, .{ operand, .none, .none });
|
||||
}
|
||||
|
||||
fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 {
|
||||
const abi_size = ty.abiSize(self.target.*);
|
||||
switch (mcv) {
|
||||
|
@ -1721,6 +1721,10 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
|
||||
.union_init => try airUnionInit(f, inst),
|
||||
.prefetch => try airPrefetch(f, inst),
|
||||
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
=> try airDbgVar(f, inst),
|
||||
|
||||
.call => try airCall(f, inst, .auto),
|
||||
.call_always_tail => try airCall(f, inst, .always_tail),
|
||||
.call_never_tail => try airCall(f, inst, .never_tail),
|
||||
@ -2651,6 +2655,16 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
return CValue.none;
|
||||
}
|
||||
|
||||
fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
const pl_op = f.air.instructions.items(.data)[inst].pl_op;
|
||||
const name = f.air.nullTerminatedString(pl_op.payload);
|
||||
const operand = try f.resolveInst(pl_op.operand);
|
||||
_ = operand;
|
||||
const writer = f.object.writer();
|
||||
try writer.print("/* var:{s} */\n", .{name});
|
||||
return CValue.none;
|
||||
}
|
||||
|
||||
fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = f.air.extraData(Air.Block, ty_pl.payload);
|
||||
|
@ -613,6 +613,8 @@ pub const Object = struct {
|
||||
.single_threaded = module.comp.bin_file.options.single_threaded,
|
||||
.di_scope = di_scope,
|
||||
.di_file = di_file,
|
||||
.prev_dbg_line = 0,
|
||||
.prev_dbg_column = 0,
|
||||
};
|
||||
defer fg.deinit();
|
||||
|
||||
@ -2885,8 +2887,7 @@ pub const DeclGen = struct {
|
||||
}
|
||||
|
||||
fn lowerPtrToVoid(dg: *DeclGen, ptr_ty: Type) !*const llvm.Value {
|
||||
const target = dg.module.getTarget();
|
||||
const alignment = ptr_ty.ptrAlignment(target);
|
||||
const alignment = ptr_ty.ptrInfo().data.@"align";
|
||||
// Even though we are pointing at something which has zero bits (e.g. `void`),
|
||||
// Pointers are defined to have bits. So we must return something here.
|
||||
// The value cannot be undefined, because we use the `nonnull` annotation
|
||||
@ -2902,6 +2903,7 @@ pub const DeclGen = struct {
|
||||
// have an "undef_but_not_null" attribute. As an example, if this `alloc` AIR
|
||||
// instruction is followed by a `wrap_optional`, it will return this value
|
||||
// verbatim, and the result should test as non-null.
|
||||
const target = dg.module.getTarget();
|
||||
const int = switch (target.cpu.arch.ptrBitWidth()) {
|
||||
32 => llvm_usize.constInt(0xaaaaaaaa, .False),
|
||||
64 => llvm_usize.constInt(0xaaaaaaaa_aaaaaaaa, .False),
|
||||
@ -3004,6 +3006,8 @@ pub const FuncGen = struct {
|
||||
builder: *const llvm.Builder,
|
||||
di_scope: ?*llvm.DIScope,
|
||||
di_file: ?*llvm.DIFile,
|
||||
prev_dbg_line: c_uint,
|
||||
prev_dbg_column: c_uint,
|
||||
|
||||
/// This stores the LLVM values used in a function, such that they can be referred to
|
||||
/// in other instructions. This table is cleared before every function is generated.
|
||||
@ -3255,6 +3259,8 @@ pub const FuncGen = struct {
|
||||
.const_ty => unreachable,
|
||||
.unreach => self.airUnreach(inst),
|
||||
.dbg_stmt => self.airDbgStmt(inst),
|
||||
.dbg_var_ptr => try self.airDbgVarPtr(inst),
|
||||
.dbg_var_val => try self.airDbgVarVal(inst),
|
||||
// zig fmt: on
|
||||
};
|
||||
if (opt_value) |val| {
|
||||
@ -3967,11 +3973,56 @@ pub const FuncGen = struct {
|
||||
fn airDbgStmt(self: *FuncGen, inst: Air.Inst.Index) ?*const llvm.Value {
|
||||
const di_scope = self.di_scope orelse return null;
|
||||
const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
|
||||
self.builder.setCurrentDebugLocation(
|
||||
@intCast(c_int, self.dg.decl.src_line + dbg_stmt.line + 1),
|
||||
@intCast(c_int, dbg_stmt.column + 1),
|
||||
di_scope,
|
||||
self.prev_dbg_line = @intCast(c_uint, self.dg.decl.src_line + dbg_stmt.line + 1);
|
||||
self.prev_dbg_column = @intCast(c_uint, dbg_stmt.column + 1);
|
||||
self.builder.setCurrentDebugLocation(self.prev_dbg_line, self.prev_dbg_column, di_scope);
|
||||
return null;
|
||||
}
|
||||
|
||||
fn airDbgVarPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
const dib = self.dg.object.di_builder orelse return null;
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const operand = try self.resolveInst(pl_op.operand);
|
||||
const name = self.air.nullTerminatedString(pl_op.payload);
|
||||
|
||||
const di_local_var = dib.createAutoVariable(
|
||||
self.di_scope.?,
|
||||
name.ptr,
|
||||
self.di_file.?,
|
||||
self.prev_dbg_line,
|
||||
try self.dg.lowerDebugType(self.air.typeOf(pl_op.operand)),
|
||||
true, // always preserve
|
||||
0, // flags
|
||||
);
|
||||
const debug_loc = llvm.getDebugLoc(self.prev_dbg_line, self.prev_dbg_column, self.di_scope.?);
|
||||
const insert_block = self.builder.getInsertBlock();
|
||||
_ = dib.insertDeclareAtEnd(operand, di_local_var, debug_loc, insert_block);
|
||||
return null;
|
||||
}
|
||||
|
||||
fn airDbgVarVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
const dib = self.dg.object.di_builder orelse return null;
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const operand = try self.resolveInst(pl_op.operand);
|
||||
const operand_ty = self.air.typeOf(pl_op.operand);
|
||||
const name = self.air.nullTerminatedString(pl_op.payload);
|
||||
|
||||
const di_local_var = dib.createAutoVariable(
|
||||
self.di_scope.?,
|
||||
name.ptr,
|
||||
self.di_file.?,
|
||||
self.prev_dbg_line,
|
||||
try self.dg.lowerDebugType(operand_ty),
|
||||
true, // always preserve
|
||||
0, // flags
|
||||
);
|
||||
const debug_loc = llvm.getDebugLoc(self.prev_dbg_line, self.prev_dbg_column, self.di_scope.?);
|
||||
const insert_block = self.builder.getInsertBlock();
|
||||
if (isByRef(operand_ty)) {
|
||||
_ = dib.insertDeclareAtEnd(operand, di_local_var, debug_loc, insert_block);
|
||||
} else {
|
||||
_ = dib.insertDbgValueIntrinsicAtEnd(operand, di_local_var, debug_loc, insert_block);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -5272,6 +5323,13 @@ pub const FuncGen = struct {
|
||||
/// put the alloca instruction at the top of the function!
|
||||
fn buildAlloca(self: *FuncGen, llvm_ty: *const llvm.Type) *const llvm.Value {
|
||||
const prev_block = self.builder.getInsertBlock();
|
||||
const prev_debug_location = self.builder.getCurrentDebugLocation2();
|
||||
defer {
|
||||
self.builder.positionBuilderAtEnd(prev_block);
|
||||
if (self.di_scope != null) {
|
||||
self.builder.setCurrentDebugLocation2(prev_debug_location);
|
||||
}
|
||||
}
|
||||
|
||||
const entry_block = self.llvm_func.getFirstBasicBlock().?;
|
||||
if (entry_block.getFirstInstruction()) |first_inst| {
|
||||
@ -5279,10 +5337,9 @@ pub const FuncGen = struct {
|
||||
} else {
|
||||
self.builder.positionBuilderAtEnd(entry_block);
|
||||
}
|
||||
self.builder.clearCurrentDebugLocation();
|
||||
|
||||
const alloca = self.builder.buildAlloca(llvm_ty, "");
|
||||
self.builder.positionBuilderAtEnd(prev_block);
|
||||
return alloca;
|
||||
return self.builder.buildAlloca(llvm_ty, "");
|
||||
}
|
||||
|
||||
fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
|
@ -840,7 +840,7 @@ pub const Builder = opaque {
|
||||
extern fn LLVMBuildExactSDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
|
||||
|
||||
pub const setCurrentDebugLocation = ZigLLVMSetCurrentDebugLocation;
|
||||
extern fn ZigLLVMSetCurrentDebugLocation(builder: *const Builder, line: c_int, column: c_int, scope: *DIScope) void;
|
||||
extern fn ZigLLVMSetCurrentDebugLocation(builder: *const Builder, line: c_uint, column: c_uint, scope: *DIScope) void;
|
||||
|
||||
pub const clearCurrentDebugLocation = ZigLLVMClearCurrentDebugLocation;
|
||||
extern fn ZigLLVMClearCurrentDebugLocation(builder: *const Builder) void;
|
||||
|
@ -233,6 +233,10 @@ const Writer = struct {
|
||||
.call_never_inline,
|
||||
=> try w.writeCall(s, inst),
|
||||
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
=> try w.writeDbgVar(s, inst),
|
||||
|
||||
.struct_field_ptr => try w.writeStructField(s, inst),
|
||||
.struct_field_val => try w.writeStructField(s, inst),
|
||||
.constant => try w.writeConstant(s, inst),
|
||||
@ -499,7 +503,7 @@ const Writer = struct {
|
||||
extra_i += inputs.len;
|
||||
|
||||
for (outputs) |output| {
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0);
|
||||
const constraint = w.air.nullTerminatedString(extra_i);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += constraint.len / 4 + 1;
|
||||
@ -515,7 +519,7 @@ const Writer = struct {
|
||||
}
|
||||
|
||||
for (inputs) |input| {
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0);
|
||||
const constraint = w.air.nullTerminatedString(extra_i);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += constraint.len / 4 + 1;
|
||||
@ -529,7 +533,7 @@ const Writer = struct {
|
||||
{
|
||||
var clobber_i: u32 = 0;
|
||||
while (clobber_i < clobbers_len) : (clobber_i += 1) {
|
||||
const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0);
|
||||
const clobber = w.air.nullTerminatedString(extra_i);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += clobber.len / 4 + 1;
|
||||
@ -548,6 +552,13 @@ const Writer = struct {
|
||||
try s.print("{d}:{d}", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 });
|
||||
}
|
||||
|
||||
fn writeDbgVar(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
||||
const pl_op = w.air.instructions.items(.data)[inst].pl_op;
|
||||
try w.writeOperand(s, inst, 0, pl_op.operand);
|
||||
const name = w.air.nullTerminatedString(pl_op.payload);
|
||||
try s.print(", {s}", .{name});
|
||||
}
|
||||
|
||||
fn writeCall(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
||||
const pl_op = w.air.instructions.items(.data)[inst].pl_op;
|
||||
const extra = w.air.extraData(Air.Call, pl_op.payload);
|
||||
|
@ -424,6 +424,10 @@ const Writer = struct {
|
||||
.param_anytype_comptime,
|
||||
=> try self.writeStrTok(stream, inst),
|
||||
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
=> try self.writeStrOp(stream, inst),
|
||||
|
||||
.param, .param_comptime => try self.writeParam(stream, inst),
|
||||
|
||||
.func => try self.writeFunc(stream, inst, false),
|
||||
@ -1837,6 +1841,13 @@ const Writer = struct {
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
fn writeStrOp(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].str_op;
|
||||
const str = inst_data.getStr(self.code);
|
||||
try self.writeInstRef(stream, inst_data.operand);
|
||||
try stream.print(", \"{}\")", .{std.zig.fmtEscapes(str)});
|
||||
}
|
||||
|
||||
fn writeFunc(
|
||||
self: *Writer,
|
||||
stream: anytype,
|
||||
|
@ -791,7 +791,9 @@ void ZigLLVMDisposeDIBuilder(ZigLLVMDIBuilder *dbuilder) {
|
||||
delete di_builder;
|
||||
}
|
||||
|
||||
void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder, int line, int column, ZigLLVMDIScope *scope) {
|
||||
void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder,
|
||||
unsigned int line, unsigned int column, ZigLLVMDIScope *scope)
|
||||
{
|
||||
DIScope* di_scope = reinterpret_cast<DIScope*>(scope);
|
||||
DebugLoc debug_loc = DILocation::get(di_scope->getContext(), line, column, di_scope, nullptr, false);
|
||||
unwrap(builder)->SetCurrentDebugLocation(debug_loc);
|
||||
|
@ -228,8 +228,8 @@ ZIG_EXTERN_C void ZigLLVMSetModulePICLevel(LLVMModuleRef module);
|
||||
ZIG_EXTERN_C void ZigLLVMSetModulePIELevel(LLVMModuleRef module);
|
||||
ZIG_EXTERN_C void ZigLLVMSetModuleCodeModel(LLVMModuleRef module, LLVMCodeModel code_model);
|
||||
|
||||
ZIG_EXTERN_C void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder, int line, int column,
|
||||
struct ZigLLVMDIScope *scope);
|
||||
ZIG_EXTERN_C void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder,
|
||||
unsigned int line, unsigned int column, struct ZigLLVMDIScope *scope);
|
||||
ZIG_EXTERN_C void ZigLLVMClearCurrentDebugLocation(LLVMBuilderRef builder);
|
||||
|
||||
ZIG_EXTERN_C struct ZigLLVMDIScope *ZigLLVMLexicalBlockToScope(struct ZigLLVMDILexicalBlock *lexical_block);
|
||||
|
@ -6,6 +6,8 @@ const native_arch = builtin.target.cpu.arch;
|
||||
var foo: u8 align(4) = 100;
|
||||
|
||||
test "global variable alignment" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
|
||||
comptime try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
|
||||
comptime try expect(@TypeOf(&foo) == *align(4) u8);
|
||||
{
|
||||
|
@ -5,6 +5,7 @@ fn foo(a: []u8) void {
|
||||
}
|
||||
|
||||
test "address of 0 length array" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
|
||||
|
@ -5,6 +5,7 @@ fn f(buf: []u8) void {
|
||||
}
|
||||
|
||||
test "crash" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
|
||||
|
@ -19,10 +19,12 @@ pub const Renderable = struct {
|
||||
var renderable: Renderable = undefined;
|
||||
|
||||
test "assignment of field with padding" {
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
|
||||
renderable = Renderable{
|
||||
.mesh = Mesh{ .id = 0 },
|
||||
.material = Material{
|
||||
|
@ -416,6 +416,8 @@ fn copyWithPartialInline(s: []u32, b: []u8) void {
|
||||
}
|
||||
|
||||
test "binary math operator in partially inlined function" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
|
||||
var s: [4]u32 = undefined;
|
||||
var b: [16]u8 = undefined;
|
||||
|
||||
@ -545,6 +547,8 @@ var simple_struct = SimpleStruct{ .field = 1234 };
|
||||
const bound_fn = simple_struct.method;
|
||||
|
||||
test "ptr to local array argument at comptime" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
|
||||
comptime {
|
||||
var bytes: [10]u8 = undefined;
|
||||
modifySomeBytes(bytes[0..]);
|
||||
|
@ -302,7 +302,11 @@ fn testExp2() !void {
|
||||
}
|
||||
|
||||
test "@log" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
|
||||
comptime try testLog();
|
||||
try testLog();
|
||||
@ -326,13 +330,22 @@ fn testLog() !void {
|
||||
try expect(math.approxEqAbs(ty, @log(@as(ty, 2)), 0.6931471805599, eps));
|
||||
try expect(math.approxEqAbs(ty, @log(@as(ty, 5)), 1.6094379124341, eps));
|
||||
}
|
||||
}
|
||||
|
||||
test "@log with vectors" {
|
||||
if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
|
||||
{
|
||||
var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
|
||||
var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
|
||||
var result = @log(v);
|
||||
try expect(math.approxEqAbs(f32, @log(@as(f32, 1.1)), result[0], epsilon));
|
||||
try expect(math.approxEqAbs(f32, @log(@as(f32, 2.2)), result[1], epsilon));
|
||||
try expect(math.approxEqAbs(f32, @log(@as(f32, 0.3)), result[2], epsilon));
|
||||
try expect(@log(@as(f32, 0.3)) == result[2]);
|
||||
try expect(math.approxEqAbs(f32, @log(@as(f32, 0.4)), result[3], epsilon));
|
||||
}
|
||||
}
|
||||
|
@ -296,6 +296,7 @@ fn voidFun(a: i32, b: void, c: i32, d: void) !void {
|
||||
}
|
||||
|
||||
test "call function with empty string" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
|
||||
acceptsString("");
|
||||
|
@ -151,6 +151,7 @@ test "2 break statements and an else" {
|
||||
}
|
||||
|
||||
test "for loop with pointer elem var" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
|
@ -170,6 +170,7 @@ fn getFirstByte(comptime T: type, mem: []const T) u8 {
|
||||
}
|
||||
|
||||
test "generic fn keeps non-generic parameter types" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
|
||||
|
@ -242,6 +242,7 @@ test "result location zero sized array inside struct field implicit cast to slic
|
||||
}
|
||||
|
||||
test "runtime safety lets us slice from len..len" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
|
||||
@ -350,6 +351,7 @@ test "empty array to slice" {
|
||||
}
|
||||
|
||||
test "@ptrCast slice to pointer" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
|
||||
const S = struct {
|
||||
|
@ -453,6 +453,7 @@ pub const FooUnion = union(enum) {
|
||||
var glbl_array: [2]FooUnion = undefined;
|
||||
|
||||
test "initialize global array of union" {
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
|
@ -1,3 +1,4 @@
|
||||
const builtin = @import("builtin");
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
const mem = std.mem;
|
||||
@ -16,6 +17,8 @@ const ET = union(enum) {
|
||||
};
|
||||
|
||||
test "enum with members" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
|
||||
const a = ET{ .SINT = -42 };
|
||||
const b = ET{ .UINT = 42 };
|
||||
var buf: [20]u8 = undefined;
|
||||
|
Loading…
Reference in New Issue
Block a user