kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 7eaf7bf92d5dc73af08024a55e2495bd71439b25
parent 52897e0145b4e42aa1cfd85e10839ed370938ba1
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue,  2 Jun 2026 05:36:01 -0700

cg: make unreachable a first-class terminator hook + IR op (Track 4b)

Diffstat:
Msrc/arch/aa64/native.c | 1-
Msrc/arch/c_target/c_emit.c | 13++++++++++---
Msrc/arch/c_target/c_emit.h | 1+
Msrc/arch/c_target/ir_emit.c | 3+++
Msrc/arch/check_target.c | 3+++
Msrc/arch/rv64/native.c | 1-
Msrc/arch/wasm/emit.c | 7++++---
Msrc/arch/wasm/ir_emit.c | 4++++
Msrc/arch/x64/native.c | 1-
Msrc/cg/cgtarget.h | 15++++++++++++---
Msrc/cg/control.c | 2+-
Msrc/cg/ir.h | 1+
Msrc/cg/ir_dump.c | 1+
Msrc/cg/ir_recorder.c | 5+++++
Msrc/cg/native_direct_target.c | 8++++++++
Msrc/interp/engine.c | 9+++++----
Msrc/interp/interp.h | 3++-
Msrc/interp/lower.c | 2++
Msrc/opt/cg_ir_lower.c | 15+++++++++++----
Msrc/opt/ir.h | 1+
Msrc/opt/ir_print.c | 2++
Msrc/opt/pass_analysis.c | 4++--
Msrc/opt/pass_cfg.c | 7+++++--
Msrc/opt/pass_dce.c | 1+
Msrc/opt/pass_lower.c | 4++--
Msrc/opt/pass_native_emit.c | 7+++++--
Msrc/opt/pass_o2.c | 4++--
Msrc/opt/pass_ssa.c | 4++--
28 files changed, 95 insertions(+), 34 deletions(-)

diff --git a/src/arch/aa64/native.c b/src/arch/aa64/native.c @@ -3347,7 +3347,6 @@ static void aa_intrinsic(NativeTarget* t, IntrinKind kind, case INTRIN_PREFETCH: return; case INTRIN_TRAP: - case INTRIN_UNREACHABLE: aa_trap(t); return; default: diff --git a/src/arch/c_target/c_emit.c b/src/arch/c_target/c_emit.c @@ -2391,6 +2391,16 @@ void c_emit_ret( t->last_was_terminator = 1; } +/* === unreachable === + * Control terminator for statically-unreachable code (the C + * __builtin_unreachable point). Ends the basic block; emit the host + * compiler's `__builtin_unreachable()` so it sees the path is dead. */ +void c_emit_unreachable(CTarget* t) { + if (t->last_was_terminator) return; + cbuf_puts(&t->body, " __builtin_unreachable();\n"); + t->last_was_terminator = 1; +} + /* === alias === * `cfree_cg_alias` makes alias_sym refer to target_sym's body. In obj-file * land that's two ObjSyms sharing a (section_id, value); in C source we @@ -2518,9 +2528,6 @@ void c_emit_intrinsic(CTarget* t, IntrinKind k, Operand* dsts, u32 ndst, const Operand* args, u32 narg) { SrcLoc loc = t->cur_fn ? t->cur_fn->loc : (SrcLoc){0, 0, 0}; switch (k) { - case INTRIN_UNREACHABLE: - cbuf_puts(&t->body, " __builtin_unreachable();\n"); - return; case INTRIN_TRAP: cbuf_puts(&t->body, " __builtin_trap();\n"); return; diff --git a/src/arch/c_target/c_emit.h b/src/arch/c_target/c_emit.h @@ -194,6 +194,7 @@ void c_emit_alias(CTarget*, ObjSymId, ObjSymId, CfreeCgTypeId); /* Re-emit a file-scope `__asm__("...")` block verbatim at TU scope. */ void c_emit_file_scope_asm(CTarget*, const char* src, size_t len); void c_emit_ret(CTarget*, const CGLocal*, u32); +void c_emit_unreachable(CTarget*); void c_emit_load_imm(CTarget*, Operand, i64); void c_emit_load_const(CTarget*, Operand, ConstBytes); void c_emit_copy(CTarget*, Operand, Operand); diff --git a/src/arch/c_target/ir_emit.c b/src/arch/c_target/ir_emit.c @@ -129,6 +129,9 @@ static void ir_emit_inst(CIrEmitter* e, const CgIrInst* in) { c_emit_ret(t, aux->values, aux->nvalues); return; } + case CG_IR_UNREACHABLE: + c_emit_unreachable(t); + return; case CG_IR_BR: c_emit_jump(t, (Label)in->extra.imm); return; diff --git a/src/arch/check_target.c b/src/arch/check_target.c @@ -237,6 +237,8 @@ static void check_ret(CgTarget* t, const CGLocal* values, u32 nvalues) { (void)nvalues; } +static void check_unreachable(CgTarget* t) { (void)t; } + static void check_alloca(CgTarget* t, Operand dst, Operand size, u32 align) { (void)t; (void)dst; @@ -401,6 +403,7 @@ static CgTarget* check_backend_make(Compiler* c, ObjBuilder* o, t->convert = check_convert; t->call = check_call; t->ret = check_ret; + t->unreachable = check_unreachable; t->alloca_ = check_alloca; t->va_start_ = check_one_operand; t->va_arg_ = check_va_arg; diff --git a/src/arch/rv64/native.c b/src/arch/rv64/native.c @@ -2624,7 +2624,6 @@ static void rv_intrinsic(NativeTarget* t, IntrinKind kind, const NativeLoc* dsts } case INTRIN_PREFETCH: return; - case INTRIN_UNREACHABLE: case INTRIN_TRAP: rv64_emit32(mc, rv_ebreak()); return; diff --git a/src/arch/wasm/emit.c b/src/arch/wasm/emit.c @@ -1588,8 +1588,6 @@ static const char* intrin_name(IntrinKind k) { return "__builtin_assume_aligned"; case INTRIN_EXPECT: return "__builtin_expect"; - case INTRIN_UNREACHABLE: - return "__builtin_unreachable"; case INTRIN_TRAP: return "__builtin_trap"; case INTRIN_SETJMP: @@ -1619,7 +1617,6 @@ void wasm_intrinsic(CGTarget* tg, IntrinKind k, Operand* dst, u32 ndst, switch (k) { case INTRIN_TRAP: - case INTRIN_UNREACHABLE: wasm_emit_unreachable(t); return; @@ -2025,6 +2022,10 @@ void wasm_emit_unreachable(WTarget* t) { t->dead = 1; } +/* Control terminator (the C __builtin_unreachable point): emit the Wasm + * `unreachable` opcode, which traps if reached. Ends the current block. */ +void wasm_unreachable(CGTarget* tg) { wasm_emit_unreachable((WTarget*)tg); } + /* ----------------------------------------------------------------- * WIR -> WasmFunc lowering * ----------------------------------------------------------------- */ diff --git a/src/arch/wasm/ir_emit.c b/src/arch/wasm/ir_emit.c @@ -41,6 +41,7 @@ void wasm_cmp(CGTarget*, CmpOp, Operand, Operand, Operand); void wasm_convert(CGTarget*, ConvKind, Operand, Operand); void wasm_call(CGTarget*, const CGCallDesc*); void wasm_ret(CGTarget*, const CGABIValue*); +void wasm_unreachable(CGTarget*); void wasm_alloca(CGTarget*, Operand, Operand, u32); void wasm_va_start(CGTarget*, Operand); void wasm_va_arg(CGTarget*, Operand, Operand, CfreeCgTypeId); @@ -639,6 +640,9 @@ static void wasm_ir_emit_inst(WasmIrEmitter* e, const CgIrFunc* f, case CG_IR_RET: wasm_ir_emit_ret(e, f, in); return; + case CG_IR_UNREACHABLE: + wasm_unreachable(t); + return; case CG_IR_BR: wasm_jump(t, (Label)in->extra.imm); return; diff --git a/src/arch/x64/native.c b/src/arch/x64/native.c @@ -3216,7 +3216,6 @@ static void x64_intrinsic(NativeTarget* t, IntrinKind kind, return; case INTRIN_PREFETCH: return; - case INTRIN_UNREACHABLE: case INTRIN_TRAP: emit_ud2(mc); return; diff --git a/src/cg/cgtarget.h b/src/cg/cgtarget.h @@ -102,8 +102,10 @@ typedef enum ConvKind { /* Compiler-intrinsic kinds dispatched through CgTarget.intrinsic and carried * on IR_INTRINSIC via IRIntrinAux.kind. The set is bounded: a backend * must know each one to choose inline-vs-libcall. Hint intrinsics - * (EXPECT/UNREACHABLE/TRAP/PREFETCH/ASSUME_ALIGNED) ride the same dispatch: + * (EXPECT/TRAP/PREFETCH/ASSUME_ALIGNED) ride the same dispatch: * the backend decides whether they emit an instruction or a no-op. + * `unreachable` is NOT here: it is a first-class control terminator with + * its own CgTarget hook (see below), not an intrinsic. * * Not every C builtin lives here. Parser-evaluated builtins * (__builtin_offsetof, __builtin_constant_p, __builtin_choose_expr, @@ -129,7 +131,6 @@ typedef enum IntrinKind { /* hints */ INTRIN_EXPECT, - INTRIN_UNREACHABLE, INTRIN_TRAP, /* non-local control */ @@ -601,6 +602,14 @@ struct CgTarget { * target supports no tail calls at all. */ const char* (*tail_call_unrealizable_reason)(CgTarget*, const CGCallDesc*); void (*ret)(CgTarget*, const CGLocal* values, u32 nvalues); + /* Control terminator marking statically-unreachable code (the C + * __builtin_unreachable point). Like ret/jump it ends the current basic + * block: no fall-through successor is implied. Backends typically emit a + * trap instruction (brk/ud2/ebreak), a Wasm `unreachable`, or a + * `__builtin_unreachable()` in the C-source target; an interpreter faults. + * Distinct from INTRIN_TRAP, which is an expression-level intrinsic that + * does not terminate the block. */ + void (*unreachable)(CgTarget*); /* ---- alloca ---- * Dynamic stack allocation. `size` is i64 bytes; `align` is the required @@ -647,7 +656,7 @@ struct CgTarget { * PREFETCH : dsts none; args = (addr [, rw [, locality]]) * ASSUME_ALIGNED : dsts[0] LOCAL; args = (ptr, align [, offset]) * EXPECT : dsts[0] LOCAL; args = (val, expected) - * UNREACHABLE / TRAP : dsts none; args none + * TRAP : dsts none; args none * SETJMP : dsts[0] LOCAL i32 result; args = (&buf) * LONGJMP : dsts none; args = (&buf, val); no return * ADD/SUB/MUL_OVERFLOW : dsts[0] LOCAL result, dsts[1] LOCAL i1 diff --git a/src/cg/control.c b/src/cg/control.c @@ -396,7 +396,7 @@ void cfree_cg_computed_goto(CfreeCg* g, const CfreeCgLabel* valid_targets, void cfree_cg_unreachable(CfreeCg* g) { if (!g) return; api_local_const_control_boundary(g); - g->target->intrinsic(g->target, INTRIN_UNREACHABLE, NULL, 0, NULL, 0); + g->target->unreachable(g->target); } /* ============================================================ diff --git a/src/cg/ir.h b/src/cg/ir.h @@ -30,6 +30,7 @@ typedef enum CgIrOp { CG_IR_CALL, CG_IR_RET, + CG_IR_UNREACHABLE, CG_IR_BR, CG_IR_CMP_BRANCH, diff --git a/src/cg/ir_dump.c b/src/cg/ir_dump.c @@ -39,6 +39,7 @@ static const char* cg_ir_op_name(CgIrOp op) { case CG_IR_CONVERT: return "convert"; case CG_IR_CALL: return "call"; case CG_IR_RET: return "ret"; + case CG_IR_UNREACHABLE: return "unreachable"; case CG_IR_BR: return "br"; case CG_IR_CMP_BRANCH: return "cmp_branch"; case CG_IR_SWITCH: return "switch"; diff --git a/src/cg/ir_recorder.c b/src/cg/ir_recorder.c @@ -429,6 +429,10 @@ static void rec_ret(CgTarget* t, const CGLocal* values, u32 nvalues) { in->extra.aux = aux; } +static void rec_unreachable(CgTarget* t) { + (void)emit(rec_of(t), CG_IR_UNREACHABLE); +} + static void rec_alloca(CgTarget* t, Operand dst, Operand size, u32 align) { CgIrInst* in = emit(rec_of(t), CG_IR_ALLOCA); Operand ops[2] = {dst, size}; @@ -639,6 +643,7 @@ CgTarget* cg_ir_recorder_new(Compiler* c, ObjBuilder* obj, r->base.call = rec_call; r->base.tail_call_unrealizable_reason = rec_tail_call_unrealizable_reason; r->base.ret = rec_ret; + r->base.unreachable = rec_unreachable; r->base.alloca_ = rec_alloca; r->base.va_start_ = rec_va_start; r->base.va_arg_ = rec_va_arg; diff --git a/src/cg/native_direct_target.c b/src/cg/native_direct_target.c @@ -1536,6 +1536,13 @@ static void nd_ret(CgTarget* t, const CGLocal* values, u32 nvalues) { d->native->ret(d->native); } +static void nd_unreachable(CgTarget* t) { + NativeDirectTarget* d = nd_of(t); + nd_flush_all(d); + ND_REQUIRE_NATIVE(d, trap, "target does not emit traps"); + d->native->trap(d->native); +} + static void nd_alloca(CgTarget* t, Operand dst, Operand size, u32 align) { NativeDirectTarget* d = nd_of(t); NativeLoc sr, dr; @@ -1787,6 +1794,7 @@ CgTarget* native_direct_target_new(Compiler* c, ObjBuilder* obj, d->base.call = nd_call; d->base.tail_call_unrealizable_reason = nd_tail_call_unrealizable_reason; d->base.ret = nd_ret; + d->base.unreachable = nd_unreachable; d->base.alloca_ = nd_alloca; d->base.va_start_ = nd_va_start; d->base.va_arg_ = nd_va_arg; diff --git a/src/interp/engine.c b/src/interp/engine.c @@ -726,7 +726,8 @@ static u64 ext_call(InterpStack* st, InterpFrame* fr, u64* regs, void* host_fp, X(IOP_ALLOCA) X(IOP_AGG_COPY) X(IOP_AGG_SET) X(IOP_BITFIELD_LOAD) \ X(IOP_BITFIELD_STORE) X(IOP_VA_START) X(IOP_VA_ARG) X(IOP_VA_END) \ X(IOP_VA_COPY) X(IOP_ATOMIC_LOAD) X(IOP_ATOMIC_STORE) X(IOP_ATOMIC_RMW) \ - X(IOP_ATOMIC_CAS) X(IOP_FENCE) X(IOP_INTRINSIC) X(IOP_TRAP) + X(IOP_ATOMIC_CAS) X(IOP_FENCE) X(IOP_INTRINSIC) X(IOP_UNREACHABLE) \ + X(IOP_TRAP) #if INTERP_DISPATCH_THREADED # define OP(name) L_##name @@ -1308,6 +1309,9 @@ CfreeInterpStatus interp_run_stack(InterpStack* st, int64_t* out_ret) { write_dst(st, fn, regs, mem_off, &I->opnds[1], ok); /* ok flag */ NEXT(); } + OP(IOP_UNREACHABLE): + fault(st, "unreachable"); + goto stop; OP(IOP_TRAP): unsupported(st, fn->reject_reason ? fn->reject_reason : "operation"); goto stop; @@ -1422,9 +1426,6 @@ static int interp_intrinsic(InterpStack* st, InterpFunc* fn, u64* regs, case INTRIN_TRAP: fault(st, "__builtin_trap"); return 0; - case INTRIN_UNREACHABLE: - fault(st, "unreachable"); - return 0; case INTRIN_SADD_OVERFLOW: case INTRIN_UADD_OVERFLOW: case INTRIN_SSUB_OVERFLOW: diff --git a/src/interp/interp.h b/src/interp/interp.h @@ -60,7 +60,8 @@ typedef enum InterpOp { IOP_ATOMIC_CAS, IOP_FENCE, IOP_INTRINSIC, - IOP_TRAP, /* unreachable/unsupported-at-runtime guard */ + IOP_UNREACHABLE, /* control terminator: faults (TRAP) if ever reached */ + IOP_TRAP, /* unsupported-at-runtime guard */ IOP__COUNT, } InterpOp; diff --git a/src/interp/lower.c b/src/interp/lower.c @@ -115,6 +115,8 @@ static InterpOp map_op(Lower* lw, const Inst* in) { IRRetAux* aux = (IRRetAux*)in->extra.aux; return (aux && aux->present) ? IOP_RET : IOP_RET_VOID; } + case IR_UNREACHABLE: + return IOP_UNREACHABLE; case IR_ALLOCA: return IOP_ALLOCA; case IR_VA_START: diff --git a/src/opt/cg_ir_lower.c b/src/opt/cg_ir_lower.c @@ -265,6 +265,7 @@ static int cg_inst_terminates(const CgIrInst* in) { switch ((CgIrOp)in->op) { case CG_IR_BR: case CG_IR_RET: + case CG_IR_UNREACHABLE: case CG_IR_CMP_BRANCH: case CG_IR_SWITCH: case CG_IR_INDIRECT_BRANCH: @@ -273,8 +274,7 @@ static int cg_inst_terminates(const CgIrInst* in) { return 1; case CG_IR_INTRINSIC: { const CgIrIntrinsicAux* aux = (const CgIrIntrinsicAux*)in->extra.aux; - return aux && (aux->kind == INTRIN_LONGJMP || aux->kind == INTRIN_TRAP || - aux->kind == INTRIN_UNREACHABLE); + return aux && (aux->kind == INTRIN_LONGJMP || aux->kind == INTRIN_TRAP); } default: return 0; @@ -847,6 +847,9 @@ static void lower_one_inst(CgIrLower* l, u32 idx) { case CG_IR_RET: op = IR_RET; break; + case CG_IR_UNREACHABLE: + op = IR_UNREACHABLE; + break; case CG_IR_BR: op = IR_BR; break; @@ -1021,6 +1024,10 @@ static void lower_one_inst(CgIrLower* l, u32 idx) { lower_ret(l, out, in); l->f->blocks[block].nsucc = 0; break; + case CG_IR_UNREACHABLE: + /* Terminator with no successors: control does not leave this block. */ + l->f->blocks[block].nsucc = 0; + break; case CG_IR_BR: out->extra.imm = block_for_label(l, (Label)in->extra.imm, in->loc); set_succ1(l, block, (u32)out->extra.imm); @@ -1181,13 +1188,13 @@ static void add_fallthrough_succs(CgIrLower* l) { case IR_SWITCH: case IR_INDIRECT_BRANCH: case IR_RET: + case IR_UNREACHABLE: case IR_BREAK_TO: case IR_CONTINUE_TO: continue; case IR_INTRINSIC: { IRIntrinAux* aux = (IRIntrinAux*)last->extra.aux; - if (aux && (aux->kind == INTRIN_LONGJMP || aux->kind == INTRIN_TRAP || - aux->kind == INTRIN_UNREACHABLE)) + if (aux && (aux->kind == INTRIN_LONGJMP || aux->kind == INTRIN_TRAP)) continue; break; } diff --git a/src/opt/ir.h b/src/opt/ir.h @@ -294,6 +294,7 @@ typedef enum IROp { IR_LOCAL_STATIC_DATA_LABEL_ADDR, /* extra.aux = CgIrLocalStaticLabelAux */ IR_LOCAL_STATIC_DATA_END, IR_RET, /* extra.aux = IRRetAux* (NULL for void). */ + IR_UNREACHABLE, /* control terminator; no operands, no successors. */ IR_SCOPE_BEGIN, /* extra.aux = IRScopeAux. */ IR_SCOPE_END, /* extra.imm = scope id (Val). */ IR_BREAK_TO, /* extra.imm = scope id (Val). */ diff --git a/src/opt/ir_print.c b/src/opt/ir_print.c @@ -88,6 +88,8 @@ static const char* op_name(IROp op) { return "local_static_data_end"; case IR_RET: return "ret"; + case IR_UNREACHABLE: + return "unreachable"; case IR_SCOPE_BEGIN: return "scope_begin"; case IR_SCOPE_END: diff --git a/src/opt/pass_analysis.c b/src/opt/pass_analysis.c @@ -343,6 +343,7 @@ static int block_has_succ(const Block* bl, u32 succ) { static int fixed_terminator_succ_count(const Inst* in, u32* count_out) { switch ((IROp)in->op) { case IR_RET: + case IR_UNREACHABLE: *count_out = 0; return 1; case IR_BR: @@ -356,8 +357,7 @@ static int fixed_terminator_succ_count(const Inst* in, u32* count_out) { return 1; case IR_INTRINSIC: { IRIntrinAux* aux = (IRIntrinAux*)in->extra.aux; - if (aux && (aux->kind == INTRIN_LONGJMP || aux->kind == INTRIN_TRAP || - aux->kind == INTRIN_UNREACHABLE)) { + if (aux && (aux->kind == INTRIN_LONGJMP || aux->kind == INTRIN_TRAP)) { *count_out = 0; return 1; } diff --git a/src/opt/pass_cfg.c b/src/opt/pass_cfg.c @@ -34,13 +34,13 @@ static int is_terminator(const Inst* in) { case IR_SWITCH: case IR_INDIRECT_BRANCH: case IR_RET: + case IR_UNREACHABLE: case IR_BREAK_TO: case IR_CONTINUE_TO: return 1; case IR_INTRINSIC: { IRIntrinAux* aux = (IRIntrinAux*)in->extra.aux; - return aux && (aux->kind == INTRIN_LONGJMP || aux->kind == INTRIN_TRAP || - aux->kind == INTRIN_UNREACHABLE); + return aux && (aux->kind == INTRIN_LONGJMP || aux->kind == INTRIN_TRAP); } default: return 0; @@ -231,6 +231,9 @@ void opt_build_cfg(Func* f) { case IR_RET: bl->nsucc = 0; break; + case IR_UNREACHABLE: + bl->nsucc = 0; + break; case IR_INTRINSIC: bl->nsucc = 0; break; diff --git a/src/opt/pass_dce.c b/src/opt/pass_dce.c @@ -53,6 +53,7 @@ int opt_inst_has_side_effect(Func* f, const Inst* in) { case IR_LOCAL_STATIC_DATA_LABEL_ADDR: case IR_LOCAL_STATIC_DATA_END: case IR_RET: + case IR_UNREACHABLE: case IR_SCOPE_BEGIN: case IR_SCOPE_END: case IR_BREAK_TO: diff --git a/src/opt/pass_lower.c b/src/opt/pass_lower.c @@ -1619,13 +1619,13 @@ static int lower_is_terminator(const Inst* in) { case IR_SWITCH: case IR_INDIRECT_BRANCH: case IR_RET: + case IR_UNREACHABLE: case IR_BREAK_TO: case IR_CONTINUE_TO: return 1; case IR_INTRINSIC: { IRIntrinAux* aux = (IRIntrinAux*)in->extra.aux; - return aux && (aux->kind == INTRIN_LONGJMP || aux->kind == INTRIN_TRAP || - aux->kind == INTRIN_UNREACHABLE); + return aux && (aux->kind == INTRIN_LONGJMP || aux->kind == INTRIN_TRAP); } default: return 0; diff --git a/src/opt/pass_native_emit.c b/src/opt/pass_native_emit.c @@ -1219,6 +1219,9 @@ static void emit_inst(NativeEmitCtx* e, u32 block, u32 order_index, Inst* in, case IR_FENCE: e->target->fence(e->target, (CfreeCgMemOrder)in->extra.imm); return; + case IR_UNREACHABLE: + e->target->trap(e->target); + return; case IR_INTRINSIC: { IRIntrinAux* aux = (IRIntrinAux*)in->extra.aux; NativeLoc* dsts = aux && aux->ndst @@ -1256,13 +1259,13 @@ static int native_emit_terminates(const Inst* in) { case IR_SWITCH: case IR_INDIRECT_BRANCH: case IR_RET: + case IR_UNREACHABLE: case IR_BREAK_TO: case IR_CONTINUE_TO: return 1; case IR_INTRINSIC: { IRIntrinAux* aux = (IRIntrinAux*)in->extra.aux; - return aux && (aux->kind == INTRIN_LONGJMP || aux->kind == INTRIN_TRAP || - aux->kind == INTRIN_UNREACHABLE); + return aux && (aux->kind == INTRIN_LONGJMP || aux->kind == INTRIN_TRAP); } default: return 0; diff --git a/src/opt/pass_o2.c b/src/opt/pass_o2.c @@ -245,13 +245,13 @@ static int o2_is_terminator(const Inst* in) { case IR_SWITCH: case IR_INDIRECT_BRANCH: case IR_RET: + case IR_UNREACHABLE: case IR_BREAK_TO: case IR_CONTINUE_TO: return 1; case IR_INTRINSIC: { IRIntrinAux* aux = (IRIntrinAux*)in->extra.aux; - return aux && (aux->kind == INTRIN_LONGJMP || aux->kind == INTRIN_TRAP || - aux->kind == INTRIN_UNREACHABLE); + return aux && (aux->kind == INTRIN_LONGJMP || aux->kind == INTRIN_TRAP); } default: return 0; diff --git a/src/opt/pass_ssa.c b/src/opt/pass_ssa.c @@ -810,13 +810,13 @@ static int ssa_is_terminator(const Inst* in) { case IR_SWITCH: case IR_INDIRECT_BRANCH: case IR_RET: + case IR_UNREACHABLE: case IR_BREAK_TO: case IR_CONTINUE_TO: return 1; case IR_INTRINSIC: { IRIntrinAux* aux = (IRIntrinAux*)in->extra.aux; - return aux && (aux->kind == INTRIN_LONGJMP || aux->kind == INTRIN_TRAP || - aux->kind == INTRIN_UNREACHABLE); + return aux && (aux->kind == INTRIN_LONGJMP || aux->kind == INTRIN_TRAP); } default: return 0;