#!/usr/bin/env bash set -euo pipefail if [[ $# -lt 1 ]]; then echo "用法: $0 [output_dir] [--run] [-O]" >&2 exit 1 fi input=$1 out_dir="test/test_result/asm" run_exec=false optimize=false input_dir=$(dirname "$input") shift while [[ $# -gt 0 ]]; do case "$1" in --run) run_exec=true ;; -O|--optimize) optimize=true ;; *) out_dir="$1" ;; esac shift done if [[ ! -f "$input" ]]; then echo "输入文件不存在: $input" >&2 exit 1 fi compiler="./build/bin/compiler" if [[ ! -x "$compiler" ]]; then echo "未找到编译器: $compiler ,请先构建。" >&2 exit 1 fi if ! command -v aarch64-linux-gnu-gcc >/dev/null 2>&1; then echo "未找到 aarch64-linux-gnu-gcc,无法汇编/链接。" >&2 exit 1 fi if ! command -v qemu-aarch64 >/dev/null 2>&1 && [[ "$run_exec" == true ]]; then echo "未找到 qemu-aarch64,无法运行生成的可执行文件。" >&2 exit 1 fi find_runtime() { if [[ -n "${SYSY_RUNTIME:-}" ]]; then if [[ -f "$SYSY_RUNTIME" ]]; then printf '%s\n' "$SYSY_RUNTIME" return 0 fi echo "环境变量 SYSY_RUNTIME 指向的文件不存在: $SYSY_RUNTIME" >&2 return 1 fi local candidates=( "./sylib/sylib.c" "./sylib.c" "./runtime/sylib.c" "./lib/sylib.c" ) local candidate for candidate in "${candidates[@]}"; do if [[ -f "$candidate" ]]; then printf '%s\n' "$candidate" return 0 fi done local found="" found=$(find . \ -path './build' -prune -o \ -path './.git' -prune -o \ -type f -name 'sylib.c' -print | head -n 1) if [[ -n "$found" ]]; then printf '%s\n' "$found" return 0 fi return 1 } # 获取当前时间戳(毫秒级) get_timestamp_ms() { if [[ "$OSTYPE" == "darwin"* ]]; then if command -v gdate >/dev/null 2>&1; then gdate +%s%3N else perl -MTime::HiRes -e 'printf "%d\n", Time::HiRes::time() * 1000' 2>/dev/null || date +%s%3N fi else date +%s%3N 2>/dev/null || date +%s000 fi } runtime_src="$(find_runtime || true)" if [[ -z "$runtime_src" ]]; then echo "未找到运行时库源码 sylib.c" >&2 echo "可以显式指定:SYSY_RUNTIME=/你的路径/sylib.c $0 [output_dir] [--run]" >&2 exit 1 fi runtime_cache_dir="./build/test_runtime" runtime_obj="$runtime_cache_dir/sylib.o" mkdir -p "$runtime_cache_dir" # 编译运行时库(如果需要) if [[ ! -f "$runtime_obj" || "$runtime_src" -nt "$runtime_obj" ]]; then aarch64-linux-gnu-gcc -O2 -c "$runtime_src" -o "$runtime_obj" fi mkdir -p "$out_dir" base=$(basename "$input") stem=${base%.sy} asm_file="$out_dir/$stem.s" exe="$out_dir/$stem" stdin_file="$input_dir/$stem.in" expected_file="$input_dir/$stem.out" # 记录编译(生成汇编)开始时间 compile_start=$(get_timestamp_ms) if [[ "$optimize" == true ]]; then "$compiler" -O --emit-asm "$input" > "$asm_file" else "$compiler" --emit-asm "$input" > "$asm_file" fi # 记录编译结束时间 compile_end=$(get_timestamp_ms) compile_time=$((compile_end - compile_start)) # 记录汇编+链接开始时间 assemble_start=$(get_timestamp_ms) aarch64-linux-gnu-gcc "$asm_file" "$runtime_obj" -o "$exe" # 记录汇编+链接结束时间 assemble_end=$(get_timestamp_ms) assemble_time=$((assemble_end - assemble_start)) if [[ "$run_exec" == true ]]; then stdout_file="$out_dir/$stem.stdout" actual_file="$out_dir/$stem.actual.out" set +e exec_start=$(get_timestamp_ms) if [[ -f "$stdin_file" ]]; then qemu-aarch64 -L /usr/aarch64-linux-gnu -s 104857600 "$exe" < "$stdin_file" > "$stdout_file" else qemu-aarch64 -L /usr/aarch64-linux-gnu -s 104857600 "$exe" < /dev/null > "$stdout_file" fi status=$? exec_end=$(get_timestamp_ms) exec_time=$((exec_end - exec_start)) set -e # 计算总时间 total_time=$((compile_time + assemble_time + exec_time)) # 保存运行时间到文件(只保存时间数值) time_file="$out_dir/${stem}_time.txt" echo "$total_time" > "$time_file" # 保存输出(用于比对) { cat "$stdout_file" if [[ -s "$stdout_file" ]] && (( $(tail -c 1 "$stdout_file" | wc -l) == 0 )); then printf '\n' fi printf '%s\n' "$status" } > "$actual_file" # 比对输出 if [[ -f "$expected_file" ]]; then temp_expected="$out_dir/$stem.expected.tmp" temp_actual="$out_dir/$stem.actual.tmp" if command -v python3 >/dev/null 2>&1; then if python3 - "$expected_file" "$actual_file" <<'PY' import sys from pathlib import Path def canon(path: str) -> bytes: data = Path(path).read_bytes() data = data.replace(b'\r\n', b'\n') while data.endswith(b'\n'): data = data[:-1] lines = data.split(b'\n') lines = [line.rstrip() for line in lines] return b'\n'.join(lines) sys.exit(0 if canon(sys.argv[1]) == canon(sys.argv[2]) else 1) PY then rm -f "$temp_expected" "$temp_actual" 2>/dev/null else echo "输出不匹配: $expected_file" >&2 rm -f "$temp_expected" "$temp_actual" 2>/dev/null exit 1 fi else tr -d '\r' < "$expected_file" > "$temp_expected" tr -d '\r' < "$actual_file" > "$temp_actual" if [[ -s "$temp_expected" ]] && (( $(tail -c 1 "$temp_expected" | wc -l) == 0 )); then if [[ -s "$temp_actual" ]] && (( $(tail -c 1 "$temp_actual" | wc -l) == 1 )); then truncate -s -1 "$temp_actual" fi fi if diff -u "$temp_expected" "$temp_actual" > /dev/null 2>&1; then rm -f "$temp_expected" "$temp_actual" else echo "输出不匹配: $expected_file" >&2 rm -f "$temp_expected" "$temp_actual" exit 1 fi fi fi fi