From d6bd00e85500fa1a7909695ae5943be438f7521d Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Fri, 3 Mar 2023 17:30:18 +0100 Subject: [PATCH 1/3] Zir: move set_cold from Inst.Tag to Inst.Extended If I could mark a builtin function as cold, I would mark @setCold as cold. We have run out of `Zir.Inst.Tag`s so I had to move a tag from Zir.Inst.Tag to Zir.Inst.Extended. This is because a new noreturn builtin will be added and noreturn builtins cannot be part of Inst.Tag: ``` /// `noreturn` instructions may not go here; they must be part of the main `Tag` enum. pub const Extended = enum(u16) { ``` Here's another reason I went for @setCold: ``` $ git grep setRuntimeSafety | wc -l 322 $ git grep setCold | wc -l 79 $ git grep setEvalBranchQuota | wc -l 82 ``` This also simply removes @setCold from Autodoc and the docs frontend because as far as I could understand it, builtins represented using Zir extended instructions are not yet supported because I couldn't find @setStackAlign or @setFloatMode there, either. --- lib/docs/main.js | 4 ---- src/AstGen.zig | 13 ++++++++++--- src/Autodoc.zig | 1 - src/Sema.zig | 18 +++++++++--------- src/Zir.zig | 10 ++++------ src/print_zir.zig | 2 +- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/docs/main.js b/lib/docs/main.js index a0647bbe61..fc99b2f861 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -1187,10 +1187,6 @@ const NAV_MODES = { payloadHtml += "panic"; break; } - case "set_cold": { - payloadHtml += "setCold"; - break; - } case "set_runtime_safety": { payloadHtml += "setRuntimeSafety"; break; diff --git a/src/AstGen.zig b/src/AstGen.zig index 41a8ccadb2..679fc2df0c 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -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, }, @@ -2658,7 +2659,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, @@ -8078,6 +8078,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); @@ -8111,7 +8119,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), diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 3cf3fff4c0..15d90b104b 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -1338,7 +1338,6 @@ fn walkInstruction( .embed_file, .error_name, .panic, - .set_cold, // @check .set_runtime_safety, // @check .sqrt, .sin, diff --git a/src/Sema.zig b/src/Sema.zig index f9a6f39867..4702d10688 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1167,6 +1167,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); @@ -1304,11 +1309,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; @@ -5721,10 +5721,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; } diff --git a/src/Zir.zig b/src/Zir.zig index 4dd2386c51..c7f2141dcc 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -808,8 +808,6 @@ pub const Inst = struct { panic, /// Same as `panic` but forces comptime. panic_comptime, - /// Implement builtin `@setCold`. Uses `un_node`. - set_cold, /// Implement builtin `@setRuntimeSafety`. Uses `un_node`. set_runtime_safety, /// Implement builtin `@sqrt`. Uses `un_node`. @@ -1187,7 +1185,6 @@ pub const Inst = struct { .bool_to_int, .embed_file, .error_name, - .set_cold, .set_runtime_safety, .sqrt, .sin, @@ -1323,7 +1320,6 @@ pub const Inst = struct { .validate_deref, .@"export", .export_value, - .set_cold, .set_runtime_safety, .memcpy, .memset, @@ -1561,7 +1557,7 @@ pub const Inst = struct { => false, .extended => switch (data.extended.opcode) { - .breakpoint, .fence => true, + .fence, .set_cold, .breakpoint => true, else => false, }, }; @@ -1750,7 +1746,6 @@ pub const Inst = struct { .error_name = .un_node, .panic = .un_node, .panic_comptime = .un_node, - .set_cold = .un_node, .set_runtime_safety = .un_node, .sqrt = .un_node, .sin = .un_node, @@ -1979,6 +1974,9 @@ 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, diff --git a/src/print_zir.zig b/src/print_zir.zig index fb9031296d..5ec9fbcdfc 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -196,7 +196,6 @@ const Writer = struct { .error_name, .panic, .panic_comptime, - .set_cold, .set_runtime_safety, .sqrt, .sin, @@ -503,6 +502,7 @@ const Writer = struct { .fence, .set_float_mode, .set_align_stack, + .set_cold, .wasm_memory_size, .error_to_int, .int_to_error, From 65368683ad92b858d0a391cb29d37c0476784b40 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Fri, 3 Mar 2023 18:35:03 +0100 Subject: [PATCH 2/3] 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. --- doc/langref.html.in | 17 ++++++++++++++++- lib/zig.h | 10 ++++++++-- src/Air.zig | 10 +++++++++- src/AstGen.zig | 8 +++++++- src/BuiltinFn.zig | 8 ++++++++ src/Liveness.zig | 2 ++ src/Sema.zig | 9 +++++++++ src/Zir.zig | 11 +++++++++-- src/arch/aarch64/CodeGen.zig | 11 ++++++++++- src/arch/arm/CodeGen.zig | 9 +++++++++ src/arch/arm/Emit.zig | 9 +++++++-- src/arch/arm/Mir.zig | 2 ++ src/arch/arm/bits.zig | 11 +++++++++++ src/arch/riscv64/CodeGen.zig | 9 +++++++++ src/arch/riscv64/Emit.zig | 2 ++ src/arch/riscv64/Mir.zig | 1 + src/arch/riscv64/bits.zig | 1 + src/arch/sparc64/CodeGen.zig | 16 ++++++++++++++++ src/arch/wasm/CodeGen.zig | 6 ++++++ src/arch/x86_64/CodeGen.zig | 10 ++++++++++ src/arch/x86_64/Emit.zig | 7 +++++++ src/arch/x86_64/Mir.zig | 3 +++ src/codegen/c.zig | 6 ++++++ src/codegen/llvm.zig | 8 ++++++++ src/print_air.zig | 1 + src/print_zir.zig | 1 + 26 files changed, 178 insertions(+), 10 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index e016ef13f8..0290d3acd6 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7818,12 +7818,14 @@ comptime {

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.

This function is only valid within function scope.

- + {#see_also|@trap#} {#header_close#} + {#header_open|@mulAdd#}
{#syntax#}@mulAdd(comptime T: type, a: T, b: T, c: T) T{#endsyntax#}

@@ -9393,6 +9395,19 @@ fn List(comptime T: type) type {

{#header_close#} + {#header_open|@trap#} +
{#syntax#}@trap() noreturn{#endsyntax#}
+

+ 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. +

+

+ This function is only valid within function scope. +

+ {#see_also|@breakpoint#} + {#header_close#} + {#header_open|@truncate#}
{#syntax#}@truncate(comptime T: type, integer: anytype) T{#endsyntax#}

diff --git a/lib/zig.h b/lib/zig.h index c10720d1bd..f3ad7db8a1 100644 --- a/lib/zig.h +++ b/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__) diff --git a/src/Air.zig b/src/Air.zig index 3ebdd319de..4646dcc89e 100644 --- a/src/Air.zig +++ b/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, diff --git a/src/AstGen.zig b/src/AstGen.zig index 679fc2df0c..fd51e73cf9 100644 --- a/src/AstGen.zig +++ b/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{ diff --git a/src/BuiltinFn.zig b/src/BuiltinFn.zig index 20edbabe47..79c6617483 100644 --- a/src/BuiltinFn.zig +++ b/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", .{ diff --git a/src/Liveness.zig b/src/Liveness.zig index 481cf25d04..8dc81aa165 100644 --- a/src/Liveness.zig +++ b/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, diff --git a/src/Sema.zig b/src/Sema.zig index 4702d10688..8940527bc0 100644 --- a/src/Sema.zig +++ b/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(); diff --git a/src/Zir.zig b/src/Zir.zig index c7f2141dcc..b8ea2ea295 100644 --- a/src/Zir.zig +++ b/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`. diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 818b04f890..a42d0539f2 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/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(); } diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index ceabe70438..cecda8fd4a 100644 --- a/src/arch/arm/CodeGen.zig +++ b/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, diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index 17540f0968..17415318de 100644 --- a/src/arch/arm/Emit.zig +++ b/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; diff --git a/src/arch/arm/Mir.zig b/src/arch/arm/Mir.zig index 07a8384c2c..736d0574bb 100644 --- a/src/arch/arm/Mir.zig +++ b/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 diff --git a/src/arch/arm/bits.zig b/src/arch/arm/bits.zig index 8e76ae9409..185c4ed921 100644 --- a/src/arch/arm/bits.zig +++ b/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 = .{ diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index afcf4b0bb7..0b45982fb3 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/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, diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index 387c735896..3b330cbd3f 100644 --- a/src/arch/riscv64/Emit.zig +++ b/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, } } diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index 97accb7642..8905b24c3c 100644 --- a/src/arch/riscv64/Mir.zig +++ b/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, diff --git a/src/arch/riscv64/bits.zig b/src/arch/riscv64/bits.zig index 6b94927df8..7b3ff0bfe9 100644 --- a/src/arch/riscv64/bits.zig +++ b/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) { diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index c8f77fe702..1b7290ddce 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/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(.{ diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 2f191fd834..d388bc8fab 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/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 diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 53d38f520a..70b51e50fd 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/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, diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 12c19915c6..e521de4bd4 100644 --- a/src/arch/x86_64/Emit.zig +++ b/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 }), diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 112d9a5982..ba71f4cddd 100644 --- a/src/arch/x86_64/Mir.zig +++ b/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, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index cf428d4bd6..c0585c3a4a 100644 --- a/src/codegen/c.zig +++ b/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; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 6f240b88f5..1f8473ac32 100644 --- a/src/codegen/llvm.zig +++ b/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", &.{}); diff --git a/src/print_air.zig b/src/print_air.zig index 447af5a9c7..f5c06daae2 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -194,6 +194,7 @@ const Writer = struct { .c_va_end, => try w.writeUnOp(s, inst), + .trap, .breakpoint, .unreach, .ret_addr, diff --git a/src/print_zir.zig b/src/print_zir.zig index 5ec9fbcdfc..5e7d0d45de 100644 --- a/src/print_zir.zig +++ b/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, From 4eb3f50fcf6fcfb6b8013571be00b9eeeb909833 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Fri, 3 Mar 2023 19:59:18 +0100 Subject: [PATCH 3/3] Wasm @breakpoint: emit unreachable This should improve the developer debugging experience. --- src/arch/wasm/CodeGen.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index d388bc8fab..dbabb436c8 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3298,6 +3298,7 @@ fn airTrap(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 // for wasm + try func.addTag(.@"unreachable"); func.finishAir(inst, .none, &.{}); }