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.

339 lines
9.0 KiB

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
BUILD_DIR="$ROOT_DIR/build"
ANTLR_DIR="$BUILD_DIR/generated/antlr4"
JAR_PATH="$ROOT_DIR/third_party/antlr-4.13.2-complete.jar"
GRAMMAR_PATH="$ROOT_DIR/src/antlr4/SysY.g4"
RUN_FUNCTIONAL=true
RUN_PERFORMANCE=true
RUN_IR=true
RUN_ASM=true
RUN_EXEC=true
DO_BUILD=true
RUN_TIMEOUT=""
OPT_LEVEL=0
OUT_ROOT=""
TIME_LOG_FILE=""
failed_cases=()
declare -A counters=()
usage() {
cat <<'EOF'
Usage: ./solution/run_lab4_batch.sh [options]
Options:
--no-build Skip ANTLR generation and project rebuild
--functional-only Run only test/test_case/functional/*.sy
--performance-only Run only test/test_case/performance/*.sy
--ir-only Run only verify_ir batch
--asm-only Run only verify_asm batch
--no-run Generate IR/asm only; skip execution and output check
--emit-only Alias of --no-run
--timeout <sec> Apply per-case timeout via the `timeout` command
-O0 Run batch with compiler flag -O0 (default)
-O1 Run batch with compiler flag -O1
--opt-level <0|1> Same as -O0 / -O1
--output-dir <dir> Set output root; default is test/test_result/lab4_batch_o<level>
--help Show this help message
EOF
}
set_opt_level() {
case "$1" in
0|O0|-O0)
OPT_LEVEL=0
;;
1|O1|-O1)
OPT_LEVEL=1
;;
*)
echo "Unsupported opt level: $1" >&2
echo "Only -O0 / -O1 are supported." >&2
exit 1
;;
esac
}
bump_counter() {
local mode=$1
local group=$2
local kind=$3
local key="$mode:$group:$kind"
counters["$key"]=$(( ${counters["$key"]:-0} + 1 ))
}
get_counter() {
local mode=$1
local group=$2
local kind=$3
echo "${counters["$mode:$group:$kind"]:-0}"
}
print_mode_summary() {
local mode=$1
local functional_total functional_passed functional_failed
local performance_total performance_passed performance_failed
local total passed failed
functional_total=$(get_counter "$mode" "functional" "total")
functional_passed=$(get_counter "$mode" "functional" "passed")
functional_failed=$(get_counter "$mode" "functional" "failed")
performance_total=$(get_counter "$mode" "performance" "total")
performance_passed=$(get_counter "$mode" "performance" "passed")
performance_failed=$(get_counter "$mode" "performance" "failed")
total=$((functional_total + performance_total))
passed=$((functional_passed + performance_passed))
failed=$((functional_failed + performance_failed))
echo " ${mode^^} functional: total=$functional_total, passed=$functional_passed, failed=$functional_failed"
echo " ${mode^^} performance: total=$performance_total, passed=$performance_passed, failed=$performance_failed"
echo " ${mode^^} overall: total=$total, passed=$passed, failed=$failed"
}
print_summary() {
local overall_total=0
local overall_passed=0
local overall_failed=0
echo
echo "Summary:"
echo " Opt level: -O$OPT_LEVEL"
echo " Execution: $([[ "$RUN_EXEC" == true ]] && echo "enabled" || echo "disabled")"
if [[ "$RUN_IR" == true ]]; then
print_mode_summary "ir"
overall_total=$((overall_total + $(get_counter "ir" "functional" "total") + $(get_counter "ir" "performance" "total")))
overall_passed=$((overall_passed + $(get_counter "ir" "functional" "passed") + $(get_counter "ir" "performance" "passed")))
overall_failed=$((overall_failed + $(get_counter "ir" "functional" "failed") + $(get_counter "ir" "performance" "failed")))
fi
if [[ "$RUN_ASM" == true ]]; then
print_mode_summary "asm"
overall_total=$((overall_total + $(get_counter "asm" "functional" "total") + $(get_counter "asm" "performance" "total")))
overall_passed=$((overall_passed + $(get_counter "asm" "functional" "passed") + $(get_counter "asm" "performance" "passed")))
overall_failed=$((overall_failed + $(get_counter "asm" "functional" "failed") + $(get_counter "asm" "performance" "failed")))
fi
echo " Overall: total=$overall_total, passed=$overall_passed, failed=$overall_failed"
if (( ${#failed_cases[@]} > 0 )); then
echo "Failed cases:"
printf ' - %s\n' "${failed_cases[@]}"
fi
echo " Per-case timing log: $TIME_LOG_FILE"
}
format_elapsed_seconds() {
local elapsed_ns=$1
awk -v ns="$elapsed_ns" 'BEGIN { printf "%.3f", ns / 1000000000 }'
}
run_case() {
local mode=$1
local group=$2
local case_file=$3
local stem out_dir log_file
local start_ns end_ns elapsed_ns elapsed_s
local -a cmd
stem="$(basename "${case_file%.sy}")"
out_dir="$OUT_ROOT/$mode/$group"
log_file="$out_dir/$stem.verify.log"
mkdir -p "$out_dir"
bump_counter "$mode" "$group" "total"
if [[ "$mode" == "ir" ]]; then
cmd=("$ROOT_DIR/scripts/verify_ir.sh" "$case_file" "$out_dir")
else
cmd=("$ROOT_DIR/scripts/verify_asm.sh" "$case_file" "$out_dir")
fi
if [[ "$RUN_EXEC" == true ]]; then
cmd+=(--run)
fi
cmd+=(-- "-O$OPT_LEVEL")
if [[ -n "$RUN_TIMEOUT" ]]; then
cmd=(timeout "$RUN_TIMEOUT" "${cmd[@]}")
fi
start_ns=$(date +%s%N)
if "${cmd[@]}" >"$log_file" 2>&1; then
end_ns=$(date +%s%N)
elapsed_ns=$((end_ns - start_ns))
elapsed_s=$(format_elapsed_seconds "$elapsed_ns")
echo "PASS [$mode] $case_file (${elapsed_s}s)"
bump_counter "$mode" "$group" "passed"
printf '%s,%s,%s,%s,%s,%s,%s\n' \
"$mode" "$group" "$case_file" "PASS" "$elapsed_ns" "$elapsed_s" "$log_file" >> "$TIME_LOG_FILE"
else
end_ns=$(date +%s%N)
elapsed_ns=$((end_ns - start_ns))
elapsed_s=$(format_elapsed_seconds "$elapsed_ns")
echo "FAIL [$mode] $case_file (${elapsed_s}s)"
cat "$log_file"
bump_counter "$mode" "$group" "failed"
failed_cases+=("[$mode] $case_file")
printf '%s,%s,%s,%s,%s,%s,%s\n' \
"$mode" "$group" "$case_file" "FAIL" "$elapsed_ns" "$elapsed_s" "$log_file" >> "$TIME_LOG_FILE"
fi
}
run_group() {
local mode=$1
local group=$2
local case_dir=$3
while IFS= read -r case_file; do
run_case "$mode" "$group" "$case_file"
done < <(find "$case_dir" -maxdepth 1 -type f -name '*.sy' | sort)
}
while [[ $# -gt 0 ]]; do
case "$1" in
--no-build)
DO_BUILD=false
;;
--functional-only)
RUN_FUNCTIONAL=true
RUN_PERFORMANCE=false
;;
--performance-only)
RUN_FUNCTIONAL=false
RUN_PERFORMANCE=true
;;
--ir-only)
RUN_IR=true
RUN_ASM=false
;;
--asm-only)
RUN_IR=false
RUN_ASM=true
;;
--no-run|--emit-only)
RUN_EXEC=false
;;
--timeout)
shift
if [[ $# -eq 0 ]]; then
echo "Missing value for --timeout" >&2
usage
exit 1
fi
RUN_TIMEOUT="$1"
;;
-O0|-O1)
set_opt_level "$1"
;;
--opt-level)
shift
if [[ $# -eq 0 ]]; then
echo "Missing value for --opt-level" >&2
usage
exit 1
fi
set_opt_level "$1"
;;
--output-dir)
shift
if [[ $# -eq 0 ]]; then
echo "Missing value for --output-dir" >&2
usage
exit 1
fi
if [[ "$1" = /* ]]; then
OUT_ROOT="$1"
else
OUT_ROOT="$ROOT_DIR/$1"
fi
;;
--help)
usage
exit 0
;;
*)
echo "Unknown option: $1" >&2
usage
exit 1
;;
esac
shift
done
if [[ -z "$OUT_ROOT" ]]; then
OUT_ROOT="$ROOT_DIR/test/test_result/lab4_batch_o$OPT_LEVEL"
fi
mkdir -p "$OUT_ROOT"
TIME_LOG_FILE="$OUT_ROOT/case_timing.csv"
echo "mode,group,case,status,elapsed_ns,elapsed_s,log_file" > "$TIME_LOG_FILE"
if [[ "$RUN_FUNCTIONAL" == false && "$RUN_PERFORMANCE" == false ]]; then
echo "No test set selected." >&2
exit 1
fi
if [[ "$RUN_IR" == false && "$RUN_ASM" == false ]]; then
echo "No verification pipeline selected." >&2
exit 1
fi
if [[ -n "$RUN_TIMEOUT" ]] && ! command -v timeout >/dev/null 2>&1; then
echo "未找到 timeout 命令,无法使用 --timeout。" >&2
exit 1
fi
cd "$ROOT_DIR"
if [[ "$DO_BUILD" == true ]]; then
echo "[1/4] Generating ANTLR sources..."
mkdir -p "$ANTLR_DIR"
java -jar "$JAR_PATH" \
-Dlanguage=Cpp \
-visitor -no-listener \
-Xexact-output-dir \
-o "$ANTLR_DIR" \
"$GRAMMAR_PATH"
echo "[2/4] Configuring CMake..."
cmake -S "$ROOT_DIR" -B "$BUILD_DIR" -DCMAKE_BUILD_TYPE=Release -DCOMPILER_PARSE_ONLY=OFF
echo "[3/4] Building project..."
cmake --build "$BUILD_DIR" -j "$(nproc)"
fi
echo "[4/4] Running Lab4 batch tests..."
if [[ "$RUN_IR" == true ]]; then
if [[ "$RUN_FUNCTIONAL" == true ]]; then
run_group "ir" "functional" "$ROOT_DIR/test/test_case/functional"
fi
if [[ "$RUN_PERFORMANCE" == true ]]; then
run_group "ir" "performance" "$ROOT_DIR/test/test_case/performance"
fi
fi
if [[ "$RUN_ASM" == true ]]; then
if [[ "$RUN_FUNCTIONAL" == true ]]; then
run_group "asm" "functional" "$ROOT_DIR/test/test_case/functional"
fi
if [[ "$RUN_PERFORMANCE" == true ]]; then
run_group "asm" "performance" "$ROOT_DIR/test/test_case/performance"
fi
fi
print_summary
if (( ${#failed_cases[@]} > 0 )); then
echo "Batch test finished with failures."
exit 1
fi
echo "Batch test passed."