Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion doc/langref.html.in
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This function is only valid within function scope.
Outside function scope, this builtin causes a compile error.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I don't think it does? I didn't check for that but I'm guessing it has or should have the same behavior as @breakpoint in that regard:

$ cat x.zig
const std = @import("std");

const x = @breakpoint();

test {
    std.debug.print("{}\n", .{x});
}
$ zig test x.zig
Test [1/1] test_0... void
All 1 tests passed.

So I don't know if that should be a compile error, but maybe because it returns noreturn instead of void. So, should it not be valid within test scope either? Not sure.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my suggested docs are correct; the compiler is wrong :-)

</p>
{#see_also|@breakpoint#}
{#header_close#}

{#header_open|@truncate#}
<pre>{#syntax#}@truncate(comptime T: type, integer: anytype) T{#endsyntax#}</pre>
<p>
Expand Down
4 changes: 0 additions & 4 deletions lib/docs/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -1187,10 +1187,6 @@ const NAV_MODES = {
payloadHtml += "panic";
break;
}
case "set_cold": {
payloadHtml += "setCold";
break;
}
case "set_runtime_safety": {
payloadHtml += "setRuntimeSafety";
break;
Expand Down
10 changes: 8 additions & 2 deletions lib/zig.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#define zig_trap() raise(SIGILL)
#define zig_trap() raise(SIGTRAP)

Copy link
Author

@ghost ghost Mar 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The references I quoted and linked in this comment might be of interest.

The SIGTRAP signal is sent to a process when an exception (or trap) occurs: a condition that a debugger has requested to be informed of

(they talk about debuggers in SIGTRAP so this is used for debugtrap below)

and then SIGILL (used for trap):

The SIGILL signal is sent to a process when it attempts to execute an illegal, malformed, unknown, or privileged instruction

I know the signal names themselves make this seem like an odd choice but that's why I did it this way.

#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__)
Expand Down
10 changes: 9 additions & 1 deletion src/Air.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -1186,6 +1193,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.ret,
.ret_load,
.unreach,
.trap,
=> return Type.initTag(.noreturn),

.breakpoint,
Expand Down
21 changes: 17 additions & 4 deletions src/AstGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2609,8 +2609,9 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {
.breakpoint,
.fence,
.set_align_stack,
.set_float_mode,
.set_align_stack,
.set_cold,
=> break :b true,
else => break :b false,
},
Expand All @@ -2630,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;
Expand Down Expand Up @@ -2658,7 +2660,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.validate_struct_init_comptime,
.validate_array_init,
.validate_array_init_comptime,
.set_cold,
.set_runtime_safety,
.closure_capture,
.memcpy,
Expand Down Expand Up @@ -8078,6 +8079,14 @@ fn builtinCall(
});
return rvalue(gz, ri, result, node);
},
.set_cold => {
const order = try expr(gz, scope, ri, params[0]);
const result = try gz.addExtendedPayload(.set_cold, Zir.Inst.UnNode{
.node = gz.nodeIndexToRelative(node),
.operand = order,
});
return rvalue(gz, ri, result, node);
},

.src => {
const token_starts = tree.tokens.items(.start);
Expand All @@ -8097,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),
Expand All @@ -8111,7 +8120,6 @@ fn builtinCall(
.bool_to_int => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .bool_to_int),
.embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], .embed_file),
.error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .anyerror_type } }, params[0], .error_name),
.set_cold => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .set_cold),
.set_runtime_safety => return simpleUnOp(gz, scope, ri, node, bool_ri, params[0], .set_runtime_safety),
.sqrt => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sqrt),
.sin => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sin),
Expand Down Expand Up @@ -8171,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{
Expand Down
1 change: 0 additions & 1 deletion src/Autodoc.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1338,7 +1338,6 @@ fn walkInstruction(
.embed_file,
.error_name,
.panic,
.set_cold, // @check
.set_runtime_safety, // @check
.sqrt,
.sin,
Expand Down
8 changes: 8 additions & 0 deletions src/BuiltinFn.zig
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ pub const Tag = enum {
sub_with_overflow,
tag_name,
This,
trap,
truncate,
Type,
type_info,
Expand Down Expand Up @@ -915,6 +916,13 @@ pub const list = list: {
.param_count = 0,
},
},
.{
"@trap",
.{
.tag = .trap,
.param_count = 0,
},
},
.{
"@truncate",
.{
Expand Down
2 changes: 2 additions & 0 deletions src/Liveness.zig
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ pub fn categorizeOperand(
.ret_ptr,
.constant,
.const_ty,
.trap,
.breakpoint,
.dbg_stmt,
.dbg_inline_begin,
Expand Down Expand Up @@ -848,6 +849,7 @@ fn analyzeInst(
.ret_ptr,
.constant,
.const_ty,
.trap,
.breakpoint,
.dbg_stmt,
.dbg_inline_begin,
Expand Down
27 changes: 18 additions & 9 deletions src/Sema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -1167,6 +1168,11 @@ fn analyzeBodyInner(
i += 1;
continue;
},
.set_cold => {
try sema.zirSetCold(block, extended);
i += 1;
continue;
},
.breakpoint => {
if (!block.is_comptime) {
_ = try block.addNoOp(.breakpoint);
Expand Down Expand Up @@ -1304,11 +1310,6 @@ fn analyzeBodyInner(
i += 1;
continue;
},
.set_cold => {
try sema.zirSetCold(block, inst);
i += 1;
continue;
},
.set_runtime_safety => {
try sema.zirSetRuntimeSafety(block, inst);
i += 1;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -5721,10 +5730,10 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst
gop.value_ptr.* = .{ .alignment = alignment, .src = src };
}

fn zirSetCold(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const is_cold = try sema.resolveConstBool(block, operand_src, inst_data.operand, "operand to @setCold must be comptime-known");
fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
const is_cold = try sema.resolveConstBool(block, operand_src, extra.operand, "operand to @setCold must be comptime-known");
const func = sema.func orelse return; // does nothing outside a function
func.is_cold = is_cold;
}
Expand Down
21 changes: 13 additions & 8 deletions src/Zir.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -808,8 +808,9 @@ pub const Inst = struct {
panic,
/// Same as `panic` but forces comptime.
panic_comptime,
/// Implement builtin `@setCold`. Uses `un_node`.
set_cold,
/// Implements `@trap`.
/// Uses the `node` field.
trap,
/// Implement builtin `@setRuntimeSafety`. Uses `un_node`.
set_runtime_safety,
/// Implement builtin `@sqrt`. Uses `un_node`.
Expand Down Expand Up @@ -1187,7 +1188,6 @@ pub const Inst = struct {
.bool_to_int,
.embed_file,
.error_name,
.set_cold,
.set_runtime_safety,
.sqrt,
.sin,
Expand Down Expand Up @@ -1277,6 +1277,7 @@ pub const Inst = struct {
.repeat_inline,
.panic,
.panic_comptime,
.trap,
.check_comptime_control_flow,
=> true,
};
Expand Down Expand Up @@ -1323,7 +1324,6 @@ pub const Inst = struct {
.validate_deref,
.@"export",
.export_value,
.set_cold,
.set_runtime_safety,
.memcpy,
.memset,
Expand Down Expand Up @@ -1553,6 +1553,7 @@ pub const Inst = struct {
.repeat_inline,
.panic,
.panic_comptime,
.trap,
.for_len,
.@"try",
.try_ptr,
Expand All @@ -1561,7 +1562,7 @@ pub const Inst = struct {
=> false,

.extended => switch (data.extended.opcode) {
.breakpoint, .fence => true,
.fence, .set_cold, .breakpoint => true,
else => false,
},
};
Expand Down Expand Up @@ -1750,7 +1751,7 @@ pub const Inst = struct {
.error_name = .un_node,
.panic = .un_node,
.panic_comptime = .un_node,
.set_cold = .un_node,
.trap = .node,
.set_runtime_safety = .un_node,
.sqrt = .un_node,
.sin = .un_node,
Expand Down Expand Up @@ -1979,11 +1980,15 @@ pub const Inst = struct {
/// Implement builtin `@setAlignStack`.
/// `operand` is payload index to `UnNode`.
set_align_stack,
/// Implements `@setCold`.
/// `operand` is payload index to `UnNode`.
set_cold,
/// Implements the `@errSetCast` builtin.
/// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.
err_set_cast,
/// `operand` is payload index to `UnNode`.
await_nosuspend,
/// Implements `@breakpoint`.
/// `operand` is `src_node: i32`.
breakpoint,
/// Implements the `@select` builtin.
Expand All @@ -1997,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`.
Expand Down
11 changes: 10 additions & 1 deletion src/arch/aarch64/CodeGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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();
}
Expand Down
Loading