From fca7639f29e160de9732fd6a34d108432e45df5f Mon Sep 17 00:00:00 2001 From: Zach Vorhies Date: Wed, 30 Sep 2026 19:25:50 -0700 Subject: [PATCH] docs: add HOW_TO_PR_UPSTREAM.md and a pr-upstream skill, mandatory for upstream PRs HOW_TO_PR_UPSTREAM.md records the full procedure behind the numbers in wild-linker/wild#2634: how the binaries are built, the captures, capture_ab.py settings (budgets, ABBA pairs, tmpfs output, discarding contaminated runs), correctness evidence and the fork CI A/B. It also covers the verification steps, the PR description format (italic AI-disclosure header, then Summary, Benchmarks, Design), the approval rule, and the lessons previously in AGENTS.md. The pr-upstream repo skill walks through it, and AGENTS.md makes reading it mandatory before authoring an upstream PR. make_small_captures.sh regenerates the tinyc and cxxdbg small-link captures. --- .claude/skills/pr-upstream/SKILL.md | 46 ++++ AGENTS.md | 51 +--- HOW_TO_PR_UPSTREAM.md | 236 ++++++++++++++++++ REUSE.toml | 2 + .../performance_guard/make_small_captures.sh | 78 ++++++ 5 files changed, 365 insertions(+), 48 deletions(-) create mode 100644 .claude/skills/pr-upstream/SKILL.md create mode 100644 HOW_TO_PR_UPSTREAM.md create mode 100755 benchmarks/performance_guard/make_small_captures.sh diff --git a/.claude/skills/pr-upstream/SKILL.md b/.claude/skills/pr-upstream/SKILL.md new file mode 100644 index 000000000..a6c0d5d4d --- /dev/null +++ b/.claude/skills/pr-upstream/SKILL.md @@ -0,0 +1,46 @@ +--- +name: pr-upstream +description: Author, update or reply on a pull request to upstream wild-linker/wild. Use whenever a PR, PR description, PR comment or review reply for wild-linker/wild is being written or changed, including performance PRs and bug fixes. Enforces HOW_TO_PR_UPSTREAM.md (benchmarks, verification, description format, disclosure header, approval). +--- + +# PR to upstream Wild + +`HOW_TO_PR_UPSTREAM.md` at the repository root is the source of truth. This skill makes sure it is +followed. Do these steps in order and don't skip any. + +1. **Read `HOW_TO_PR_UPSTREAM.md` in full**, from the checked-in copy, not from memory. Also read + upstream's `CONTRIBUTING.md`. +2. **Confirm the target.** Upstream writes need the user's explicit request in this conversation + (see `AGENTS.md`, "GitHub target"). If the request doesn't name `wild-linker/wild` or ask for an + upstream change, stop and ask. +3. **Branch** from upstream's current `main` as `up/` (guide §1). Keep the generic path + unchanged and the central diff small. Report it. +4. **Benchmarks** for any performance claim: follow guide §2 exactly. + - Build both binaries the same way, with `RUSTUP_TOOLCHAIN=1.98.1 SOLDR_ALLOW_UNPINNED=1`. + - Run `capture_ab.py` with `--extra=--no-fork`, `TMPDIR` on tmpfs and the listed budgets. + - Cover at least `wild-lto-debug`, `wild` and `tinyc`; create the small captures with + `benchmarks/performance_guard/make_small_captures.sh`. + - Prove byte-identical output with both build-id modes, and prove any fallback path. + - Run the fork CI A/B through a draft `[bench]` PR. + - Paste tables from `pr_table.py` only. + - Rerun everything if upstream `main` moved. +5. **Verify** (guide §3): tests, clippy, nightly rustfmt, and the `wasm32-wasip1` unit tests. Use + cross-arch CI when relocation order or parallelism changes. +6. **Draft the description** from the template in guide §4. + - The first line is the italic disclosure header, exactly: + `*AI generated description: authored by [AI](https://github.com/zackees/clud), approved by Zach Vorhies*`. + - Then Summary, Benchmarks (or Repro and When it fails for bug fixes), and Design. + - Keep it short. Mention AI nowhere else. +7. **Get approval.** Show Zach Vorhies the final title, description and any comment text, and wait + for an explicit OK. The header says "approved by Zach Vorhies", so nothing goes upstream without + it. +8. **Publish and follow up** (guide §5). + - Post a short comment when reviewers need to know about a change. + - Cross-reference superseded or stacked PRs both ways. + - Check `gh api rate_limit` before bursts, and don't hand-roll polling loops. + +Before finishing, report: +- the PR URL; +- the commands and captures used for the tables; +- where the raw JSON is kept; +- anything from the guide that couldn't be done, and why. diff --git a/AGENTS.md b/AGENTS.md index 52cef2bad..8e6cf6e20 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,52 +22,7 @@ - Every performance PR proposed to `wild-linker/wild` must include the per-benchmark statistics table described in `benchmarks/performance_guard/README.md` ("Upstream performance PRs"), comparing the exact PR head against the exact upstream `main` it is based on. - Generate the tables with `benchmarks/performance_guard/capture_ab.py` and paste the output of `pr_table.py`; never hand-type or edit the numbers. Fork CI's tables (the `performance` label) compare against the fork's merge base and are not a substitute. -## Upstream contributions: lessons learned +## Upstream pull requests (mandatory) -These come from getting debug-relocation work into `wild-linker/wild` (#2620 merged; #2621 and #2629 reviewed; #2628 and #2630 open). Follow them to avoid repeat review rounds. - -**What the maintainers weigh** - -- Every gain is weighed against complexity. #2620 merged because it was "relatively simple" with a large speedup; #2621 drew a tentative reject for adding "a whole new layer of abstraction", despite being faster. -- Keep the generic path unchanged. Put an optimization in one private module with a single `pub(crate)` entry point that falls back to the existing code for anything it doesn't handle. Avoid new `Arch`/`Platform` trait items and edits spread across the arch files. -- Report and minimise the central diff: lines touched outside the new module. Restoring or deleting code in shared paths is a strength; say so. -- One change per PR. Upstream squashes each PR into one commit; separable ideas go in separate PRs, each with its own numbers. Don't open many PRs at once. - -**Measuring** - -- Ablate before proposing: measure each piece on its own and drop anything inside the noise. In #2621's work, a symbol cache produced most of the gain, and three small standalone ideas were noise on real links. -- Always include a small-link regression check (many small objects, e.g. a 5,000-file C link). Per-call setup cost, such as a heap-allocated cache per debug section, is invisible on big links and dominant on small ones. That is what caused the "small slowdowns" on #2621. -- Measure the exact PR head against the exact current upstream `main`. If `main` moves, rebase and measure again before publishing numbers. -- Local hosts are often loaded by other work. Use instruction counts and paired, interleaved, pinned runs locally; take wall time from a quiet window or from the fork CI's real-link A/B. -- `perf annotate` the hot loop. The biggest wins came from store-forwarding stalls (copying byte-aligned structs such as the raw `Rela`), a runtime division in an alignment check, and a variable-length `copy_from_slice` that became a `memcpy` call. -- Byte-identical output against `main` with `--build-id=none` and `--build-id=fast` is expected in every performance PR description. - -**Writing the PR** - -- Follow `CONTRIBUTING.md`: short descriptions, in the contributor's own voice. They become the squash commit message. Titles are release-notes friendly with a conventional prefix (`perf:`, `fix:`). Format with nightly rustfmt. -- Structure: Summary (2–3 sentences with the headline numbers), then what changed or why it fails, then the benchmark tables, then the byte-identity statement. For bug fixes, include a minimal repro that you have actually run against upstream's binary, and when it triggers. -- Name the benchmarks you couldn't run (zed, clang, chrome-android and rust-analyzer take hours to build) and invite the maintainer to run their suite. -- Don't mention LLMs, agents or token spend; `CONTRIBUTING.md` asks for contributor-written PRs. - -**Review etiquette** - -- Answer review concerns with data and a concrete plan, addressed to whoever raised them. Implementing the reviewer's suggested shape and measuring it beats arguing. -- Don't replace code under a PR the maintainer has just validated. Send a follow-up or superseding PR, state "Stacked on #N; review only the last commit" when stacked, and cross-reference both ways. -- Editing a PR description doesn't notify anyone; post a short comment when reviewers need to know. - -**Upstream CI gotchas** - -- `cargo test -p libwild --target wasm32-wasip1` runs every unit test under WASI, which has no temp dir or process spawning. Guard such tests with `if crate::host::os::SANDBOXED { return; }`. -- On RISC-V and LoongArch some debug relocations read the bytes they patch (add/subtract and ULEB128 pairs). Anything that reorders or parallelises relocation application must keep those serial. -- Check that the PR, its sibling PRs and current `main` merge and compile together before claiming they're independent. - -**Fork and GitHub mechanics** - -- To benchmark an upstream PR on fork CI, open a draft `[bench]` PR whose base is upstream's `main` plus only the fork's CI/infra commits. `pull_request` workflows run from the PR's merge ref, so a pure-upstream base has no fork workflows. -- `gh pr create --label` applies the label after the `opened` event fires, so label-gated jobs don't start. Add the label, then close and reopen the PR (or push). -- The GitHub API allows 5,000 requests an hour, shared across every tool and agent. Avoid hand-written polling loops; use `clud tool run github/pr_merge_watch.py` with long intervals and check `gh api rate_limit` before bursts. - -**Local environment** - -- Before blaming a tool, reproduce the failure in a stock container and hash the suspect files. A "soldr cache bug" turned out to be toolchain rlibs zeroed through wild's save-dir hard links (fixed in this fork by #41; proposed upstream as wild-linker/wild#2628); `find ~/.rustup ~/.soldr/rustup -path '*rustlib*' -size 0` finds that damage. -- soldr's `stable` is rustc 1.98.0; this repository pins 1.98.1 (`rust-toolchain.toml`). Build upstream checkouts, which have no toolchain file, with `RUSTUP_TOOLCHAIN=1.98.1 SOLDR_ALLOW_UNPINNED=1`. +- **Before authoring, updating or replying on any `wild-linker/wild` pull request, read [`HOW_TO_PR_UPSTREAM.md`](HOW_TO_PR_UPSTREAM.md) in full, and use the `pr-upstream` skill (`.claude/skills/pr-upstream/SKILL.md`).** It holds the required benchmark procedure, the verification steps, the description format (an italic AI-disclosure header, then Summary, Benchmarks, Design), the approval rule, and the lessons from earlier upstream PRs. +- Nothing is posted upstream without Zach Vorhies's explicit approval of the final text. diff --git a/HOW_TO_PR_UPSTREAM.md b/HOW_TO_PR_UPSTREAM.md new file mode 100644 index 000000000..a6a10549f --- /dev/null +++ b/HOW_TO_PR_UPSTREAM.md @@ -0,0 +1,236 @@ +# How to send a PR to upstream Wild + +**Mandatory.** Read this whole document before authoring, updating or replying on any pull request +to [`wild-linker/wild`](https://github.com/wild-linker/wild). The `pr-upstream` repo skill +(`.claude/skills/pr-upstream/SKILL.md`) walks through it step by step; use it. + +Upstream is a professional project with two maintainers who review every change. A PR that follows +this document should need one review round, not three. + +## 1. Before you write code + +- Read upstream's [`CONTRIBUTING.md`](CONTRIBUTING.md). In short: short descriptions, one change + per PR, nightly rustfmt, and the PR description becomes the squash commit message. +- Branch from upstream's current `main`, not the fork's: `git fetch upstream && git worktree add + -b up/ ../wild- upstream/main`. Push `up/` to `origin` and open the PR from + `zackees:up/`. Never include fork-only commits. +- Keep the generic path unchanged. Every gain is weighed against complexity: #2620 merged + because it was "relatively simple" with a large speedup, while #2621 drew a tentative reject for + adding "a whole new layer of abstraction", even though it was faster. Put an optimization in one + private module with a single `pub(crate)` or `pub(super)` entry point that falls back to the + existing code for anything it doesn't handle (see #2634). Avoid new `Arch`/`Platform` trait items + and edits spread across the arch files. Report the *central diff* (lines touched outside the new + module) and keep it small. +- Find the cost before designing the fix: `perf record` the link and `perf annotate` the hot loop. + The biggest wins in the #2621 work were store-forwarding stalls (byte-aligned structs such as the + raw `Rela` copied to the stack), a runtime division in an alignment check, and a variable-length + `copy_from_slice` that compiled to a `memcpy` call. +- One idea per PR. Measure each piece on its own before proposing it, and drop anything that is + noise on real links. In the #2621 work, a symbol cache produced most of the gain, and three small + standalone ideas were noise. + +## 2. Benchmarks (mandatory for every performance PR) + +Every performance claim must come from the procedure below. Tables are pasted from the tool's +output, never typed or edited. This is how the numbers in #2634 were produced. + +### 2.1 Build the two binaries identically + +`#1` is the exact upstream `main` commit the branch is based on; `#2` is the exact PR head. +Upstream has no `rust-toolchain.toml`, so pin the toolchain: + +```sh +git fetch upstream +git worktree add --detach ../wild-base upstream/main +for tree in ../wild-base ../wild-; do + (cd "$tree" && RUSTUP_TOOLCHAIN=1.98.1 SOLDR_ALLOW_UNPINNED=1 \ + soldr cargo build --release -p wild-linker --bin wild) +done +``` + +If upstream `main` moves before you publish, rebase, rebuild both and measure again. Old numbers +are context only. + +### 2.2 Captures + +Benchmarks relink save-dir captures. Capture a link by building the project with Wild as the linker +and `WILD_SAVE_BASE` set, then keep the numbered directory whose `run-with` ends with the main +binary's `# Original output file:`: + +```sh +RUSTFLAGS="-Clinker=clang -Clink-arg=--ld-path=$PWD/../wild-base/target/release/wild" \ + WILD_SAVE_BASE=~/save/ RUSTUP_TOOLCHAIN=1.98.1 SOLDR_ALLOW_UNPINNED=1 \ + soldr cargo build --release -p wild-linker --bin wild +``` + +| Benchmark | Link | How to capture | Required | +| --- | --- | --- | --- | +| `wild-lto-debug` | Wild with fat LTO and full debug info | as above plus `CARGO_PROFILE_RELEASE_LTO=fat CARGO_PROFILE_RELEASE_DEBUG=full CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1` | always | +| `wild` | Wild, ordinary release build | as above | always | +| `tinyc` | 5,000 one-function C files plus a main, DWARF 5 | `benchmarks/performance_guard/make_small_captures.sh ` | always (small-link regression check) | +| `cxxdbg` | 1,500 C++ files using `std::string`/`vector`/`map`, DWARF 5 | the same script | when debug or C++ paths change | +| `rust-analyzer`, `zed-release`, `clang-release`, `clang-debug`, `chrome-android` | the maintainer's larger suite | see `benchmarks/performance_guard/README.md` | optional: hours of build time and up to 25 GiB RAM | + +The small-link check is not optional. Per-call setup cost, such as a heap-allocated cache per debug +section, is invisible on big links and dominant on links with thousands of small objects. That is +what caused the "small slowdowns" on #2621. + +Name the benchmarks you didn't run in the PR and invite the maintainer to run them; David has the +large captures. + +### 2.3 Run the A/B + +Use `benchmarks/performance_guard/capture_ab.py` from the fork's `main`. It runs warmups, then +interleaved ABBA pairs until `--pairs`, then keeps going until `--budget-seconds` is spent (capped +by `--max-pairs`). It records wall time, user, system and peak RSS (from `wait4`), plus cycles, +instructions, cache references and misses, and branches and branch misses (from +`perf_event_open`). It also checks that the two outputs are byte-identical apart from `.comment`. + +```sh +export TMPDIR=/dev/shm/wild-bench # outputs on tmpfs, see below +mkdir -p "$TMPDIR" +env -u BASH_ENV python3 benchmarks/performance_guard/capture_ab.py \ + --capture ~/save/wild-lto-debug --benchmark wild-lto-debug \ + --baseline ../wild-base/target/release/wild \ + --baseline-label "upstream-main $(git -C ../wild-base rev-parse --short=12 HEAD)" \ + --candidate ../wild-/target/release/wild \ + --candidate-label "pr-head $(git -C ../wild- rev-parse --short=12 HEAD)" \ + --extra=--no-fork --pairs 100 --max-pairs 20000 --budget-seconds 600 \ + --output ~/bench//wild-lto-debug.json +python3 benchmarks/performance_guard/pr_table.py ~/bench//*.json +``` + +- Budgets: 600 seconds for `wild-lto-debug` and `wild`, 300 for `tinyc` and `cxxdbg`. That gave + n = 2,340 and 5,438 pairs for #2634 and confidence intervals of about 0.2%. +- `--no-fork` is required, so peak RSS and CPU time are the linker's own. +- Put outputs on tmpfs (`TMPDIR=/dev/shm/...`). On ext4, page faults followed each arm's output + file rather than the binary, which once made a candidate look 28% worse in system time. +- `env -u BASH_ENV` is required here; the harness's `BASH_ENV` breaks the generated `run-with` + scripts. +- The host is shared. Record the 1-minute load (`/proc/loadavg`) with each run. Discard and rerun + any run that overlapped other heavy work; don't average it in. Add `--cpus` to pin both arms if + load can't be avoided. Instruction counts are robust to load; wall time is not. +- Remove `/dev/shm/wild-bench` when you're done; it holds hundreds of MB of RAM. + +### 2.4 Correctness evidence + +- Byte-identical output to `main`, apart from `.comment`, with `--build-id=none` **and** + `--build-id=fast`, on every capture above. `capture_ab.py` checks this per table; also link each + capture once with both build-id modes and `cmp` the results. +- If the change has a fast path or a fallback, also prove the fallback: force it, for example with + a temporary build that makes the fast path always fail, and check output is still identical. +- Changes that reorder or parallelise relocation application must stay correct on RISC-V and + LoongArch, where some debug relocations read the bytes they patch. Normal-sized tests may never + reach your path; lower the threshold in a throwaway branch (as in #2634) and run the cross-arch + CI. + +### 2.5 Fork CI (4-vCPU runner numbers) + +Also run the real fat-LTO A/B on a GitHub-hosted runner: + +1. Build a base branch: upstream `main` plus only the fork's CI and infrastructure commits. + `pull_request` workflows run from the PR's merge ref, so a pure-upstream base has no fork + workflows. +2. Push the PR's commits onto it as `bench/`, and open a draft PR titled + `[bench] , do not merge` against the base branch. +3. Add the `performance` label *after* creating the PR, then close and reopen it. A label applied by + `gh pr create --label` lands after the `opened` event, so label-gated jobs don't start. +4. Read the `capture-ab` job summary and artifact. Hosted runners have no hardware counters, so + those rows read `n/a`. Close the draft when you're done. + +## 3. Verification before pushing + +```sh +env -u BASH_ENV RUSTUP_TOOLCHAIN=1.98.1 SOLDR_ALLOW_UNPINNED=1 soldr cargo test --release \ + -p linker-utils -p libwild -p wild-linker -- --skip tidy_tests::check_toml_format +RUSTUP_TOOLCHAIN=1.98.1 SOLDR_ALLOW_UNPINNED=1 soldr cargo clippy --release -p libwild --all-targets +RUSTUP_TOOLCHAIN=nightly SOLDR_ALLOW_UNPINNED=1 soldr cargo fmt --all -- --check +``` + +- `check_toml_format` fails locally when a worktree contains an untracked `.cargo/registry`; + CI runs it. +- Upstream CI runs `cargo test --profile ci -p libwild --target wasm32-wasip1` under wasmtime. + WASI has no temp dir or process spawning; guard such tests with + `if crate::host::os::SANDBOXED { return; }`. To run it locally, set + `CARGO_TARGET_WASM32_WASIP1_RUNNER=" --dir . --dir src --dir /"`. Point + `CC_wasm32_wasip1`/`AR_wasm32_wasip1` at a WASI C compiler + (`nix build nixpkgs#pkgsCross.wasi32.stdenv.cc`). Add + `CARGO_TARGET_WASM32_WASIP1_RUSTFLAGS="--cfg local_wasi_cc"` if soldr serves a stale host-built + `zstd_sys`. +- If you claim the PR is independent of other open PRs, check that they merge and compile together + with current `main`. + +## 4. The PR description + +The description is the squash commit message, so keep it short. It starts with the disclosure +header, in italics, then exactly these sections in this order: Summary, Benchmarks, Design. + +````markdown +*AI generated description: authored by [AI](https://github.com/zackees/clud), approved by Zach Vorhies* + +## Summary + +<2–3 sentences: what changes, the headline numbers, and any PRs this supersedes or is stacked on.> + +## Benchmarks + +`#1` is `main` (), `#2` is this PR. , `--no-fork`, interleaved ABBA pairs, output on +tmpfs. + +: + +``` + +``` + + + +On a 4-vCPU GitHub-hosted runner ( pairs, fat-LTO link of Wild, vs `main`): wall and + CPU (`--build-id=fast`: and ). + +Output is byte-identical to `main` with `--build-id=none` and `--build-id=fast`, apart from +`.comment`, on . + +I couldn't run . , if you have time to run your suite on this branch, that +would cover them. + +## Design + +- +- +- +- Central diff: <+N/−M in files>; is lines including tests. + +```` + +- For bug fixes, replace Benchmarks with **Repro** (a minimal script you have actually run against + upstream's binary, with before and after results) and add **When it fails**, as in #2628. +- Stacked PRs start with "Stacked on #N. Please review only the last commit." +- Titles are release-notes friendly with a conventional prefix: `perf:`, `fix:`, `chore:`. +- The first line is always the disclosure header, exactly as above and in italics: + `*AI generated description: authored by [AI](https://github.com/zackees/clud), approved by Zach Vorhies*`. It is the only place AI authorship is mentioned. Don't mention token counts, CI cost or + tooling anywhere else. +- **"approved by Zach Vorhies" must be true.** Show Zach the final title and description and get + explicit approval before creating or editing the PR. + +## 5. After you open it + +- Editing a description notifies nobody. Post a short comment when reviewers need to know + something changed. +- Answer review concerns with data and a concrete plan, addressed to whoever raised them. + Implementing the reviewer's suggested shape and measuring it beats arguing. Draft any reply with + a sensitive tone for Zach to approve; don't post it unasked. +- Don't replace the code under a PR the maintainer has just validated. Open a follow-up or + superseding PR and cross-reference both ways. Close the old PR only when the new one is on par. +- The GitHub API allows 5,000 requests an hour, shared by every tool and agent. Don't hand-roll + polling loops. Use `clud tool run github/pr_merge_watch.py` with long intervals, and check + `gh api rate_limit` before bursts. + +## 6. Local environment + +- Before blaming a tool, reproduce the failure in a stock container and hash the suspect files. + A "soldr cache bug" turned out to be toolchain rlibs zeroed through wild's save-dir hard links + (fixed in this fork by #41; proposed upstream as wild-linker/wild#2628). + `find ~/.rustup ~/.soldr/rustup -path '*rustlib*' -size 0` finds that damage. +- soldr's `stable` is rustc 1.98.0. Upstream checkouts have no toolchain file, so always pass + `RUSTUP_TOOLCHAIN=1.98.1 SOLDR_ALLOW_UNPINNED=1`. diff --git a/REUSE.toml b/REUSE.toml index 19d33867b..9f99b5090 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -26,6 +26,8 @@ path = [ "LINKER_SCRIPT_SUPPORT.md", "PACKAGING.md", "AGENTS.md", + "HOW_TO_PR_UPSTREAM.md", + ".claude/**", "README.md", "RELEASING.md", "FAQ.md", diff --git a/benchmarks/performance_guard/make_small_captures.sh b/benchmarks/performance_guard/make_small_captures.sh new file mode 100755 index 000000000..e7a2a3e83 --- /dev/null +++ b/benchmarks/performance_guard/make_small_captures.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# Generates the small-link regression captures used by HOW_TO_PR_UPSTREAM.md: +# +# tinyc 5,000 one-function C files plus a main that calls them all (many tiny debug sections) +# cxxdbg 1,500 C++ files using std::string/vector/map plus a main (moderate debug info) +# +# Both are compiled with clang at -O1 with DWARF 5 debug info, one function/data section per +# symbol, then linked once by the given Wild binary under WILD_SAVE_BASE, leaving a save-dir capture +# that capture_ab.py can relink. +# +# usage: make_small_captures.sh +# -> /cap-tinyc and /cap-cxxdbg (each contains run-with) +set -euo pipefail + +wild=$(realpath "$1") +out=$(realpath -m "$2") +jobs=$(nproc) +cflags=(-c -O1 -g -gdwarf-5 -ffunction-sections -fdata-sections) + +mkdir -p "$out" + +make_tinyc() { + local dir=$out/tinyc + mkdir -p "$dir/src" "$dir/obj" + { + for i in $(seq 0 4999); do echo "int f$i(int);"; done + echo "int main(int c, char **v) { int s = 0;" + for i in $(seq 0 4999); do echo " s += f$i(c);"; done + echo " return s & 1; }" + } > "$dir/src/main.c" + for i in $(seq 0 4999); do + printf 'int g%d;\nint f%d(int x) { return x * %d + g%d; }\n' "$i" "$i" "$i" "$i" > "$dir/src/t$i.c" + done + ls "$dir"/src/*.c | xargs -P "$jobs" -I{} sh -c \ + 'clang '"${cflags[*]}"' "$1" -o "$2/obj/$(basename "$1" .c).o"' _ {} "$dir" + rm -rf "$out/cap-tinyc-all" + WILD_SAVE_BASE="$out/cap-tinyc-all" clang --ld-path="$wild" "$dir"/obj/*.o -o "$dir/tinyc.bin" + ln -sfn "$out/cap-tinyc-all/0" "$out/cap-tinyc" +} + +make_cxxdbg() { + local dir=$out/cxxdbg + mkdir -p "$dir/src" "$dir/obj" + for i in $(seq 0 1499); do + cat > "$dir/src/f$i.cpp" < +#include +#include +namespace m$i { +struct Item$i { std::string name; int value; double weight; }; +static std::map table$i; +int work$i(int n) { + std::vector v; + for (int k = 0; k < n; ++k) v.push_back(Item$i{"item" + std::to_string(k), k * $i, k * 0.5}); + int s = 0; + for (auto &it : v) { s += it.value + (int)it.name.size(); table$i[it.name] = s; } + return s; +} +} +int entry$i(int n) { return m$i::work$i(n); } +EOF + done + { + for i in $(seq 0 1499); do echo "int entry$i(int);"; done + echo "int main(int argc, char**) { int s = 0;" + for i in $(seq 0 1499); do echo " s += entry$i(argc);"; done + echo " return s & 1; }" + } > "$dir/src/main.cpp" + ls "$dir"/src/*.cpp | xargs -P "$jobs" -I{} sh -c \ + 'clang++ '"${cflags[*]}"' "$1" -o "$2/obj/$(basename "$1" .cpp).o"' _ {} "$dir" + rm -rf "$out/cap-cxxdbg-all" + WILD_SAVE_BASE="$out/cap-cxxdbg-all" clang++ --ld-path="$wild" "$dir"/obj/*.o -o "$dir/cxxdbg.bin" + ln -sfn "$out/cap-cxxdbg-all/0" "$out/cap-cxxdbg" +} + +make_tinyc +make_cxxdbg +echo "captures: $out/cap-tinyc $out/cap-cxxdbg"