|
|
#!/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
|