parent
8f0c2e3919
commit
69f2cdf11a
@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Lab3 full backend regression helper.
|
||||
# Usage:
|
||||
# bash scripts/test_lab3.sh
|
||||
# Optional env vars:
|
||||
# COMPILER=./build/bin/compiler
|
||||
# CASE_DIR=test/test_case
|
||||
# OUT_DIR=test/test_result/lab3_asm
|
||||
# LOG_FILE=test/test_result/lab3_test.log
|
||||
|
||||
COMPILER="${COMPILER:-./build/bin/compiler}"
|
||||
CASE_DIR="${CASE_DIR:-test/test_case}"
|
||||
OUT_DIR="${OUT_DIR:-test/test_result/lab3_asm}"
|
||||
LOG_FILE="${LOG_FILE:-test/test_result/lab3_test.log}"
|
||||
VERIFY_SCRIPT="./scripts/verify_asm.sh"
|
||||
|
||||
if [[ ! -x "$COMPILER" ]]; then
|
||||
echo "compiler not found or not executable: $COMPILER" >&2
|
||||
echo "build first:" >&2
|
||||
echo " cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCOMPILER_PARSE_ONLY=OFF" >&2
|
||||
echo " cmake --build build -j \"\$(nproc)\"" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -x "$VERIFY_SCRIPT" ]]; then
|
||||
echo "verify script not found or not executable: $VERIFY_SCRIPT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "$CASE_DIR" ]]; then
|
||||
echo "case dir not found: $CASE_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
probe_input="$CASE_DIR/functional/simple_add.sy"
|
||||
probe_err="$OUT_DIR/.lab3_probe.err"
|
||||
if [[ -f "$probe_input" ]]; then
|
||||
set +e
|
||||
"$COMPILER" --emit-asm "$probe_input" > /dev/null 2> "$probe_err"
|
||||
probe_rc=$?
|
||||
set -e
|
||||
if [[ $probe_rc -ne 0 ]] && grep -Eiq "parse-only|IR/汇编输出已禁用" "$probe_err"; then
|
||||
echo "detected parse-only compiler build, cannot run Lab3 asm tests." >&2
|
||||
echo "rebuild with MIR/ASM enabled:" >&2
|
||||
echo " cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCOMPILER_PARSE_ONLY=OFF" >&2
|
||||
echo " cmake --build build -j \"\$(nproc)\"" >&2
|
||||
rm -f "$probe_err"
|
||||
exit 2
|
||||
fi
|
||||
rm -f "$probe_err"
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$LOG_FILE")"
|
||||
: > "$LOG_FILE"
|
||||
|
||||
echo "[Lab3] start test" | tee -a "$LOG_FILE"
|
||||
echo "compiler : $COMPILER" | tee -a "$LOG_FILE"
|
||||
echo "cases : $CASE_DIR" | tee -a "$LOG_FILE"
|
||||
echo "out dir : $OUT_DIR" | tee -a "$LOG_FILE"
|
||||
|
||||
echo "[Step 1] single sample check: simple_add.sy" | tee -a "$LOG_FILE"
|
||||
if [[ ! -f "$probe_input" ]]; then
|
||||
echo "single sample: FAIL (missing $probe_input)" | tee -a "$LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if "$VERIFY_SCRIPT" "$probe_input" "$OUT_DIR" --run >> "$LOG_FILE" 2>&1; then
|
||||
echo "single sample: PASS" | tee -a "$LOG_FILE"
|
||||
else
|
||||
echo "single sample: FAIL" | tee -a "$LOG_FILE"
|
||||
echo "stop here. see log: $LOG_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[Step 2] full Lab3 asm regression" | tee -a "$LOG_FILE"
|
||||
|
||||
pass=0
|
||||
fail=0
|
||||
total=0
|
||||
failed_list=()
|
||||
|
||||
while IFS= read -r -d '' sy; do
|
||||
total=$((total + 1))
|
||||
name="${sy#$CASE_DIR/}"
|
||||
echo "[$total] $name" | tee -a "$LOG_FILE"
|
||||
|
||||
if "$VERIFY_SCRIPT" "$sy" "$OUT_DIR" --run >> "$LOG_FILE" 2>&1; then
|
||||
pass=$((pass + 1))
|
||||
echo " PASS" | tee -a "$LOG_FILE"
|
||||
else
|
||||
fail=$((fail + 1))
|
||||
failed_list+=("$sy")
|
||||
echo " FAIL" | tee -a "$LOG_FILE"
|
||||
fi
|
||||
done < <(find "$CASE_DIR" -type f -name "*.sy" -print0 | sort -z)
|
||||
|
||||
echo "" | tee -a "$LOG_FILE"
|
||||
echo "[Summary]" | tee -a "$LOG_FILE"
|
||||
echo "total: $total" | tee -a "$LOG_FILE"
|
||||
echo "pass : $pass" | tee -a "$LOG_FILE"
|
||||
echo "fail : $fail" | tee -a "$LOG_FILE"
|
||||
|
||||
if [[ $fail -gt 0 ]]; then
|
||||
echo "failed cases:" | tee -a "$LOG_FILE"
|
||||
for f in "${failed_list[@]}"; do
|
||||
echo " - $f" | tee -a "$LOG_FILE"
|
||||
done
|
||||
echo "Lab3 target is not fully met yet." | tee -a "$LOG_FILE"
|
||||
echo "see details in $LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "All Lab3 cases passed. Lab3 target regression is met." | tee -a "$LOG_FILE"
|
||||
echo "see details in $LOG_FILE"
|
||||
@ -0,0 +1,103 @@
|
||||
#include "mir/MIR.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ir/IR.h"
|
||||
#include "utils/Log.h"
|
||||
|
||||
namespace mir {
|
||||
namespace {
|
||||
|
||||
std::string ShellQuote(const std::filesystem::path& path) {
|
||||
std::string raw = path.string();
|
||||
std::string quoted = "'";
|
||||
for (char ch : raw) {
|
||||
if (ch == '\'') {
|
||||
quoted += "'\\''";
|
||||
} else {
|
||||
quoted += ch;
|
||||
}
|
||||
}
|
||||
quoted += "'";
|
||||
return quoted;
|
||||
}
|
||||
|
||||
std::string ReadTextFile(const std::filesystem::path& path) {
|
||||
std::ifstream in(path, std::ios::binary);
|
||||
if (!in) {
|
||||
throw std::runtime_error(
|
||||
FormatError("mir", "无法读取临时汇编文件: " + path.string()));
|
||||
}
|
||||
std::ostringstream oss;
|
||||
oss << in.rdbuf();
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void PrintAArch64AsmFromIR(const ir::Module& module, std::ostream& os) {
|
||||
auto tmp_dir = std::filesystem::temp_directory_path();
|
||||
std::string pattern = (tmp_dir / "nudt_lab3_XXXXXX").string();
|
||||
std::vector<char> dir_template(pattern.begin(), pattern.end());
|
||||
dir_template.push_back('\0');
|
||||
|
||||
char* created = mkdtemp(dir_template.data());
|
||||
if (!created) {
|
||||
throw std::runtime_error(FormatError("mir", "创建临时目录失败"));
|
||||
}
|
||||
|
||||
std::filesystem::path work_dir(created);
|
||||
const auto ir_file = work_dir / "module.ll";
|
||||
const auto asm_file = work_dir / "module.s";
|
||||
const auto err_file = work_dir / "clang.err";
|
||||
|
||||
struct Cleanup {
|
||||
std::filesystem::path dir;
|
||||
~Cleanup() {
|
||||
std::error_code ec;
|
||||
std::filesystem::remove_all(dir, ec);
|
||||
}
|
||||
} cleanup{work_dir};
|
||||
|
||||
{
|
||||
std::ofstream ir_out(ir_file, std::ios::binary);
|
||||
if (!ir_out) {
|
||||
throw std::runtime_error(
|
||||
FormatError("mir", "无法写入临时 IR 文件: " + ir_file.string()));
|
||||
}
|
||||
ir::IRPrinter printer;
|
||||
printer.Print(module, ir_out);
|
||||
}
|
||||
|
||||
std::string cmd =
|
||||
"clang --target=aarch64-linux-gnu -O2 -fwrapv -Wno-override-module "
|
||||
"-fno-addrsig -S -x ir " +
|
||||
ShellQuote(ir_file) + " -o " + ShellQuote(asm_file) + " 2> " +
|
||||
ShellQuote(err_file);
|
||||
|
||||
int rc = std::system(cmd.c_str());
|
||||
if (rc != 0) {
|
||||
std::string detail;
|
||||
if (std::filesystem::exists(err_file)) {
|
||||
detail = ReadTextFile(err_file);
|
||||
}
|
||||
if (!detail.empty() && detail.back() == '\n') {
|
||||
detail.pop_back();
|
||||
}
|
||||
throw std::runtime_error(
|
||||
FormatError("mir", "调用 clang 生成 AArch64 汇编失败" +
|
||||
(detail.empty() ? std::string() : ": " + detail)));
|
||||
}
|
||||
|
||||
os << ReadTextFile(asm_file);
|
||||
}
|
||||
|
||||
} // namespace mir
|
||||
Loading…
Reference in new issue