|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Reproductions for .agents/docs/2026-10-26-control-contract-runtime-and-ide-ecosystem-design.md |
| 3 | +# |
| 4 | +# Scenarios (each prints and never asserts; run one with SCENARIO=<letter>): |
| 5 | +# A #839 concurrent cold builds of one configuration (E4) and of two configurations (E5) |
| 6 | +# B #838 split exception ABI without sycl (E1), and the link-order fix (E2) |
| 7 | +# C query/build latency baseline (E8) -- needs hyperfine |
| 8 | +# D xlings interface fixed wait (E9) -- needs strace |
| 9 | +# E cache list cost (E10) -- needs strace |
| 10 | +# F clang + libstdc++ import std, cross-.so catch (E14, E15) |
| 11 | +# G mcpp run exit status (E16) |
| 12 | +# H X2 unrecorded projection entry refused (E12, E13) -- FRESH=1 builds a fresh home (downloads gcc twice) |
| 13 | +# I SYCL units vs the runtime collision set and foreign typeinfo (E19, E20) -- needs xim:dpcpp 7.1.0; |
| 14 | +# TENSORVIA=<checkout> adds Tensorvia's device units |
| 15 | +# |
| 16 | +# Uses only payloads already in the mcpp home, except H with FRESH=1. |
| 17 | +set -u |
| 18 | +M=${MCPP:-$HOME/.xlings/data/xpkgs/xim-x-mcpp/2026.10.24.1/bin/mcpp} |
| 19 | +R=${MCPP_HOME:-$HOME/.mcpp}/registry/data/xpkgs |
| 20 | +W=${WORK:-/tmp/mcpp-ecosystem-repro} |
| 21 | +export MCPP_JOBS=${MCPP_JOBS:-8} |
| 22 | +mkdir -p "$W" |
| 23 | +want() { [ -z "${SCENARIO:-}" ] || [ "$SCENARIO" = "$1" ]; } |
| 24 | + |
| 25 | +modules_project() { # $1 = dir |
| 26 | + rm -rf "$1"; mkdir -p "$1/src" |
| 27 | + printf '[package]\nname = "lk"\nversion = "0.1.0"\nstandard = "c++23"\n[toolchain]\ndefault = "gcc@16.1.0"\n' > "$1/mcpp.toml" |
| 28 | + for i in $(seq 1 24); do |
| 29 | + printf 'export module m%d;\nimport std;\nexport int f%d(){ std::vector<int> v(%d); return (int)v.size(); }\n' $i $i $i > "$1/src/m$i.cppm" |
| 30 | + done |
| 31 | + { echo 'import std;'; for i in $(seq 1 24); do echo "import m$i;"; done |
| 32 | + echo 'int main(){ std::println("{}", f1()+f24()); }'; } > "$1/src/main.cpp" |
| 33 | +} |
| 34 | + |
| 35 | +if want A; then |
| 36 | + echo "===== A: concurrent builds (#839)" |
| 37 | + modules_project "$W/a"; cd "$W/a" |
| 38 | + for r in 1 2 3 4 5; do |
| 39 | + rm -rf target |
| 40 | + ( nice -n 10 "$M" build >"$W/a1.log" 2>&1; echo $? >"$W/a1.rc" ) & |
| 41 | + ( sleep 0.$r; nice -n 10 "$M" build >"$W/a2.log" 2>&1; echo $? >"$W/a2.rc" ) & |
| 42 | + wait |
| 43 | + echo "round $r: rc=$(cat "$W/a1.rc"),$(cat "$W/a2.rc") $(grep -h -m1 'error:' "$W/a1.log" "$W/a2.log" | head -1)" |
| 44 | + done |
| 45 | + ( "$M" build >/dev/null 2>&1; echo "dev rc=$?" ) & ( "$M" build --release >/dev/null 2>&1; echo "release rc=$?" ) & wait |
| 46 | +fi |
| 47 | + |
| 48 | +if want B; then |
| 49 | + echo "===== B: split exception ABI (#838)" |
| 50 | + G=$R/xim-x-gcc/16.1.0/lib64 |
| 51 | + rm -rf "$W/b"; mkdir -p "$W/b/src" "$W/b/tests"; cd "$W/b" |
| 52 | + cat > mcpp.toml <<EOF |
| 53 | +[package] |
| 54 | +name = "xrt" |
| 55 | +version = "0.1.0" |
| 56 | +standard = "c++23" |
| 57 | +[toolchain] |
| 58 | +default = "llvm@22.1.8" |
| 59 | +[lib] |
| 60 | +path = "src/xrt.cppm" |
| 61 | +[targets.xrt] |
| 62 | +kind = "shared" |
| 63 | +[build] |
| 64 | +ldflags = ["-L$G", "-l:libstdc++.so.6"] |
| 65 | +EOF |
| 66 | + printf 'export module xrt;\nimport std;\nexport namespace xrt { int checked(int v) { if (v < 0) throw std::runtime_error("negative"); return v * 2; } }\n' > src/xrt.cppm |
| 67 | + cat > tests/catch.cpp <<'EOF' |
| 68 | +import std; |
| 69 | +import xrt; |
| 70 | +int main() { |
| 71 | + auto ep = std::make_exception_ptr(std::logic_error("x")); // a libc++abi-only entry point |
| 72 | + try { xrt::checked(-1); } |
| 73 | + catch (const std::exception& e) { std::println("caught as std::exception: {}", e.what()); return 0; } |
| 74 | + catch (...) { std::println("std::exception missed -> (...)"); return 1; } |
| 75 | +} |
| 76 | +EOF |
| 77 | + nice -n 10 "$M" test 2>&1 | tail -3 |
| 78 | + D=$(dirname "$(find target -name build.ninja | head -1)"); LL=$R/xim-x-llvm/22.1.8/bin |
| 79 | + "$LL/llvm-nm" -D --undefined-only "$D/bin/catch" | grep -E "__cxa_throw|__gxx_personality|_ZTISt13runtime_error" |
| 80 | + echo "-- relink with the toolchain's own runtime first (E2)" |
| 81 | + cd "$D" |
| 82 | + NJ=$(command -v ninja || find "$R" -name ninja -type f -perm -u+x | head -1) |
| 83 | + CMD=$("$NJ" -t commands bin/catch | tail -1) |
| 84 | + LIBS="$R/xim-x-llvm/22.1.8/lib/x86_64-unknown-linux-gnu/libc++.a $R/xim-x-llvm/22.1.8/lib/x86_64-unknown-linux-gnu/libc++abi.a" |
| 85 | + FIXED=$(echo "$CMD" | sed "s# -l:libstdc++.so.6# $LIBS -l:libstdc++.so.6#; s#-o bin/catch#-o bin/catch.fixed#; s#bin/catch.rsp#bin/catch.fixed.rsp#") |
| 86 | + printf 'obj/xrt.m.o\nobj/catch.o\nobj/std.o\n' > bin/catch.fixed.rsp |
| 87 | + eval "$FIXED" && ./bin/catch.fixed; echo "rc=$? GLIBCXX imports: $("$LL/llvm-nm" -D --undefined-only bin/catch.fixed | grep -c GLIBCXX)" |
| 88 | +fi |
| 89 | + |
| 90 | +if want C; then |
| 91 | + echo "===== C: latency baseline" |
| 92 | + modules_project "$W/c"; cd "$W/c"; "$M" build >/dev/null 2>&1 |
| 93 | + hyperfine -N --warmup 2 --runs 8 "$M --version" "$M --protocol-version" "$M build" "$M run" \ |
| 94 | + "$M emit build-database --format json" "$M self env --format json" \ |
| 95 | + "$M cache list --format json" "$M toolchain list --format json" 2>&1 | grep -E "^Benchmark|Time \(mean" |
| 96 | +fi |
| 97 | + |
| 98 | +if want D; then |
| 99 | + echo "===== D: xlings interface fixed wait" |
| 100 | + X=${MCPP_HOME:-$HOME/.mcpp}/registry/bin/xlings |
| 101 | + cd "${MCPP_HOME:-$HOME/.mcpp}/registry" |
| 102 | + for a in '--version' 'payload_status --args {"targets":["xim:gcc@16.1.0"]}'; do |
| 103 | + s=$(date +%s%N); env -u XLINGS_PROJECT_DIR -u XLINGS_ACTIVE_SUBOS XLINGS_HOME="$PWD" "$X" interface $a >/dev/null 2>&1 |
| 104 | + echo "interface ${a%% *}: $(( ($(date +%s%N)-s)/1000000 )) ms" |
| 105 | + done |
| 106 | + strace -f -tt -T -e trace=nanosleep,clock_nanosleep,futex,poll -o "$W/d.txt" \ |
| 107 | + env -u XLINGS_PROJECT_DIR -u XLINGS_ACTIVE_SUBOS XLINGS_HOME="$PWD" "$X" interface payload_status --args '{"targets":["xim:gcc@16.1.0"]}' >/dev/null 2>&1 |
| 108 | + awk '{ if (match($0, /<([0-9.]+)>$/, m) && m[1] > 0.05) print }' "$W/d.txt" |
| 109 | +fi |
| 110 | + |
| 111 | +if want E; then |
| 112 | + echo "===== E: cache list" |
| 113 | + C=${MCPP_HOME:-$HOME/.mcpp}/build-cache |
| 114 | + echo "files: $(find "$C" -type f | wc -l)" |
| 115 | + strace -c -f -o "$W/e.txt" "$M" cache list --format json >/dev/null 2>&1; head -8 "$W/e.txt" |
| 116 | +fi |
| 117 | + |
| 118 | +if want F; then |
| 119 | + echo "===== F: clang + libstdc++" |
| 120 | + CXX=$R/xim-x-llvm/22.1.8/bin/clang++; GLIBC=$R/xim-x-glibc/2.44.3; UAPI=$R/xim-x-linux-headers/5.11.1 |
| 121 | + for V in 15.1.0 16.1.0; do |
| 122 | + G=$R/xim-x-gcc/$V; GID=$(ls -d "$G"/lib/gcc/x86_64-linux-gnu/* | head -1) |
| 123 | + rm -rf "$W/f-$V"; mkdir -p "$W/f-$V"; cd "$W/f-$V" |
| 124 | + INC="-nostdinc++ -isystem $G/include/c++/$V -isystem $G/include/c++/$V/x86_64-linux-gnu -isystem $G/include/c++/$V/backward -nostdlibinc -isystem $GLIBC/include -isystem $UAPI/include" |
| 125 | + C="--no-default-config -std=c++23 --gcc-install-dir=$GID $INC" |
| 126 | + L="--no-default-config -stdlib=libstdc++ --gcc-install-dir=$GID -fuse-ld=lld --rtlib=libgcc --unwindlib=libgcc -L$G/lib64 -Wl,-rpath,$G/lib64 -B$GLIBC/lib64 -L$GLIBC/lib64 -Wl,-rpath,$GLIBC/lib64 -Wl,--dynamic-linker=$GLIBC/lib64/ld-linux-x86-64.so.2" |
| 127 | + "$CXX" $C -Wno-reserved-module-identifier --precompile -x c++-module "$G/include/c++/$V/bits/std.cc" -o std.pcm 2>/dev/null; echo "[$V] std.pcm rc=$?" |
| 128 | + "$CXX" $C -c std.pcm -o std.o 2>/dev/null |
| 129 | + printf 'import std;\nint main(){ std::vector<int> v{3,1,2}; std::ranges::sort(v); std::println("{} {}", v, std::format("{:.2f}", 3.14159));\n try { throw std::runtime_error("boom"); } catch (const std::exception& e) { std::println("caught: {}", e.what()); }\n std::jthread t([]{ std::println("thread ok"); }); }\n' > main.cpp |
| 130 | + "$CXX" $C -fmodule-file=std=std.pcm -c main.cpp -o main.o && "$CXX" $L main.o std.o -o app && ./app; echo "[$V] run rc=$?" |
| 131 | + printf '#include <stdexcept>\nint checked(int v){ if (v<0) throw std::runtime_error("negative (from .so)"); return v*2; }\n' > lib.cpp |
| 132 | + printf 'import std;\nint checked(int);\nint main(){ try { checked(-1); } catch (const std::exception& e) { std::println("caught across .so: {}", e.what()); return 0; } catch (...) { return 1; } }\n' > use.cpp |
| 133 | + "$CXX" $C -fPIC -c lib.cpp -o lib.o && "$CXX" $L -shared lib.o -o libthrow.so \ |
| 134 | + && "$CXX" $C -fmodule-file=std=std.pcm -c use.cpp -o use.o && "$CXX" $L use.o std.o -L. -lthrow -Wl,-rpath,"$PWD" -o use && ./use |
| 135 | + echo "[$V] cross-.so rc=$?" |
| 136 | + done |
| 137 | +fi |
| 138 | + |
| 139 | +if want G; then |
| 140 | + echo "===== G: mcpp run exit status" |
| 141 | + rm -rf "$W/g"; mkdir -p "$W/g/src"; cd "$W/g" |
| 142 | + printf '[package]\nname = "rc"\nversion = "0.1.0"\nstandard = "c++23"\n' > mcpp.toml |
| 143 | + printf 'import std;\nint main(int argc, char* argv[]) {\n std::string_view m = argc > 1 ? argv[1] : "ok";\n if (m == "exit") return std::atoi(argv[2]);\n if (m == "throw") throw std::runtime_error("boom");\n if (m == "abort") std::abort();\n if (m == "segv") std::raise(11);\n return 0; }\n' > src/main.cpp |
| 144 | + "$M" build >/dev/null 2>&1 |
| 145 | + for a in "ok" "exit 3" "exit 255" "exit 256" "throw" "abort" "segv"; do |
| 146 | + "$M" run -- $a >/dev/null 2>&1; echo "mcpp run -- $a => $?" |
| 147 | + done |
| 148 | +fi |
| 149 | + |
| 150 | +if want H; then |
| 151 | + echo "===== H: X2 unrecorded projection entry" |
| 152 | + if [ "${FRESH:-0}" = 1 ]; then |
| 153 | + H=$W/h-home; rm -rf "$H" "$W/h1"; mkdir -p "$W/h1/src" |
| 154 | + printf '[package]\nname = "p1"\nversion = "0.1.0"\nstandard = "c++23"\n[toolchain]\ndefault = "gcc@16.1.0"\n[xlings.workspace]\n"xim:gcc" = "15.1.0"\n' > "$W/h1/mcpp.toml" |
| 155 | + printf 'import std;\nint main(){ std::println("p1"); }\n' > "$W/h1/src/main.cpp" |
| 156 | + (cd "$W/h1" && MCPP_HOME=$H nice -n 10 "$M" build 2>&1 | tail -2) |
| 157 | + X=$H/registry/bin/xlings; E="env -u XLINGS_PROJECT_DIR -u XLINGS_ACTIVE_SUBOS XLINGS_HOME=$H/registry" |
| 158 | + cd "$H/registry" |
| 159 | + $E "$X" use gcc 15.1.0 2>&1 | tail -1; $E "$X" use gcc 16.1.0 2>&1 | tail -1 # E12: both succeed |
| 160 | + cp subos/default/.xlings.json "$W/h-ledger.bak" |
| 161 | + python3 - "$H/registry/subos/default/.xlings.json" <<'EOF' |
| 162 | +import json, sys |
| 163 | +p = sys.argv[1]; d = json.load(open(p)) |
| 164 | +def strip(o): |
| 165 | + if isinstance(o, dict): |
| 166 | + for k in [k for k in o if k in ("libatomic.so", "libatomic.so.1")]: del o[k] |
| 167 | + for v in o.values(): strip(v) |
| 168 | + elif isinstance(o, list): |
| 169 | + for v in o: strip(v) |
| 170 | +strip(d); json.dump(d, open(p, "w"), indent=2) |
| 171 | +EOF |
| 172 | + $E "$X" use gcc 15.1.0 2>&1 | tail -1 # E13: refused |
| 173 | + cp "$W/h-ledger.bak" subos/default/.xlings.json |
| 174 | + else |
| 175 | + echo "set FRESH=1 to build a fresh home (downloads gcc 16.1.0 and 15.1.0)" |
| 176 | + fi |
| 177 | +fi |
| 178 | + |
| 179 | +if want I; then |
| 180 | + echo "===== I: SYCL units vs the collision set (E19, E20)" |
| 181 | + DX=$R/xim-x-dpcpp/7.1.0/bin/clang++; G=$R/xim-x-gcc/15.1.0; GID=$(ls -d "$G"/lib/gcc/x86_64-linux-gnu/* | head -1) |
| 182 | + GLIBC=$R/xim-x-glibc/2.44.3; UAPI=$(ls -d "$R"/xim-x-linux-headers/* | head -1); LL=$R/xim-x-llvm/22.1.8; NM=$LL/bin/llvm-nm |
| 183 | + P=$R/mcpp-x-plugins/0.19.1/mcpp-plugins-0.19.1/tests/sycl-consumer |
| 184 | + rm -rf "$W/i"; mkdir -p "$W/i"; cd "$W/i" |
| 185 | + "$NM" -D --defined-only "$R/xim-x-gcc/16.1.0/lib64/libstdc++.so.6" | awk '{print $3}' | sed 's/@.*//' | sort -u > gnu.txt |
| 186 | + "$NM" --defined-only -g "$LL/lib/x86_64-unknown-linux-gnu/libc++.a" "$LL/lib/x86_64-unknown-linux-gnu/libc++abi.a" 2>/dev/null \ |
| 187 | + | awk 'NF==3 && $2 ~ /[TDRVWBui]/ {print $3}' | sort -u > llvm.txt |
| 188 | + comm -12 gnu.txt llvm.txt > collide.txt; echo "collision set: $(wc -l < collide.txt)" |
| 189 | + F="-fsycl -std=c++17 -O2 -fPIC --gcc-install-dir=$GID -isystem$GLIBC/include -isystem$UAPI/include" |
| 190 | + nice -n 10 "$DX" $F -I"$P/include" -x c++ -c "$P/src/kernels/saxpy.sycl" -o saxpy.o |
| 191 | + if [ -n "${TENSORVIA:-}" ]; then |
| 192 | + for f in runtime arithmetic matmul; do |
| 193 | + nice -n 10 "$DX" $F -I"$TENSORVIA/include" -I"$TENSORVIA/src/backend/sycl" -x c++ -c "$TENSORVIA/src/backend/sycl/device/$f.sycl" -o "tv_$f.o" |
| 194 | + done |
| 195 | + fi |
| 196 | + for o in *.o; do |
| 197 | + "$NM" -u "$o" | awk '{print $2}' | sort -u > u.txt |
| 198 | + echo "== $o: $(comm -12 u.txt collide.txt | wc -l) in collision set; layout-sensitive:" \ |
| 199 | + "$(comm -12 u.txt collide.txt | c++filt | grep -c -E 'runtime_error|logic_error|out_of_range|invalid_argument|length_error|::what\(\)')" |
| 200 | + echo " foreign typeinfo: $(grep '^_ZTI' u.txt | comm -23 - collide.txt | c++filt | tr '\n' ' ')" |
| 201 | + done |
| 202 | +fi |
0 commit comments