diff --git a/src/Air.zig b/src/Air.zig index a1d5e1e8d998..7ccb6ebc9659 100644 --- a/src/Air.zig +++ b/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]; +} diff --git a/src/AstGen.zig b/src/AstGen.zig index f54af8d3fb8c..646560fd5a38 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -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, diff --git a/src/Liveness.zig b/src/Liveness.zig index c91288f35410..849b2c52c312 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -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 }); diff --git a/src/Sema.zig b/src/Sema.zig index 13b18157d0ac..1a760be4efe3 100644 --- a/src/Sema.zig +++ b/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(); diff --git a/src/Zir.zig b/src/Zir.zig index acf259607f13..8080ad9d417d 100644 --- a/src/Zir.zig +++ b/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, }; }; diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 10e93cf23cc2..1a8765ac3a4a 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -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); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index e1eed9a94150..5dda14a07dea 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -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); diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index f6a4b9c08e06..e25848cd8578 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -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; diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 6c54699831d7..74af207a0a6c 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -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), diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 842246413970..34facf4773f5 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -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) { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index f1da1fdc0ca6..5ab84520a4cb 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -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); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index c942a975df69..c1c7ac06f050 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -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 { diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index f590e46c2300..6afd754d37b5 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -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; diff --git a/src/print_air.zig b/src/print_air.zig index 749e4751fb31..d70fdf5c24be 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -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); diff --git a/src/print_zir.zig b/src/print_zir.zig index 256fd4f141b3..8a4e8bbc2e31 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -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, diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 21e83319d9a7..528dd2e08a2f 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -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(scope); DebugLoc debug_loc = DILocation::get(di_scope->getContext(), line, column, di_scope, nullptr, false); unwrap(builder)->SetCurrentDebugLocation(debug_loc); diff --git a/src/zig_llvm.h b/src/zig_llvm.h index b19ff1f9479e..5fae61479305 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -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); diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 50ba13073d0a..fa74138d0185 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -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); { diff --git a/test/behavior/bugs/2692.zig b/test/behavior/bugs/2692.zig index 0692c51b8c56..af53888a1717 100644 --- a/test/behavior/bugs/2692.zig +++ b/test/behavior/bugs/2692.zig @@ -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; diff --git a/test/behavior/bugs/4954.zig b/test/behavior/bugs/4954.zig index 737333a3d7a4..aa339de3265e 100644 --- a/test/behavior/bugs/4954.zig +++ b/test/behavior/bugs/4954.zig @@ -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; diff --git a/test/behavior/bugs/5398.zig b/test/behavior/bugs/5398.zig index 4041bb20780b..9e66020adc40 100644 --- a/test/behavior/bugs/5398.zig +++ b/test/behavior/bugs/5398.zig @@ -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{ diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 9832cd2161fa..1847953550b4 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -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..]); diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 11c15e76123d..d82637b24dab 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -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)); } } diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 2038fd12e428..93bc712227ae 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -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(""); diff --git a/test/behavior/for.zig b/test/behavior/for.zig index 44c135197a1e..ff4b26a4e46d 100644 --- a/test/behavior/for.zig +++ b/test/behavior/for.zig @@ -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 diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 656acb61eb7a..6f64d658712d 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -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; diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 09025df69f3c..d7d6233c9060 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -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 { diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 3f53fec3abc0..bcd25754141f 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -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; diff --git a/test/behavior/union_with_members.zig b/test/behavior/union_with_members.zig index 84277289a5ed..32ed0292dc06 100644 --- a/test/behavior/union_with_members.zig +++ b/test/behavior/union_with_members.zig @@ -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;