一键导入
stop-chasing-the-optimizer-reduce-instead
After two failed anti-optimization patches, stop and reduce; don't keep bolting on `volatile` / `noinline`.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
After two failed anti-optimization patches, stop and reduce; don't keep bolting on `volatile` / `noinline`.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
A TRefCountPtr/TSharedPtr member in a UE class needs the pointee's full definition, not a forward decl.
When WSL's mirrored networking fails and falls back to "None", plus /etc/wsl.conf has generateResolvConf=false, the distro has no DNS; fix both layers.
When a Windows shell (PowerShell/cmd) feeds a bash script into WSL, CRLF line endings can corrupt the first shell builtin; force LF or pipe via a temp file.
Before "fixing" a recurring error, check git log to see if it was already fixed upstream — the working tree may just be stale.
Always add a space after URL brackets in Markdown to prevent 404 errors with special characters.
Plan a Python 3 modernization sweep (f-strings, super(), type hints) as a series of mechanical PRs, not one mega-PR.
| name | stop-chasing-the-optimizer-reduce-instead |
| description | After two failed anti-optimization patches, stop and reduce; don't keep bolting on `volatile` / `noinline`. |
| tags | ["debugging-strategy","compilers","gcc","clang","optimizer","cpp"] |
You have a hot function that passes on one toolchain (typically MSVC)
but miscompiles or crashes under GCC / Clang at -O2 (or higher), and
you're on attempt three of sprinkling anti-optimization decorations:
volatile on a local / a parameter__attribute__((noinline)) on the callee__attribute__((optimize("O0"))) on the callee-fno-strict-aliasing on the TUasm volatile("" ::: "memory") barriersEach one "almost works" — the symptom moves by a few bytes or into a different test case — but never fully goes away. That's the signal to stop.
Adding anti-optimization pragmas is a local fix applied to a global belief: "the optimizer is wrong." Three things go wrong in practice:
noinline in one
spot shifts the inlining decision elsewhere; the miscompile now
lives in a different frame. You spend another iteration finding it.__attribute__((optimize("O0")))
on a single function inside an -O2 TU is known to produce ABI
mismatches between the -O0 callee and the -O2 caller on GCC
(frame pointer, red-zone, stack alignment). The "fix" introduces a
new SIGSEGV on the first call.-O2" — and have very different fixes. Patching blindly
leaves the root cause unknown, so the same bug returns the next
time someone touches the file.The heuristic: after two anti-optimization patches in a row have failed to fully fix the symptom, the next step is not a third patch. It's reduction.
Switch from "patch" mode to "reduce" mode:
.cpp of ≤ 200 lines that links with nothing
but libc. Must reproduce the divergence between MSVC and GCC/Clang
-O2. If it doesn't reproduce standalone, the bug is in how the
function is called, not the function itself — go up a frame.g++ -O2 -S -masm=intel repro.cpp vs
clang++ -O2 -S -masm=intel repro.cpp vs MSVC /FAs. Look for
loads from offsets you never wrote, or stores that the compiler
elided. This usually tells you within minutes whether it's UB
(compiler is within its rights) or a real miscompile.creduce / cvise. Feed the standalone repro plus
a predicate script (g++ -O2 x.cpp && ./a.out; [ $? -ne 0 ]) to
cvise. 200 lines typically collapses to 20.x >> 64 with a
branch, use memcpy instead of pointer-punning, add an
explicit bounds check). No volatile needed.memcpy or __attribute__((may_alias))
at the type, not -fno-strict-aliasing on the whole TU.The key is that step 4 is a single, documented change, not another round of sprinkling.
Real case (the one this skill came from): a bit-blit routine
appBitsCpyFast passed all MSVC tests but produced zeros on Linux GCC
-O2. Sequence that didn't work, in order:
// Attempt 1: mark the output volatile at call sites. Symptom moves.
// Attempt 2: __attribute__((noinline)) on the inner helper. Passes
// aligned cases, still fails unaligned.
// Attempt 3: __attribute__((optimize("O0"))) on the helper.
// Now SIGSEGVs on the first call (ABI mismatch). Worse.
At this point the right move is not attempt 4 (yet another decoration). It is to switch modes: stop patching, start reducing. The concrete recipe:
# 1. Rip the function into a standalone repro.cpp that links only
# against libc and still reproduces the MSVC-vs-GCC divergence.
# 2. Write a predicate script that exits 0 iff the bug reproduces.
cat > check.sh <<'EOF'
#!/bin/sh
g++ -std=c++17 -O2 -o /tmp/a repro.cpp || exit 1
/tmp/a | grep -q 'FAIL'
EOF
chmod +x check.sh
# 3. Let cvise shrink it.
cvise check.sh repro.cpp # typically 180 lines → ~20
The point of this skill is the mode switch, not a specific root cause — so this Example deliberately stops at "run the reducer." Whatever the 20-line output turns out to be (UB you wrote, a real miscompile, or an aliasing assumption), the next step is one documented fix per the four bullets in the Solution section, not a fourth anti-optimization decoration.
Status note: on the project that inspired this skill, the reduction step above has not yet been performed at the time of writing. The skill is deliberately published before the fix lands, because the lesson ("after two failed decorations, reduce") is independent of which root cause reduction eventually uncovers. If you want the specific root cause, check the project's issue tracker rather than trusting a plausible-sounding example in a skill file.
__attribute__((optimize("O0"))) on a single function inside an
-O2 TU on GCC can crash. The -O0 callee and -O2 caller
disagree on frame-pointer / red-zone / alignment convention. If you
really need a function at lower optimization, put it in its own TU
and compile that whole TU at -O1 (not -O0) via the build system.-fno-strict-aliasing is a TU-wide sledgehammer. If you reach
for it as a fix, first check whether a targeted memcpy (or
std::bit_cast in C++20) expresses what you meant. The sledgehammer
also disables legitimate optimizations for every other function in
the file.cvise is unavailable, a manual binary-chop of the function body
(delete the second half, does it still fail?) gets you 80% of the
way in 15 minutes.cvise project: https://github.com/marxin/cvisecreduce project: https://github.com/csmith-project/creduce