mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-09 15:19:07 +00:00
add @trap builtin
This introduces a new builtin function that compiles down to something that results in an illegal instruction exception/interrupt. It can be used to exit a program abnormally. This implements the builtin for all backends.
This commit is contained in:
parent
d6bd00e855
commit
65368683ad
26 changed files with 178 additions and 10 deletions
|
|
@ -7818,12 +7818,14 @@ comptime {
|
||||||
<p>
|
<p>
|
||||||
This function inserts a platform-specific debug trap instruction which causes
|
This function inserts a platform-specific debug trap instruction which causes
|
||||||
debuggers to break there.
|
debuggers to break there.
|
||||||
|
Unlike for {#syntax#}@trap(){#endsyntax#}, execution may continue after this point if the program is resumed.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
This function is only valid within function scope.
|
This function is only valid within function scope.
|
||||||
</p>
|
</p>
|
||||||
|
{#see_also|@trap#}
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
|
||||||
{#header_open|@mulAdd#}
|
{#header_open|@mulAdd#}
|
||||||
<pre>{#syntax#}@mulAdd(comptime T: type, a: T, b: T, c: T) T{#endsyntax#}</pre>
|
<pre>{#syntax#}@mulAdd(comptime T: type, a: T, b: T, c: T) T{#endsyntax#}</pre>
|
||||||
<p>
|
<p>
|
||||||
|
|
@ -9393,6 +9395,19 @@ fn List(comptime T: type) type {
|
||||||
</p>
|
</p>
|
||||||
{#header_close#}
|
{#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#}
|
{#header_open|@truncate#}
|
||||||
<pre>{#syntax#}@truncate(comptime T: type, integer: anytype) T{#endsyntax#}</pre>
|
<pre>{#syntax#}@truncate(comptime T: type, integer: anytype) T{#endsyntax#}</pre>
|
||||||
<p>
|
<p>
|
||||||
|
|
|
||||||
10
lib/zig.h
10
lib/zig.h
|
|
@ -180,10 +180,16 @@ typedef char bool;
|
||||||
#define zig_export(sig, symbol, name) __asm(name " = " symbol)
|
#define zig_export(sig, symbol, name) __asm(name " = " symbol)
|
||||||
#endif
|
#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)
|
#if zig_has_builtin(debugtrap)
|
||||||
#define zig_breakpoint() __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__)
|
#elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
|
||||||
#define zig_breakpoint() __debugbreak()
|
#define zig_breakpoint() __debugbreak()
|
||||||
#elif defined(__i386__) || defined(__x86_64__)
|
#elif defined(__i386__) || defined(__x86_64__)
|
||||||
|
|
|
||||||
10
src/Air.zig
10
src/Air.zig
|
|
@ -232,7 +232,14 @@ pub const Inst = struct {
|
||||||
/// Result type is always noreturn; no instructions in a block follow this one.
|
/// Result type is always noreturn; no instructions in a block follow this one.
|
||||||
/// Uses the `br` field.
|
/// Uses the `br` field.
|
||||||
br,
|
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.
|
/// Result type is always void.
|
||||||
breakpoint,
|
breakpoint,
|
||||||
/// Yields the return address of the current function.
|
/// Yields the return address of the current function.
|
||||||
|
|
@ -1186,6 +1193,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
|
||||||
.ret,
|
.ret,
|
||||||
.ret_load,
|
.ret_load,
|
||||||
.unreach,
|
.unreach,
|
||||||
|
.trap,
|
||||||
=> return Type.initTag(.noreturn),
|
=> return Type.initTag(.noreturn),
|
||||||
|
|
||||||
.breakpoint,
|
.breakpoint,
|
||||||
|
|
|
||||||
|
|
@ -2631,6 +2631,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
|
||||||
.repeat_inline,
|
.repeat_inline,
|
||||||
.panic,
|
.panic,
|
||||||
.panic_comptime,
|
.panic_comptime,
|
||||||
|
.trap,
|
||||||
.check_comptime_control_flow,
|
.check_comptime_control_flow,
|
||||||
=> {
|
=> {
|
||||||
noreturn_src_node = statement;
|
noreturn_src_node = statement;
|
||||||
|
|
@ -8178,6 +8179,11 @@ fn builtinCall(
|
||||||
try emitDbgNode(gz, node);
|
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);
|
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 => {
|
.error_to_int => {
|
||||||
const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
|
const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
|
||||||
const result = try gz.addExtendedPayload(.error_to_int, Zir.Inst.UnNode{
|
const result = try gz.addExtendedPayload(.error_to_int, Zir.Inst.UnNode{
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,7 @@ pub const Tag = enum {
|
||||||
sub_with_overflow,
|
sub_with_overflow,
|
||||||
tag_name,
|
tag_name,
|
||||||
This,
|
This,
|
||||||
|
trap,
|
||||||
truncate,
|
truncate,
|
||||||
Type,
|
Type,
|
||||||
type_info,
|
type_info,
|
||||||
|
|
@ -915,6 +916,13 @@ pub const list = list: {
|
||||||
.param_count = 0,
|
.param_count = 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
.{
|
||||||
|
"@trap",
|
||||||
|
.{
|
||||||
|
.tag = .trap,
|
||||||
|
.param_count = 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
.{
|
.{
|
||||||
"@truncate",
|
"@truncate",
|
||||||
.{
|
.{
|
||||||
|
|
|
||||||
|
|
@ -226,6 +226,7 @@ pub fn categorizeOperand(
|
||||||
.ret_ptr,
|
.ret_ptr,
|
||||||
.constant,
|
.constant,
|
||||||
.const_ty,
|
.const_ty,
|
||||||
|
.trap,
|
||||||
.breakpoint,
|
.breakpoint,
|
||||||
.dbg_stmt,
|
.dbg_stmt,
|
||||||
.dbg_inline_begin,
|
.dbg_inline_begin,
|
||||||
|
|
@ -848,6 +849,7 @@ fn analyzeInst(
|
||||||
.ret_ptr,
|
.ret_ptr,
|
||||||
.constant,
|
.constant,
|
||||||
.const_ty,
|
.const_ty,
|
||||||
|
.trap,
|
||||||
.breakpoint,
|
.breakpoint,
|
||||||
.dbg_stmt,
|
.dbg_stmt,
|
||||||
.dbg_inline_begin,
|
.dbg_inline_begin,
|
||||||
|
|
|
||||||
|
|
@ -1101,6 +1101,7 @@ fn analyzeBodyInner(
|
||||||
.@"unreachable" => break sema.zirUnreachable(block, inst),
|
.@"unreachable" => break sema.zirUnreachable(block, inst),
|
||||||
.panic => break sema.zirPanic(block, inst, false),
|
.panic => break sema.zirPanic(block, inst, false),
|
||||||
.panic_comptime => break sema.zirPanic(block, inst, true),
|
.panic_comptime => break sema.zirPanic(block, inst, true),
|
||||||
|
.trap => break sema.zirTrap(block, inst),
|
||||||
// zig fmt: on
|
// zig fmt: on
|
||||||
|
|
||||||
.extended => ext: {
|
.extended => ext: {
|
||||||
|
|
@ -5144,6 +5145,14 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index, force_comptime: bo
|
||||||
return always_noreturn;
|
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 {
|
fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||||
const tracy = trace(@src());
|
const tracy = trace(@src());
|
||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
|
||||||
11
src/Zir.zig
11
src/Zir.zig
|
|
@ -617,7 +617,7 @@ pub const Inst = struct {
|
||||||
/// Uses the `un_node` field.
|
/// Uses the `un_node` field.
|
||||||
typeof_log2_int_type,
|
typeof_log2_int_type,
|
||||||
/// Asserts control-flow will not reach this instruction (`unreachable`).
|
/// Asserts control-flow will not reach this instruction (`unreachable`).
|
||||||
/// Uses the `unreachable` union field.
|
/// Uses the `@"unreachable"` union field.
|
||||||
@"unreachable",
|
@"unreachable",
|
||||||
/// Bitwise XOR. `^`
|
/// Bitwise XOR. `^`
|
||||||
/// Uses the `pl_node` union field. Payload is `Bin`.
|
/// Uses the `pl_node` union field. Payload is `Bin`.
|
||||||
|
|
@ -808,6 +808,9 @@ pub const Inst = struct {
|
||||||
panic,
|
panic,
|
||||||
/// Same as `panic` but forces comptime.
|
/// Same as `panic` but forces comptime.
|
||||||
panic_comptime,
|
panic_comptime,
|
||||||
|
/// Implements `@trap`.
|
||||||
|
/// Uses the `node` field.
|
||||||
|
trap,
|
||||||
/// Implement builtin `@setRuntimeSafety`. Uses `un_node`.
|
/// Implement builtin `@setRuntimeSafety`. Uses `un_node`.
|
||||||
set_runtime_safety,
|
set_runtime_safety,
|
||||||
/// Implement builtin `@sqrt`. Uses `un_node`.
|
/// Implement builtin `@sqrt`. Uses `un_node`.
|
||||||
|
|
@ -1274,6 +1277,7 @@ pub const Inst = struct {
|
||||||
.repeat_inline,
|
.repeat_inline,
|
||||||
.panic,
|
.panic,
|
||||||
.panic_comptime,
|
.panic_comptime,
|
||||||
|
.trap,
|
||||||
.check_comptime_control_flow,
|
.check_comptime_control_flow,
|
||||||
=> true,
|
=> true,
|
||||||
};
|
};
|
||||||
|
|
@ -1549,6 +1553,7 @@ pub const Inst = struct {
|
||||||
.repeat_inline,
|
.repeat_inline,
|
||||||
.panic,
|
.panic,
|
||||||
.panic_comptime,
|
.panic_comptime,
|
||||||
|
.trap,
|
||||||
.for_len,
|
.for_len,
|
||||||
.@"try",
|
.@"try",
|
||||||
.try_ptr,
|
.try_ptr,
|
||||||
|
|
@ -1746,6 +1751,7 @@ pub const Inst = struct {
|
||||||
.error_name = .un_node,
|
.error_name = .un_node,
|
||||||
.panic = .un_node,
|
.panic = .un_node,
|
||||||
.panic_comptime = .un_node,
|
.panic_comptime = .un_node,
|
||||||
|
.trap = .node,
|
||||||
.set_runtime_safety = .un_node,
|
.set_runtime_safety = .un_node,
|
||||||
.sqrt = .un_node,
|
.sqrt = .un_node,
|
||||||
.sin = .un_node,
|
.sin = .un_node,
|
||||||
|
|
@ -1982,6 +1988,7 @@ pub const Inst = struct {
|
||||||
err_set_cast,
|
err_set_cast,
|
||||||
/// `operand` is payload index to `UnNode`.
|
/// `operand` is payload index to `UnNode`.
|
||||||
await_nosuspend,
|
await_nosuspend,
|
||||||
|
/// Implements `@breakpoint`.
|
||||||
/// `operand` is `src_node: i32`.
|
/// `operand` is `src_node: i32`.
|
||||||
breakpoint,
|
breakpoint,
|
||||||
/// Implements the `@select` builtin.
|
/// Implements the `@select` builtin.
|
||||||
|
|
@ -1995,7 +2002,7 @@ pub const Inst = struct {
|
||||||
int_to_error,
|
int_to_error,
|
||||||
/// Implement builtin `@Type`.
|
/// Implement builtin `@Type`.
|
||||||
/// `operand` is payload index to `UnNode`.
|
/// `operand` is payload index to `UnNode`.
|
||||||
/// `small` contains `NameStrategy
|
/// `small` contains `NameStrategy`.
|
||||||
reify,
|
reify,
|
||||||
/// Implements the `@asyncCall` builtin.
|
/// Implements the `@asyncCall` builtin.
|
||||||
/// `operand` is payload index to `AsyncCall`.
|
/// `operand` is payload index to `AsyncCall`.
|
||||||
|
|
|
||||||
|
|
@ -737,6 +737,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||||
.bitcast => try self.airBitCast(inst),
|
.bitcast => try self.airBitCast(inst),
|
||||||
.block => try self.airBlock(inst),
|
.block => try self.airBlock(inst),
|
||||||
.br => try self.airBr(inst),
|
.br => try self.airBr(inst),
|
||||||
|
.trap => try self.airTrap(),
|
||||||
.breakpoint => try self.airBreakpoint(),
|
.breakpoint => try self.airBreakpoint(),
|
||||||
.ret_addr => try self.airRetAddr(inst),
|
.ret_addr => try self.airRetAddr(inst),
|
||||||
.frame_addr => try self.airFrameAddress(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 });
|
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 {
|
fn airBreakpoint(self: *Self) !void {
|
||||||
_ = try self.addInst(.{
|
_ = try self.addInst(.{
|
||||||
.tag = .brk,
|
.tag = .brk,
|
||||||
.data = .{ .imm16 = 1 },
|
.data = .{ .imm16 = 0xf000 },
|
||||||
});
|
});
|
||||||
return self.finishAirBookkeeping();
|
return self.finishAirBookkeeping();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -721,6 +721,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||||
.bitcast => try self.airBitCast(inst),
|
.bitcast => try self.airBitCast(inst),
|
||||||
.block => try self.airBlock(inst),
|
.block => try self.airBlock(inst),
|
||||||
.br => try self.airBr(inst),
|
.br => try self.airBr(inst),
|
||||||
|
.trap => try self.airTrap(),
|
||||||
.breakpoint => try self.airBreakpoint(),
|
.breakpoint => try self.airBreakpoint(),
|
||||||
.ret_addr => try self.airRetAddr(inst),
|
.ret_addr => try self.airRetAddr(inst),
|
||||||
.frame_addr => try self.airFrameAddress(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 });
|
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 {
|
fn airBreakpoint(self: *Self) !void {
|
||||||
_ = try self.addInst(.{
|
_ = try self.addInst(.{
|
||||||
.tag = .bkpt,
|
.tag = .bkpt,
|
||||||
|
|
|
||||||
|
|
@ -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
|
//! machine code
|
||||||
|
|
||||||
const Emit = @This();
|
const Emit = @This();
|
||||||
|
|
@ -15,7 +15,7 @@ const Target = std.Target;
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const Instruction = bits.Instruction;
|
const Instruction = bits.Instruction;
|
||||||
const Register = bits.Register;
|
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 DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
|
||||||
const CodeGen = @import("CodeGen.zig");
|
const CodeGen = @import("CodeGen.zig");
|
||||||
|
|
||||||
|
|
@ -100,6 +100,7 @@ pub fn emitMir(
|
||||||
|
|
||||||
.b => try emit.mirBranch(inst),
|
.b => try emit.mirBranch(inst),
|
||||||
|
|
||||||
|
.undefined_instruction => try emit.mirUndefinedInstruction(),
|
||||||
.bkpt => try emit.mirExceptionGeneration(inst),
|
.bkpt => try emit.mirExceptionGeneration(inst),
|
||||||
|
|
||||||
.blx => try emit.mirBranchExchange(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 {
|
fn mirExceptionGeneration(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||||
const tag = emit.mir.instructions.items(.tag)[inst];
|
const tag = emit.mir.instructions.items(.tag)[inst];
|
||||||
const imm16 = emit.mir.instructions.items(.data)[inst].imm16;
|
const imm16 = emit.mir.instructions.items(.data)[inst].imm16;
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@ pub const Inst = struct {
|
||||||
asr,
|
asr,
|
||||||
/// Branch
|
/// Branch
|
||||||
b,
|
b,
|
||||||
|
/// Undefined instruction
|
||||||
|
undefined_instruction,
|
||||||
/// Breakpoint
|
/// Breakpoint
|
||||||
bkpt,
|
bkpt,
|
||||||
/// Branch with Link and Exchange
|
/// Branch with Link and Exchange
|
||||||
|
|
|
||||||
|
|
@ -307,6 +307,9 @@ pub const Instruction = union(enum) {
|
||||||
fixed: u4 = 0b1111,
|
fixed: u4 = 0b1111,
|
||||||
cond: u4,
|
cond: u4,
|
||||||
},
|
},
|
||||||
|
undefined_instruction: packed struct {
|
||||||
|
imm32: u32 = 0xe7ffdefe,
|
||||||
|
},
|
||||||
breakpoint: packed struct {
|
breakpoint: packed struct {
|
||||||
imm4: u4,
|
imm4: u4,
|
||||||
fixed_1: u4 = 0b0111,
|
fixed_1: u4 = 0b0111,
|
||||||
|
|
@ -613,6 +616,7 @@ pub const Instruction = union(enum) {
|
||||||
.branch => |v| @bitCast(u32, v),
|
.branch => |v| @bitCast(u32, v),
|
||||||
.branch_exchange => |v| @bitCast(u32, v),
|
.branch_exchange => |v| @bitCast(u32, v),
|
||||||
.supervisor_call => |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),
|
.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 {
|
fn breakpoint(imm: u16) Instruction {
|
||||||
return Instruction{
|
return Instruction{
|
||||||
.breakpoint = .{
|
.breakpoint = .{
|
||||||
|
|
|
||||||
|
|
@ -550,6 +550,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||||
.bitcast => try self.airBitCast(inst),
|
.bitcast => try self.airBitCast(inst),
|
||||||
.block => try self.airBlock(inst),
|
.block => try self.airBlock(inst),
|
||||||
.br => try self.airBr(inst),
|
.br => try self.airBr(inst),
|
||||||
|
.trap => try self.airTrap(),
|
||||||
.breakpoint => try self.airBreakpoint(),
|
.breakpoint => try self.airBreakpoint(),
|
||||||
.ret_addr => try self.airRetAddr(inst),
|
.ret_addr => try self.airRetAddr(inst),
|
||||||
.frame_addr => try self.airFrameAddress(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 });
|
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 {
|
fn airBreakpoint(self: *Self) !void {
|
||||||
_ = try self.addInst(.{
|
_ = try self.addInst(.{
|
||||||
.tag = .ebreak,
|
.tag = .ebreak,
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ pub fn emitMir(
|
||||||
|
|
||||||
.ebreak => try emit.mirSystem(inst),
|
.ebreak => try emit.mirSystem(inst),
|
||||||
.ecall => try emit.mirSystem(inst),
|
.ecall => try emit.mirSystem(inst),
|
||||||
|
.unimp => try emit.mirSystem(inst),
|
||||||
|
|
||||||
.dbg_line => try emit.mirDbgLine(inst),
|
.dbg_line => try emit.mirDbgLine(inst),
|
||||||
|
|
||||||
|
|
@ -153,6 +154,7 @@ fn mirSystem(emit: *Emit, inst: Mir.Inst.Index) !void {
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
.ebreak => try emit.writeInstruction(Instruction.ebreak),
|
.ebreak => try emit.writeInstruction(Instruction.ebreak),
|
||||||
.ecall => try emit.writeInstruction(Instruction.ecall),
|
.ecall => try emit.writeInstruction(Instruction.ecall),
|
||||||
|
.unimp => try emit.writeInstruction(Instruction.unimp),
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ pub const Inst = struct {
|
||||||
dbg_epilogue_begin,
|
dbg_epilogue_begin,
|
||||||
/// Pseudo-instruction: Update debug line
|
/// Pseudo-instruction: Update debug line
|
||||||
dbg_line,
|
dbg_line,
|
||||||
|
unimp,
|
||||||
ebreak,
|
ebreak,
|
||||||
ecall,
|
ecall,
|
||||||
jalr,
|
jalr,
|
||||||
|
|
|
||||||
|
|
@ -380,6 +380,7 @@ pub const Instruction = union(enum) {
|
||||||
|
|
||||||
pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000);
|
pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000);
|
||||||
pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001);
|
pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001);
|
||||||
|
pub const unimp = iType(0, 0, .zero, .zero, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Register = enum(u6) {
|
pub const Register = enum(u6) {
|
||||||
|
|
|
||||||
|
|
@ -566,6 +566,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||||
.bitcast => try self.airBitCast(inst),
|
.bitcast => try self.airBitCast(inst),
|
||||||
.block => try self.airBlock(inst),
|
.block => try self.airBlock(inst),
|
||||||
.br => try self.airBr(inst),
|
.br => try self.airBr(inst),
|
||||||
|
.trap => try self.airTrap(),
|
||||||
.breakpoint => try self.airBreakpoint(),
|
.breakpoint => try self.airBreakpoint(),
|
||||||
.ret_addr => @panic("TODO try self.airRetAddr(inst)"),
|
.ret_addr => @panic("TODO try self.airRetAddr(inst)"),
|
||||||
.frame_addr => @panic("TODO try self.airFrameAddress(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 });
|
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 {
|
fn airBreakpoint(self: *Self) !void {
|
||||||
// ta 0x01
|
// ta 0x01
|
||||||
_ = try self.addInst(.{
|
_ = try self.addInst(.{
|
||||||
|
|
|
||||||
|
|
@ -1829,6 +1829,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
|
||||||
.arg => func.airArg(inst),
|
.arg => func.airArg(inst),
|
||||||
.bitcast => func.airBitcast(inst),
|
.bitcast => func.airBitcast(inst),
|
||||||
.block => func.airBlock(inst),
|
.block => func.airBlock(inst),
|
||||||
|
.trap => func.airTrap(inst),
|
||||||
.breakpoint => func.airBreakpoint(inst),
|
.breakpoint => func.airBreakpoint(inst),
|
||||||
.br => func.airBr(inst),
|
.br => func.airBr(inst),
|
||||||
.bool_to_int => func.airBoolToInt(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});
|
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 {
|
fn airBreakpoint(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
|
||||||
// unsupported by wasm itfunc. Can be implemented once we support DWARF
|
// unsupported by wasm itfunc. Can be implemented once we support DWARF
|
||||||
// for wasm
|
// for wasm
|
||||||
|
|
|
||||||
|
|
@ -638,6 +638,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||||
.bitcast => try self.airBitCast(inst),
|
.bitcast => try self.airBitCast(inst),
|
||||||
.block => try self.airBlock(inst),
|
.block => try self.airBlock(inst),
|
||||||
.br => try self.airBr(inst),
|
.br => try self.airBr(inst),
|
||||||
|
.trap => try self.airTrap(),
|
||||||
.breakpoint => try self.airBreakpoint(),
|
.breakpoint => try self.airBreakpoint(),
|
||||||
.ret_addr => try self.airRetAddr(inst),
|
.ret_addr => try self.airRetAddr(inst),
|
||||||
.frame_addr => try self.airFrameAddress(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 {
|
fn airBreakpoint(self: *Self) !void {
|
||||||
_ = try self.addInst(.{
|
_ = try self.addInst(.{
|
||||||
.tag = .interrupt,
|
.tag = .interrupt,
|
||||||
|
|
|
||||||
|
|
@ -166,6 +166,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
|
||||||
|
|
||||||
.@"test" => try emit.mirTest(inst),
|
.@"test" => try emit.mirTest(inst),
|
||||||
|
|
||||||
|
.ud => try emit.mirUndefinedInstruction(),
|
||||||
.interrupt => try emit.mirInterrupt(inst),
|
.interrupt => try emit.mirInterrupt(inst),
|
||||||
.nop => {}, // just skip it
|
.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 {
|
fn mirInterrupt(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
|
||||||
const tag = emit.mir.instructions.items(.tag)[inst];
|
const tag = emit.mir.instructions.items(.tag)[inst];
|
||||||
assert(tag == .interrupt);
|
assert(tag == .interrupt);
|
||||||
|
|
@ -1279,6 +1284,7 @@ const Tag = enum {
|
||||||
push,
|
push,
|
||||||
pop,
|
pop,
|
||||||
@"test",
|
@"test",
|
||||||
|
ud2,
|
||||||
int3,
|
int3,
|
||||||
nop,
|
nop,
|
||||||
imul,
|
imul,
|
||||||
|
|
@ -1571,6 +1577,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) OpCode {
|
||||||
.zo => return switch (tag) {
|
.zo => return switch (tag) {
|
||||||
.ret_near => OpCode.init(&.{0xc3}),
|
.ret_near => OpCode.init(&.{0xc3}),
|
||||||
.ret_far => OpCode.init(&.{0xcb}),
|
.ret_far => OpCode.init(&.{0xcb}),
|
||||||
|
.ud2 => OpCode.init(&.{ 0x0F, 0x0B }),
|
||||||
.int3 => OpCode.init(&.{0xcc}),
|
.int3 => OpCode.init(&.{0xcc}),
|
||||||
.nop => OpCode.init(&.{0x90}),
|
.nop => OpCode.init(&.{0x90}),
|
||||||
.syscall => OpCode.init(&.{ 0x0f, 0x05 }),
|
.syscall => OpCode.init(&.{ 0x0f, 0x05 }),
|
||||||
|
|
|
||||||
|
|
@ -329,6 +329,9 @@ pub const Inst = struct {
|
||||||
/// TODO handle more cases
|
/// TODO handle more cases
|
||||||
@"test",
|
@"test",
|
||||||
|
|
||||||
|
/// Undefined Instruction
|
||||||
|
ud,
|
||||||
|
|
||||||
/// Breakpoint form:
|
/// Breakpoint form:
|
||||||
/// 0b00 int3
|
/// 0b00 int3
|
||||||
interrupt,
|
interrupt,
|
||||||
|
|
|
||||||
|
|
@ -2741,6 +2741,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
|
||||||
.const_ty => unreachable, // excluded from function bodies
|
.const_ty => unreachable, // excluded from function bodies
|
||||||
.arg => try airArg(f, inst),
|
.arg => try airArg(f, inst),
|
||||||
|
|
||||||
|
.trap => try airTrap(f.object.writer()),
|
||||||
.breakpoint => try airBreakpoint(f.object.writer()),
|
.breakpoint => try airBreakpoint(f.object.writer()),
|
||||||
.ret_addr => try airRetAddr(f, inst),
|
.ret_addr => try airRetAddr(f, inst),
|
||||||
.frame_addr => try airFrameAddress(f, inst),
|
.frame_addr => try airFrameAddress(f, inst),
|
||||||
|
|
@ -4428,6 +4429,11 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||||
return local;
|
return local;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn airTrap(writer: anytype) !CValue {
|
||||||
|
try writer.writeAll("zig_trap();\n");
|
||||||
|
return .none;
|
||||||
|
}
|
||||||
|
|
||||||
fn airBreakpoint(writer: anytype) !CValue {
|
fn airBreakpoint(writer: anytype) !CValue {
|
||||||
try writer.writeAll("zig_breakpoint();\n");
|
try writer.writeAll("zig_breakpoint();\n");
|
||||||
return .none;
|
return .none;
|
||||||
|
|
|
||||||
|
|
@ -4590,6 +4590,7 @@ pub const FuncGen = struct {
|
||||||
.block => try self.airBlock(inst),
|
.block => try self.airBlock(inst),
|
||||||
.br => try self.airBr(inst),
|
.br => try self.airBr(inst),
|
||||||
.switch_br => try self.airSwitchBr(inst),
|
.switch_br => try self.airSwitchBr(inst),
|
||||||
|
.trap => try self.airTrap(inst),
|
||||||
.breakpoint => try self.airBreakpoint(inst),
|
.breakpoint => try self.airBreakpoint(inst),
|
||||||
.ret_addr => try self.airRetAddr(inst),
|
.ret_addr => try self.airRetAddr(inst),
|
||||||
.frame_addr => try self.airFrameAddress(inst),
|
.frame_addr => try self.airFrameAddress(inst),
|
||||||
|
|
@ -8256,6 +8257,13 @@ pub const FuncGen = struct {
|
||||||
return fg.load(ptr, ptr_ty);
|
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 {
|
fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
|
||||||
_ = inst;
|
_ = inst;
|
||||||
const llvm_fn = self.getIntrinsic("llvm.debugtrap", &.{});
|
const llvm_fn = self.getIntrinsic("llvm.debugtrap", &.{});
|
||||||
|
|
|
||||||
|
|
@ -194,6 +194,7 @@ const Writer = struct {
|
||||||
.c_va_end,
|
.c_va_end,
|
||||||
=> try w.writeUnOp(s, inst),
|
=> try w.writeUnOp(s, inst),
|
||||||
|
|
||||||
|
.trap,
|
||||||
.breakpoint,
|
.breakpoint,
|
||||||
.unreach,
|
.unreach,
|
||||||
.ret_addr,
|
.ret_addr,
|
||||||
|
|
|
||||||
|
|
@ -410,6 +410,7 @@ const Writer = struct {
|
||||||
.alloc_inferred_comptime_mut,
|
.alloc_inferred_comptime_mut,
|
||||||
.ret_ptr,
|
.ret_ptr,
|
||||||
.ret_type,
|
.ret_type,
|
||||||
|
.trap,
|
||||||
=> try self.writeNode(stream, inst),
|
=> try self.writeNode(stream, inst),
|
||||||
|
|
||||||
.error_value,
|
.error_value,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue