LLVM backend improvements: - Add native LLVM backend support (NYASH_LLVM_BACKEND=native) - Add crate backend selector with priority (crate > llvmlite) - Add native_llvm_builder.py for native IR generation - Add NYASH_LLVM_NATIVE_TRACE=1 for IR dump MIR builder enhancements: - Refactor lower_if_compare_* boxes for better code generation - Refactor lower_return_* boxes for optimized returns - Refactor lower_loop_* boxes for loop handling - Refactor lower_method_* boxes for method calls - Update pattern_util_box for better pattern matching Smoke tests: - Add phase2100 S3 backend selector tests (17 new tests) - Add phase2120 native backend tests (4 new tests) - Add phase2034 MIR builder internal tests (2 new tests) - Add phase2211 TLV shim parity test Documentation: - Update ENV_VARS.md with LLVM backend variables - Update CURRENT_TASK.md with progress - Update README.md and CHANGELOG.md Config: - Add NYASH_LLVM_BACKEND env support in src/config/env.rs - Update ny_mir_builder.sh for backend selection - Update dispatch.rs for backend routing Tools: - Add tools/native_llvm_builder.py - Update smokes/v2/profiles/quick/core/phase2100/run_all.sh Known: Many Hako builder internal files modified for optimization
214 lines
7.6 KiB
Bash
214 lines
7.6 KiB
Bash
#!/usr/bin/env bash
|
||
# ny_mir_builder.sh — Minimal MIR Builder CLI (shell wrapper)
|
||
# Purpose: consume Nyash JSON IR and emit {obj|exe|ll|json} using the existing nyash LLVM harness.
|
||
|
||
set -euo pipefail
|
||
[[ "${NYASH_CLI_VERBOSE:-0}" == "1" ]] && set -x
|
||
|
||
usage() {
|
||
cat << USAGE
|
||
Usage: tools/ny_mir_builder.sh [--in <file>|--stdin] [--emit {obj|exe|ll|json}] -o <out> [--target <triple>] [--nyrt <path>] [--quiet] [--verify-llvm]
|
||
|
||
Notes:
|
||
- This is a Phase-15 shell wrapper that leverages the nyash LLVM harness.
|
||
- Input must be Nyash JSON IR (v0/v1). When --stdin is used, reads from stdin.
|
||
- For --emit exe, NyRT must be built (crates/nyrt). Use default paths if --nyrt omitted.
|
||
USAGE
|
||
}
|
||
|
||
IN_MODE="stdin" # stdin | file
|
||
IN_FILE=""
|
||
EMIT="obj" # obj | exe | ll | json
|
||
OUT=""
|
||
TARGET=""
|
||
NYRT_DIR=""
|
||
VERIFY=0
|
||
QUIET=0
|
||
# Backend selection (21.11): default to 'crate' when ny-llvmc is available, otherwise fallback to llvmlite.
|
||
# Explicit env NYASH_LLVM_BACKEND overrides this auto-detection.
|
||
if [[ -n "${NYASH_LLVM_BACKEND:-}" ]]; then
|
||
BACKEND="${NYASH_LLVM_BACKEND}"
|
||
else
|
||
if [[ -x "./target/release/ny-llvmc" ]]; then
|
||
BACKEND="crate"
|
||
else
|
||
BACKEND="llvmlite"
|
||
fi
|
||
fi
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
-h|--help) usage; exit 0 ;;
|
||
--in) IN_MODE="file"; IN_FILE="$2"; shift 2 ;;
|
||
--stdin) IN_MODE="stdin"; shift ;;
|
||
--emit) EMIT="$2"; shift 2 ;;
|
||
-o) OUT="$2"; shift 2 ;;
|
||
--target) TARGET="$2"; shift 2 ;;
|
||
--nyrt) NYRT_DIR="$2"; shift 2 ;;
|
||
--verify-llvm) VERIFY=1; shift ;;
|
||
--quiet) QUIET=1; shift ;;
|
||
*) echo "unknown arg: $1" >&2; usage; exit 2 ;;
|
||
esac
|
||
done
|
||
|
||
if [[ -z "$OUT" ]]; then
|
||
case "$EMIT" in
|
||
obj) OUT="$(pwd)/target/aot_objects/a.o" ;;
|
||
ll) OUT="$(pwd)/target/aot_objects/a.ll" ;;
|
||
exe) OUT="a.out" ;;
|
||
json) OUT="/dev/stdout" ;;
|
||
*) echo "error: invalid emit kind: $EMIT" >&2; exit 2 ;;
|
||
esac
|
||
fi
|
||
|
||
# Require LLVM18 only for llvmlite backend
|
||
if [[ "${NYASH_LLVM_BACKEND:-$BACKEND}" == "llvmlite" ]]; then
|
||
if ! command -v llvm-config-18 >/dev/null 2>&1; then
|
||
echo "error: llvm-config-18 not found (install LLVM 18 dev)" >&2
|
||
exit 3
|
||
fi
|
||
fi
|
||
|
||
# Build nyash + NyRT as needed(skip allowed)
|
||
LLVM_FEATURE=${NYASH_LLVM_FEATURE:-llvm}
|
||
SKIP_BUILD=${NYASH_LLVM_SKIP_BUILD:-0}
|
||
BUILD_TIMEOUT=${NYASH_LLVM_BUILD_TIMEOUT:-180}
|
||
if [[ "$SKIP_BUILD" != "1" ]]; then
|
||
if [[ "$LLVM_FEATURE" == "llvm-inkwell-legacy" ]]; then
|
||
_LLVMPREFIX=$(llvm-config-18 --prefix)
|
||
timeout "$BUILD_TIMEOUT" \
|
||
LLVM_SYS_181_PREFIX="${_LLVMPREFIX}" LLVM_SYS_180_PREFIX="${_LLVMPREFIX}" \
|
||
cargo build --release -j 24 --features "${LLVM_FEATURE}" >/dev/null
|
||
else
|
||
timeout "$BUILD_TIMEOUT" cargo build --release -j 24 --features "${LLVM_FEATURE}" >/dev/null
|
||
fi
|
||
# Prebuild ny-llvmc when using crate backend
|
||
if [[ "$BACKEND" == "crate" ]]; then
|
||
(cd "$(dirname "$0")/.." && timeout "$BUILD_TIMEOUT" cargo build --release -j 24 -p nyash-llvm-compiler >/dev/null) || true
|
||
fi
|
||
if [[ "$EMIT" == "exe" ]]; then
|
||
(cd crates/nyrt && timeout "$BUILD_TIMEOUT" cargo build --release -j 24 >/dev/null)
|
||
fi
|
||
fi
|
||
|
||
mkdir -p "$PWD/target/aot_objects"
|
||
|
||
# Prepare input
|
||
_STDIN_BUF=""
|
||
if [[ "$IN_MODE" == "stdin" ]]; then
|
||
# Read all to a temp file to allow re-use
|
||
_TMP_JSON=$(mktemp)
|
||
cat > "$_TMP_JSON"
|
||
IN_FILE="$_TMP_JSON"
|
||
fi
|
||
|
||
cleanup() { [[ -n "${_TMP_JSON:-}" && -f "$_TMP_JSON" ]] && rm -f "$_TMP_JSON" || true; }
|
||
trap cleanup EXIT
|
||
|
||
case "$EMIT" in
|
||
json)
|
||
# Normalization placeholder: currently pass-through
|
||
cat "$IN_FILE" > "$OUT"
|
||
[[ "$QUIET" == "0" ]] && echo "OK json:$OUT"
|
||
;;
|
||
ll)
|
||
# Ask nyash harness to dump LLVM IR (if supported via env)
|
||
export NYASH_LLVM_DUMP_LL=1
|
||
export NYASH_LLVM_LL_OUT="$OUT"
|
||
if [[ "$VERIFY" == "1" ]]; then export NYASH_LLVM_VERIFY=1; fi
|
||
# Prefer 'hakorune' binary if present (nyash is deprecated)
|
||
BIN="./target/release/hakorune"
|
||
[[ -x "$BIN" ]] || BIN="./target/release/nyash"
|
||
if [[ "$LLVM_FEATURE" == "llvm-inkwell-legacy" ]]; then
|
||
cat "$IN_FILE" | NYASH_LLVM_USE_HARNESS=1 LLVM_SYS_181_PREFIX="${_LLVMPREFIX}" LLVM_SYS_180_PREFIX="${_LLVMPREFIX}" \
|
||
"$BIN" --backend llvm --ny-parser-pipe >/dev/null || true
|
||
else
|
||
cat "$IN_FILE" | NYASH_LLVM_USE_HARNESS=1 \
|
||
"$BIN" --backend llvm --ny-parser-pipe >/dev/null || true
|
||
fi
|
||
if [[ ! -f "$OUT" ]]; then echo "error: failed to produce $OUT" >&2; exit 4; fi
|
||
[[ "$QUIET" == "0" ]] && echo "OK ll:$OUT"
|
||
;;
|
||
obj)
|
||
case "$BACKEND" in
|
||
crate)
|
||
BIN_NYLLVMC="./target/release/ny-llvmc"
|
||
if [[ ! -x "$BIN_NYLLVMC" ]]; then
|
||
echo "error: ny-llvmc not found (cargo build -p nyash-llvm-compiler)" >&2; exit 4
|
||
fi
|
||
rm -f "$OUT"
|
||
"$BIN_NYLLVMC" --in "$IN_FILE" --emit obj --out "$OUT" >/dev/null 2>&1 || { echo "error: ny-llvmc failed" >&2; exit 4; }
|
||
;;
|
||
native)
|
||
if ! command -v llc >/dev/null 2>&1; then
|
||
echo "error: llc not found (install LLVM tools)" >&2; exit 4
|
||
fi
|
||
rm -f "$OUT"
|
||
if ! python3 "$PWD/tools/native_llvm_builder.py" --in "$IN_FILE" --emit obj --out "$OUT" >/dev/null 2>&1; then
|
||
echo "error: native builder failed" >&2; exit 4
|
||
fi
|
||
;;
|
||
llvmlite|*)
|
||
# Directly use llvmlite harness with MIR v1 JSON input
|
||
rm -f "$OUT"
|
||
if ! python3 "$PWD/tools/llvmlite_harness.py" --in "$IN_FILE" --out "$OUT" >/dev/null 2>&1; then
|
||
echo "error: harness failed to produce $OUT" >&2; exit 4
|
||
fi
|
||
;;
|
||
esac
|
||
if [[ ! -f "$OUT" ]]; then echo "error: failed to produce $OUT" >&2; exit 4; fi
|
||
[[ "$QUIET" == "0" ]] && echo "OK obj:$OUT"
|
||
;;
|
||
exe)
|
||
# Emit obj then link
|
||
OBJ="$PWD/target/aot_objects/__tmp_builder.o"
|
||
rm -f "$OBJ"
|
||
case "$BACKEND" in
|
||
crate)
|
||
BIN_NYLLVMC="./target/release/ny-llvmc"
|
||
if [[ ! -x "$BIN_NYLLVMC" ]]; then
|
||
echo "error: ny-llvmc not found (cargo build -p nyash-llvm-compiler)" >&2; exit 4
|
||
fi
|
||
# Produce exe directly via ny-llvmc (lets ny-llvmc link)
|
||
LIBS="${HAKO_AOT_LDFLAGS:-}"
|
||
if ! "$BIN_NYLLVMC" --in "$IN_FILE" --emit exe --nyrt target/release --libs "$LIBS" --out "$OUT" >/dev/null 2>&1; then
|
||
echo "error: ny-llvmc failed to link exe" >&2; exit 4
|
||
fi
|
||
;;
|
||
native)
|
||
if ! command -v llc >/dev/null 2>&1; then
|
||
echo "error: llc not found (install LLVM tools)" >&2; exit 4
|
||
fi
|
||
if ! python3 "$PWD/tools/native_llvm_builder.py" --in "$IN_FILE" --emit obj --out "$OBJ" >/dev/null 2>&1; then
|
||
echo "error: native builder failed to produce object $OBJ" >&2; exit 4
|
||
fi
|
||
if [[ ! -f "$OBJ" ]]; then echo "error: failed to produce object $OBJ" >&2; exit 4; fi
|
||
# Link with NyRT (same as llvmlite branch)
|
||
NYRT_BASE=${NYRT_DIR:-"$PWD/crates/nyash_kernel"}
|
||
cc "$OBJ" \
|
||
-L target/release \
|
||
-L "$NYRT_BASE/target/release" \
|
||
-Wl,--whole-archive -lnyash_kernel -Wl,--no-whole-archive \
|
||
-lpthread -ldl -lm -o "$OUT"
|
||
;;
|
||
llvmlite|*)
|
||
if ! python3 "$PWD/tools/llvmlite_harness.py" --in "$IN_FILE" --out "$OBJ" >/dev/null 2>&1; then
|
||
echo "error: harness failed to produce object $OBJ" >&2; exit 4
|
||
fi
|
||
if [[ ! -f "$OBJ" ]]; then echo "error: failed to produce object $OBJ" >&2; exit 4; fi
|
||
# Link with NyRT
|
||
NYRT_BASE=${NYRT_DIR:-"$PWD/crates/nyash_kernel"}
|
||
cc "$OBJ" \
|
||
-L target/release \
|
||
-L "$NYRT_BASE/target/release" \
|
||
-Wl,--whole-archive -lnyash_kernel -Wl,--no-whole-archive \
|
||
-lpthread -ldl -lm -o "$OUT"
|
||
;;
|
||
esac
|
||
[[ "$QUIET" == "0" ]] && echo "OK exe:$OUT"
|
||
;;
|
||
*) echo "error: invalid emit kind: $EMIT" >&2; exit 2 ;;
|
||
esac
|
||
|
||
exit 0
|