|
|
#!/usr/bin/env bash
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
if [[ $# -lt 1 ]]; then
|
|
|
echo "用法: $0 <input.sy> [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 <input.sy> [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" > "$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 |