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

410 lines
11 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
# test2_instcount.sh - Lab2(IR生成)指令数量统计脚本
set -u
set -o pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
TEST_ROOT="./test_merged"
OUTPUT_DIR="./lab2_instcount_results"
COMPILER="./build/bin/compiler"
VERIFY_SCRIPT="./scripts/verify_ir.sh"
MAX_CASES=0
STOP_ON_FIRST_FAILURE=false
START_FROM=1
KEEP_OLD=true
RUN_TESTS=false
# 指令数量统计相关变量
total_orig_inst=0
total_opt_inst=0
inst_cases_count=0
total_reduction_sum=0
reduction_count=0
total_increase_sum=0
increase_count=0
show_help() {
cat << 'EOF'
用法: ./test2_instcount.sh [选项]
说明:
批量执行 Lab2(IR 生成)指令数量统计。
比较优化前后的IR指令数量变化。
结果保存在 lab2_incount_results 文件夹中。
选项:
-m, --max N 最多执行 N 个用例0 表示不限制,默认: 0
-x, --stop-on-fail 遇到第一个失败时停止
-s, --start-from N 从第 N 个测试用例开始(默认: 1
-k, --keep 保留旧的输出目录,不删除
-r, --run 运行测试用例验证正确性(默认不运行)
-h, --help 显示帮助
示例:
./test2_instcount.sh # 仅统计指令数量
./test2_instcount.sh -r # 统计指令数量并运行验证
./test2_instcount.sh --max 10 # 只运行前10个
./test2_instcount.sh --start-from 50 # 从第50个开始
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
-m|--max)
MAX_CASES="$2"
shift 2
;;
-x|--stop-on-fail)
STOP_ON_FIRST_FAILURE=true
shift
;;
-s|--start-from)
START_FROM="$2"
shift 2
;;
-k|--keep)
KEEP_OLD=true
shift
;;
-r|--run)
RUN_TESTS=true
shift
;;
-h|--help)
show_help
exit 0
;;
*)
echo -e "${RED}错误: 未知选项 $1${NC}"
show_help
exit 1
;;
esac
done
if ! [[ "$START_FROM" =~ ^[0-9]+$ ]] || [[ "$START_FROM" -lt 1 ]]; then
echo -e "${RED}错误: --start-from 需要正整数${NC}"
exit 1
fi
if [[ ! -d "$TEST_ROOT" ]]; then
echo -e "${RED}错误: 测试目录不存在: $TEST_ROOT${NC}"
exit 1
fi
if [[ ! -x "$COMPILER" ]]; then
echo -e "${RED}错误: 编译器不可执行: $COMPILER${NC}"
echo -e "${YELLOW}提示: 请先构建: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release && cmake --build build -j \"\$(nproc)\"${NC}"
exit 1
fi
if [[ "$RUN_TESTS" == "true" && ! -x "$VERIFY_SCRIPT" ]]; then
echo -e "${RED}错误: verify脚本不可执行: $VERIFY_SCRIPT${NC}"
exit 1
fi
if ! [[ "$MAX_CASES" =~ ^[0-9]+$ ]]; then
echo -e "${RED}错误: --max 需要非负整数${NC}"
exit 1
fi
mkdir -p "$OUTPUT_DIR"
# 删除旧的输出目录(除非指定了 --keep
if [[ "$KEEP_OLD" != "true" ]]; then
if [[ -d "$OUTPUT_DIR" ]]; then
echo "正在删除旧的输出目录..."
rm -rf "$OUTPUT_DIR"/*
echo "已删除旧的输出目录"
fi
fi
mkdir -p "$OUTPUT_DIR"
LOG_FILE="$OUTPUT_DIR/lab2_instcount.log"
FAIL_FILE="$OUTPUT_DIR/failed_cases.txt"
ERROR_LOG_FILE="$OUTPUT_DIR/error_log.txt"
INST_REPORT_FILE="$OUTPUT_DIR/instruction_report.txt"
INST_SUMMARY_FILE="$OUTPUT_DIR/instruction_summary.txt"
: > "$LOG_FILE"
: > "$FAIL_FILE"
: > "$ERROR_LOG_FILE"
: > "$INST_SUMMARY_FILE"
# 初始化指令报告文件
{
echo "================================================"
echo "Lab2 IR指令数量统计报告"
echo "================================================"
echo "测试模式: IR"
echo "运行验证: ${RUN_TESTS}"
echo "生成时间: $(date)"
echo "================================================"
echo ""
printf "%-60s | %12s\n" "测试用例" "IR指令数"
echo "------------------------------------------------"
} > "$INST_REPORT_FILE"
echo "Lab2 指令数量统计日志 - $(date)" >> "$LOG_FILE"
echo "TEST_ROOT=$TEST_ROOT" >> "$LOG_FILE"
echo "OUTPUT_DIR=$OUTPUT_DIR" >> "$LOG_FILE"
echo "MAX_CASES=$MAX_CASES" >> "$LOG_FILE"
echo "RUN_TESTS=$RUN_TESTS" >> "$LOG_FILE"
echo "================================================" >> "$LOG_FILE"
# 统计IR指令数量的函数
count_ir_instructions() {
local ir_file="$1"
if [[ ! -f "$ir_file" ]]; then
echo "0"
return
fi
local count=0
local term_count=0
local store_count=0
local call_count=0
# 统计赋值指令(包含变量定义)
count=$(grep -cE '^\s*%?[a-zA-Z0-9_]+\s*=' "$ir_file" 2>/dev/null || echo "0")
count=$(echo "$count" | tr -d '[:space:]')
[[ ! "$count" =~ ^[0-9]+$ ]] && count=0
# 统计终止指令ret, br, switch
term_count=$(grep -cE '^\s*(ret|br|switch)' "$ir_file" 2>/dev/null || echo "0")
term_count=$(echo "$term_count" | tr -d '[:space:]')
[[ ! "$term_count" =~ ^[0-9]+$ ]] && term_count=0
# 统计store指令
store_count=$(grep -cE '^\s*store ' "$ir_file" 2>/dev/null || echo "0")
store_count=$(echo "$store_count" | tr -d '[:space:]')
[[ ! "$store_count" =~ ^[0-9]+$ ]] && store_count=0
# 统计call/invoke指令
call_count=$(grep -cE '^\s*(call|invoke)' "$ir_file" 2>/dev/null || echo "0")
call_count=$(echo "$call_count" | tr -d '[:space:]')
[[ ! "$call_count" =~ ^[0-9]+$ ]] && call_count=0
echo $((count + term_count + store_count + call_count))
}
# 运行单个测试用例
run_single_test() {
local file="$1"
local case_num="$2"
local rel_path="${file#$TEST_ROOT/}"
local filename="$(basename "$file")"
local base_name="${filename%.sy}"
local rel_dir="$(dirname "$rel_path")"
local case_out_dir="$OUTPUT_DIR/$rel_dir"
mkdir -p "$case_out_dir"
# 删除已有的输出文件,确保重新生成
rm -f "$case_out_dir/${base_name}.ll"
rm -f "$case_out_dir/${base_name}.o"
rm -f "$case_out_dir/${base_name}"
rm -f "$case_out_dir/${base_name}.stdout"
rm -f "$case_out_dir/${base_name}.actual.out"
rm -f "$case_out_dir/${base_name}_time.txt"
echo -ne "${YELLOW}[$case_num] $filename ... ${NC}"
local ir_file="$case_out_dir/${base_name}.ll"
# 生成IR文件
set +e
"$COMPILER" --emit-ir "$file" > "$ir_file" 2>/dev/null
local ir_code=$?
set -e
if [[ $ir_code -ne 0 ]]; then
echo -e "${RED}编译失败${NC}"
echo "[FAILED] $file (compile error: ir=$ir_code)" >> "$LOG_FILE"
FAILED=$((FAILED + 1))
echo "$file" >> "$FAIL_FILE"
# 写入详细错误信息
echo "========================================" >> "$ERROR_LOG_FILE"
echo "编译失败: $file" >> "$ERROR_LOG_FILE"
echo "退出码: ir=$ir_code" >> "$ERROR_LOG_FILE"
echo "时间: $(date)" >> "$ERROR_LOG_FILE"
echo "========================================" >> "$ERROR_LOG_FILE"
if [[ "$STOP_ON_FIRST_FAILURE" = true ]]; then
echo -e "${RED}========================================${NC}"
echo -e "${RED}在第一个失败处停止测试${NC}"
echo -e "${RED}失败文件: $file${NC}"
echo -e "${RED}日志: $LOG_FILE${NC}"
echo -e "${RED}错误日志: $ERROR_LOG_FILE${NC}"
echo -e "${RED}========================================${NC}"
return 1
fi
return 0
fi
# 如果需要运行验证
if [[ "$RUN_TESTS" == "true" ]]; then
"$VERIFY_SCRIPT" "$file" "$case_out_dir" --run > /dev/null 2>&1
local run_code=$?
if [[ $run_code -ne 0 ]]; then
echo -e "${RED}运行失败${NC}"
echo "[FAILED] $file (runtime error, exit=$run_code)" >> "$LOG_FILE"
FAILED=$((FAILED + 1))
echo "$file" >> "$FAIL_FILE"
echo "========================================" >> "$ERROR_LOG_FILE"
echo "运行失败: $file" >> "$ERROR_LOG_FILE"
echo "退出码: $run_code" >> "$ERROR_LOG_FILE"
echo "时间: $(date)" >> "$ERROR_LOG_FILE"
echo "========================================" >> "$ERROR_LOG_FILE"
if [[ "$STOP_ON_FIRST_FAILURE" = true ]]; then
return 1
fi
return 0
fi
fi
# 统计指令数量
local ir_count=0
ir_count=$(count_ir_instructions "$ir_file")
SUCCESS=$((SUCCESS + 1))
# 累加统计
total_orig_inst=$((total_orig_inst + ir_count))
inst_cases_count=$((inst_cases_count + 1))
# 显示结果
echo -ne "${GREEN}成功${NC} "
echo -ne "(指令数: ${ir_count})${NC}"
# 写入报告
printf "%-60s | %12d\n" "$rel_path" "$ir_count" >> "$INST_REPORT_FILE"
echo "$rel_path: inst=$ir_count" >> "$INST_SUMMARY_FILE"
echo ""
echo "[SUCCESS] $file (inst=$ir_count)" >> "$LOG_FILE"
return 0
}
# 主函数
main() {
mapfile -d '' -t CASES < <(find "$TEST_ROOT" -type f -name '*.sy' -print0 | sort -z)
TOTAL_FOUND=${#CASES[@]}
if [[ $TOTAL_FOUND -eq 0 ]]; then
echo -e "${YELLOW}未找到任何 .sy 用例,请检查目录: $TEST_ROOT${NC}"
exit 0
fi
TOTAL=0
SUCCESS=0
FAILED=0
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}Lab2 IR指令数量统计开始${NC}"
echo -e "${BLUE}测试根目录: $TEST_ROOT${NC}"
echo -e "${BLUE}找到用例数: $TOTAL_FOUND${NC}"
echo -e "${BLUE}输出目录: $OUTPUT_DIR${NC}"
echo -e "${BLUE}运行验证: $RUN_TESTS${NC}"
if [[ "$KEEP_OLD" == "true" ]]; then
echo -e "${BLUE}模式: 保留旧输出${NC}"
else
echo -e "${BLUE}模式: 全新输出${NC}"
fi
if [[ "$START_FROM" -gt 1 ]]; then
echo -e "${BLUE}起始用例: $START_FROM${NC}"
fi
echo -e "${BLUE}========================================${NC}"
for file in "${CASES[@]}"; do
TOTAL=$((TOTAL + 1))
# 跳过起始用例之前的测试
if [[ $TOTAL -lt $START_FROM ]]; then
continue
fi
# 检查是否已达到最大用例数限制
if [[ $MAX_CASES -gt 0 && $((TOTAL - START_FROM + 1)) -gt $MAX_CASES ]]; then
break
fi
set +e
run_single_test "$file" "$TOTAL"
test_result=$?
set -e
if [[ $test_result -eq 1 ]] && [[ "$STOP_ON_FIRST_FAILURE" = true ]]; then
break
fi
done
# 生成指令报告摘要
{
echo ""
echo "================================================"
echo "指令数量统计摘要"
echo "================================================"
echo "总指令数: $total_orig_inst"
echo "测试用例数: $inst_cases_count"
if [[ $inst_cases_count -gt 0 ]]; then
local avg_inst=$((total_orig_inst / inst_cases_count))
echo "平均指令数: ${avg_inst}"
fi
echo "================================================"
} >> "$INST_REPORT_FILE"
# 计算成功率
RATE="0.00"
if [[ $TOTAL -gt 0 ]]; then
RATE=$(awk -v s="$SUCCESS" -v t="$TOTAL" 'BEGIN { printf "%.2f", (s*100.0)/t }')
fi
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}Lab2 指令数量统计完成${NC}"
echo -e "${BLUE}执行用例: $TOTAL${NC}"
echo -e "${GREEN}成功: $SUCCESS${NC}"
echo -e "${RED}失败: $FAILED${NC}"
echo -e "${BLUE}成功率: ${RATE}%${NC}"
echo ""
echo -e "${BLUE}指令数量统计:${NC}"
echo " 总指令数: $total_orig_inst"
echo " 测试用例数: $inst_cases_count"
if [[ $inst_cases_count -gt 0 ]]; then
local avg_inst=$((total_orig_inst / inst_cases_count))
echo -e " 平均指令数: ${avg_inst}"
fi
echo ""
echo -e "${BLUE}详细报告: $INST_REPORT_FILE${NC}"
echo -e "${BLUE}指令汇总: $INST_SUMMARY_FILE${NC}"
echo -e "${BLUE}日志: $LOG_FILE${NC}"
if [[ $FAILED -gt 0 ]]; then
echo -e "${RED}失败清单: $FAIL_FILE${NC}"
echo -e "${RED}错误日志: $ERROR_LOG_FILE${NC}"
fi
echo -e "${BLUE}========================================${NC}"
exit 0
}
main