143 lines
4.6 KiB
Python
143 lines
4.6 KiB
Python
#!/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)
|