Skip to content

Stop build_cc from defining options set to OFF - #873

Open
Alexander Theißen (athei) wants to merge 1 commit into
microsoft:mainfrom
athei:fix-build-cc-boolean-defines
Open

Stop build_cc from defining options set to OFF#873
Alexander Theißen (athei) wants to merge 1 commit into
microsoft:mainfrom
athei:fix-build-cc-boolean-defines

Conversation

@athei

Copy link
Copy Markdown

snmalloc-sys/build.rs passes CMake-style booleans through define(key, "ON"/"OFF"). CMake reads -DKEY=OFF as "not enabled", but the C preprocessor does not: -DKEY=OFF defines KEY, and snmalloc guards these options with #if defined(KEY) / #ifdef KEY. So on the build_cc path every one of these options is enabled precisely when it was meant to be disabled. The cmake path is unaffected.

Two of the five have live consumers in the library:

SNMALLOC_QEMU_WORKAROUND is on for every build_cc build, no qemu feature required. On 64-bit targets that selects the ds_core/sizeclassconfig.h branch intended for QEMU CI, raising MIN_CHUNK_BITS from 14 to 17 and MAX_SMALL_SIZECLASS_BITS from 16 to 19, which moves the slab/chunk split from 64 KiB to 512 KiB. On Linux it also trips the #ifndef in pal/pal_linux.h, so zero() loses the madvise(MADV_DONTNEED) fast path for large blocks and falls back to memset on every architecture, 32-bit included.

SNMALLOC_RUST_LIBC_API compiles the libc API shims in override/rust.cc unconditionally, exporting symbols the libc-api feature is meant to gate.

The other three are benign but go through the same call: USE_SNMALLOC_STATS only reaches test code, and SNMALLOC_ENABLE_DYNAMIC_LOADING / SNMALLOC_USE_CXX17 have no preprocessor consumer at all (they are CMake options, inert on the cc path in either direction).

The fix

Adds define_bool to BuilderDefine rather than patching the flags one at a time, so a future boolean option cannot reintroduce this. The cc impl emits a bare -DKEY only when the option is on; the cmake impl keeps passing ON/OFF exactly as before. This generalises what SNMALLOC_CHECK_LOADS, SNMALLOC_PAGEID and SNMALLOC_USE_WAIT_ON_ADDRESS already do by hand with per-flag cfg(feature = "build_cc") branches.

If you would prefer a smaller diff, the same bug can be fixed by giving those five flags the same per-flag branches the three above already use. I went with the trait method because it removes the failure mode rather than the instances, but I am happy to switch.

Verification

Grepping the compiler invocation under CC_ENABLE_DEBUG_OUTPUT=1.

Before, cargo build -p snmalloc-sys --no-default-features --features build_cc,usewait-on-address:

"-DSNMALLOC_CHECK_LOADS=false"
"-DSNMALLOC_ENABLE_DYNAMIC_LOADING=OFF"
"-DSNMALLOC_PAGEID=false"
"-DSNMALLOC_QEMU_WORKAROUND=OFF"
"-DSNMALLOC_RUST_LIBC_API=OFF"
"-DSNMALLOC_USE_CXX17=OFF"
"-DSNMALLOC_USE_WAIT_ON_ADDRESS=1"
"-DUSE_SNMALLOC_STATS=OFF"

After, the same command:

"-DSNMALLOC_CHECK_LOADS=false"
"-DSNMALLOC_PAGEID=false"
"-DSNMALLOC_USE_WAIT_ON_ADDRESS=1"

And with --features build_cc,qemu,libc-api, confirming the enabled direction still reaches the preprocessor:

"-DSNMALLOC_QEMU_WORKAROUND"
"-DSNMALLOC_RUST_LIBC_API"

cargo test passes on both the cc and the cmake path.

How I ran into it

I maintain a D3D9 translation layer that uses snmalloc as the global allocator for a 32-bit PE running under Wine on macOS, and I was reading sizeclassconfig.h to work out exactly which allocation sizes miss the per-thread cache. The 64-bit build's numbers did not match the source I was reading, which is what led here.

`build.rs` passes CMake-style booleans through `define(key, "ON"/"OFF")`.
CMake reads `-DKEY=OFF` as "not enabled", but the C preprocessor does not:
`-DKEY=OFF` defines `KEY`, and snmalloc guards these options with
`#if defined(KEY)` / `#ifdef KEY`. On the `build_cc` path every one of
them is therefore enabled precisely when it was meant to be disabled.

Two have live consumers in the library:

`SNMALLOC_QEMU_WORKAROUND` is on for every `build_cc` build. On 64-bit
targets that takes the `sizeclassconfig.h` branch intended for QEMU CI,
raising `MIN_CHUNK_BITS` from 14 to 17 and `MAX_SMALL_SIZECLASS_BITS`
from 16 to 19, so the slab/chunk split moves from 64 KiB to 512 KiB. On
Linux it also trips the `#ifndef` in `pal_linux.h`, dropping the
`madvise(MADV_DONTNEED)` fast path for zeroing large blocks and falling
back to `memset` on every target, 32-bit included.

`SNMALLOC_RUST_LIBC_API` compiles the libc API shims in `rust.cc`
unconditionally, exporting symbols the `libc-api` feature is supposed to
gate.

`USE_SNMALLOC_STATS` is defined too but only reaches test code, so it is
harmless here. `SNMALLOC_ENABLE_DYNAMIC_LOADING` and `SNMALLOC_USE_CXX17`
have no preprocessor consumer at all; they are CMake options, so on the
cc path they are inert in either direction. All five go through the same
call, so all five are converted.

Adds `define_bool` to `BuilderDefine` rather than fixing the flags one at
a time, so the next boolean option cannot reintroduce this. The cc impl
emits a bare `-DKEY` only when enabled; the cmake impl keeps passing
`ON`/`OFF`, unchanged. This follows what `SNMALLOC_CHECK_LOADS`,
`SNMALLOC_PAGEID` and `SNMALLOC_USE_WAIT_ON_ADDRESS` already do by hand
with per-flag `cfg(feature = "build_cc")` branches.

Verified by grepping the compiler invocation under
`CC_ENABLE_DEBUG_OUTPUT=1`. Before, a default `build_cc` build emitted
`-DSNMALLOC_QEMU_WORKAROUND=OFF`, `-DSNMALLOC_RUST_LIBC_API=OFF`,
`-DUSE_SNMALLOC_STATS=OFF`, `-DSNMALLOC_ENABLE_DYNAMIC_LOADING=OFF` and
`-DSNMALLOC_USE_CXX17=OFF`. After, none of them appear; building with
`--features qemu,libc-api` emits `-DSNMALLOC_QEMU_WORKAROUND` and
`-DSNMALLOC_RUST_LIBC_API` with no value. Binding tests pass on both the
cc and cmake paths.
@athei

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant