You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
c: __builtin_unreachable() as an operand of a larger expression still emits rows after UNREACHABLE (sink((__builtin_unreachable(), x));) — invalid IR by default, rejected under -fno-frontend-ssa #1748
#1682 and #1743 (closing #1503 and #1736) keep the block open after a noreturncall until the value's consumer, or the whole expression statement, has emitted its rows. __builtin_unreachable() does not go through that path. It always ends the block at once, so the rest of the enclosing expression is emitted behind the UNREACHABLE.
The C below is valid, and Clang 18.1.3 and GCC 13.3.0 accept it with -fsyntax-only -Wall:
The __builtin_unreachable branch of the prepared-call loop (c_gen.c:20646–20660 at 126cc43) appends UNREACHABLE and stores terminated = true unconditionally:
The noreturn-call path goes through c_ir_end_control_flow_after_call, which asks c_ir_lowering_branch_resumes_after_call whether a consumer resumes after the value, and defers the close to the end of an expression statement (#1743). The builtin branch skips both. Routing it through c_ir_end_control_flow_after_call(builder, true, unreachable_source) would probably fix the first three rows. That is untested; the #1325 branch noted on #1503 made that same change.
Known fix, with measurements
#1453 merged with this main (local merge commit 81172a59) handles the cases like this:
Summary
#1682 and #1743 (closing #1503 and #1736) keep the block open after a
noreturncall until the value's consumer, or the whole expression statement, has emitted its rows.__builtin_unreachable()does not go through that path. It always ends the block at once, so the rest of the enclosing expression is emitted behind theUNREACHABLE.The C below is valid, and Clang 18.1.3 and GCC 13.3.0 accept it with
-fsyntax-only -Wall:Observed on
main126cc437e5935f2c829780597c0a8f67e203a22d-fverify-codegen-fno-frontend-ssa(no verify)argcanonical input, error 3, … instruction 3, opcode 18local-promotion output, error 3, … opcode 18initcanonical input, error 3, … opcode 14local-promotion output, error 3, … opcode 14assigncanonical input, error 3, … opcode 14local-promotion output, error 3, … opcode 14retunsupported C function-body statement or expression near 'x'stmtINSTRUCTION_AFTER_TERMINATOR.sink((die(), x));,(void)exit(1);) still emits rows after UNREACHABLE on main — invalid IR by default, rejected under -fno-frontend-ssa #1736, the default path does not detect the invalid IR. Certified input skips the input check, and a failing FAST guard only disables FAST (c(C23):T f();is treated as unprototyped in type compatibility, so a conflicting prototype is accepted and canonical IR is invalid #1306).ade6ac4, from before c: keep a noreturn call's block open when a return/initializer consumes its value (#1503) #1682, gives the same results for all five. So this is older than those fixes; they just do not cover the builtin.Cause
The
__builtin_unreachablebranch of the prepared-call loop (c_gen.c:20646–20660 at126cc43) appendsUNREACHABLEand storesterminated = trueunconditionally:The noreturn-call path goes through
c_ir_end_control_flow_after_call, which asksc_ir_lowering_branch_resumes_after_callwhether a consumer resumes after the value, and defers the close to the end of an expression statement (#1743). The builtin branch skips both. Routing it throughc_ir_end_control_flow_after_call(builder, true, unreachable_source)would probably fix the first three rows. That is untested; the #1325 branch noted on #1503 made that same change.Known fix, with measurements
#1453 merged with this
main(local merge commit81172a59) handles the cases like this:arg,init,assign: they compile and pass-fverify-codegenunder both-ffrontend-ssaand-fno-frontend-ssa. ir: make block-row validity (ownership, termination, result binding) a construction-time invariant #1453'sc_ir_reopen_unreachable_markerretracts the edge-less marker when the next row arrives.stmt: it passes.ret: it passes under-fno-frontend-ssa. Under-ffrontend-ssait is still rejected with the same "unsupported … near 'x'". That rejection is separate and not addressed by ir: make block-row validity (ownership, termination, result binding) a construction-time invariant #1453.Done when
arg,initandassignpass-fverify-codegenin both frontend forms, and-fno-frontend-ssaaccepts them.retcompiles, likereturn (die(), x);does since c: keep a noreturn call's block open when a return/initializer consumes its value (#1503) #1682.__builtin_unreachable();still ends its block inUNREACHABLE.c_test_noreturn_call_value_operandsand c: an expression statement containing a noreturn call ends its block after its own rows (#1736) #1743's tests.Environment
idebuilt from126cc437and fromade6ac4, with Clang 18.1.3 on x86-64 Linux. This uses the hosted-Clang bootstrap exception, because the container has no TCC (dev-env: agent cloud containers have no TCC, so ./build.sh exits 127 and sessions fall back to a Clang-built driver the guides call non-trusted #1383).Related
return die_value();(returning a noreturn call's value) is rejected as "unsupported", while -fsyntax-only accepts it #1503 / c: keep a noreturn call's block open when a return/initializer consumes its value (#1503) #1682 and c: a noreturn call inside an expression statement (sink((die(), x));,(void)exit(1);) still emits rows after UNREACHABLE on main — invalid IR by default, rejected under -fno-frontend-ssa #1736 / c: an expression statement containing a noreturn call ends its block after its own rows (#1736) #1743: the same fault fornoreturncalls.arg,initandassign.