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/run_ir_test.sh

308 lines
9.2 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
# 串行执行IR测试脚本实时输出结果
set -euo pipefail
PROJECT_ROOT=$(cd "$(dirname "$0")/.." ; pwd)
# 默认参数
TEST_CASE_DIR="${PROJECT_ROOT}/test/test_case"
TEST_RESULT_DIR="${PROJECT_ROOT}/test/test_result/ir"
RUN_EXEC=false
VERBOSE=false
OPT_FLAG="-O1" # 默认开启优化
# 解析命令行参数
while [[ $# -gt 0 ]]; do
case "$1" in
--run)
RUN_EXEC=true
;;
--verbose|-v)
VERBOSE=true
;;
--O0)
OPT_FLAG=""
;;
--O1)
OPT_FLAG="-O1"
;;
--test-dir=*)
TEST_CASE_DIR="${1#*=}"
;;
--result-dir=*)
TEST_RESULT_DIR="${1#*=}"
;;
*)
echo "未知参数: $1" >&2
echo "用法: $0 [--run] [--O0|--O1] [--verbose] [--test-dir=<dir>] [--result-dir=<dir>]" >&2
exit 1
;;
esac
shift
done
# 检查编译器是否存在
compiler="${PROJECT_ROOT}/build/bin/compiler"
if [[ ! -x "$compiler" ]]; then
echo "错误:未找到编译器 $compiler" >&2
echo "请先构建项目: mkdir -p build && cd build && cmake .. && make -j" >&2
exit 1
fi
# 创建输出目录
mkdir -p "$TEST_RESULT_DIR"
# 统计变量
total_tests=0
passed_tests=0
failed_tests=0
# 汇总日志文件
summary_log="${TEST_RESULT_DIR}/summary.log"
> "$summary_log"
# 失败测试列表
failed_list=""
echo "=== 开始IR测试 ==="
echo "测试目录: $TEST_CASE_DIR"
echo "结果目录: $TEST_RESULT_DIR"
echo "优化级别: ${OPT_FLAG:--O0}"
echo "运行可执行文件: $RUN_EXEC"
echo ""
# 串行遍历所有测试用例
while read -r test_file; do
total_tests=$((total_tests + 1))
# 计算相对路径
full_path=$(readlink -f "$test_file")
test_case_path=$(readlink -f "$TEST_CASE_DIR")
relative_path="${full_path#$test_case_path}"
# 确保路径以 / 开头
if [[ "${relative_path:0:1}" != "/" ]]; then
relative_path="/$relative_path"
fi
# 计算输出文件路径
base=$(basename "$test_file")
stem="${base%.sy}"
output_file="${TEST_RESULT_DIR}/${relative_path%.sy}.ll"
output_dir=$(dirname "$output_file")
# 创建输出目录
mkdir -p "$output_dir"
# 获取输入和预期输出文件路径
input_dir=$(dirname "$test_file")
stdin_file="${input_dir}/${stem}.in"
expected_file="${input_dir}/${stem}.out"
# 每个测试用例的详细日志文件
test_log="${output_dir}/${stem}.log"
> "$test_log"
echo "[$total_tests] 处理: $relative_path"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 开始处理: $relative_path" >> "$test_log"
echo "输入文件: $test_file" >> "$test_log"
echo "输出目录: $output_dir" >> "$test_log"
# 生成IR
if $VERBOSE; then
echo " 生成IR..."
fi
echo "步骤1: 生成IR" >> "$test_log"
# 计时编译
compile_start=$(date +%s.%N)
set +e
"$compiler" "$test_file" -IR -o "$output_file" $OPT_FLAG 2>&1
ir_status=$?
set -e
compile_end=$(date +%s.%N)
compile_time=$(python3 -c "print(f'{float($compile_end)-float($compile_start):.3f}s')" 2>/dev/null || echo "-")
if [[ $ir_status -ne 0 ]]; then
echo " ✗ IR生成失败"
echo "结果: FAILED (IR生成失败)" >> "$test_log"
echo "错误信息:" >> "$test_log"
cat "$output_file" >> "$test_log"
failed_tests=$((failed_tests + 1))
failed_list="$failed_list\n[$total_tests] $relative_path - IR生成失败"
if $VERBOSE; then
cat "$output_file"
fi
echo ""
continue
fi
echo "IR文件: $output_file" >> "$test_log"
if $VERBOSE; then
echo " ✓ IR已生成: $output_file"
fi
# 如果需要运行可执行文件
if [[ "$RUN_EXEC" == true ]]; then
echo "步骤2: 编译和运行" >> "$test_log"
if ! command -v llc >/dev/null 2>&1; then
echo " 警告: 未找到 llc跳过执行测试" >&2
echo "警告: 未找到 llc跳过执行测试" >> "$test_log"
echo "结果: SKIPPED (缺少llc)" >> "$test_log"
passed_tests=$((passed_tests + 1))
echo ""
continue
fi
if ! command -v clang >/dev/null 2>&1; then
echo " 警告: 未找到 clang跳过执行测试" >&2
echo "警告: 未找到 clang跳过执行测试" >> "$test_log"
echo "结果: SKIPPED (缺少clang)" >> "$test_log"
passed_tests=$((passed_tests + 1))
echo ""
continue
fi
obj="${output_dir}/${stem}.o"
exe="${output_dir}/${stem}"
stdout_file="${output_dir}/${stem}.stdout"
actual_file="${output_dir}/${stem}.actual.out"
# 编译IR为目标文件
if $VERBOSE; then
echo " 编译IR..."
fi
echo "编译IR: llc -filetype=obj $output_file -o $obj" >> "$test_log"
llc -filetype=obj "$output_file" -o "$obj" 2>/dev/null
if [[ $? -ne 0 ]]; then
echo " ✗ IR编译失败"
echo "结果: FAILED (IR编译失败)" >> "$test_log"
failed_tests=$((failed_tests + 1))
failed_list="$failed_list\n[$total_tests] $relative_path - IR编译失败"
echo ""
continue
fi
echo "目标文件: $obj" >> "$test_log"
# 链接为可执行文件
echo "链接: clang $obj ${PROJECT_ROOT}/sylib/sylib.c -o $exe -lm" >> "$test_log"
clang "$obj" "${PROJECT_ROOT}/sylib/sylib.c" -o "$exe" -lm 2>/dev/null
if [[ $? -ne 0 ]]; then
echo " ✗ 链接失败"
echo "结果: FAILED (链接失败)" >> "$test_log"
failed_tests=$((failed_tests + 1))
failed_list="$failed_list\n[$total_tests] $relative_path - 链接失败"
echo ""
continue
fi
echo "可执行文件: $exe" >> "$test_log"
# 运行可执行文件
if $VERBOSE; then
echo " 运行..."
fi
echo "运行命令: $exe" >> "$test_log"
if [[ -f "$stdin_file" ]]; then
echo "标准输入: $stdin_file" >> "$test_log"
fi
run_start=$(date +%s.%N)
set +e
if [[ -f "$stdin_file" ]]; then
(ulimit -s unlimited; "$exe" < "$stdin_file") > "$stdout_file"
else
(ulimit -s unlimited; "$exe") > "$stdout_file"
fi
status=$?
set -e
run_end=$(date +%s.%N)
run_time=$(python3 -c "print(f'{float($run_end)-float($run_start):.3f}s')" 2>/dev/null || echo "-")
echo "退出码: $status" >> "$test_log"
echo "标准输出:" >> "$test_log"
cat "$stdout_file" >> "$test_log"
# 保存实际输出(包含退出码)
{
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"
# 比对输出
echo "步骤3: 比对输出" >> "$test_log"
if [[ -f "$expected_file" ]]; then
echo "预期输出: $expected_file" >> "$test_log"
echo "实际输出: $actual_file" >> "$test_log"
if diff <(tr -d '\r' < "$expected_file" | sed -e '$a\') \
<(tr -d '\r' < "$actual_file" | sed -e '$a\') > /dev/null 2>&1; then
echo " ✓ 编译:${compile_time} 运行:${run_time}"
echo "结果: PASSED (输出匹配)" >> "$test_log"
passed_tests=$((passed_tests + 1))
else
echo " ✗ 输出不匹配"
echo "结果: FAILED (输出不匹配)" >> "$test_log"
echo "差异:" >> "$test_log"
diff <(tr -d '\r' < "$expected_file" | sed -e '$a\') \
<(tr -d '\r' < "$actual_file" | sed -e '$a\') >> "$test_log" 2>&1 || true
failed_tests=$((failed_tests + 1))
failed_list="$failed_list\n[$total_tests] $relative_path - 输出不匹配"
if $VERBOSE; then
echo " 预期:"
cat "$expected_file"
echo " 实际:"
cat "$actual_file"
fi
fi
else
echo " ? 无预期输出文件,跳过比对 (编译:${compile_time})"
echo "警告: 无预期输出文件,跳过比对" >> "$test_log"
echo "结果: SKIPPED (无预期输出)" >> "$test_log"
passed_tests=$((passed_tests + 1))
fi
else
echo "步骤2: 跳过执行测试 (--run未启用)" >> "$test_log"
echo "结果: PASSED (仅IR生成)" >> "$test_log"
passed_tests=$((passed_tests + 1))
echo " ✓ 编译:${compile_time}"
fi
echo ""
done < <(find "$TEST_CASE_DIR" -name "*.sy" | sort)
# 输出统计结果到终端
echo "=== 测试完成 ==="
echo "总测试数: $total_tests"
echo "通过: $passed_tests"
echo "失败: $failed_tests"
# 写入汇总日志
echo "=== IR测试汇总报告 ===" > "$summary_log"
echo "测试时间: $(date '+%Y-%m-%d %H:%M:%S')" >> "$summary_log"
echo "测试目录: $TEST_CASE_DIR" >> "$summary_log"
echo "结果目录: $TEST_RESULT_DIR" >> "$summary_log"
echo "运行可执行文件: $RUN_EXEC" >> "$summary_log"
echo "" >> "$summary_log"
echo "=== 统计结果 ===" >> "$summary_log"
echo "总测试数: $total_tests" >> "$summary_log"
echo "通过: $passed_tests" >> "$summary_log"
echo "失败: $failed_tests" >> "$summary_log"
echo "成功率: $((passed_tests * 100 / total_tests))%" >> "$summary_log"
echo "" >> "$summary_log"
if [[ $failed_tests -gt 0 ]]; then
echo "=== 失败测试列表 ===" >> "$summary_log"
echo -e "$failed_list" >> "$summary_log"
fi
echo "详细日志已保存到各测试用例目录"
echo "汇总日志: $summary_log"
if [[ $failed_tests -eq 0 ]]; then
echo "所有测试通过!"
exit 0
else
echo "$failed_tests 个测试失败"
exit 1
fi