From 7b8ef1a9017c13b636f5ce2192c7635622a6e4d2 Mon Sep 17 00:00:00 2001 From: Christian Zietz Date: Fri, 27 Feb 2026 19:16:30 +0100 Subject: [PATCH] Atomics can't cause exceptions with Microsoft Visual C++ (#25559) The `enforcenoraises` pragma prevents generation of exception checking code for atomic... functions when compiling with Microsoft Visual C++ as backend. Fixes #25445 Without this change, the following test program: ```nim import std/sysatomics var x: ptr uint64 = cast[ptr uint64](uint64(0)) var y: ptr uint64 = cast[ptr uint64](uint64(42)) let z = atomicExchangeN(addr x, y, ATOMIC_ACQ_REL) let a = atomicCompareExchangeN(addr x, addr y, y, true, ATOMIC_ACQ_REL, ATOMIC_ACQ_REL) var v = 42 atomicStoreN(addr v, 43, ATOMIC_ACQ_REL) let w = atomicLoadN(addr v, ATOMIC_ACQ_REL) ``` ... generates this C code when compiling with `--cc:vcc`: ```c N_LIB_PRIVATE N_NIMCALL(void, NimMainModule)(void) { { NU64* T1_; NIM_BOOL T2_; NI T3_; NIM_BOOL* nimErr_; nimfr_("testexcept", "/tmp/testexcept.nim"); nimErr_ = nimErrorFlag(); nimlf_(7, "/tmp/testexcept.nim");T1_ = ((NU64*) 0); T1_ = atomicExchangeN__testexcept_u4((&x__testexcept_u2), y__testexcept_u3, ((int) 4)); if (NIM_UNLIKELY((*nimErr_))) { goto BeforeRet_; } z__testexcept_u32 = T1_; nimln_(9);T2_ = ((NIM_BOOL) 0); T2_ = atomicCompareExchangeN__testexcept_u33((&x__testexcept_u2), (&y__testexcept_u3), y__testexcept_u3, NIM_TRUE, ((int) 4), ((int) 4)); if (NIM_UNLIKELY((*nimErr_))) { goto BeforeRet_; } a__testexcept_u45 = T2_; nimln_(12);atomicStoreN__testexcept_u47(((&v__testexcept_u46)), ((NI) 43)); if (NIM_UNLIKELY((*nimErr_))) { goto BeforeRet_; } nimln_(13);T3_ = ((NI) 0); T3_ = atomicLoadN__testexcept_u53(((&v__testexcept_u46))); if (NIM_UNLIKELY((*nimErr_))) { goto BeforeRet_; } w__testexcept_u59 = T3_; BeforeRet_: ; nimTestErrorFlag(); popFrame(); } } ``` Note the repeated checks for `*nimErr_`. With this PR applied, the checks vanish: ```c N_LIB_PRIVATE N_NIMCALL(void, NimMainModule)(void) { { nimfr_("testexcept", "/tmp/testexcept.nim"); nimlf_(7, "/tmp/testexcept.nim");z__testexcept_u32 = atomicExchangeN__testexcept_u4((&x__testexcept_u2), y__testexcept_u3, ((int) 4)); nimln_(9);a__testexcept_u45 = atomicCompareExchangeN__testexcept_u33((&x__testexcept_u2), (&y__testexcept_u3), y__testexcept_u3, NIM_TRUE, ((int) 4), ((int) 4)); nimln_(12);atomicStoreN__testexcept_u47(((&v__testexcept_u46)), ((NI) 43)); nimln_(13);w__testexcept_u59 = atomicLoadN__testexcept_u53(((&v__testexcept_u46))); nimTestErrorFlag(); popFrame(); } } ``` For reference, with gcc as backend the generated code looks as follows: ```c N_LIB_PRIVATE N_NIMCALL(void, NimMainModule)(void) { { nimfr_("testexcept", "/tmp/testexcept.nim"); nimlf_(7, "/tmp/testexcept.nim");z__testexcept_u9 = __atomic_exchange_n((&x__testexcept_u2), y__testexcept_u3, __ATOMIC_ACQ_REL); nimln_(9);a__testexcept_u18 = __atomic_compare_exchange_n((&x__testexcept_u2), (&y__testexcept_u3), y__testexcept_u3, NIM_TRUE, __ATOMIC_ACQ_REL, __ATOMIC_ACQ_REL); nimln_(12);__atomic_store_n(((&v__testexcept_u19)), ((NI) 43), __ATOMIC_ACQ_REL); nimln_(13);w__testexcept_u29 = __atomic_load_n(((&v__testexcept_u19)), __ATOMIC_ACQ_REL); nimTestErrorFlag(); popFrame(); } } ``` With this PR the program from #25445 yields the correct output `Error: unhandled exception: index 4 not in 0 .. 3 [IndexDefect]` instead of crashing with a SIGSEGV. PS: Unfortunately, I did not find out how to run the tests with MSVC. `./koch tests --cc:vcc` doesn't use MSVC. (cherry picked from commit 49961a54dd43e2ab2d97eacdc158ee9fe1ebe04e) --- lib/std/sysatomics.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/std/sysatomics.nim b/lib/std/sysatomics.nim index cc6000c206..ac5acf76e6 100644 --- a/lib/std/sysatomics.nim +++ b/lib/std/sysatomics.nim @@ -219,16 +219,16 @@ elif someVcc: elif mem == ATOMIC_ACQ_REL: fence() elif mem == ATOMIC_SEQ_CST: fence() - proc atomicStoreN*[T: AtomType](p: ptr T, val: T, mem: static[AtomMemModel]) = + proc atomicStoreN*[T: AtomType](p: ptr T, val: T, mem: static[AtomMemModel]) {.enforcenoraises.} = barrier(mem) p[] = val - proc atomicLoadN*[T: AtomType](p: ptr T, mem: static[AtomMemModel]): T = + proc atomicLoadN*[T: AtomType](p: ptr T, mem: static[AtomMemModel]): T {.enforcenoraises.} = result = p[] barrier(mem) proc atomicCompareExchangeN*[T: ptr](p, expected: ptr T, desired: T, - weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool = + weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {.enforcenoraises.} = when sizeof(T) == 8: interlockedCompareExchange64(p, cast[int64](desired), cast[int64](expected[])) == cast[int64](expected[]) @@ -236,7 +236,7 @@ elif someVcc: interlockedCompareExchange32(p, cast[int32](desired), cast[int32](expected[])) == cast[int32](expected[]) - proc atomicExchangeN*[T: ptr](p: ptr T, val: T, mem: AtomMemModel): T = + proc atomicExchangeN*[T: ptr](p: ptr T, val: T, mem: AtomMemModel): T {.enforcenoraises.} = when sizeof(T) == 8: cast[T](interlockedExchange64(p, cast[int64](val))) elif sizeof(T) == 4: