Files
hakorune/tools/llvmlite_harness.py

143 lines
4.6 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
Nyash llvmlite harness (scaffold)
Usage:
- python3 tools/llvmlite_harness.py --out out.o # dummy ny_main -> object
- python3 tools/llvmlite_harness.py --in mir.json --out out.o # MIR(JSON) -> object (partial support)
Notes:
- For initial scaffolding, when --in is omitted, a trivial ny_main that returns 0 is emitted.
- When --in is provided, this script delegates to src/llvm_py/llvm_builder.py.
"""
import argparse
import json
import os
import sys
from pathlib import Path
_default_root = Path(__file__).resolve().parents[1]
_env_root = None
try:
env_root_str = os.environ.get('NYASH_ROOT')
if env_root_str:
cand = Path(env_root_str).resolve()
if (cand / "src" / "llvm_py" / "llvm_builder.py").exists():
_env_root = cand
except Exception:
_env_root = None
ROOT = _env_root or _default_root
PY_BUILDER = ROOT / "src" / "llvm_py" / "llvm_builder.py"
_OPT_ENV_KEYS = ("HAKO_LLVM_OPT_LEVEL", "NYASH_LLVM_OPT_LEVEL")
def _parse_opt_level_env() -> int:
"""Parse optimization level from env (0-3, default 2)."""
for key in _OPT_ENV_KEYS:
raw = os.environ.get(key)
if not raw:
continue
value = raw.strip()
if not value:
continue
upper = value.upper()
if upper.startswith("O"):
value = upper[1:]
try:
lvl = int(value)
except ValueError:
continue
if lvl < 0:
lvl = 0
if lvl > 3:
lvl = 3
return lvl
return 2
def _resolve_llvm_opt_level():
level = _parse_opt_level_env()
try:
import llvmlite.binding as llvm_binding
names = {0: "None", 1: "Less", 2: "Default", 3: "Aggressive"}
attr = names.get(level, "Default")
enum = getattr(llvm_binding, "CodeGenOptLevel")
return getattr(enum, attr)
except Exception:
return level
def _maybe_trace_opt(source: str) -> None:
if os.environ.get("NYASH_CLI_VERBOSE") == "1":
try:
level = _parse_opt_level_env()
print(f"[llvmlite harness] opt-level={level} ({source})", file=sys.stderr)
except Exception:
pass
def run_dummy(out_path: str) -> None:
# Minimal llvmlite program: ny_main() -> i32 0
import llvmlite.ir as ir
import llvmlite.binding as llvm
llvm.initialize()
llvm.initialize_native_target()
llvm.initialize_native_asmprinter()
mod = ir.Module(name="nyash_module")
i32 = ir.IntType(32)
ny_main_ty = ir.FunctionType(i32, [])
ny_main = ir.Function(mod, ny_main_ty, name="ny_main")
entry = ny_main.append_basic_block("entry")
b = ir.IRBuilder(entry)
b.ret(ir.Constant(i32, 0))
# Emit object via target machine
m = llvm.parse_assembly(str(mod))
m.verify()
target = llvm.Target.from_default_triple()
tm = target.create_target_machine(opt=_resolve_llvm_opt_level())
_maybe_trace_opt("dummy")
obj = tm.emit_object(m)
Path(out_path).parent.mkdir(parents=True, exist_ok=True)
with open(out_path, "wb") as f:
f.write(obj)
def run_from_json(in_path: str, out_path: str) -> None:
# Delegate to python builder to keep code unified
import runpy
# Enable safe defaults for prepasses unless explicitly disabled by env
os.environ.setdefault('NYASH_LLVM_PREPASS_LOOP', os.environ.get('NYASH_LLVM_PREPASS_LOOP', '0'))
os.environ.setdefault('NYASH_LLVM_PREPASS_IFMERGE', os.environ.get('NYASH_LLVM_PREPASS_IFMERGE', '1'))
# Ensure src/llvm_py is on sys.path for relative imports
builder_dir = str(PY_BUILDER.parent)
if builder_dir not in sys.path:
sys.path.insert(0, builder_dir)
# Simulate "python llvm_builder.py <in> -o <out>"
sys.argv = [str(PY_BUILDER), str(in_path), "-o", str(out_path)]
runpy.run_path(str(PY_BUILDER), run_name="__main__")
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--in", dest="infile", help="MIR JSON input", default=None)
ap.add_argument("--out", dest="outfile", help="output object (.o)", required=True)
args = ap.parse_args()
if args.infile is None:
# Dummy path
run_dummy(args.outfile)
print(f"[harness] dummy object written: {args.outfile}")
else:
run_from_json(args.infile, args.outfile)
print(f"[harness] object written: {args.outfile}")
if __name__ == "__main__":
try:
main()
except Exception as e:
import traceback
print(f"[harness] error: {e}", file=sys.stderr)
if os.environ.get('NYASH_CLI_VERBOSE') == '1':
traceback.print_exc()
sys.exit(1)