kit

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

commit 8b8960344ffcc9a12161a218fece7e8bf2beaf8b
parent c8b9d53d52c4d396b6caed50da9e576413595199
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sat,  6 Jun 2026 04:33:21 -0700

Keep volatile locals memory resident

Diffstat:
Mdoc/plan/TODO.md | 31-------------------------------
Minclude/kit/cg.h | 4++++
Mlang/c/parse/cg_adapter.c | 7+++++++
Msrc/cg/local.c | 1+
4 files changed, 12 insertions(+), 31 deletions(-)

diff --git a/doc/plan/TODO.md b/doc/plan/TODO.md @@ -5,37 +5,6 @@ fixed, remove it instead of checking it off or keeping a closed entry. Add new deferred fixes below as they are discovered. -## setjmp/longjmp miscompiled at `-O1`: the longjmp'd `setjmp` return value is wrong - -A textbook setjmp/longjmp round-trip returns the right answer at `-O0` but the -wrong one at `-O1` on **all three** native arches (aa64/x64/rv64) — a wrong- -answer miscompile, not a crash. `test/rt/cases/setjmp_runtime.c` exits 42 at O0 -and **1** at O1: the `int rc = setjmp(env)` value observed after the `longjmp` -is not `1`. (`marker` is `volatile`, so the test is well-formed — the bad read is -the `setjmp` result itself, not the local.) - -Minimal repro (`kit cc -O1`, run it): - -```c -#include <setjmp.h> -int test_main(void) { - jmp_buf env; - volatile int marker = 11; - int rc = setjmp(env); /* O1: second return value not observed */ - if (rc == 0) { marker = 31; longjmp(env, 1); } - return (marker == 31 && rc == 1) ? 42 : 1; /* O0: 42, O1: 1 */ -} -``` - -Classic "`setjmp` not modeled as returns-twice": the optimizer treats the call as -returning once, so the SSA value for `rc` (and anything else live across the -`setjmp`) is folded/cached to the first-return value rather than reloaded on the -`longjmp` re-entry. Target-independent (fails on every arch), so the fix is in the -opt/IR layer — mark `setjmp`-family calls returns-twice (force a reload of values -live across them; pin them to memory), as GCC/Clang do. Found sweeping the rt -runtime corpus at O0+O1 for the WS4 backtrace work (doc/plan/BACKTRACE.md); left -red (`test-rt-runtime`, `setjmp_runtime/O1`). - ## `-no-pie` does not produce a non-PIE (ET_EXEC) executable `-no-pie` sets `o->target.pic = KIT_PIC_NONE` (`driver/cmd/cc.c:1185`) but does diff --git a/include/kit/cg.h b/include/kit/cg.h @@ -492,6 +492,10 @@ typedef enum KitCgLocalFlag { KIT_CG_LOCAL_ARTIFICIAL = 1u << 1, KIT_CG_LOCAL_OPTIMIZED_OUT = 1u << 2, KIT_CG_LOCAL_COMPILER_TEMP = 1u << 3, + /* Force an addressable storage home even when the type is scalar. Frontends + * use this for source objects whose accesses must remain memory operations + * (for example C volatile automatic objects). */ + KIT_CG_LOCAL_MEMORY_REQUIRED = 1u << 4, } KitCgLocalFlag; typedef struct KitCgLocalAttrs { diff --git a/lang/c/parse/cg_adapter.c b/lang/c/parse/cg_adapter.c @@ -383,12 +383,18 @@ KitCgAtomicOp pcg_atomic_op(AtomicOp op) { KitCgMemOrder pcg_mem_order(MemOrder ord) { return (KitCgMemOrder)ord; } +static int pcg_slot_is_volatile(const FrameSlotDesc* fsd) { + return fsd && ((fsd->flags & FSF_VOLATILE) || + (fsd->type && (fsd->type->qual & Q_VOLATILE))); +} + FrameSlot pcg_local(Parser* p, const FrameSlotDesc* fsd) { KitCgLocalAttrs attrs; memset(&attrs, 0, sizeof attrs); if (!pcg_emit_enabled(p)) return FRAME_SLOT_NONE; attrs.name = fsd->name; attrs.align = fsd->align; + if (pcg_slot_is_volatile(fsd)) attrs.flags |= KIT_CG_LOCAL_MEMORY_REQUIRED; /* FSF_ADDR_TAKEN is no longer propagated to CG: there is no * KIT_CG_LOCAL_ADDRESS_TAKEN attribute. The C-side flag stays for any * parser-internal uses; opt's opt_promote_scalar_locals (Stream I) decides @@ -402,6 +408,7 @@ FrameSlot pcg_param_slot(Parser* p, u32 index, const FrameSlotDesc* fsd) { memset(&attrs, 0, sizeof attrs); attrs.name = fsd->name; attrs.align = fsd->align; + if (pcg_slot_is_volatile(fsd)) attrs.flags |= KIT_CG_LOCAL_MEMORY_REQUIRED; return kit_cg_param(p->cg, index, pcg_tid(p, fsd->type), attrs); } diff --git a/src/cg/local.c b/src/cg/local.c @@ -3,6 +3,7 @@ int api_local_requires_memory(KitCg* g, KitCgTypeId ty, KitCgLocalAttrs attrs) { u32 hidden_flags = KIT_CG_LOCAL_ARTIFICIAL | KIT_CG_LOCAL_OPTIMIZED_OUT | KIT_CG_LOCAL_COMPILER_TEMP; + if (attrs.flags & KIT_CG_LOCAL_MEMORY_REQUIRED) return 1; if (g && g->debug && attrs.name && (attrs.flags & hidden_flags) == 0) return 1; /* Aggregates (records, arrays), wide16 (f128/i128), split-lane wide8, vararg