Files
hakorune/tools/llvmlite_harness.py

84 lines
2.3 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
Experimental llvmlite-based LLVM emission harness for Nyash.
Usage:
python3 tools/llvmlite_harness.py [--in MIR.json] --out OUTPUT.o
Notes:
- First cut emits a trivial ny_main that returns 0 to validate toolchain.
- Extend to lower MIR14 JSON incrementally.
"""
from __future__ import annotations
import argparse
import json
import os
import sys
try:
from llvmlite import ir, binding
except Exception as e: # noqa: BLE001
sys.stderr.write(
"llvmlite is required. Install with: python3 -m pip install llvmlite\n"
)
sys.stderr.write(f"Import error: {e}\n")
sys.exit(2)
def parse_args() -> argparse.Namespace:
ap = argparse.ArgumentParser(description="Nyash llvmlite harness")
ap.add_argument("--in", dest="in_path", help="MIR14 JSON input (optional)")
ap.add_argument("--out", dest="out_path", required=True, help="Output object file path")
return ap.parse_args()
def load_mir(path: str | None) -> dict | None:
if not path:
return None
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def build_trivial_module() -> ir.Module:
mod = ir.Module(name="nyash_harness")
mod.triple = binding.get_default_triple()
i64 = ir.IntType(64)
i8 = ir.IntType(8)
i8p = i8.as_pointer()
i8pp = i8p.as_pointer()
fn_ty = ir.FunctionType(i64, [i64, i8pp])
fn = ir.Function(mod, fn_ty, name="ny_main")
entry = fn.append_basic_block(name="entry")
b = ir.IRBuilder(entry)
b.ret(ir.Constant(i64, 0))
return mod
def emit_object(mod: ir.Module, out_path: str) -> None:
binding.initialize()
binding.initialize_native_target()
binding.initialize_native_asmprinter()
target = binding.Target.from_default_triple()
tm = target.create_target_machine()
llvm_mod = binding.parse_assembly(str(mod))
llvm_mod.verify()
obj = tm.emit_object(llvm_mod)
with open(out_path, "wb") as f:
f.write(obj)
def main() -> int:
ns = parse_args()
_mir = load_mir(ns.in_path)
# For now, ignore MIR content and emit a trivial module.
mod = build_trivial_module()
os.makedirs(os.path.dirname(ns.out_path) or ".", exist_ok=True)
emit_object(mod, ns.out_path)
return 0
if __name__ == "__main__": # pragma: no cover
raise SystemExit(main())