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 49961a54dd)
This commit is contained in:
Christian Zietz
2026-02-27 19:16:30 +01:00
committed by narimiran
parent df41cb4b25
commit 7b8ef1a901

View File

@@ -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: