#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" COMPILER="$ROOT_DIR/build/bin/compiler" TMP_DIR="$ROOT_DIR/build/test_compiler" TEST_DIRS=("$ROOT_DIR/test/test_case/functional" "$ROOT_DIR/test/test_case/performance") CC_BIN="${CC:-cc}" RUNTIME_SRC="$ROOT_DIR/sylib/sylib.c" RUNTIME_OBJ="$TMP_DIR/sylib.o" LLC_BIN="${LLC:-llc}" CLANG_BIN="${CLANG:-clang}" if [[ ! -x "$COMPILER" ]]; then echo "未找到编译器: $COMPILER" echo "请先构建编译器,例如: mkdir -p build && cd build && cmake .. && make -j" exit 1 fi mkdir -p "$TMP_DIR" if ! command -v "$LLC_BIN" >/dev/null 2>&1; then echo "未找到 llc: $LLC_BIN" echo "请安装 LLVM,或通过 LLC 环境变量指定 llc 路径" exit 1 fi if ! command -v "$CLANG_BIN" >/dev/null 2>&1; then echo "未找到 clang: $CLANG_BIN" echo "请安装 Clang,或通过 CLANG 环境变量指定 clang 路径" exit 1 fi # 编译运行库(供链接生成的可执行文件) runtime_ready=0 if [[ -f "$RUNTIME_SRC" ]]; then if "$CC_BIN" -c "$RUNTIME_SRC" -o "$RUNTIME_OBJ" >/dev/null 2>&1; then runtime_ready=1 else echo "[WARN] 运行库编译失败,生成的可执行文件将不链接 sylib: $RUNTIME_SRC" fi else echo "[WARN] 未找到运行库源码: $RUNTIME_SRC" fi ir_total=0 ir_pass=0 result_total=0 result_pass=0 ir_failures=() result_failures=() function normalize_file() { sed 's/\r$//' "$1" } for test_dir in "${TEST_DIRS[@]}"; do if [[ ! -d "$test_dir" ]]; then echo "跳过不存在的测试目录: $test_dir" continue fi shopt -s nullglob for input in "$test_dir"/*.sy; do ir_total=$((ir_total+1)) base=$(basename "$input") stem=${base%.sy} out_dir="$TMP_DIR/$(basename "$test_dir")" mkdir -p "$out_dir" ll_file="$out_dir/$stem.ll" stdout_file="$out_dir/$stem.stdout" expected_file="$test_dir/$stem.out" stdin_file="$test_dir/$stem.in" echo "[TEST] $input" # 编译并捕获所有输出 compiler_status=0 compiler_output="" compiler_output=$("$COMPILER" --emit-ir "$input" 2>&1) || compiler_status=$? # 临时文件存储原始输出 raw_ll="$out_dir/$stem.raw.ll" printf '%s\n' "$compiler_output" > "$raw_ll" # 检查编译是否成功 if [[ $compiler_status -ne 0 ]]; then echo " [IR] 编译失败: 返回码 $compiler_status" ir_failures+=("$input: compiler failed ($compiler_status)") # 失败:保留原始输出(包含所有调试信息) cp "$raw_ll" "$ll_file" rm -f "$raw_ll" continue fi # 从混杂输出中提取 IR: # - 顶层实体:define/declare/@global # - 基本块标签 # - 缩进的指令行 # - 函数结束花括号 grep -E '^(define |declare |@|[[:space:]]|})|^[A-Za-z_.$%][A-Za-z0-9_.$%]*:$' "$raw_ll" > "$ll_file" # 检查是否生成了有效函数定义 if ! grep -qE '^define ' "$ll_file"; then echo " [IR] 失败: 未生成有效函数定义" ir_failures+=("$input: invalid IR output") # 失败:保留原始输出 cp "$raw_ll" "$ll_file" rm -f "$raw_ll" continue fi # 可选:删除多余的空行 sed -i '/^$/N;/\n$/D' "$ll_file" rm -f "$raw_ll" ir_pass=$((ir_pass+1)) echo " [IR] 生成成功 (IR已保存到: $ll_file)" # 运行测试 # 运行测试部分 if [[ -f "$expected_file" ]]; then result_total=$((result_total+1)) # 运行生成的可执行文件(优先链接运行库) run_status=0 obj_file="$out_dir/$stem.o" exe_file="$out_dir/$stem" if ! "$LLC_BIN" -filetype=obj "$ll_file" -o "$obj_file" > "$stdout_file" 2>&1; then echo " [RUN] llc 失败" result_failures+=("$input: llc failed") continue fi if [[ $runtime_ready -eq 1 ]]; then if ! "$CLANG_BIN" "$obj_file" "$RUNTIME_OBJ" -o "$exe_file" >> "$stdout_file" 2>&1; then echo " [RUN] clang 链接失败" result_failures+=("$input: clang link failed") continue fi else if ! "$CLANG_BIN" "$obj_file" -o "$exe_file" >> "$stdout_file" 2>&1; then echo " [RUN] clang 链接失败" result_failures+=("$input: clang link failed") continue fi fi if [[ -f "$stdin_file" ]]; then "$exe_file" < "$stdin_file" > "$stdout_file" 2>&1 || run_status=$? else "$exe_file" > "$stdout_file" 2>&1 || run_status=$? fi # 读取预期文件内容 expected_content=$(normalize_file "$expected_file") # 判断预期文件是只包含退出码,还是包含输出+退出码 if [[ "$expected_content" =~ ^[0-9]+$ ]]; then # 只包含退出码 expected=$expected_content if [[ "$run_status" -eq "$expected" ]]; then result_pass=$((result_pass+1)) echo " [RUN] 返回值匹配: $run_status" rm -f "$stdout_file" else echo " [RUN] 返回值不匹配: got $run_status, expected $expected" result_failures+=("$input: exit code mismatch (got $run_status, expected $expected)") fi else # 包含输出和退出码(最后一行是退出码) expected_output=$(head -n -1 <<< "$expected_content") expected_exit=$(tail -n 1 <<< "$expected_content") actual_output=$(cat "$stdout_file") if [[ "$run_status" -eq "$expected_exit" ]] && [[ "$actual_output" == "$expected_output" ]]; then result_pass=$((result_pass+1)) echo " [RUN] 成功: 退出码和输出都匹配" rm -f "$stdout_file" else echo " [RUN] 不匹配: 退出码 got $run_status, expected $expected_exit" if [[ "$actual_output" != "$expected_output" ]]; then echo " 输出不匹配" fi result_failures+=("$input: mismatch") fi fi else echo " [RUN] 未找到预期返回值文件 $expected_file,跳过结果验证" fi done shopt -u nullglob done # 输出统计 cat <