From fdf4f0a13b04ae9ee8877ce20262aa569e876665 Mon Sep 17 00:00:00 2001 From: Dusk-92 Date: Tue, 8 Sep 2026 09:35:37 +0200 Subject: [PATCH] ci: build and ABI-gate B2 in-place GC --- .github/workflows/wp24b2-inplace-gc.yml | 128 ++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 .github/workflows/wp24b2-inplace-gc.yml diff --git a/.github/workflows/wp24b2-inplace-gc.yml b/.github/workflows/wp24b2-inplace-gc.yml new file mode 100644 index 0000000..3b4605a --- /dev/null +++ b/.github/workflows/wp24b2-inplace-gc.yml @@ -0,0 +1,128 @@ +name: Build WeirdPerformance 2.4-B2 in-place GC test + +on: + push: + branches: + - test/wp24b2-gc-inplace-sweep + paths: + - 'experiments/wp24b-gc/**' + - '.github/workflows/wp24b2-inplace-gc.yml' + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: mlugg/setup-zig@v2 + with: + version: '0.17.0-dev.1970+67f39b551' + - name: Build and ABI-validate x86 DLL + working-directory: experiments/wp24b-gc + run: | + set -euo pipefail + zig build --fetch + python3 - <<'PY' + from pathlib import Path + matches = list(Path("zig-pkg").glob("zhook-*/src/zhook.zig")) + if len(matches) != 1: + raise SystemExit(f"expected one pinned zhook source, found {len(matches)}") + p = matches[0] + text = p.read_text(encoding="utf-8") + old = "var patch: [MAX_STOLEN]u8 = .{0x90} ** MAX_STOLEN;" + new = "var patch: [MAX_STOLEN]u8 = @splat(0x90);" + if old not in text: + raise SystemExit("expected pinned zhook repeat initializer not found") + p.write_text(text.replace(old, new), encoding="utf-8") + PY + + zig build -Doptimize=small + DLL=zig-out/bin/weirdperformance_gc24b2.dll + test -s "$DLL" + file "$DLL" | tee BINARY_INFO.txt + file "$DLL" | grep -Eq 'PE32.*Intel (80386|i386)' + strings "$DLL" | grep -q '2.4-B2-gc-inplace-safe-sweep' + + if command -v llvm-objdump >/dev/null 2>&1; then + llvm-objdump -d --x86-asm-syntax=intel "$DLL" > ABI_DISASM.txt + else + objdump -d -M intel "$DLL" > ABI_DISASM.txt + fi + + python3 - <<'PY' + import re + from pathlib import Path + + asm = Path("ABI_DISASM.txt").read_text(encoding="utf-8", errors="replace").lower() + + def require(pattern, label): + if not re.search(pattern, asm, re.S): + raise SystemExit(f"ABI validation failed: {label}") + + # Detour callbacks: L must arrive in ECX on WoW 5875 x86. + m = re.search(r"mov\s+edx,\s*0x6f7340.{0,240}?push\s+0x([0-9a-f]+)", asm, re.S) + if not m: + raise SystemExit("ABI validation failed: collector callback address not found") + cb = m.group(1).lstrip("0") or "0" + pos = asm.find(cb + ":") + if pos < 0 or not re.search(r"mov\s+esi,\s*ecx", asm[pos:pos+800]): + raise SystemExit("ABI validation failed: collector callback does not consume L from ECX") + + m = re.search(r"mov\s+edx,\s*0x6f6ef0.{0,240}?push\s+0x([0-9a-f]+)", asm, re.S) + if not m: + raise SystemExit("ABI validation failed: lua_close callback address not found") + cb = m.group(1).lstrip("0") or "0" + pos = asm.find(cb + ":") + if pos < 0 or not re.search(r"mov\s+esi,\s*ecx", asm[pos:pos+600]): + raise SystemExit("ABI validation failed: lua_close callback does not consume L from ECX") + + # Native helper ABI observed directly in WoW 5875: + # lua_gc_remove_objects: ECX=L, EDX=&list, stack arg all=0. + require( + r"lea\s+edx,\s*\[edi\s*\+\s*0x14\].{0,200}?" + r"mov\s+eax,\s*0x6f7210.{0,160}?" + r"mov\s+ecx,\s*esi.{0,160}?" + r"push\s+0x0.{0,100}?call\s+eax", + "rootudata lua_gc_remove_objects ABI", + ) + + # B2 also calls lua_gc_remove_objects through a runtime link address; + # require at least one call site with ECX loaded from L and EDX nonzero. + require( + r"mov\s+eax,\s*0x6f7210.{0,220}?mov\s+ecx,\s*e[a-z]{2}.{0,180}?call\s+eax", + "chunk lua_gc_remove_objects fastcall callsite", + ) + + require( + r"mov\s+eax,\s*0x6f72f0.{0,160}?" + r"mov\s+ecx,\s*esi.{0,120}?" + r"xor\s+edx,\s*edx.{0,100}?call\s+eax", + "lua_gc_sweep_all_lists fastcall ABI", + ) + + for addr, label in ( + ("6f73e0", "lua_gc_full_collection"), + ("6f7370", "lua_gc_shrink_memory"), + ("6f7080", "luaCallUserDataGC"), + ): + require( + rf"mov\s+eax,\s*0x{addr}.{{0,180}}?mov\s+ecx,\s*e[a-z]{{2}}.{{0,120}}?call\s+eax", + f"{label} ECX ABI", + ) + + print("ABI validation: PASS") + PY + + sha256sum "$DLL" | tee SHA256SUMS.txt + - uses: actions/upload-artifact@v4 + with: + name: WeirdPerformance-2.4-B2-GC-InPlace-Safe-Sweep + path: | + experiments/wp24b-gc/zig-out/bin/weirdperformance_gc24b2.dll + experiments/wp24b-gc/SHA256SUMS.txt + experiments/wp24b-gc/BINARY_INFO.txt + experiments/wp24b-gc/ABI_DISASM.txt + if-no-files-found: error