You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nudt-compiler-cpp/scripts/test_compiler.sh

222 lines
6.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/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 <<EOF
测试完成。
IR 生成: $ir_pass / $ir_total
结果匹配: $result_pass / $result_total
EOF
# 输出失败列表
if [[ ${#ir_failures[@]} -gt 0 ]]; then
echo ""
echo "IR 失败列表:"
for item in "${ir_failures[@]}"; do
echo " $item"
done
fi
if [[ ${#result_failures[@]} -gt 0 ]]; then
echo ""
echo "结果失败列表:"
for item in "${result_failures[@]}"; do
echo " $item"
done
echo ""
echo "失败的测试文件已保留在: $TMP_DIR"
echo "可以查看对应的.ll文件进行调试"
fi