Commit 65368683ad
Changed files (26)
lib
src
doc/langref.html.in
@@ -7818,12 +7818,14 @@ comptime {
<p>
This function inserts a platform-specific debug trap instruction which causes
debuggers to break there.
+ Unlike for {#syntax#}@trap(){#endsyntax#}, execution may continue after this point if the program is resumed.
</p>
<p>
This function is only valid within function scope.
</p>
-
+ {#see_also|@trap#}
{#header_close#}
+
{#header_open|@mulAdd#}
<pre>{#syntax#}@mulAdd(comptime T: type, a: T, b: T, c: T) T{#endsyntax#}</pre>
<p>
@@ -9393,6 +9395,19 @@ fn List(comptime T: type) type {
</p>
{#header_close#}
+ {#header_open|@trap#}
+ <pre>{#syntax#}@trap() noreturn{#endsyntax#}</pre>
+ <p>
+ This function inserts a platform-specific trap/jam instruction which can be used to exit the program abnormally.
+ This may be implemented by explicitly emitting an invalid instruction which may cause an illegal instruction exception of some sort.
+ Unlike for {#syntax#}@breakpoint(){#endsyntax#}, execution does not continue after this point.
+ </p>
+ <p>
+ This function is only valid within function scope.
+ </p>
+ {#see_also|@breakpoint#}
+ {#header_close#}
+
{#header_open|@truncate#}
<pre>{#syntax#}@truncate(comptime T: type, integer: anytype) T{#endsyntax#}</pre>
<p>
lib/zig.h
@@ -180,10 +180,16 @@ typedef char bool;
#define zig_export(sig, symbol, name) __asm(name " = " symbol)
#endif
+#if zig_has_builtin(trap)
+#define zig_trap() __builtin_trap()
+#elif defined(__i386__) || defined(__x86_64__)
+#define zig_trap() __asm__ volatile("ud2");
+#else
+#define zig_trap() raise(SIGILL)
+#endif
+
#if zig_has_builtin(debugtrap)
#define zig_breakpoint() __builtin_debugtrap()
-#elif zig_has_builtin(trap) || defined(zig_gnuc)
-#define zig_breakpoint() __builtin_trap()
#elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
#define zig_breakpoint() __debugbreak()
#elif defined(__i386__) || defined(__x86_64__)
src/arch/aarch64/CodeGen.zig
@@ -737,6 +737,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.bitcast => try self.airBitCast(inst),
.block => try self.airBlock(inst),
.br => try self.airBr(inst),
+ .trap => try self.airTrap(),
.breakpoint => try self.airBreakpoint(),
.ret_addr => try self.airRetAddr(inst),
.frame_addr => try self.airFrameAddress(inst),
@@ -4198,10 +4199,18 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ .none, .none, .none });
}
+fn airTrap(self: *Self) !void {
+ _ = try self.addInst(.{
+ .tag = .brk,
+ .data = .{ .imm16 = 0x0001 },
+ });
+ return self.finishAirBookkeeping();
+}
+
fn airBreakpoint(self: *Self) !void {
_ = try self.addInst(.{
.tag = .brk,
- .data = .{ .imm16 = 1 },
+ .data = .{ .imm16 = 0xf000 },
});
return self.finishAirBookkeeping();
}
src/arch/arm/bits.zig
@@ -307,6 +307,9 @@ pub const Instruction = union(enum) {
fixed: u4 = 0b1111,
cond: u4,
},
+ undefined_instruction: packed struct {
+ imm32: u32 = 0xe7ffdefe,
+ },
breakpoint: packed struct {
imm4: u4,
fixed_1: u4 = 0b0111,
@@ -613,6 +616,7 @@ pub const Instruction = union(enum) {
.branch => |v| @bitCast(u32, v),
.branch_exchange => |v| @bitCast(u32, v),
.supervisor_call => |v| @bitCast(u32, v),
+ .undefined_instruction => |v| v.imm32,
.breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20),
};
}
@@ -890,6 +894,13 @@ pub const Instruction = union(enum) {
};
}
+ // This instruction has no official mnemonic equivalent so it is public as-is.
+ pub fn undefinedInstruction() Instruction {
+ return Instruction{
+ .undefined_instruction = .{},
+ };
+ }
+
fn breakpoint(imm: u16) Instruction {
return Instruction{
.breakpoint = .{
src/arch/arm/CodeGen.zig
@@ -721,6 +721,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.bitcast => try self.airBitCast(inst),
.block => try self.airBlock(inst),
.br => try self.airBr(inst),
+ .trap => try self.airTrap(),
.breakpoint => try self.airBreakpoint(),
.ret_addr => try self.airRetAddr(inst),
.frame_addr => try self.airFrameAddress(inst),
@@ -4146,6 +4147,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ .none, .none, .none });
}
+fn airTrap(self: *Self) !void {
+ _ = try self.addInst(.{
+ .tag = .undefined_instruction,
+ .data = .{ .nop = {} },
+ });
+ return self.finishAirBookkeeping();
+}
+
fn airBreakpoint(self: *Self) !void {
_ = try self.addInst(.{
.tag = .bkpt,
src/arch/arm/Emit.zig
@@ -1,4 +1,4 @@
-//! This file contains the functionality for lowering AArch64 MIR into
+//! This file contains the functionality for lowering AArch32 MIR into
//! machine code
const Emit = @This();
@@ -15,7 +15,7 @@ const Target = std.Target;
const assert = std.debug.assert;
const Instruction = bits.Instruction;
const Register = bits.Register;
-const log = std.log.scoped(.aarch64_emit);
+const log = std.log.scoped(.aarch32_emit);
const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
const CodeGen = @import("CodeGen.zig");
@@ -100,6 +100,7 @@ pub fn emitMir(
.b => try emit.mirBranch(inst),
+ .undefined_instruction => try emit.mirUndefinedInstruction(),
.bkpt => try emit.mirExceptionGeneration(inst),
.blx => try emit.mirBranchExchange(inst),
@@ -494,6 +495,10 @@ fn mirBranch(emit: *Emit, inst: Mir.Inst.Index) !void {
}
}
+fn mirUndefinedInstruction(emit: *Emit) !void {
+ try emit.writeInstruction(Instruction.undefinedInstruction());
+}
+
fn mirExceptionGeneration(emit: *Emit, inst: Mir.Inst.Index) !void {
const tag = emit.mir.instructions.items(.tag)[inst];
const imm16 = emit.mir.instructions.items(.data)[inst].imm16;
src/arch/arm/Mir.zig
@@ -35,6 +35,8 @@ pub const Inst = struct {
asr,
/// Branch
b,
+ /// Undefined instruction
+ undefined_instruction,
/// Breakpoint
bkpt,
/// Branch with Link and Exchange
src/arch/riscv64/bits.zig
@@ -380,6 +380,7 @@ pub const Instruction = union(enum) {
pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000);
pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001);
+ pub const unimp = iType(0, 0, .zero, .zero, 0);
};
pub const Register = enum(u6) {
src/arch/riscv64/CodeGen.zig
@@ -550,6 +550,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.bitcast => try self.airBitCast(inst),
.block => try self.airBlock(inst),
.br => try self.airBr(inst),
+ .trap => try self.airTrap(),
.breakpoint => try self.airBreakpoint(),
.ret_addr => try self.airRetAddr(inst),
.frame_addr => try self.airFrameAddress(inst),
@@ -1652,6 +1653,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, mcv, .{ .none, .none, .none });
}
+fn airTrap(self: *Self) !void {
+ _ = try self.addInst(.{
+ .tag = .unimp,
+ .data = .{ .nop = {} },
+ });
+ return self.finishAirBookkeeping();
+}
+
fn airBreakpoint(self: *Self) !void {
_ = try self.addInst(.{
.tag = .ebreak,
src/arch/riscv64/Emit.zig
@@ -51,6 +51,7 @@ pub fn emitMir(
.ebreak => try emit.mirSystem(inst),
.ecall => try emit.mirSystem(inst),
+ .unimp => try emit.mirSystem(inst),
.dbg_line => try emit.mirDbgLine(inst),
@@ -153,6 +154,7 @@ fn mirSystem(emit: *Emit, inst: Mir.Inst.Index) !void {
switch (tag) {
.ebreak => try emit.writeInstruction(Instruction.ebreak),
.ecall => try emit.writeInstruction(Instruction.ecall),
+ .unimp => try emit.writeInstruction(Instruction.unimp),
else => unreachable,
}
}
src/arch/riscv64/Mir.zig
@@ -32,6 +32,7 @@ pub const Inst = struct {
dbg_epilogue_begin,
/// Pseudo-instruction: Update debug line
dbg_line,
+ unimp,
ebreak,
ecall,
jalr,
src/arch/sparc64/CodeGen.zig
@@ -566,6 +566,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.bitcast => try self.airBitCast(inst),
.block => try self.airBlock(inst),
.br => try self.airBr(inst),
+ .trap => try self.airTrap(),
.breakpoint => try self.airBreakpoint(),
.ret_addr => @panic("TODO try self.airRetAddr(inst)"),
.frame_addr => @panic("TODO try self.airFrameAddress(inst)"),
@@ -1160,6 +1161,21 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
}
+fn airTrap(self: *Self) !void {
+ // ta 0x05
+ _ = try self.addInst(.{
+ .tag = .tcc,
+ .data = .{
+ .trap = .{
+ .is_imm = true,
+ .cond = .al,
+ .rs2_or_imm = .{ .imm = 0x05 },
+ },
+ },
+ });
+ return self.finishAirBookkeeping();
+}
+
fn airBreakpoint(self: *Self) !void {
// ta 0x01
_ = try self.addInst(.{
src/arch/wasm/CodeGen.zig
@@ -1829,6 +1829,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
.arg => func.airArg(inst),
.bitcast => func.airBitcast(inst),
.block => func.airBlock(inst),
+ .trap => func.airTrap(inst),
.breakpoint => func.airBreakpoint(inst),
.br => func.airBr(inst),
.bool_to_int => func.airBoolToInt(inst),
@@ -3289,6 +3290,11 @@ fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
func.finishAir(inst, result, &.{ty_op.operand});
}
+fn airTrap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
+ try func.addTag(.@"unreachable");
+ func.finishAir(inst, .none, &.{});
+}
+
fn airBreakpoint(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
// unsupported by wasm itfunc. Can be implemented once we support DWARF
// for wasm
src/arch/x86_64/CodeGen.zig
@@ -638,6 +638,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.bitcast => try self.airBitCast(inst),
.block => try self.airBlock(inst),
.br => try self.airBr(inst),
+ .trap => try self.airTrap(),
.breakpoint => try self.airBreakpoint(),
.ret_addr => try self.airRetAddr(inst),
.frame_addr => try self.airFrameAddress(inst),
@@ -3917,6 +3918,15 @@ fn genVarDbgInfo(
}
}
+fn airTrap(self: *Self) !void {
+ _ = try self.addInst(.{
+ .tag = .ud,
+ .ops = Mir.Inst.Ops.encode(.{}),
+ .data = undefined,
+ });
+ return self.finishAirBookkeeping();
+}
+
fn airBreakpoint(self: *Self) !void {
_ = try self.addInst(.{
.tag = .interrupt,
src/arch/x86_64/Emit.zig
@@ -166,6 +166,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
.@"test" => try emit.mirTest(inst),
+ .ud => try emit.mirUndefinedInstruction(),
.interrupt => try emit.mirInterrupt(inst),
.nop => {}, // just skip it
@@ -234,6 +235,10 @@ fn fixupRelocs(emit: *Emit) InnerError!void {
}
}
+fn mirUndefinedInstruction(emit: *Emit) InnerError!void {
+ return lowerToZoEnc(.ud2, emit.code);
+}
+
fn mirInterrupt(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
const tag = emit.mir.instructions.items(.tag)[inst];
assert(tag == .interrupt);
@@ -1279,6 +1284,7 @@ const Tag = enum {
push,
pop,
@"test",
+ ud2,
int3,
nop,
imul,
@@ -1571,6 +1577,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) OpCode {
.zo => return switch (tag) {
.ret_near => OpCode.init(&.{0xc3}),
.ret_far => OpCode.init(&.{0xcb}),
+ .ud2 => OpCode.init(&.{ 0x0F, 0x0B }),
.int3 => OpCode.init(&.{0xcc}),
.nop => OpCode.init(&.{0x90}),
.syscall => OpCode.init(&.{ 0x0f, 0x05 }),
src/arch/x86_64/Mir.zig
@@ -329,6 +329,9 @@ pub const Inst = struct {
/// TODO handle more cases
@"test",
+ /// Undefined Instruction
+ ud,
+
/// Breakpoint form:
/// 0b00 int3
interrupt,
src/codegen/c.zig
@@ -2741,6 +2741,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
.const_ty => unreachable, // excluded from function bodies
.arg => try airArg(f, inst),
+ .trap => try airTrap(f.object.writer()),
.breakpoint => try airBreakpoint(f.object.writer()),
.ret_addr => try airRetAddr(f, inst),
.frame_addr => try airFrameAddress(f, inst),
@@ -4428,6 +4429,11 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
return local;
}
+fn airTrap(writer: anytype) !CValue {
+ try writer.writeAll("zig_trap();\n");
+ return .none;
+}
+
fn airBreakpoint(writer: anytype) !CValue {
try writer.writeAll("zig_breakpoint();\n");
return .none;
src/codegen/llvm.zig
@@ -4590,6 +4590,7 @@ pub const FuncGen = struct {
.block => try self.airBlock(inst),
.br => try self.airBr(inst),
.switch_br => try self.airSwitchBr(inst),
+ .trap => try self.airTrap(inst),
.breakpoint => try self.airBreakpoint(inst),
.ret_addr => try self.airRetAddr(inst),
.frame_addr => try self.airFrameAddress(inst),
@@ -8256,6 +8257,13 @@ pub const FuncGen = struct {
return fg.load(ptr, ptr_ty);
}
+ fn airTrap(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
+ _ = inst;
+ const llvm_fn = self.getIntrinsic("llvm.trap", &.{});
+ _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, undefined, 0, .Cold, .Auto, "");
+ return null;
+ }
+
fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
_ = inst;
const llvm_fn = self.getIntrinsic("llvm.debugtrap", &.{});
src/Air.zig
@@ -232,7 +232,14 @@ pub const Inst = struct {
/// Result type is always noreturn; no instructions in a block follow this one.
/// Uses the `br` field.
br,
- /// Lowers to a hardware trap instruction, or the next best thing.
+ /// Lowers to a trap/jam instruction causing program abortion.
+ /// This may lower to an instruction known to be invalid.
+ /// Sometimes, for the lack of a better instruction, `trap` and `breakpoint` may compile down to the same code.
+ /// Result type is always noreturn; no instructions in a block follow this one.
+ trap,
+ /// Lowers to a trap instruction causing debuggers to break here, or the next best thing.
+ /// The debugger or something else may allow the program to resume after this point.
+ /// Sometimes, for the lack of a better instruction, `trap` and `breakpoint` may compile down to the same code.
/// Result type is always void.
breakpoint,
/// Yields the return address of the current function.
@@ -1186,6 +1193,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.ret,
.ret_load,
.unreach,
+ .trap,
=> return Type.initTag(.noreturn),
.breakpoint,
src/AstGen.zig
@@ -2631,6 +2631,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.repeat_inline,
.panic,
.panic_comptime,
+ .trap,
.check_comptime_control_flow,
=> {
noreturn_src_node = statement;
@@ -8105,7 +8106,7 @@ fn builtinCall(
.error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node),
.frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node),
.frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node),
- .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node),
+ .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node),
.type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),
.size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),
@@ -8178,6 +8179,11 @@ fn builtinCall(
try emitDbgNode(gz, node);
return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], if (gz.force_comptime) .panic_comptime else .panic);
},
+ .trap => {
+ try emitDbgNode(gz, node);
+ _ = try gz.addNode(.trap, node);
+ return rvalue(gz, ri, .void_value, node);
+ },
.error_to_int => {
const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
const result = try gz.addExtendedPayload(.error_to_int, Zir.Inst.UnNode{
src/BuiltinFn.zig
@@ -109,6 +109,7 @@ pub const Tag = enum {
sub_with_overflow,
tag_name,
This,
+ trap,
truncate,
Type,
type_info,
@@ -915,6 +916,13 @@ pub const list = list: {
.param_count = 0,
},
},
+ .{
+ "@trap",
+ .{
+ .tag = .trap,
+ .param_count = 0,
+ },
+ },
.{
"@truncate",
.{
src/Liveness.zig
@@ -226,6 +226,7 @@ pub fn categorizeOperand(
.ret_ptr,
.constant,
.const_ty,
+ .trap,
.breakpoint,
.dbg_stmt,
.dbg_inline_begin,
@@ -848,6 +849,7 @@ fn analyzeInst(
.ret_ptr,
.constant,
.const_ty,
+ .trap,
.breakpoint,
.dbg_stmt,
.dbg_inline_begin,
src/print_air.zig
@@ -194,6 +194,7 @@ const Writer = struct {
.c_va_end,
=> try w.writeUnOp(s, inst),
+ .trap,
.breakpoint,
.unreach,
.ret_addr,
src/print_zir.zig
@@ -410,6 +410,7 @@ const Writer = struct {
.alloc_inferred_comptime_mut,
.ret_ptr,
.ret_type,
+ .trap,
=> try self.writeNode(stream, inst),
.error_value,
src/Sema.zig
@@ -1101,6 +1101,7 @@ fn analyzeBodyInner(
.@"unreachable" => break sema.zirUnreachable(block, inst),
.panic => break sema.zirPanic(block, inst, false),
.panic_comptime => break sema.zirPanic(block, inst, true),
+ .trap => break sema.zirTrap(block, inst),
// zig fmt: on
.extended => ext: {
@@ -5144,6 +5145,14 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index, force_comptime: bo
return always_noreturn;
}
+fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
+ const src_node = sema.code.instructions.items(.data)[inst].node;
+ const src = LazySrcLoc.nodeOffset(src_node);
+ sema.src = src;
+ _ = try block.addNoOp(.trap);
+ return always_noreturn;
+}
+
fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
src/Zir.zig
@@ -617,7 +617,7 @@ pub const Inst = struct {
/// Uses the `un_node` field.
typeof_log2_int_type,
/// Asserts control-flow will not reach this instruction (`unreachable`).
- /// Uses the `unreachable` union field.
+ /// Uses the `@"unreachable"` union field.
@"unreachable",
/// Bitwise XOR. `^`
/// Uses the `pl_node` union field. Payload is `Bin`.
@@ -808,6 +808,9 @@ pub const Inst = struct {
panic,
/// Same as `panic` but forces comptime.
panic_comptime,
+ /// Implements `@trap`.
+ /// Uses the `node` field.
+ trap,
/// Implement builtin `@setRuntimeSafety`. Uses `un_node`.
set_runtime_safety,
/// Implement builtin `@sqrt`. Uses `un_node`.
@@ -1274,6 +1277,7 @@ pub const Inst = struct {
.repeat_inline,
.panic,
.panic_comptime,
+ .trap,
.check_comptime_control_flow,
=> true,
};
@@ -1549,6 +1553,7 @@ pub const Inst = struct {
.repeat_inline,
.panic,
.panic_comptime,
+ .trap,
.for_len,
.@"try",
.try_ptr,
@@ -1746,6 +1751,7 @@ pub const Inst = struct {
.error_name = .un_node,
.panic = .un_node,
.panic_comptime = .un_node,
+ .trap = .node,
.set_runtime_safety = .un_node,
.sqrt = .un_node,
.sin = .un_node,
@@ -1982,6 +1988,7 @@ pub const Inst = struct {
err_set_cast,
/// `operand` is payload index to `UnNode`.
await_nosuspend,
+ /// Implements `@breakpoint`.
/// `operand` is `src_node: i32`.
breakpoint,
/// Implements the `@select` builtin.
@@ -1995,7 +2002,7 @@ pub const Inst = struct {
int_to_error,
/// Implement builtin `@Type`.
/// `operand` is payload index to `UnNode`.
- /// `small` contains `NameStrategy
+ /// `small` contains `NameStrategy`.
reify,
/// Implements the `@asyncCall` builtin.
/// `operand` is payload index to `AsyncCall`.