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

391 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
# test3_instcount.sh - Lab3(代码生成)汇编指令数量统计脚本
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="./lab3_instcount_results"
COMPILER="./build/bin/compiler"
VERIFY_SCRIPT="./scripts/verify_asm.sh"
MAX_CASES=0
STOP_ON_FIRST_FAILURE=false
START_FROM=1
KEEP_OLD=false
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'
用法: ./test3_instcount.sh [选项]
说明:
批量执行 Lab3(代码生成)汇编指令数量统计。
统计生成的汇编指令数量。
结果保存在 lab3_instcount_results 文件夹中。
选项:
-m, --max N 最多执行 N 个用例0 表示不限制,默认: 0
-x, --stop-on-fail 遇到第一个失败时停止
-s, --start-from N 从第 N 个测试用例开始(默认: 1
-k, --keep 保留旧的输出目录,不删除
-r, --run 运行测试用例验证正确性(默认不运行)
-h, --help 显示帮助
示例:
./test3_instcount.sh # 仅统计指令数量
./test3_instcount.sh -r # 统计指令数量并运行验证
./test3_instcount.sh --max 10 # 只运行前10个
./test3_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/lab3_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 "Lab3 汇编指令数量统计报告"
echo "================================================"
echo "测试模式: ASM"
echo "运行验证: ${RUN_TESTS}"
echo "生成时间: $(date)"
echo "================================================"
echo ""
printf "%-60s | %12s\n" "测试用例" "汇编指令数"
echo "------------------------------------------------"
} > "$INST_REPORT_FILE"
echo "Lab3 汇编指令数量统计日志 - $(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"
# 统计汇编指令数量的函数
count_asm_instructions() {
local asm_file="$1"
if [[ ! -f "$asm_file" ]]; then
echo "0"
return
fi
local count=0
# 统计实际的汇编指令(以小写字母开头的行,排除标签、注释、空行等)
count=$(grep -cE '^\s+[a-z]+' "$asm_file" 2>/dev/null || echo "0")
count=$(echo "$count" | tr -d '[:space:]')
[[ ! "$count" =~ ^[0-9]+$ ]] && count=0
echo "$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}.s"
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 asm_file="$case_out_dir/${base_name}.s"
# 生成汇编文件
set +e
"$COMPILER" --emit-asm "$file" > "$asm_file" 2>/dev/null
local asm_code=$?
set -e
if [[ $asm_code -ne 0 ]]; then
echo -e "${RED}编译失败${NC}"
echo "[FAILED] $file (compile error: asm=$asm_code)" >> "$LOG_FILE"
FAILED=$((FAILED + 1))
echo "$file" >> "$FAIL_FILE"
# 写入详细错误信息
echo "========================================" >> "$ERROR_LOG_FILE"
echo "编译失败: $file" >> "$ERROR_LOG_FILE"
echo "退出码: asm=$asm_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 asm_count=0
asm_count=$(count_asm_instructions "$asm_file")
SUCCESS=$((SUCCESS + 1))
# 累加统计
total_orig_inst=$((total_orig_inst + asm_count))
inst_cases_count=$((inst_cases_count + 1))
# 显示结果
echo -ne "${GREEN}成功${NC} "
echo -ne "(指令数: ${asm_count})${NC}"
# 写入报告
printf "%-60s | %12d\n" "$rel_path" "$asm_count" >> "$INST_REPORT_FILE"
echo "$rel_path: inst=$asm_count" >> "$INST_SUMMARY_FILE"
echo ""
echo "[SUCCESS] $file (inst=$asm_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}Lab3 汇编指令数量统计开始${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}Lab3 汇编指令数量统计完成${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