Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions nx2/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

The flags are a64-kit's for lifted code (its build.py, cmd_native): -O1, LoopRotate's header copies capped (most of
the compile time in goto-dense giant functions otherwise), capture tracking past LLVM's 100 uses (the context
parameter is A64_NOESC), one section per function. The guest model is the 64-bit window (rtgen.py), shadowing the
vendored rt/, with the loop-header interrupt polls on (A64_POLLING, nx2/poll.py). Incremental: an object is rebuilt
only when its source or the flags changed.
parameter is A64_NOESC), one section per function, no lifetime markers. The guest model is the 64-bit window
(rtgen.py), shadowing the vendored rt/, with the loop-header interrupt polls on (A64_POLLING, nx2/poll.py).
Incremental: an object is rebuilt only when its source or the flags changed.

hookshot_game.dll = the lifted chunks + a64-kit's tables and runtime + nx2/rt/eden_shim.c (the emulator ABI, mkw_host.h)
+ mkw_info.c (the image base and main's build id, generated here from work/image/image.json). Linking it is also the
Expand Down Expand Up @@ -605,7 +605,9 @@ def flags(gen, rt64, opt, polls):
return target() + CPU + [
"-I" + rt64, "-I" + gen, "-I" + os.path.join(kit.KIT, "rt"), "-I" + NX2_RT, "-Wno-everything",
"-fno-strict-aliasing", "-mcx16", "-" + opt, "-g0", "-mllvm", "-rotation-max-header-size=0",
"-ffunction-sections", "-mllvm", "-capture-tracking-max-uses-to-explore=5000"] + \
"-ffunction-sections", "-mllvm", "-capture-tracking-max-uses-to-explore=5000",
# (a compiler's peak memory ~10% lower on a lifted chunk and on a hot piece, clang 23; the lifted code runs as fast)
"-Xclang", "-disable-lifetime-markers"] + \
(["-DA64_POLLING"] if polls else []) + WRITE_TRACK


Expand Down
69 changes: 69 additions & 0 deletions tests/test_lifted_flags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""The lifted code's compile flags (nx2/build.py flags): no lifetime markers, for the chunks and the hot pieces alike. Without them a
compiler's peak memory is about 10% lower on a lifted chunk and on a hot piece (clang 23), and the lifted code runs as fast. With
--cxx naming clang, the flags also compile a scoped local to IR with no llvm.lifetime call (the option is a cc1 one: a clang that
dropped it would stop the build).

python -I -B tests/test_lifted_flags.py [--cxx clang++]
"""
import argparse
import os
import subprocess
import sys
import tempfile
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "nx2"))
import build # noqa: E402

RUN_ALL_ARGS = ["--cxx", "{cxx}"]

SOURCE = """extern void use(long *);
long f(long n) {
long s = 0;
for (long i = 0; i < n; i++) {
long tmp[8];
use(tmp);
s += tmp[i & 7];
}
return s;
}
"""


def check(what, ok):
if not ok:
raise SystemExit("FAIL: " + what)


def ir(cxx, fl, d):
src, out = os.path.join(d, "t.c"), os.path.join(d, "t.ll")
Path(src).write_text(SOURCE, newline=chr(10))
r = subprocess.run([cxx, "-x", "c"] + fl + ["-S", "-emit-llvm", "-o", out, src], capture_output=True, text=True)
check("clang compiles with the lifted flags (%s): %s" % (" ".join(fl), r.stderr.strip()), r.returncode == 0)
return Path(out).read_text()


def main():
p = argparse.ArgumentParser()
p.add_argument("--cxx")
a = p.parse_args()
for opt in ("O1", "O2"):
fl = build.flags("gen", "rt64", opt, True)
pairs = list(zip(fl, fl[1:]))
check("-%s: -disable-lifetime-markers goes to the compiler itself (%s)" % (opt, " ".join(fl)),
("-Xclang", "-disable-lifetime-markers") in pairs)
check("-%s: the level is still the one asked for" % opt, "-" + opt in fl)
if a.cxx and "clang" in os.path.basename(a.cxx).lower():
fl = [f for f in build.flags("gen", "rt64", "O1", True) if not f.startswith(("-I", "--target=", "--sysroot="))]
with tempfile.TemporaryDirectory(prefix="hookshot-lifted-flags-") as d:
check("the reference compile has lifetime markers, so the check below means something",
"llvm.lifetime" in ir(a.cxx, [f for f in fl if f not in ("-Xclang", "-disable-lifetime-markers")], d))
check("the lifted flags leave no lifetime markers", "llvm.lifetime" not in ir(a.cxx, fl, d))
else:
print("lifted flags: no clang given (--cxx): the compile is not checked")
print("lifted flags: ok")


if __name__ == "__main__":
main()
Loading