Compare commits

...

20 Commits

Author SHA1 Message Date
rain2133
a1cca3c95a [midend-llvmirprint]修改i1类型不存在引入的临时寄存器保存比较结果重命名的逻辑,减少冲突的可能性 2025-08-11 19:10:11 +08:00
rain2133
1361156b0d [midend-CFGOpt]修复上一次提交的漏洞 2025-08-11 18:46:07 +08:00
rain2133
46179e3866 [midend-CFGOpt]修复部分指令删除逻辑错误 2025-08-11 18:40:58 +08:00
rain2133
038552f58b [midend-SCCP]修复了函数参数没有正确初始化为Bottom的问题,现在f/64样例可以-O1通过 2025-08-11 16:47:03 +08:00
rain2133
4d0e2d73ea [midend-LATG]将调试信息改为DEBUG生效 2025-08-10 17:32:45 +08:00
rain2133
ad19a6715f [midend]删除了错误的sra指令生成逻辑 2025-08-10 16:57:40 +08:00
rain2133
d1ba140657 [midend-llvnirprint]修改浮点为字面值打印,修复assign的类型推断 2025-08-10 16:12:09 +08:00
rain2133
2c5e4cead1 [midend-llvmirprint]修复部分打印逻辑,修复生成IR时的基本块错误的前后驱关系链接 2025-08-10 15:19:24 +08:00
rain2133
6b92020bc4 [midend-llvmirprint]修复全局数组打印格式问题 2025-08-09 22:41:32 +08:00
rain2133
c867bda9b4 [midend]解决部分变量重命名问题 2025-08-09 22:30:09 +08:00
rain2133
6b9ad0566d [midend-llvmirprint]修复进度(162/199),修复若干打印问题,修复若干ir生成逻辑问题 2025-08-09 21:28:44 +08:00
Lixuanwang
c507b98199 [midend-llvmprint]更新脚本,支持-eir执行IR测试 2025-08-07 23:45:26 +08:00
rain2133
ba21bb3203 [midend]修复内存泄漏和Heap-buffer-overflow问题(getexternalfunction中及其隐秘的错误),修复全局常量标量访问的错误 2025-08-07 02:53:36 +08:00
rain2133
8aa5ba692f [midend]初步修复内存泄漏问题(仍然剩余11处) 2025-08-07 01:34:00 +08:00
ladev789
d732800149 [midend-llvmprint]更新脚本,禁用内存泄漏检查 2025-08-07 00:36:43 +08:00
rain2133
37f2a01783 [midend-llvmirprint]修复了gep指令对不含维度信息的数组指针的处理逻辑,修复若干打印bug,在-s ir/ird -o <llvmir.ll file>的参数下最终会打印ir到file中,优化过程中的打印逻辑待更改。 2025-08-06 15:28:54 +08:00
rain2133
5d343f42a5 [midend-llvmirprint]检查并修复了初始值的打印逻辑 2025-08-06 02:02:05 +08:00
rain2133
a4406e0112 [midend]增加了指令重命名逻辑。 2025-08-06 01:31:23 +08:00
rain2133
08fcda939b [midend-llvmirprint]实现了大部分函数的print方法,TODO:需要完善func和module的print方法以及重命名的逻辑 2025-08-06 01:02:11 +08:00
rain2133
5f63554ca3 [midend]修正合并基本块链会将entry块纳入考虑范围的问题。上次commit同时修改了alloca创建的逻辑保证了alloca的声明全部在entry块中 2025-08-04 18:56:57 +08:00
10 changed files with 1870 additions and 514 deletions

View File

@@ -2,66 +2,67 @@
# runit-single.sh - 用于编译和测试单个或少量 SysY 程序的脚本 # runit-single.sh - 用于编译和测试单个或少量 SysY 程序的脚本
# 模仿 runit.sh 的功能,但以具体文件路径作为输入。 # 模仿 runit.sh 的功能,但以具体文件路径作为输入。
# 此脚本应该位于 mysysy/script/
export ASAN_OPTIONS=detect_leaks=0
# --- 配置区 --- # --- 配置区 ---
# 请根据你的环境修改这些路径
# 假设此脚本位于你的项目根目录或一个脚本目录中
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
# 默认寻找项目根目录下的 build 和 lib
BUILD_BIN_DIR="${SCRIPT_DIR}/../build/bin" BUILD_BIN_DIR="${SCRIPT_DIR}/../build/bin"
LIB_DIR="${SCRIPT_DIR}/../lib" LIB_DIR="${SCRIPT_DIR}/../lib"
# 临时文件会存储在脚本所在目录的 tmp 子目录中
TMP_DIR="${SCRIPT_DIR}/tmp" TMP_DIR="${SCRIPT_DIR}/tmp"
# 定义编译器和模拟器 # 定义编译器和模拟器
SYSYC="${BUILD_BIN_DIR}/sysyc" SYSYC="${BUILD_BIN_DIR}/sysyc"
LLC_CMD="llc-19" # 新增
GCC_RISCV64="riscv64-linux-gnu-gcc" GCC_RISCV64="riscv64-linux-gnu-gcc"
QEMU_RISCV64="qemu-riscv64" QEMU_RISCV64="qemu-riscv64"
# --- 初始化变量 --- # --- 初始化变量 ---
EXECUTE_MODE=false EXECUTE_MODE=false
IR_EXECUTE_MODE=false # 新增
CLEAN_MODE=false CLEAN_MODE=false
OPTIMIZE_FLAG="" # 用于存储 -O1 标志 OPTIMIZE_FLAG=""
SYSYC_TIMEOUT=10 # sysyc 编译超时 (秒) SYSYC_TIMEOUT=30
GCC_TIMEOUT=10 # gcc 编译超时 (秒) LLC_TIMEOUT=10 # 新增
EXEC_TIMEOUT=5 # qemu 自动化执行超时 (秒) GCC_TIMEOUT=10
MAX_OUTPUT_LINES=50 # 对比失败时显示的最大行数 EXEC_TIMEOUT=30
SY_FILES=() # 存储用户提供的 .sy 文件列表 MAX_OUTPUT_LINES=20
SY_FILES=()
PASSED_CASES=0 PASSED_CASES=0
FAILED_CASES_LIST="" FAILED_CASES_LIST=""
INTERRUPTED=false # 新增
# =================================================================
# --- 函数定义 --- # --- 函数定义 ---
# =================================================================
show_help() { show_help() {
echo "用法: $0 [文件1.sy] [文件2.sy] ... [选项]" echo "用法: $0 [文件1.sy] [文件2.sy] ... [选项]"
echo "编译并测试指定的 .sy 文件。" echo "编译并测试指定的 .sy 文件。必须提供 -e 或 -eir 之一。"
echo ""
echo "如果找到对应的 .in/.out 文件,则进行自动化测试。否则,进入交互模式。"
echo "" echo ""
echo "选项:" echo "选项:"
echo " -e, --executable 编译为可执行文件并运行测试 (必须)。" echo " -e 通过汇编运行测试 (sysyc -> gcc -> qemu)。"
echo " -eir 通过IR运行测试 (sysyc -> llc -> gcc -> qemu)。"
echo " -c, --clean 清理 tmp 临时目录下的所有文件。" echo " -c, --clean 清理 tmp 临时目录下的所有文件。"
echo " -O1 启用 sysyc 的 -O1 优化。" echo " -O1 启用 sysyc 的 -O1 优化。"
echo " -sct N 设置 sysyc 编译超时为 N 秒 (默认: 10)。" echo " -sct N 设置 sysyc 编译超时为 N 秒 (默认: 30)。"
echo " -lct N 设置 llc-19 编译超时为 N 秒 (默认: 10)。"
echo " -gct N 设置 gcc 交叉编译超时为 N 秒 (默认: 10)。" echo " -gct N 设置 gcc 交叉编译超时为 N 秒 (默认: 10)。"
echo " -et N 设置 qemu 自动化执行超时为 N 秒 (默认: 5)。" echo " -et N 设置 qemu 自动化执行超时为 N 秒 (默认: 30)。"
echo " -ml N, --max-lines N 当输出对比失败时,最多显示 N 行内容 (默认: 50)。" echo " -ml N, --max-lines N 当输出对比失败时,最多显示 N 行内容 (默认: 20)。"
echo " -h, --help 显示此帮助信息并退出。" echo " -h, --help 显示此帮助信息并退出。"
echo ""
echo "可在任何时候按 Ctrl+C 来中断测试并显示当前已完成的测例总结。"
} }
# --- 新增功能: 显示文件内容并根据行数截断 ---
display_file_content() { display_file_content() {
local file_path="$1" local file_path="$1"
local title="$2" local title="$2"
local max_lines="$3" local max_lines="$3"
if [ ! -f "$file_path" ]; then return; fi
if [ ! -f "$file_path" ]; then
return
fi
echo -e "$title" echo -e "$title"
local line_count local line_count
line_count=$(wc -l < "$file_path") line_count=$(wc -l < "$file_path")
if [ "$line_count" -gt "$max_lines" ]; then if [ "$line_count" -gt "$max_lines" ]; then
head -n "$max_lines" "$file_path" head -n "$max_lines" "$file_path"
echo -e "\e[33m[... 输出已截断,共 ${line_count} 行 ...]\e[0m" echo -e "\e[33m[... 输出已截断,共 ${line_count} 行 ...]\e[0m"
@@ -70,55 +71,79 @@ display_file_content() {
fi fi
} }
# --- 新增:总结报告函数 ---
print_summary() {
local total_cases=${#SY_FILES[@]}
echo ""
echo "======================================================================"
if [ "$INTERRUPTED" = true ]; then
echo -e "\e[33m测试被中断。正在汇总已完成的结果...\e[0m"
else
echo "所有测试完成"
fi
local failed_count
if [ -n "$FAILED_CASES_LIST" ]; then
failed_count=$(echo -e -n "${FAILED_CASES_LIST}" | wc -l)
else
failed_count=0
fi
local executed_count=$((PASSED_CASES + failed_count))
echo "测试结果: [通过: ${PASSED_CASES}, 失败: ${failed_count}, 已执行: ${executed_count}/${total_cases}]"
if [ -n "$FAILED_CASES_LIST" ]; then
echo ""
echo -e "\e[31m未通过的测例:\e[0m"
printf "%b" "${FAILED_CASES_LIST}"
fi
echo "======================================================================"
if [ "$failed_count" -gt 0 ]; then
exit 1
else
exit 0
fi
}
# --- 新增SIGINT 信号处理函数 ---
handle_sigint() {
INTERRUPTED=true
print_summary
}
# =================================================================
# --- 主逻辑开始 ---
# =================================================================
# --- 新增:设置 trap 来捕获 SIGINT ---
trap handle_sigint SIGINT
# --- 参数解析 --- # --- 参数解析 ---
# 使用标准的 while 循环来健壮地处理任意顺序的参数
while [[ "$#" -gt 0 ]]; do while [[ "$#" -gt 0 ]]; do
case "$1" in case "$1" in
-e|--executable) -e|--executable) EXECUTE_MODE=true; shift ;;
EXECUTE_MODE=true -eir) IR_EXECUTE_MODE=true; shift ;; # 新增
shift # 消耗选项 -c|--clean) CLEAN_MODE=true; shift ;;
;; -O1) OPTIMIZE_FLAG="-O1"; shift ;;
-c|--clean) -lct) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then LLC_TIMEOUT="$2"; shift 2; else echo "错误: -lct 需要一个正整数参数。" >&2; exit 1; fi ;; # 新增
CLEAN_MODE=true -sct) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then SYSYC_TIMEOUT="$2"; shift 2; else echo "错误: -sct 需要一个正整数参数。" >&2; exit 1; fi ;;
shift # 消耗选项 -gct) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then GCC_TIMEOUT="$2"; shift 2; else echo "错误: -gct 需要一个正整数参数。" >&2; exit 1; fi ;;
;; -et) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then EXEC_TIMEOUT="$2"; shift 2; else echo "错误: -et 需要一个正整数参数。" >&2; exit 1; fi ;;
-O1) -ml|--max-lines) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then MAX_OUTPUT_LINES="$2"; shift 2; else echo "错误: --max-lines 需要一个正整数参数。" >&2; exit 1; fi ;;
OPTIMIZE_FLAG="-O1" -h|--help) show_help; exit 0 ;;
shift # 消耗选项 -*) echo "未知选项: $1"; show_help; exit 1 ;;
;; *)
-sct)
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then SYSYC_TIMEOUT="$2"; shift 2; else echo "错误: -sct 需要一个正整数参数。" >&2; exit 1; fi
;;
-gct)
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then GCC_TIMEOUT="$2"; shift 2; else echo "错误: -gct 需要一个正整数参数。" >&2; exit 1; fi
;;
-et)
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then EXEC_TIMEOUT="$2"; shift 2; else echo "错误: -et 需要一个正整数参数。" >&2; exit 1; fi
;;
-ml|--max-lines)
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then MAX_OUTPUT_LINES="$2"; shift 2; else echo "错误: --max-lines 需要一个正整数参数。" >&2; exit 1; fi
;;
-h|--help)
show_help
exit 0
;;
-*) # 未知选项
echo "未知选项: $1"
show_help
exit 1
;;
*) # 其他参数被视为文件路径
if [[ -f "$1" && "$1" == *.sy ]]; then if [[ -f "$1" && "$1" == *.sy ]]; then
SY_FILES+=("$1") SY_FILES+=("$1")
else else
echo "警告: 无效文件或不是 .sy 文件,已忽略: $1" echo "警告: 无效文件或不是 .sy 文件,已忽略: $1"
fi fi
shift # 消耗文件参数 shift
;; ;;
esac esac
done done
if ${CLEAN_MODE}; then if ${CLEAN_MODE}; then
echo "检测到 -c/--clean 选项,正在清空 ${TMP_DIR}..." echo "检测到 -c/--clean 选项,正在清空 ${TMP_DIR}..."
if [ -d "${TMP_DIR}" ]; then if [ -d "${TMP_DIR}" ]; then
@@ -127,19 +152,22 @@ if ${CLEAN_MODE}; then
else else
echo "临时目录 ${TMP_DIR} 不存在,无需清理。" echo "临时目录 ${TMP_DIR} 不存在,无需清理。"
fi fi
if [ ${#SY_FILES[@]} -eq 0 ] && ! ${EXECUTE_MODE} && ! ${IR_EXECUTE_MODE}; then
if [ ${#SY_FILES[@]} -eq 0 ] && ! ${EXECUTE_MODE}; then
exit 0 exit 0
fi fi
fi fi
# --- 主逻辑开始 --- if ! ${EXECUTE_MODE} && ! ${IR_EXECUTE_MODE}; then
if ! ${EXECUTE_MODE}; then echo "错误: 请提供 -e 或 -eir 选项来运行测试。"
echo "错误: 请提供 -e 或 --executable 选项来运行测试。"
show_help show_help
exit 1 exit 1
fi fi
if ${EXECUTE_MODE} && ${IR_EXECUTE_MODE}; then
echo -e "\e[31m错误: -e 和 -eir 选项不能同时使用。\e[0m" >&2
exit 1
fi
if [ ${#SY_FILES[@]} -eq 0 ]; then if [ ${#SY_FILES[@]} -eq 0 ]; then
echo "错误: 未提供任何 .sy 文件作为输入。" echo "错误: 未提供任何 .sy 文件作为输入。"
show_help show_help
@@ -151,18 +179,17 @@ TOTAL_CASES=${#SY_FILES[@]}
echo "SysY 单例测试运行器启动..." echo "SysY 单例测试运行器启动..."
if [ -n "$OPTIMIZE_FLAG" ]; then echo "优化等级: ${OPTIMIZE_FLAG}"; fi if [ -n "$OPTIMIZE_FLAG" ]; then echo "优化等级: ${OPTIMIZE_FLAG}"; fi
echo "超时设置: sysyc=${SYSYC_TIMEOUT}s, gcc=${GCC_TIMEOUT}s, qemu=${EXEC_TIMEOUT}s" echo "超时设置: sysyc=${SYSYC_TIMEOUT}s, llc=${LLC_TIMEOUT}s, gcc=${GCC_TIMEOUT}s, qemu=${EXEC_TIMEOUT}s"
echo "失败输出最大行数: ${MAX_OUTPUT_LINES}"
echo "" echo ""
for sy_file in "${SY_FILES[@]}"; do for sy_file in "${SY_FILES[@]}"; do
is_passed=1 is_passed=1
compilation_ok=1
base_name=$(basename "${sy_file}" .sy) base_name=$(basename "${sy_file}" .sy)
source_dir=$(dirname "${sy_file}") source_dir=$(dirname "${sy_file}")
ir_file="${TMP_DIR}/${base_name}_sysyc_riscv64.ll" ir_file="${TMP_DIR}/${base_name}.ll"
assembly_file="${TMP_DIR}/${base_name}.s" assembly_file="${TMP_DIR}/${base_name}.s"
assembly_debug_file="${TMP_DIR}/${base_name}_d.s"
executable_file="${TMP_DIR}/${base_name}" executable_file="${TMP_DIR}/${base_name}"
input_file="${source_dir}/${base_name}.in" input_file="${source_dir}/${base_name}.in"
output_reference_file="${source_dir}/${base_name}.out" output_reference_file="${source_dir}/${base_name}.out"
@@ -171,47 +198,39 @@ for sy_file in "${SY_FILES[@]}"; do
echo "======================================================================" echo "======================================================================"
echo "正在处理: ${sy_file}" echo "正在处理: ${sy_file}"
# --- 本次修改点: 拷贝源文件到 tmp 目录 --- # --- 编译阶段 ---
echo " 拷贝源文件到 ${TMP_DIR}..." if ${IR_EXECUTE_MODE}; then
cp "${sy_file}" "${TMP_DIR}/$(basename "${sy_file}")" # 路径1: sysyc -> llc -> gcc
if [ -f "${input_file}" ]; then echo " [1/3] 使用 sysyc 编译为 IR (超时 ${SYSYC_TIMEOUT}s)..."
cp "${input_file}" "${TMP_DIR}/$(basename "${input_file}")" timeout -s KILL ${SYSYC_TIMEOUT} "${SYSYC}" -s ir "${sy_file}" ${OPTIMIZE_FLAG} -o "${ir_file}"
fi if [ $? -ne 0 ]; then echo -e "\e[31m错误: SysY (IR) 编译失败或超时。\e[0m"; compilation_ok=0; fi
if [ -f "${output_reference_file}" ]; then
cp "${output_reference_file}" "${TMP_DIR}/$(basename "${output_reference_file}")" if [ "$compilation_ok" -eq 1 ]; then
echo " [2/3] 使用 llc 编译为汇编 (超时 ${LLC_TIMEOUT}s)..."
timeout -s KILL ${LLC_TIMEOUT} "${LLC_CMD}" -march=riscv64 -mcpu=generic-rv64 -mattr=+m,+a,+f,+d,+c -filetype=asm "${ir_file}" -o "${assembly_file}"
if [ $? -ne 0 ]; then echo -e "\e[31m错误: llc 编译失败或超时。\e[0m"; compilation_ok=0; fi
fi fi
# 步骤 1: sysyc 编译 if [ "$compilation_ok" -eq 1 ]; then
echo " 使用 sysyc 编译 (超时 ${SYSYC_TIMEOUT}s)..." echo " [3/3] 使用 gcc 编译 (超时 ${GCC_TIMEOUT}s)..."
timeout -s KILL ${SYSYC_TIMEOUT} "${SYSYC}" -S "${sy_file}" ${OPTIMIZE_FLAG} -o "${assembly_file}"
timeout -s KILL ${SYSYC_TIMEOUT} "${SYSYC}" -s ir "${sy_file}" ${OPTIMIZE_FLAG} > "${ir_file}"
# timeout -s KILL ${SYSYC_TIMEOUT} "${SYSYC}" -s asmd "${sy_file}" > "${assembly_debug_file}" 2>&1
SYSYC_STATUS=$?
if [ $SYSYC_STATUS -eq 124 ]; then
echo -e "\e[31m错误: SysY 编译 ${sy_file} IR超时\e[0m"
is_passed=0
elif [ $SYSYC_STATUS -ne 0 ]; then
echo -e "\e[31m错误: SysY 编译 ${sy_file} IR失败退出码: ${SYSYC_STATUS}\e[0m"
is_passed=0
fi
if [ $? -ne 0 ]; then
echo -e "\e[31m错误: SysY 编译失败或超时。\e[0m"
is_passed=0
fi
# 步骤 2: GCC 编译
if [ "$is_passed" -eq 1 ]; then
echo " 使用 gcc 编译 (超时 ${GCC_TIMEOUT}s)..."
timeout -s KILL ${GCC_TIMEOUT} "${GCC_RISCV64}" "${assembly_file}" -o "${executable_file}" -L"${LIB_DIR}" -lsysy_riscv -static timeout -s KILL ${GCC_TIMEOUT} "${GCC_RISCV64}" "${assembly_file}" -o "${executable_file}" -L"${LIB_DIR}" -lsysy_riscv -static
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then echo -e "\e[31m错误: GCC 编译失败或超时。\e[0m"; compilation_ok=0; fi
echo -e "\e[31m错误: GCC 编译失败或超时。\e[0m" fi
is_passed=0 else # EXECUTE_MODE
# 路径2: sysyc -> gcc
echo " [1/2] 使用 sysyc 编译为汇编 (超时 ${SYSYC_TIMEOUT}s)..."
timeout -s KILL ${SYSYC_TIMEOUT} "${SYSYC}" -S "${sy_file}" ${OPTIMIZE_FLAG} -o "${assembly_file}"
if [ $? -ne 0 ]; then echo -e "\e[31m错误: SysY (汇编) 编译失败或超时。\e[0m"; compilation_ok=0; fi
if [ "$compilation_ok" -eq 1 ]; then
echo " [2/2] 使用 gcc 编译 (超时 ${GCC_TIMEOUT}s)..."
timeout -s KILL ${GCC_TIMEOUT} "${GCC_RISCV64}" "${assembly_file}" -o "${executable_file}" -L"${LIB_DIR}" -lsysy_riscv -static
if [ $? -ne 0 ]; then echo -e "\e[31m错误: GCC 编译失败或超时。\e[0m"; compilation_ok=0; fi
fi fi
fi fi
# 步骤 3: 执行与测试 # --- 执行与测试阶段 (公共逻辑) ---
if [ "$is_passed" -eq 1 ]; then if [ "$compilation_ok" -eq 1 ]; then
# 检查是自动化测试还是交互模式
if [ -f "${input_file}" ] || [ -f "${output_reference_file}" ]; then if [ -f "${input_file}" ] || [ -f "${output_reference_file}" ]; then
# --- 自动化测试模式 --- # --- 自动化测试模式 ---
echo " 检测到 .in/.out 文件,进入自动化测试模式..." echo " 检测到 .in/.out 文件,进入自动化测试模式..."
@@ -234,24 +253,26 @@ for sy_file in "${SY_FILES[@]}"; do
EXPECTED_RETURN_CODE="$LAST_LINE_TRIMMED" EXPECTED_RETURN_CODE="$LAST_LINE_TRIMMED"
EXPECTED_STDOUT_FILE="${TMP_DIR}/${base_name}.expected_stdout" EXPECTED_STDOUT_FILE="${TMP_DIR}/${base_name}.expected_stdout"
head -n -1 "${output_reference_file}" > "${EXPECTED_STDOUT_FILE}" head -n -1 "${output_reference_file}" > "${EXPECTED_STDOUT_FILE}"
if [ "$ACTUAL_RETURN_CODE" -ne "$EXPECTED_RETURN_CODE" ]; then echo -e "\e[31m 返回码测试失败: 期望 ${EXPECTED_RETURN_CODE}, 实际 ${ACTUAL_RETURN_CODE}\e[0m"; is_passed=0; fi
ret_ok=1
if [ "$ACTUAL_RETURN_CODE" -ne "$EXPECTED_RETURN_CODE" ]; then echo -e "\e[31m 返回码测试失败: 期望 ${EXPECTED_RETURN_CODE}, 实际 ${ACTUAL_RETURN_CODE}\e[0m"; ret_ok=0; fi
out_ok=1
if ! diff -q <(tr -d '[:space:]' < "${output_actual_file}") <(tr -d '[:space:]' < "${EXPECTED_STDOUT_FILE}") >/dev/null 2>&1; then if ! diff -q <(tr -d '[:space:]' < "${output_actual_file}") <(tr -d '[:space:]' < "${EXPECTED_STDOUT_FILE}") >/dev/null 2>&1; then
echo -e "\e[31m 标准输出测试失败。\e[0m" echo -e "\e[31m 标准输出测试失败。\e[0m"; out_ok=0
is_passed=0
display_file_content "${EXPECTED_STDOUT_FILE}" " \e[36m--- 期望输出 ---\e[0m" "${MAX_OUTPUT_LINES}" display_file_content "${EXPECTED_STDOUT_FILE}" " \e[36m--- 期望输出 ---\e[0m" "${MAX_OUTPUT_LINES}"
display_file_content "${output_actual_file}" " \e[36m--- 实际输出 ---\e[0m" "${MAX_OUTPUT_LINES}" display_file_content "${output_actual_file}" " \e[36m--- 实际输出 ---\e[0m" "${MAX_OUTPUT_LINES}"
echo -e " \e[36m----------------\e[0m"
fi fi
if [ "$ret_ok" -eq 1 ] && [ "$out_ok" -eq 1 ]; then echo -e "\e[32m 返回码与标准输出测试成功。\e[0m"; else is_passed=0; fi
else else
if diff -q <(tr -d '[:space:]' < "${output_actual_file}") <(tr -d '[:space:]' < "${output_reference_file}") >/dev/null 2>&1; then if diff -q <(tr -d '[:space:]' < "${output_actual_file}") <(tr -d '[:space:]' < "${output_reference_file}") >/dev/null 2>&1; then
echo -e "\e[32m 标准输出测试成功。\e[0m" echo -e "\e[32m 标准输出测试成功。\e[0m"
else else
echo -e "\e[31m 标准输出测试失败。\e[0m" echo -e "\e[31m 标准输出测试失败。\e[0m"; is_passed=0
is_passed=0
display_file_content "${output_reference_file}" " \e[36m--- 期望输出 ---\e[0m" "${MAX_OUTPUT_LINES}" display_file_content "${output_reference_file}" " \e[36m--- 期望输出 ---\e[0m" "${MAX_OUTPUT_LINES}"
display_file_content "${output_actual_file}" " \e[36m--- 实际输出 ---\e[0m" "${MAX_OUTPUT_LINES}" display_file_content "${output_actual_file}" " \e[36m--- 实际输出 ---\e[0m" "${MAX_OUTPUT_LINES}"
echo -e " \e[36m----------------\e[0m"
fi fi
fi fi
else else
@@ -260,20 +281,16 @@ for sy_file in "${SY_FILES[@]}"; do
fi fi
else else
# --- 交互模式 --- # --- 交互模式 ---
echo -e "\e[33m" echo -e "\e[33m\n 未找到 .in 或 .out 文件,进入交互模式...\e[0m"
echo " **********************************************************"
echo " ** 未找到 .in 或 .out 文件,进入交互模式。 **"
echo " ** 程序即将运行,你可以直接在终端中输入。 **"
echo " ** 按下 Ctrl+D (EOF) 或以其他方式结束程序以继续。 **"
echo " **********************************************************"
echo -e "\e[0m"
"${QEMU_RISCV64}" "${executable_file}" "${QEMU_RISCV64}" "${executable_file}"
INTERACTIVE_RET_CODE=$? INTERACTIVE_RET_CODE=$?
echo -e "\e[33m\n 交互模式执行完毕,程序返回码: ${INTERACTIVE_RET_CODE}\e[0m" echo -e "\e[33m\n 交互模式执行完毕,程序返回码: ${INTERACTIVE_RET_CODE} (此结果未经验证)\e[0m"
echo " 注意: 交互模式的结果未经验证。"
fi fi
else
is_passed=0
fi fi
# --- 状态总结 ---
if [ "$is_passed" -eq 1 ]; then if [ "$is_passed" -eq 1 ]; then
echo -e "\e[32m状态: 通过\e[0m" echo -e "\e[32m状态: 通过\e[0m"
((PASSED_CASES++)) ((PASSED_CASES++))
@@ -284,20 +301,4 @@ for sy_file in "${SY_FILES[@]}"; do
done done
# --- 打印最终总结 --- # --- 打印最终总结 ---
echo "======================================================================" print_summary
echo "所有测试完成"
echo "测试通过率: [${PASSED_CASES}/${TOTAL_CASES}]"
if [ -n "$FAILED_CASES_LIST" ]; then
echo ""
echo -e "\e[31m未通过的测例:\e[0m"
echo -e "${FAILED_CASES_LIST}"
fi
echo "======================================================================"
if [ "$PASSED_CASES" -eq "$TOTAL_CASES" ]; then
exit 0
else
exit 1
fi

View File

@@ -1,31 +1,41 @@
#!/bin/bash #!/bin/bash
# runit.sh - 用于编译和测试 SysY 程序的脚本 # runit.sh - 用于编译和测试 SysY 程序的脚本
# 此脚本应该位于 mysysy/test_script/ # 此脚本应该位于 mysysy/script/
export ASAN_OPTIONS=detect_leaks=0
# 定义相对于脚本位置的目录 # 定义相对于脚本位置的目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
TESTDATA_DIR="${SCRIPT_DIR}/../testdata" TESTDATA_DIR="${SCRIPT_DIR}/../testdata"
BUILD_BIN_DIR="${SCRIPT_DIR}/../build/bin" BUILD_BIN_DIR="${SCRIPT_DIR}/../build/bin"
LIB_DIR="${SCRIPT_DIR}/../lib" LIB_DIR="${SCRIPT_DIR}/../lib"
# TMP_DIR="${SCRIPT_DIR}/tmp"
TMP_DIR="${SCRIPT_DIR}/tmp" TMP_DIR="${SCRIPT_DIR}/tmp"
# 定义编译器和模拟器 # 定义编译器和模拟器
SYSYC="${BUILD_BIN_DIR}/sysyc" SYSYC="${BUILD_BIN_DIR}/sysyc"
LLC_CMD="llc-19"
GCC_RISCV64="riscv64-linux-gnu-gcc" GCC_RISCV64="riscv64-linux-gnu-gcc"
QEMU_RISCV64="qemu-riscv64" QEMU_RISCV64="qemu-riscv64"
# --- 状态变量 ---
EXECUTE_MODE=false EXECUTE_MODE=false
OPTIMIZE_FLAG="" # 用于存储 -O1 标志 IR_EXECUTE_MODE=false
SYSYC_TIMEOUT=10 # sysyc 编译超时 (秒) OPTIMIZE_FLAG=""
GCC_TIMEOUT=10 # gcc 编译超时 (秒) SYSYC_TIMEOUT=30
EXEC_TIMEOUT=5 # qemu 执行超时 (秒) LLC_TIMEOUT=10
MAX_OUTPUT_LINES=50 # 对比失败时显示的最大行数 GCC_TIMEOUT=10
TEST_SETS=() # 用于存储要运行的测试集 EXEC_TIMEOUT=30
MAX_OUTPUT_LINES=20
TEST_SETS=()
TOTAL_CASES=0 TOTAL_CASES=0
PASSED_CASES=0 PASSED_CASES=0
FAILED_CASES_LIST="" # 用于存储未通过的测例列表 FAILED_CASES_LIST=""
INTERRUPTED=false # 新增:用于标记是否被中断
# =================================================================
# --- 函数定义 ---
# =================================================================
# 显示帮助信息的函数 # 显示帮助信息的函数
show_help() { show_help() {
@@ -33,31 +43,32 @@ show_help() {
echo "此脚本用于按文件名前缀数字升序编译和测试 .sy 文件。" echo "此脚本用于按文件名前缀数字升序编译和测试 .sy 文件。"
echo "" echo ""
echo "选项:" echo "选项:"
echo " -e, --executable 编译为可执行文件并运行测试。" echo " -e, --executable 编译为汇编并运行测试 (sysyc -> gcc -> qemu)。"
echo " -eir 通过IR编译为可执行文件并运行测试 (sysyc -> llc -> gcc -> qemu)。"
echo " -c, --clean 清理 'tmp' 目录下的所有生成文件。" echo " -c, --clean 清理 'tmp' 目录下的所有生成文件。"
echo " -O1 启用 sysyc 的 -O1 优化。" echo " -O1 启用 sysyc 的 -O1 优化。"
echo " -set [f|h|p|all]... 指定要运行的测试集 (functional, h_functional, performance)。可多选,默认为 all。" echo " -set [f|h|p|all]... 指定要运行的测试集 (functional, h_functional, performance)。可多选,默认为 all。"
echo " -sct N 设置 sysyc 编译超时为 N 秒 (默认: 10)。" echo " -sct N 设置 sysyc 编译超时为 N 秒 (默认: 30)。"
echo " -lct N 设置 llc-19 编译超时为 N 秒 (默认: 10)。"
echo " -gct N 设置 gcc 交叉编译超时为 N 秒 (默认: 10)。" echo " -gct N 设置 gcc 交叉编译超时为 N 秒 (默认: 10)。"
echo " -et N 设置 qemu 执行超时为 N 秒 (默认: 5)。" echo " -et N 设置 qemu 执行超时为 N 秒 (默认: 30)。"
echo " -ml N, --max-lines N 当输出对比失败时,最多显示 N 行内容 (默认: 50)。" echo " -ml N, --max-lines N 当输出对比失败时,最多显示 N 行内容 (默认: 20)。"
echo " -h, --help 显示此帮助信息并退出。" echo " -h, --help 显示此帮助信息并退出。"
echo ""
echo "注意: 默认行为 (无 -e 或 -eir) 是将 .sy 文件同时编译为 .s (汇编) 和 .ll (IR),不执行。"
echo " 可在任何时候按 Ctrl+C 来中断测试并显示当前已完成的测例总结。"
} }
# 显示文件内容并根据行数截断的函数 # 显示文件内容并根据行数截断的函数
display_file_content() { display_file_content() {
local file_path="$1" local file_path="$1"
local title="$2" local title="$2"
local max_lines="$3" local max_lines="$3"
if [ ! -f "$file_path" ]; then return; fi
if [ ! -f "$file_path" ]; then
return
fi
echo -e "$title" echo -e "$title"
local line_count local line_count
line_count=$(wc -l < "$file_path") line_count=$(wc -l < "$file_path")
if [ "$line_count" -gt "$max_lines" ]; then if [ "$line_count" -gt "$max_lines" ]; then
head -n "$max_lines" "$file_path" head -n "$max_lines" "$file_path"
echo -e "\e[33m[... 输出已截断,共 ${line_count} 行 ...]\e[0m" echo -e "\e[33m[... 输出已截断,共 ${line_count} 行 ...]\e[0m"
@@ -72,63 +83,90 @@ clean_tmp() {
rm -rf "${TMP_DIR}"/* rm -rf "${TMP_DIR}"/*
} }
# 如果临时目录不存在,则创建它 # --- 新增:总结报告函数 ---
print_summary() {
echo "" # 确保从新的一行开始
echo "========================================"
if [ "$INTERRUPTED" = true ]; then
echo -e "\e[33m测试被中断。正在汇总已完成的结果...\e[0m"
else
echo "测试完成"
fi
local failed_count
if [ -n "$FAILED_CASES_LIST" ]; then
# `wc -l` 计算由换行符分隔的列表项数
failed_count=$(echo -e -n "${FAILED_CASES_LIST}" | wc -l)
else
failed_count=0
fi
local executed_count=$((PASSED_CASES + failed_count))
echo "测试结果: [通过: ${PASSED_CASES}, 失败: ${failed_count}, 已执行: ${executed_count}/${TOTAL_CASES}]"
if [ -n "$FAILED_CASES_LIST" ]; then
echo ""
echo -e "\e[31m未通过的测例:\e[0m"
# 使用 printf 保证原样输出
printf "%b" "${FAILED_CASES_LIST}"
fi
echo "========================================"
if [ "$failed_count" -gt 0 ]; then
exit 1
else
exit 0
fi
}
# --- 新增SIGINT 信号处理函数 ---
handle_sigint() {
INTERRUPTED=true
print_summary
}
# =================================================================
# --- 主逻辑开始 ---
# =================================================================
# --- 新增:设置 trap 来捕获 SIGINT ---
trap handle_sigint SIGINT
mkdir -p "${TMP_DIR}" mkdir -p "${TMP_DIR}"
# 解析命令行参数 # 解析命令行参数
while [[ "$#" -gt 0 ]]; do while [[ "$#" -gt 0 ]]; do
case "$1" in case "$1" in
-e|--executable) -e|--executable) EXECUTE_MODE=true; shift ;;
EXECUTE_MODE=true -eir) IR_EXECUTE_MODE=true; shift ;;
shift -c|--clean) clean_tmp; exit 0 ;;
;; -O1) OPTIMIZE_FLAG="-O1"; shift ;;
-c|--clean)
clean_tmp
exit 0
;;
-O1)
OPTIMIZE_FLAG="-O1"
shift
;;
-set) -set)
shift # 移过 '-set'
while [[ "$#" -gt 0 && ! "$1" =~ ^- ]]; do
TEST_SETS+=("$1")
shift shift
done while [[ "$#" -gt 0 && ! "$1" =~ ^- ]]; do TEST_SETS+=("$1"); shift; done
;;
-sct)
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then SYSYC_TIMEOUT="$2"; shift 2; else echo "错误: -sct 需要一个正整数参数。" >&2; exit 1; fi
;;
-gct)
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then GCC_TIMEOUT="$2"; shift 2; else echo "错误: -gct 需要一个正整数参数。" >&2; exit 1; fi
;;
-et)
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then EXEC_TIMEOUT="$2"; shift 2; else echo "错误: -et 需要一个正整数参数。" >&2; exit 1; fi
;;
-ml|--max-lines)
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then MAX_OUTPUT_LINES="$2"; shift 2; else echo "错误: --max-lines 需要一个正整数参数。" >&2; exit 1; fi
;;
-h|--help)
show_help
exit 0
;;
*)
echo "未知选项: $1"
show_help
exit 1
;; ;;
-sct) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then SYSYC_TIMEOUT="$2"; shift 2; else echo "错误: -sct 需要一个正整数参数。" >&2; exit 1; fi ;;
-lct) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then LLC_TIMEOUT="$2"; shift 2; else echo "错误: -lct 需要一个正整数参数。" >&2; exit 1; fi ;;
-gct) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then GCC_TIMEOUT="$2"; shift 2; else echo "错误: -gct 需要一个正整数参数。" >&2; exit 1; fi ;;
-et) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then EXEC_TIMEOUT="$2"; shift 2; else echo "错误: -et 需要一个正整数参数。" >&2; exit 1; fi ;;
-ml|--max-lines) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then MAX_OUTPUT_LINES="$2"; shift 2; else echo "错误: --max-lines 需要一个正整数参数。" >&2; exit 1; fi ;;
-h|--help) show_help; exit 0 ;;
*) echo "未知选项: $1"; show_help; exit 1 ;;
esac esac
done done
# --- 本次修改点: 根据 -set 参数构建查找路径 --- if ${EXECUTE_MODE} && ${IR_EXECUTE_MODE}; then
echo -e "\e[31m错误: -e 和 -eir 选项不能同时使用。\e[0m" >&2
exit 1
fi
declare -A SET_MAP declare -A SET_MAP
SET_MAP[f]="functional" SET_MAP[f]="functional"
SET_MAP[h]="h_functional" SET_MAP[h]="h_functional"
SET_MAP[p]="performance" SET_MAP[p]="performance"
SEARCH_PATHS=() SEARCH_PATHS=()
if [ ${#TEST_SETS[@]} -eq 0 ] || [[ " ${TEST_SETS[@]} " =~ " all " ]]; then if [ ${#TEST_SETS[@]} -eq 0 ] || [[ " ${TEST_SETS[@]} " =~ " all " ]]; then
SEARCH_PATHS+=("${TESTDATA_DIR}") SEARCH_PATHS+=("${TESTDATA_DIR}")
else else
@@ -150,9 +188,21 @@ echo "SysY 测试运行器启动..."
if [ -n "$OPTIMIZE_FLAG" ]; then echo "优化等级: ${OPTIMIZE_FLAG}"; fi if [ -n "$OPTIMIZE_FLAG" ]; then echo "优化等级: ${OPTIMIZE_FLAG}"; fi
echo "输入目录: ${SEARCH_PATHS[@]}" echo "输入目录: ${SEARCH_PATHS[@]}"
echo "临时目录: ${TMP_DIR}" echo "临时目录: ${TMP_DIR}"
echo "执行模式: ${EXECUTE_MODE}"
if ${EXECUTE_MODE}; then RUN_MODE_INFO=""
echo "超时设置: sysyc=${SYSYC_TIMEOUT}s, gcc=${GCC_TIMEOUT}s, qemu=${EXEC_TIMEOUT}s" if ${IR_EXECUTE_MODE}; then
RUN_MODE_INFO="IR执行模式 (-eir)"
TIMEOUT_INFO="超时设置: sysyc=${SYSYC_TIMEOUT}s, llc=${LLC_TIMEOUT}s, gcc=${GCC_TIMEOUT}s, qemu=${EXEC_TIMEOUT}s"
elif ${EXECUTE_MODE}; then
RUN_MODE_INFO="直接执行模式 (-e)"
TIMEOUT_INFO="超时设置: sysyc=${SYSYC_TIMEOUT}s, gcc=${GCC_TIMEOUT}s, qemu=${EXEC_TIMEOUT}s"
else
RUN_MODE_INFO="编译模式 (默认)"
TIMEOUT_INFO="超时设置: sysyc=${SYSYC_TIMEOUT}s"
fi
echo "运行模式: ${RUN_MODE_INFO}"
echo "${TIMEOUT_INFO}"
if ${EXECUTE_MODE} || ${IR_EXECUTE_MODE}; then
echo "失败输出最大行数: ${MAX_OUTPUT_LINES}" echo "失败输出最大行数: ${MAX_OUTPUT_LINES}"
fi fi
echo "" echo ""
@@ -165,132 +215,228 @@ fi
TOTAL_CASES=$(echo "$sy_files" | wc -w) TOTAL_CASES=$(echo "$sy_files" | wc -w)
while IFS= read -r sy_file; do while IFS= read -r sy_file; do
is_passed=1 # 1 表示通过, 0 表示失败 is_passed=0 # 0 表示失败, 1 表示通过
relative_path_no_ext=$(realpath --relative-to="${TESTDATA_DIR}" "${sy_file%.*}") relative_path_no_ext=$(realpath --relative-to="${TESTDATA_DIR}" "${sy_file%.*}")
output_base_name=$(echo "${relative_path_no_ext}" | tr '/' '_') output_base_name=$(echo "${relative_path_no_ext}" | tr '/' '_')
assembly_file="${TMP_DIR}/${output_base_name}_sysyc_riscv64.s" assembly_file_S="${TMP_DIR}/${output_base_name}_sysyc_S.s"
executable_file="${TMP_DIR}/${output_base_name}_sysyc_riscv64" executable_file_S="${TMP_DIR}/${output_base_name}_sysyc_S"
output_actual_file_S="${TMP_DIR}/${output_base_name}_sysyc_S.actual_out"
ir_file="${TMP_DIR}/${output_base_name}_sysyc_ir.ll"
assembly_file_from_ir="${TMP_DIR}/${output_base_name}_from_ir.s"
executable_file_from_ir="${TMP_DIR}/${output_base_name}_from_ir"
output_actual_file_from_ir="${TMP_DIR}/${output_base_name}_from_ir.actual_out"
input_file="${sy_file%.*}.in" input_file="${sy_file%.*}.in"
output_reference_file="${sy_file%.*}.out" output_reference_file="${sy_file%.*}.out"
output_actual_file="${TMP_DIR}/${output_base_name}_sysyc_riscv64.actual_out"
echo "正在处理: $(basename "$sy_file") (路径: ${relative_path_no_ext}.sy)" echo "正在处理: $(basename "$sy_file") (路径: ${relative_path_no_ext}.sy)"
echo " 使用 sysyc 编译 (超时 ${SYSYC_TIMEOUT}s)..."
timeout -s KILL ${SYSYC_TIMEOUT} "${SYSYC}" -S "${sy_file}" -o "${assembly_file}" ${OPTIMIZE_FLAG} # --- 模式 1: IR 执行模式 (-eir) ---
if ${IR_EXECUTE_MODE}; then
step_failed=0
test_logic_passed=0
echo " [1/4] 使用 sysyc 编译为 IR (超时 ${SYSYC_TIMEOUT}s)..."
timeout -s KILL ${SYSYC_TIMEOUT} "${SYSYC}" -s ir "${sy_file}" -o "${ir_file}" ${OPTIMIZE_FLAG}
SYSYC_STATUS=$? SYSYC_STATUS=$?
if [ $SYSYC_STATUS -eq 124 ]; then if [ $SYSYC_STATUS -ne 0 ]; then
echo -e "\e[31m错误: SysY 编译 ${sy_file} 超时\e[0m" [ $SYSYC_STATUS -eq 124 ] && echo -e "\e[31m错误: SysY (IR) 编译超时\e[0m" || echo -e "\e[31m错误: SysY (IR) 编译失败,退出码: ${SYSYC_STATUS}\e[0m"
is_passed=0 step_failed=1
elif [ $SYSYC_STATUS -ne 0 ]; then
echo -e "\e[31m错误: SysY 编译 ${sy_file} 失败,退出码: ${SYSYC_STATUS}\e[0m"
is_passed=0
fi fi
if ${EXECUTE_MODE} && [ "$is_passed" -eq 1 ]; then if [ "$step_failed" -eq 0 ]; then
echo " 使用 gcc 编译 (超时 ${GCC_TIMEOUT}s)..." echo " [2/4] 使用 llc-19 编译为汇编 (超时 ${LLC_TIMEOUT}s)..."
timeout -s KILL ${GCC_TIMEOUT} "${GCC_RISCV64}" "${assembly_file}" -o "${executable_file}" -L"${LIB_DIR}" -lsysy_riscv -static timeout -s KILL ${LLC_TIMEOUT} "${LLC_CMD}" -march=riscv64 -mcpu=generic-rv64 -mattr=+m,+a,+f,+d,+c -filetype=asm "${ir_file}" -o "${assembly_file_from_ir}"
LLC_STATUS=$?
if [ $LLC_STATUS -ne 0 ]; then
[ $LLC_STATUS -eq 124 ] && echo -e "\e[31m错误: llc-19 编译超时\e[0m" || echo -e "\e[31m错误: llc-19 编译失败,退出码: ${LLC_STATUS}\e[0m"
step_failed=1
fi
fi
if [ "$step_failed" -eq 0 ]; then
echo " [3/4] 使用 gcc 编译 (超时 ${GCC_TIMEOUT}s)..."
timeout -s KILL ${GCC_TIMEOUT} "${GCC_RISCV64}" "${assembly_file_from_ir}" -o "${executable_file_from_ir}" -L"${LIB_DIR}" -lsysy_riscv -static
GCC_STATUS=$? GCC_STATUS=$?
if [ $GCC_STATUS -eq 124 ]; then if [ $GCC_STATUS -ne 0 ]; then
echo -e "\e[31m错误: GCC 编译 ${assembly_file} 超时\e[0m" [ $GCC_STATUS -eq 124 ] && echo -e "\e[31m错误: GCC 编译超时\e[0m" || echo -e "\e[31m错误: GCC 编译失败,退出码: ${GCC_STATUS}\e[0m"
is_passed=0 step_failed=1
elif [ $GCC_STATUS -ne 0 ]; then
echo -e "\e[31m错误: GCC 编译 ${assembly_file} 失败,退出码: ${GCC_STATUS}\e[0m"
is_passed=0
fi fi
elif ! ${EXECUTE_MODE}; then
echo " 跳过执行模式。仅生成汇编文件。"
if [ "$is_passed" -eq 1 ]; then
((PASSED_CASES++))
else
FAILED_CASES_LIST+="${relative_path_no_ext}.sy\n"
fi
echo ""
continue
fi fi
if [ "$is_passed" -eq 1 ]; then if [ "$step_failed" -eq 0 ]; then
echo " 正在执行 (超时 ${EXEC_TIMEOUT}s)..." echo " [4/4] 正在执行 (超时 ${EXEC_TIMEOUT}s)..."
exec_cmd="${QEMU_RISCV64} \"${executable_file_from_ir}\""
exec_cmd="${QEMU_RISCV64} \"${executable_file}\"" [ -f "${input_file}" ] && exec_cmd+=" < \"${input_file}\""
if [ -f "${input_file}" ]; then exec_cmd+=" > \"${output_actual_file_from_ir}\""
exec_cmd+=" < \"${input_file}\""
fi
exec_cmd+=" > \"${output_actual_file}\""
eval "timeout -s KILL ${EXEC_TIMEOUT} ${exec_cmd}" eval "timeout -s KILL ${EXEC_TIMEOUT} ${exec_cmd}"
ACTUAL_RETURN_CODE=$? ACTUAL_RETURN_CODE=$?
if [ "$ACTUAL_RETURN_CODE" -eq 124 ]; then if [ "$ACTUAL_RETURN_CODE" -eq 124 ]; then
echo -e "\e[31m 执行超时: ${sy_file} 运行超过 ${EXEC_TIMEOUT} 秒\e[0m" echo -e "\e[31m 执行超时: 运行超过 ${EXEC_TIMEOUT} 秒\e[0m"
is_passed=0
else else
if [ -f "${output_reference_file}" ]; then if [ -f "${output_reference_file}" ]; then
LAST_LINE_TRIMMED=$(tail -n 1 "${output_reference_file}" | tr -d '[:space:]') LAST_LINE_TRIMMED=$(tail -n 1 "${output_reference_file}" | tr -d '[:space:]')
test_logic_passed=1
if [[ "$LAST_LINE_TRIMMED" =~ ^[-+]?[0-9]+$ ]]; then if [[ "$LAST_LINE_TRIMMED" =~ ^[-+]?[0-9]+$ ]]; then
EXPECTED_RETURN_CODE="$LAST_LINE_TRIMMED" EXPECTED_RETURN_CODE="$LAST_LINE_TRIMMED"
EXPECTED_STDOUT_FILE="${TMP_DIR}/${output_base_name}_sysyc_riscv64.expected_stdout" EXPECTED_STDOUT_FILE="${TMP_DIR}/${output_base_name}_from_ir.expected_stdout"
head -n -1 "${output_reference_file}" > "${EXPECTED_STDOUT_FILE}" head -n -1 "${output_reference_file}" > "${EXPECTED_STDOUT_FILE}"
if [ "$ACTUAL_RETURN_CODE" -eq "$EXPECTED_RETURN_CODE" ]; then if [ "$ACTUAL_RETURN_CODE" -eq "$EXPECTED_RETURN_CODE" ]; then
echo -e "\e[32m 返回码测试成功: (${ACTUAL_RETURN_CODE}) 与期望值 (${EXPECTED_RETURN_CODE}) 匹配\e[0m" echo -e "\e[32m 返回码测试成功: (${ACTUAL_RETURN_CODE}) 与期望值 (${EXPECTED_RETURN_CODE}) 匹配\e[0m"
else else
echo -e "\e[31m 返回码测试失败: 期望: ${EXPECTED_RETURN_CODE}, 实际: ${ACTUAL_RETURN_CODE}\e[0m" echo -e "\e[31m 返回码测试失败: 期望: ${EXPECTED_RETURN_CODE}, 实际: ${ACTUAL_RETURN_CODE}\e[0m"
is_passed=0 test_logic_passed=0
fi fi
if ! diff -q <(tr -d '[:space:]' < "${output_actual_file}") <(tr -d '[:space:]' < "${EXPECTED_STDOUT_FILE}") >/dev/null 2>&1; then if diff -q <(tr -d '[:space:]' < "${output_actual_file_from_ir}") <(tr -d '[:space:]' < "${EXPECTED_STDOUT_FILE}") >/dev/null 2>&1; then
[ "$test_logic_passed" -eq 1 ] && echo -e "\e[32m 标准输出测试成功\e[0m"
else
echo -e "\e[31m 标准输出测试失败\e[0m" echo -e "\e[31m 标准输出测试失败\e[0m"
is_passed=0
display_file_content "${EXPECTED_STDOUT_FILE}" " \e[36m---------- 期望输出 ----------\e[0m" "${MAX_OUTPUT_LINES}" display_file_content "${EXPECTED_STDOUT_FILE}" " \e[36m---------- 期望输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
display_file_content "${output_actual_file}" " \e[36m---------- 实际输出 ----------\e[0m" "${MAX_OUTPUT_LINES}" display_file_content "${output_actual_file_from_ir}" " \e[36m---------- 实际输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
echo -e " \e[36m------------------------------\e[0m" test_logic_passed=0
fi fi
else else
if [ $ACTUAL_RETURN_CODE -ne 0 ]; then if [ $ACTUAL_RETURN_CODE -ne 0 ]; then echo -e "\e[33m警告: 程序以非零状态 ${ACTUAL_RETURN_CODE} 退出 (纯输出比较模式)。\e[0m"; fi
echo -e "\e[33m警告: 程序以非零状态 ${ACTUAL_RETURN_CODE} 退出 (纯输出比较模式)。\e[0m" if diff -q <(tr -d '[:space:]' < "${output_actual_file_from_ir}") <(tr -d '[:space:]' < "${output_reference_file}") >/dev/null 2>&1; then
fi
if diff -q <(tr -d '[:space:]' < "${output_actual_file}") <(tr -d '[:space:]' < "${output_reference_file}") >/dev/null 2>&1; then
echo -e "\e[32m 成功: 输出与参考输出匹配\e[0m" echo -e "\e[32m 成功: 输出与参考输出匹配\e[0m"
else else
echo -e "\e[31m 失败: 输出不匹配\e[0m" echo -e "\e[31m 失败: 输出不匹配\e[0m"
is_passed=0
display_file_content "${output_reference_file}" " \e[36m---------- 期望输出 ----------\e[0m" "${MAX_OUTPUT_LINES}" display_file_content "${output_reference_file}" " \e[36m---------- 期望输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
display_file_content "${output_actual_file}" " \e[36m---------- 实际输出 ----------\e[0m" "${MAX_OUTPUT_LINES}" display_file_content "${output_actual_file_from_ir}" " \e[36m---------- 实际输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
echo -e " \e[36m------------------------------\e[0m" test_logic_passed=0
fi fi
fi fi
else else
echo " 无参考输出文件。程序返回码: ${ACTUAL_RETURN_CODE}" echo " 无参考输出文件。程序返回码: ${ACTUAL_RETURN_CODE}"
test_logic_passed=1
fi fi
fi fi
fi fi
[ "$step_failed" -eq 0 ] && [ "$test_logic_passed" -eq 1 ] && is_passed=1
# --- 模式 2: 直接执行模式 (-e) ---
elif ${EXECUTE_MODE}; then
step_failed=0
test_logic_passed=0
echo " [1/3] 使用 sysyc 编译为汇编 (超时 ${SYSYC_TIMEOUT}s)..."
timeout -s KILL ${SYSYC_TIMEOUT} "${SYSYC}" -S "${sy_file}" -o "${assembly_file_S}" ${OPTIMIZE_FLAG}
SYSYC_STATUS=$?
if [ $SYSYC_STATUS -ne 0 ]; then
[ $SYSYC_STATUS -eq 124 ] && echo -e "\e[31m错误: SysY (汇编) 编译超时\e[0m" || echo -e "\e[31m错误: SysY (汇编) 编译失败,退出码: ${SYSYC_STATUS}\e[0m"
step_failed=1
fi
if [ "$step_failed" -eq 0 ]; then
echo " [2/3] 使用 gcc 编译 (超时 ${GCC_TIMEOUT}s)..."
timeout -s KILL ${GCC_TIMEOUT} "${GCC_RISCV64}" "${assembly_file_S}" -o "${executable_file_S}" -L"${LIB_DIR}" -lsysy_riscv -static
GCC_STATUS=$?
if [ $GCC_STATUS -ne 0 ]; then
[ $GCC_STATUS -eq 124 ] && echo -e "\e[31m错误: GCC 编译超时\e[0m" || echo -e "\e[31m错误: GCC 编译失败,退出码: ${GCC_STATUS}\e[0m"
step_failed=1
fi
fi
if [ "$step_failed" -eq 0 ]; then
echo " [3/3] 正在执行 (超时 ${EXEC_TIMEOUT}s)..."
exec_cmd="${QEMU_RISCV64} \"${executable_file_S}\""
[ -f "${input_file}" ] && exec_cmd+=" < \"${input_file}\""
exec_cmd+=" > \"${output_actual_file_S}\""
eval "timeout -s KILL ${EXEC_TIMEOUT} ${exec_cmd}"
ACTUAL_RETURN_CODE=$?
if [ "$ACTUAL_RETURN_CODE" -eq 124 ]; then
echo -e "\e[31m 执行超时: 运行超过 ${EXEC_TIMEOUT} 秒\e[0m"
else
if [ -f "${output_reference_file}" ]; then
LAST_LINE_TRIMMED=$(tail -n 1 "${output_reference_file}" | tr -d '[:space:]')
test_logic_passed=1
if [[ "$LAST_LINE_TRIMMED" =~ ^[-+]?[0-9]+$ ]]; then
EXPECTED_RETURN_CODE="$LAST_LINE_TRIMMED"
EXPECTED_STDOUT_FILE="${TMP_DIR}/${output_base_name}_sysyc_S.expected_stdout"
head -n -1 "${output_reference_file}" > "${EXPECTED_STDOUT_FILE}"
if [ "$ACTUAL_RETURN_CODE" -eq "$EXPECTED_RETURN_CODE" ]; then
echo -e "\e[32m 返回码测试成功: (${ACTUAL_RETURN_CODE}) 与期望值 (${EXPECTED_RETURN_CODE}) 匹配\e[0m"
else
echo -e "\e[31m 返回码测试失败: 期望: ${EXPECTED_RETURN_CODE}, 实际: ${ACTUAL_RETURN_CODE}\e[0m"
test_logic_passed=0
fi
if diff -q <(tr -d '[:space:]' < "${output_actual_file_S}") <(tr -d '[:space:]' < "${EXPECTED_STDOUT_FILE}") >/dev/null 2>&1; then
[ "$test_logic_passed" -eq 1 ] && echo -e "\e[32m 标准输出测试成功\e[0m"
else
echo -e "\e[31m 标准输出测试失败\e[0m"
display_file_content "${EXPECTED_STDOUT_FILE}" " \e[36m---------- 期望输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
display_file_content "${output_actual_file_S}" " \e[36m---------- 实际输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
test_logic_passed=0
fi
else
if [ $ACTUAL_RETURN_CODE -ne 0 ]; then echo -e "\e[33m警告: 程序以非零状态 ${ACTUAL_RETURN_CODE} 退出 (纯输出比较模式)。\e[0m"; fi
if diff -q <(tr -d '[:space:]' < "${output_actual_file_S}") <(tr -d '[:space:]' < "${output_reference_file}") >/dev/null 2>&1; then
echo -e "\e[32m 成功: 输出与参考输出匹配\e[0m"
else
echo -e "\e[31m 失败: 输出不匹配\e[0m"
display_file_content "${output_reference_file}" " \e[36m---------- 期望输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
display_file_content "${output_actual_file_S}" " \e[36m---------- 实际输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
test_logic_passed=0
fi
fi
else
echo " 无参考输出文件。程序返回码: ${ACTUAL_RETURN_CODE}"
test_logic_passed=1
fi
fi
fi
[ "$step_failed" -eq 0 ] && [ "$test_logic_passed" -eq 1 ] && is_passed=1
# --- 模式 3: 默认编译模式 ---
else
s_compile_ok=0
ir_compile_ok=0
echo " [1/2] 使用 sysyc 编译为汇编 (超时 ${SYSYC_TIMEOUT}s)..."
timeout -s KILL ${SYSYC_TIMEOUT} "${SYSYC}" -S "${sy_file}" -o "${assembly_file_S}" ${OPTIMIZE_FLAG}
SYSYC_S_STATUS=$?
if [ $SYSYC_S_STATUS -eq 0 ]; then
s_compile_ok=1
echo -e " \e[32m-> ${assembly_file_S} [成功]\e[0m"
else
[ $SYSYC_S_STATUS -eq 124 ] && echo -e " \e[31m-> [编译超时]\e[0m" || echo -e " \e[31m-> [编译失败, 退出码: ${SYSYC_S_STATUS}]\e[0m"
fi
echo " [2/2] 使用 sysyc 编译为 IR (超时 ${SYSYC_TIMEOUT}s)..."
timeout -s KILL ${SYSYC_TIMEOUT} "${SYSYC}" -s ir "${sy_file}" -o "${ir_file}" ${OPTIMIZE_FLAG}
SYSYC_IR_STATUS=$?
if [ $SYSYC_IR_STATUS -eq 0 ]; then
ir_compile_ok=1
echo -e " \e[32m-> ${ir_file} [成功]\e[0m"
else
[ $SYSYC_IR_STATUS -eq 124 ] && echo -e " \e[31m-> [编译超时]\e[0m" || echo -e " \e[31m-> [编译失败, 退出码: ${SYSYC_IR_STATUS}]\e[0m"
fi
if [ "$s_compile_ok" -eq 1 ] && [ "$ir_compile_ok" -eq 1 ]; then
is_passed=1
fi
fi
# --- 统计结果 ---
if [ "$is_passed" -eq 1 ]; then if [ "$is_passed" -eq 1 ]; then
((PASSED_CASES++)) ((PASSED_CASES++))
else else
# 确保 FAILED_CASES_LIST 的每一项都以换行符结尾
FAILED_CASES_LIST+="${relative_path_no_ext}.sy\n" FAILED_CASES_LIST+="${relative_path_no_ext}.sy\n"
fi fi
echo "" echo ""
done <<< "$sy_files" done <<< "$sy_files"
echo "========================================" # --- 修改:调用总结函数 ---
echo "测试完成" print_summary
echo "测试通过率: [${PASSED_CASES}/${TOTAL_CASES}]"
if [ -n "$FAILED_CASES_LIST" ]; then
echo ""
echo -e "\e[31m未通过的测例:\e[0m"
echo -e "${FAILED_CASES_LIST}"
fi
echo "========================================"
if [ "$PASSED_CASES" -eq "$TOTAL_CASES" ]; then
exit 0
else
exit 1
fi

View File

@@ -20,6 +20,10 @@
#include <algorithm> #include <algorithm>
namespace sysy { namespace sysy {
// Global cleanup function to release all statically allocated IR objects
void cleanupIRPools();
/** /**
* \defgroup type Types * \defgroup type Types
* @brief Sysy的类型系统 * @brief Sysy的类型系统
@@ -83,6 +87,7 @@ class Type {
auto as() const -> std::enable_if_t<std::is_base_of_v<Type, T>, T *> { auto as() const -> std::enable_if_t<std::is_base_of_v<Type, T>, T *> {
return dynamic_cast<T *>(const_cast<Type *>(this)); return dynamic_cast<T *>(const_cast<Type *>(this));
} }
virtual void print(std::ostream& os) const;
}; };
class PointerType : public Type { class PointerType : public Type {
@@ -95,6 +100,9 @@ class PointerType : public Type {
public: public:
static PointerType* get(Type *baseType); ///< 获取指向baseType的Pointer类型 static PointerType* get(Type *baseType); ///< 获取指向baseType的Pointer类型
// Cleanup method to release all cached pointer types (call at program exit)
static void cleanup();
public: public:
Type* getBaseType() const { return baseType; } ///< 获取指向的类型 Type* getBaseType() const { return baseType; } ///< 获取指向的类型
}; };
@@ -112,6 +120,9 @@ class FunctionType : public Type {
/// 获取返回值类型为returnType 形参类型列表为paramTypes的Function类型 /// 获取返回值类型为returnType 形参类型列表为paramTypes的Function类型
static FunctionType* get(Type *returnType, const std::vector<Type *> &paramTypes = {}); static FunctionType* get(Type *returnType, const std::vector<Type *> &paramTypes = {});
// Cleanup method to release all cached function types (call at program exit)
static void cleanup();
public: public:
Type* getReturnType() const { return returnType; } ///< 获取返回值类信息 Type* getReturnType() const { return returnType; } ///< 获取返回值类信息
auto getParamTypes() const { return make_range(paramTypes); } ///< 获取形参类型列表 auto getParamTypes() const { return make_range(paramTypes); } ///< 获取形参类型列表
@@ -124,6 +135,9 @@ class ArrayType : public Type {
// numElements该维度的大小 (例如int[3] 的 numElements 是 3) // numElements该维度的大小 (例如int[3] 的 numElements 是 3)
static ArrayType *get(Type *elementType, unsigned numElements); static ArrayType *get(Type *elementType, unsigned numElements);
// Cleanup method to release all cached array types (call at program exit)
static void cleanup();
Type *getElementType() const { return elementType; } Type *getElementType() const { return elementType; }
unsigned getNumElements() const { return numElements; } unsigned getNumElements() const { return numElements; }
@@ -206,6 +220,7 @@ class Use {
User* getUser() const { return user; } ///< 返回使用者 User* getUser() const { return user; } ///< 返回使用者
Value* getValue() const { return value; } ///< 返回被使用的值 Value* getValue() const { return value; } ///< 返回被使用的值
void setValue(Value *newValue) { value = newValue; } ///< 将被使用的值设置为newValue void setValue(Value *newValue) { value = newValue; } ///< 将被使用的值设置为newValue
void print(std::ostream& os) const;
}; };
//! The base class of all value types //! The base class of all value types
@@ -238,6 +253,7 @@ class Value {
uses.remove(use); uses.remove(use);
} ///< 删除使用关系use } ///< 删除使用关系use
void removeAllUses(); void removeAllUses();
virtual void print(std::ostream& os) const = 0; ///< 输出值信息到输出流
}; };
/** /**
@@ -365,6 +381,9 @@ public:
// Static factory method to get a canonical ConstantValue from the pool // Static factory method to get a canonical ConstantValue from the pool
static ConstantValue* get(Type* type, ConstantValVariant val); static ConstantValue* get(Type* type, ConstantValVariant val);
// Cleanup method to release all cached constants (call at program exit)
static void cleanup();
// Helper methods to access constant values with appropriate casting // Helper methods to access constant values with appropriate casting
int getInt() const { int getInt() const {
auto val = getVal(); auto val = getVal();
@@ -402,6 +421,7 @@ public:
virtual bool isZero() const = 0; virtual bool isZero() const = 0;
virtual bool isOne() const = 0; virtual bool isOne() const = 0;
void print(std::ostream& os) const = 0;
}; };
class ConstantInteger : public ConstantValue { class ConstantInteger : public ConstantValue {
@@ -428,6 +448,7 @@ public:
bool isZero() const override { return constVal == 0; } bool isZero() const override { return constVal == 0; }
bool isOne() const override { return constVal == 1; } bool isOne() const override { return constVal == 1; }
void print(std::ostream& os) const;
}; };
class ConstantFloating : public ConstantValue { class ConstantFloating : public ConstantValue {
@@ -454,6 +475,7 @@ public:
bool isZero() const override { return constFVal == 0.0f; } bool isZero() const override { return constFVal == 0.0f; }
bool isOne() const override { return constFVal == 1.0f; } bool isOne() const override { return constFVal == 1.0f; }
void print(std::ostream& os) const;
}; };
class UndefinedValue : public ConstantValue { class UndefinedValue : public ConstantValue {
@@ -469,6 +491,9 @@ protected:
public: public:
static UndefinedValue* get(Type* type); static UndefinedValue* get(Type* type);
// Cleanup method to release all cached undefined values (call at program exit)
static void cleanup();
size_t hash() const override { size_t hash() const override {
return std::hash<Type*>{}(getType()); return std::hash<Type*>{}(getType());
} }
@@ -485,6 +510,7 @@ public:
bool isZero() const override { return false; } bool isZero() const override { return false; }
bool isOne() const override { return false; } bool isOne() const override { return false; }
void print(std::ostream& os) const;
}; };
// --- End of refactored ConstantValue and related classes --- // --- End of refactored ConstantValue and related classes ---
@@ -625,6 +651,11 @@ public:
} }
} ///< 移除指定位置的指令 } ///< 移除指定位置的指令
iterator moveInst(iterator sourcePos, iterator targetPos, BasicBlock *block); iterator moveInst(iterator sourcePos, iterator targetPos, BasicBlock *block);
/// 清理基本块中的所有使用关系
void cleanup();
void print(std::ostream& os) const;
}; };
//! User is the abstract base type of `Value` types which use other `Value` as //! User is the abstract base type of `Value` types which use other `Value` as
@@ -659,6 +690,9 @@ class User : public Value {
} ///< 增加多个操作数 } ///< 增加多个操作数
void replaceOperand(unsigned index, Value *value); ///< 替换操作数 void replaceOperand(unsigned index, Value *value); ///< 替换操作数
void setOperand(unsigned index, Value *value); ///< 设置操作数 void setOperand(unsigned index, Value *value); ///< 设置操作数
/// 清理用户的所有操作数使用关系
void cleanup();
}; };
/*! /*!
@@ -736,57 +770,57 @@ public:
std::string getKindString() const{ std::string getKindString() const{
switch (kind) { switch (kind) {
case kInvalid: case kInvalid:
return "Invalid"; return "invalid";
case kAdd: case kAdd:
return "Add"; return "add";
case kSub: case kSub:
return "Sub"; return "sub";
case kMul: case kMul:
return "Mul"; return "mul";
case kDiv: case kDiv:
return "Div"; return "sdiv";
case kRem: case kRem:
return "Rem"; return "srem";
case kICmpEQ: case kICmpEQ:
return "ICmpEQ"; return "icmp eq";
case kICmpNE: case kICmpNE:
return "ICmpNE"; return "icmp ne";
case kICmpLT: case kICmpLT:
return "ICmpLT"; return "icmp slt";
case kICmpGT: case kICmpGT:
return "ICmpGT"; return "icmp sgt";
case kICmpLE: case kICmpLE:
return "ICmpLE"; return "icmp sle";
case kICmpGE: case kICmpGE:
return "ICmpGE"; return "icmp sge";
case kFAdd: case kFAdd:
return "FAdd"; return "fadd";
case kFSub: case kFSub:
return "FSub"; return "fsub";
case kFMul: case kFMul:
return "FMul"; return "fmul";
case kFDiv: case kFDiv:
return "FDiv"; return "fdiv";
case kFCmpEQ: case kFCmpEQ:
return "FCmpEQ"; return "fcmp oeq";
case kFCmpNE: case kFCmpNE:
return "FCmpNE"; return "fcmp one";
case kFCmpLT: case kFCmpLT:
return "FCmpLT"; return "fcmp olt";
case kFCmpGT: case kFCmpGT:
return "FCmpGT"; return "fcmp ogt";
case kFCmpLE: case kFCmpLE:
return "FCmpLE"; return "fcmp ole";
case kFCmpGE: case kFCmpGE:
return "FCmpGE"; return "fcmp oge";
case kAnd: case kAnd:
return "And"; return "and";
case kOr: case kOr:
return "Or"; return "or";
case kNeg: case kNeg:
return "Neg"; return "neg";
case kNot: case kNot:
return "Not"; return "not";
case kFNeg: case kFNeg:
return "FNeg"; return "FNeg";
case kFNot: case kFNot:
@@ -794,33 +828,35 @@ public:
case kFtoI: case kFtoI:
return "FtoI"; return "FtoI";
case kItoF: case kItoF:
return "IToF"; return "iToF";
case kCall: case kCall:
return "Call"; return "call";
case kCondBr: case kCondBr:
return "CondBr"; return "condBr";
case kBr: case kBr:
return "Br"; return "br";
case kReturn: case kReturn:
return "Return"; return "return";
case kUnreachable:
return "unreachable";
case kAlloca: case kAlloca:
return "Alloca"; return "alloca";
case kLoad: case kLoad:
return "Load"; return "load";
case kStore: case kStore:
return "Store"; return "store";
case kGetElementPtr: case kGetElementPtr:
return "GetElementPtr"; return "getElementPtr";
case kMemset: case kMemset:
return "Memset"; return "memset";
case kPhi: case kPhi:
return "Phi"; return "phi";
case kBitItoF: case kBitItoF:
return "BitItoF"; return "BitItoF";
case kBitFtoI: case kBitFtoI:
return "BitFtoI"; return "BitFtoI";
case kSRA: case kSRA:
return "SRA"; return "ashr";
default: default:
return "Unknown"; return "Unknown";
} }
@@ -887,6 +923,10 @@ public:
static constexpr uint64_t DefineOpMask = kAlloca | kStore | kPhi; static constexpr uint64_t DefineOpMask = kAlloca | kStore | kPhi;
return (kind & DefineOpMask) != 0U; return (kind & DefineOpMask) != 0U;
} }
virtual ~Instruction() = default;
virtual void print(std::ostream& os) const = 0;
}; // class Instruction }; // class Instruction
class Function; class Function;
@@ -957,6 +997,7 @@ class PhiInst : public Instruction {
} }
} ///< 刷新块到值的映射关系 } ///< 刷新块到值的映射关系
auto getValues() { return make_range(std::next(operand_begin()), operand_end()); } auto getValues() { return make_range(std::next(operand_begin()), operand_end()); }
void print(std::ostream& os) const override;
}; };
@@ -965,16 +1006,14 @@ class CallInst : public Instruction {
friend class IRBuilder; friend class IRBuilder;
protected: protected:
CallInst(Function *callee, const std::vector<Value *> &args = {}, CallInst(Function *callee, const std::vector<Value *> &args, BasicBlock *parent = nullptr, const std::string &name = "");
BasicBlock *parent = nullptr, const std::string &name = "");
public: public:
Function* getCallee() const; Function *getCallee() const;
auto getArguments() const { auto getArguments() const {
return make_range(std::next(operand_begin()), operand_end()); return make_range(std::next(operand_begin()), operand_end());
} }
void print(std::ostream& os) const override;
}; // class CallInst }; // class CallInst
//! Unary instruction, includes '!', '-' and type conversion. //! Unary instruction, includes '!', '-' and type conversion.
@@ -992,7 +1031,7 @@ protected:
public: public:
Value* getOperand() const { return User::getOperand(0); } Value* getOperand() const { return User::getOperand(0); }
void print(std::ostream& os) const override;
}; // class UnaryInst }; // class UnaryInst
//! Binary instruction, e.g., arithmatic, relation, logic, etc. //! Binary instruction, e.g., arithmatic, relation, logic, etc.
@@ -1071,6 +1110,7 @@ public:
// 后端处理数组访存操作时需要创建计算地址的指令,需要在外部构造 BinaryInst 对象 // 后端处理数组访存操作时需要创建计算地址的指令,需要在外部构造 BinaryInst 对象
return new BinaryInst(kind, type, lhs, rhs, parent, name); return new BinaryInst(kind, type, lhs, rhs, parent, name);
} }
void print(std::ostream& os) const override;
}; // class BinaryInst }; // class BinaryInst
//! The return statement //! The return statement
@@ -1091,6 +1131,7 @@ class ReturnInst : public Instruction {
Value* getReturnValue() const { Value* getReturnValue() const {
return hasReturnValue() ? getOperand(0) : nullptr; return hasReturnValue() ? getOperand(0) : nullptr;
} }
void print(std::ostream& os) const override;
}; };
//! Unconditional branch //! Unconditional branch
@@ -1120,7 +1161,7 @@ public:
} }
return succs; return succs;
} }
void print(std::ostream& os) const override;
}; // class UncondBrInst }; // class UncondBrInst
//! Conditional branch //! Conditional branch
@@ -1160,7 +1201,7 @@ public:
} }
return succs; return succs;
} }
void print(std::ostream& os) const override;
}; // class CondBrInst }; // class CondBrInst
class UnreachableInst : public Instruction { class UnreachableInst : public Instruction {
@@ -1168,7 +1209,7 @@ public:
// 构造函数:设置指令类型为 kUnreachable // 构造函数:设置指令类型为 kUnreachable
explicit UnreachableInst(const std::string& name, BasicBlock *parent = nullptr) explicit UnreachableInst(const std::string& name, BasicBlock *parent = nullptr)
: Instruction(kUnreachable, Type::getVoidType(), parent, "") {} : Instruction(kUnreachable, Type::getVoidType(), parent, "") {}
void print(std::ostream& os) const { os << "unreachable"; }
}; };
//! Allocate memory for stack variables, used for non-global variable declartion //! Allocate memory for stack variables, used for non-global variable declartion
@@ -1186,7 +1227,7 @@ public:
Type* getAllocatedType() const { Type* getAllocatedType() const {
return getType()->as<PointerType>()->getBaseType(); return getType()->as<PointerType>()->getBaseType();
} ///< 获取分配的类型 } ///< 获取分配的类型
void print(std::ostream& os) const override;
}; // class AllocaInst }; // class AllocaInst
@@ -1224,6 +1265,7 @@ public:
BasicBlock *parent = nullptr, const std::string &name = "") { BasicBlock *parent = nullptr, const std::string &name = "") {
return new GetElementPtrInst(resultType, basePointer, indices, parent, name); return new GetElementPtrInst(resultType, basePointer, indices, parent, name);
} }
void print(std::ostream& os) const override;
}; };
//! Load a value from memory address specified by a pointer value //! Load a value from memory address specified by a pointer value
@@ -1241,7 +1283,7 @@ protected:
public: public:
Value* getPointer() const { return getOperand(0); } Value* getPointer() const { return getOperand(0); }
void print(std::ostream& os) const override;
}; // class LoadInst }; // class LoadInst
//! Store a value to memory address specified by a pointer value //! Store a value to memory address specified by a pointer value
@@ -1260,7 +1302,7 @@ protected:
public: public:
Value* getValue() const { return getOperand(0); } Value* getValue() const { return getOperand(0); }
Value* getPointer() const { return getOperand(1); } Value* getPointer() const { return getOperand(1); }
void print(std::ostream& os) const override;
}; // class StoreInst }; // class StoreInst
//! Memset instruction //! Memset instruction
@@ -1290,7 +1332,7 @@ public:
Value* getBegin() const { return getOperand(1); } Value* getBegin() const { return getOperand(1); }
Value* getSize() const { return getOperand(2); } Value* getSize() const { return getOperand(2); }
Value* getValue() const { return getOperand(3); } Value* getValue() const { return getOperand(3); }
void print(std::ostream& os) const override;
}; };
class GlobalValue; class GlobalValue;
@@ -1308,6 +1350,11 @@ public:
public: public:
Function* getParent() const { return func; } Function* getParent() const { return func; }
int getIndex() const { return index; } int getIndex() const { return index; }
/// 清理参数的使用关系
void cleanup();
void print(std::ostream& os) const;
}; };
@@ -1385,6 +1432,11 @@ protected:
blocks.emplace_front(block); blocks.emplace_front(block);
return block; return block;
} }
/// 清理函数中的所有使用关系
void cleanup();
void print(std::ostream& os) const;
}; };
//! Global value declared at file scope //! Global value declared at file scope
@@ -1450,6 +1502,7 @@ public:
return getByIndex(index); return getByIndex(index);
} ///< 通过多维索引indices获取初始值 } ///< 通过多维索引indices获取初始值
const ValueCounter& getInitValues() const { return initValues; } const ValueCounter& getInitValues() const { return initValues; }
void print(std::ostream& os) const;
}; // class GlobalValue }; // class GlobalValue
@@ -1507,6 +1560,8 @@ class ConstantVariable : public Value {
return getByIndex(index); return getByIndex(index);
} ///< 通过多维索引indices获取初始值 } ///< 通过多维索引indices获取初始值
const ValueCounter& getInitValues() const { return initValues; } ///< 获取初始值 const ValueCounter& getInitValues() const { return initValues; } ///< 获取初始值
void print(std::ostream& os) const;
void print_init(std::ostream& os) const;
}; };
using SymbolTableNode = struct SymbolTableNode { using SymbolTableNode = struct SymbolTableNode {
@@ -1529,6 +1584,8 @@ class SymbolTable {
Value* getVariable(const std::string &name) const; ///< 根据名字name以及当前作用域获取变量 Value* getVariable(const std::string &name) const; ///< 根据名字name以及当前作用域获取变量
Value* addVariable(const std::string &name, Value *variable); ///< 添加变量 Value* addVariable(const std::string &name, Value *variable); ///< 添加变量
void registerParameterName(const std::string &name); ///< 注册函数参数名字避免alloca重名
void addVariableDirectly(const std::string &name, Value *variable); ///< 直接添加变量到当前作用域,不重命名
std::vector<std::unique_ptr<GlobalValue>>& getGlobals(); ///< 获取全局变量列表 std::vector<std::unique_ptr<GlobalValue>>& getGlobals(); ///< 获取全局变量列表
const std::vector<std::unique_ptr<ConstantVariable>>& getConsts() const; ///< 获取全局常量列表 const std::vector<std::unique_ptr<ConstantVariable>>& getConsts() const; ///< 获取全局常量列表
void enterNewScope(); ///< 进入新的作用域 void enterNewScope(); ///< 进入新的作用域
@@ -1536,6 +1593,9 @@ class SymbolTable {
bool isInGlobalScope() const; ///< 是否位于全局作用域 bool isInGlobalScope() const; ///< 是否位于全局作用域
void enterGlobalScope(); ///< 进入全局作用域 void enterGlobalScope(); ///< 进入全局作用域
bool isCurNodeNull() { return curNode == nullptr; } bool isCurNodeNull() { return curNode == nullptr; }
/// 清理符号表中的所有内容
void cleanup();
}; };
//! IR unit for representing a SysY compile unit //! IR unit for representing a SysY compile unit
@@ -1588,6 +1648,12 @@ class Module {
void addVariable(const std::string &name, AllocaInst *variable) { void addVariable(const std::string &name, AllocaInst *variable) {
variableTable.addVariable(name, variable); variableTable.addVariable(name, variable);
} ///< 添加变量 } ///< 添加变量
void addVariableDirectly(const std::string &name, AllocaInst *variable) {
variableTable.addVariableDirectly(name, variable);
} ///< 直接添加变量到当前作用域,不重命名
void registerParameterName(const std::string &name) {
variableTable.registerParameterName(name);
} ///< 注册函数参数名字避免alloca重名
Value* getVariable(const std::string &name) { Value* getVariable(const std::string &name) {
return variableTable.getVariable(name); return variableTable.getVariable(name);
} ///< 根据名字name和当前作用域获取变量 } ///< 根据名字name和当前作用域获取变量
@@ -1600,7 +1666,7 @@ class Module {
} ///< 获取函数 } ///< 获取函数
Function* getExternalFunction(const std::string &name) const { Function* getExternalFunction(const std::string &name) const {
auto result = externalFunctions.find(name); auto result = externalFunctions.find(name);
if (result == functions.end()) { if (result == externalFunctions.end()) {
return nullptr; return nullptr;
} }
return result->second.get(); return result->second.get();
@@ -1620,6 +1686,11 @@ class Module {
void leaveScope() { variableTable.leaveScope(); } ///< 离开作用域 void leaveScope() { variableTable.leaveScope(); } ///< 离开作用域
bool isInGlobalArea() const { return variableTable.isInGlobalScope(); } ///< 是否位于全局作用域 bool isInGlobalArea() const { return variableTable.isInGlobalScope(); } ///< 是否位于全局作用域
/// 清理模块中的所有对象,包括函数、基本块、指令等
void cleanup();
void print(std::ostream& os) const;
}; };
/*! /*!

View File

@@ -350,38 +350,31 @@ class IRBuilder {
Type *currentWalkType = pointerType->as<PointerType>()->getBaseType(); Type *currentWalkType = pointerType->as<PointerType>()->getBaseType();
// 遍历所有索引来深入类型层次结构。 // 遍历所有索引来深入类型层次结构。
// `indices` 向量包含了所有 GEP 索引,包括由 `visitLValue` 等函数添加的初始 `0` 索引 // 重要:第一个索引总是用于"解引用"指针,后续索引才用于数组/结构体的索引
for (int i = 0; i < indices.size(); ++i) { for (int i = 0; i < indices.size(); ++i) {
if (i == 0) {
// 第一个索引:总是用于"解引用"基指针不改变currentWalkType
// 例如:对于 `[4 x i32]* ptr, i32 0`第一个0只是说"访问ptr指向的对象"
// currentWalkType 保持为 `[4 x i32]`
continue;
} else {
// 后续索引:用于实际的数组/结构体索引
if (currentWalkType->isArray()) { if (currentWalkType->isArray()) {
// 情况一:当前遍历类型是 `ArrayType`。 // 数组索引:选择数组中的元素
// 索引用于选择数组元素,`currentWalkType` 更新为数组的元素类型。
currentWalkType = currentWalkType->as<ArrayType>()->getElementType(); currentWalkType = currentWalkType->as<ArrayType>()->getElementType();
} else if (currentWalkType->isPointer()) { } else if (currentWalkType->isPointer()) {
// 情况二:当前遍历类型是 `PointerType`。 // 指针索引:解引用指针并继续
// 这意味着我们正在通过一个指针来访问其指向的内存。
// 索引用于选择该指针所指向的“数组”的元素。
// `currentWalkType` 更新为该指针所指向的基础类型。
// 例如:如果 `currentWalkType` 是 `i32*`,它将变为 `i32`。
// 如果 `currentWalkType` 是 `[10 x i32]*`,它将变为 `[10 x i32]`。
currentWalkType = currentWalkType->as<PointerType>()->getBaseType(); currentWalkType = currentWalkType->as<PointerType>()->getBaseType();
} else { } else {
// 情况三:当前遍历类型是标量类型 (例如 `i32`, `float` 等非聚合、非指针类型)。 // 标量类型:不能进一步索引
//
// 如果 `currentWalkType` 是标量,并且当前索引 `i` **不是** `indices` 向量中的最后一个索引,
// 这意味着尝试对一个标量类型进行进一步的结构性索引,这是**无效的**。
// 例如:`int x; x[0];` 对应的 GEP 链中,`x` 的类型是 `i32`,再加 `[0]` 索引就是错误。
//
// 如果 `currentWalkType` 是标量,且这是**最后一个索引** (`i == indices.size() - 1`)
// 那么 GEP 是合法的,它只是计算一个偏移地址,最终的类型就是这个标量类型。
// 此时 `currentWalkType` 保持不变,循环结束。
if (i < indices.size() - 1) { if (i < indices.size() - 1) {
assert(false && "Invalid GEP indexing: attempting to index into a non-aggregate/non-pointer type with further indices."); assert(false && "Invalid GEP indexing: attempting to index into a non-aggregate/non-pointer type with further indices.");
return nullptr; // 返回空指针表示类型推断失败 return nullptr;
}
// 如果是最后一个索引,且当前类型是标量,则类型保持不变,这是合法的。
// 循环会自然结束,返回正确的 `currentWalkType`。
} }
} }
}
}
// 所有索引处理完毕后,`currentWalkType` 就是 GEP 指令最终计算出的地址所指向的元素的类型。 // 所有索引处理完毕后,`currentWalkType` 就是 GEP 指令最终计算出的地址所指向的元素的类型。
return currentWalkType; return currentWalkType;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -52,13 +52,15 @@ bool LargeArrayToGlobalPass::runOnModule(Module *M, AnalysisManager &AM) {
// Calculate the size of the allocated type // Calculate the size of the allocated type
unsigned size = calculateTypeSize(allocatedType); unsigned size = calculateTypeSize(allocatedType);
if(DEBUG){
// Debug: print size information // Debug: print size information
std::cout << "LargeArrayToGlobalPass: Found alloca with size " << size std::cout << "LargeArrayToGlobalPass: Found alloca with size " << size
<< " for type " << typeToString(allocatedType) << std::endl; << " for type " << typeToString(allocatedType) << std::endl;
}
// Convert arrays of 1KB (1024 bytes) or larger to global variables // Convert arrays of 1KB (1024 bytes) or larger to global variables
if (size >= 1024) { if (size >= 1024) {
if(DEBUG)
std::cout << "LargeArrayToGlobalPass: Converting array of size " << size << " to global" << std::endl; std::cout << "LargeArrayToGlobalPass: Converting array of size " << size << " to global" << std::endl;
allocasToConvert.emplace_back(alloca, F); allocasToConvert.emplace_back(alloca, F);
} }

View File

@@ -280,6 +280,22 @@ void SCCPContext::ProcessInstruction(Instruction *inst) {
return; // 不处理不可达块中的指令的实际值 return; // 不处理不可达块中的指令的实际值
} }
if(DEBUG) {
std::cout << "Processing instruction: " << inst->getName() << " in block " << inst->getParent()->getName() << std::endl;
std::cout << "Old state: ";
if (oldState.state == LatticeVal::Top) {
std::cout << "Top";
} else if (oldState.state == LatticeVal::Constant) {
if (oldState.constant_type == ValueType::Integer) {
std::cout << "Const<int>(" << std::get<int>(oldState.constantVal) << ")";
} else {
std::cout << "Const<float>(" << std::get<float>(oldState.constantVal) << ")";
}
} else {
std::cout << "Bottom";
}
}
switch (inst->getKind()) { switch (inst->getKind()) {
case Instruction::kAdd: case Instruction::kAdd:
case Instruction::kSub: case Instruction::kSub:
@@ -398,6 +414,7 @@ void SCCPContext::ProcessInstruction(Instruction *inst) {
newState = SSAPValue(); // 保持 Top newState = SSAPValue(); // 保持 Top
break; break;
case Instruction::kCall: case Instruction::kCall:
// TODO: 处理 Call 指令根据副作用分析可以推断的常量
// 大多数 Call 指令都假定为 Bottom除非是纯函数且所有参数都是常量 // 大多数 Call 指令都假定为 Bottom除非是纯函数且所有参数都是常量
newState = SSAPValue(LatticeVal::Bottom); newState = SSAPValue(LatticeVal::Bottom);
break; break;
@@ -417,19 +434,71 @@ void SCCPContext::ProcessInstruction(Instruction *inst) {
} }
case Instruction::kPhi: { case Instruction::kPhi: {
PhiInst *phi = static_cast<PhiInst *>(inst); PhiInst *phi = static_cast<PhiInst *>(inst);
if(DEBUG) {
std::cout << "Processing Phi node: " << phi->getName() << std::endl;
}
// 标准SCCP的phi节点处理
// 只考虑可执行前驱,但要保证单调性
SSAPValue currentPhiState = GetValueState(phi);
SSAPValue phiResult = SSAPValue(); // 初始为 Top SSAPValue phiResult = SSAPValue(); // 初始为 Top
bool hasAnyExecutablePred = false;
for (unsigned i = 0; i < phi->getNumIncomingValues(); ++i) { for (unsigned i = 0; i < phi->getNumIncomingValues(); ++i) {
Value *incomingVal = phi->getIncomingValue(i);
BasicBlock *incomingBlock = phi->getIncomingBlock(i); BasicBlock *incomingBlock = phi->getIncomingBlock(i);
if (executableBlocks.count(incomingBlock)) { // 仅考虑可执行前驱 if (executableBlocks.count(incomingBlock)) {
phiResult = Meet(phiResult, GetValueState(incomingVal)); hasAnyExecutablePred = true;
if (phiResult.state == LatticeVal::Bottom) Value *incomingVal = phi->getIncomingValue(i);
break; // 如果已经 Bottom则提前退出 SSAPValue incomingState = GetValueState(incomingVal);
if(DEBUG) {
std::cout << " Incoming from block " << incomingBlock->getName()
<< " with value " << incomingVal->getName() << " state: ";
if (incomingState.state == LatticeVal::Top)
std::cout << "Top";
else if (incomingState.state == LatticeVal::Constant) {
if (incomingState.constant_type == ValueType::Integer)
std::cout << "Const<int>(" << std::get<int>(incomingState.constantVal) << ")";
else
std::cout << "Const<float>(" << std::get<float>(incomingState.constantVal) << ")";
} else
std::cout << "Bottom";
std::cout << std::endl;
}
phiResult = Meet(phiResult, incomingState);
if (phiResult.state == LatticeVal::Bottom) {
break; // 提前退出优化
} }
} }
// 不可执行前驱暂时被忽略
// 这是标准SCCP的做法依赖于单调性保证正确性
}
if (!hasAnyExecutablePred) {
// 没有可执行前驱保持Top状态
newState = SSAPValue();
} else {
// 关键修复:使用严格的单调性
// 确保phi的值只能从Top -> Constant -> Bottom单向变化
if (currentPhiState.state == LatticeVal::Top) {
// 从Top状态可以变为任何计算结果
newState = phiResult; newState = phiResult;
} else if (currentPhiState.state == LatticeVal::Constant) {
// 从Constant状态只能保持相同常量或变为Bottom
if (phiResult.state == LatticeVal::Constant &&
currentPhiState.constantVal == phiResult.constantVal &&
currentPhiState.constant_type == phiResult.constant_type) {
// 保持相同的常量
newState = currentPhiState;
} else {
// 不同的值必须变为Bottom
newState = SSAPValue(LatticeVal::Bottom);
}
} else {
// 已经是Bottom保持Bottom
newState = currentPhiState;
}
}
break; break;
} }
case Instruction::kAlloca: // 对应 kAlloca case Instruction::kAlloca: // 对应 kAlloca
@@ -486,6 +555,22 @@ void SCCPContext::ProcessInstruction(Instruction *inst) {
} }
} }
} }
if (DEBUG) {
std::cout << "New state: ";
if (newState.state == LatticeVal::Top) {
std::cout << "Top";
} else if (newState.state == LatticeVal::Constant) {
if (newState.constant_type == ValueType::Integer) {
std::cout << "Const<int>(" << std::get<int>(newState.constantVal) << ")";
} else {
std::cout << "Const<float>(" << std::get<float>(newState.constantVal) << ")";
}
} else {
std::cout << "Bottom";
}
std::cout << std::endl;
}
} }
// 辅助函数:处理单条控制流边 // 辅助函数:处理单条控制流边
@@ -493,14 +578,22 @@ void SCCPContext::ProcessEdge(const std::pair<BasicBlock *, BasicBlock *> &edge)
BasicBlock *fromBB = edge.first; BasicBlock *fromBB = edge.first;
BasicBlock *toBB = edge.second; BasicBlock *toBB = edge.second;
// 检查目标块是否已经可执行
bool wasAlreadyExecutable = executableBlocks.count(toBB) > 0;
// 标记目标块为可执行(如果还不是的话)
MarkBlockExecutable(toBB); MarkBlockExecutable(toBB);
// 对于目标块中的所有 Phi 指令,重新评估其值,因为可能有新的前驱被激活 // 如果目标块之前就已经可执行那么需要重新处理其中的phi节点
// 因为现在有新的前驱变为可执行phi节点的值可能需要更新
if (wasAlreadyExecutable) {
for (auto &inst_ptr : toBB->getInstructions()) { for (auto &inst_ptr : toBB->getInstructions()) {
if (dynamic_cast<PhiInst *>(inst_ptr.get())) { if (dynamic_cast<PhiInst *>(inst_ptr.get())) {
instWorkList.push(inst_ptr.get()); instWorkList.push(inst_ptr.get());
} }
} }
}
// 如果目标块是新变为可执行的MarkBlockExecutable已经添加了所有指令
} }
// 阶段1: 常量传播与折叠 // 阶段1: 常量传播与折叠
@@ -515,18 +608,29 @@ bool SCCPContext::PropagateConstants(Function *func) {
} }
} }
// 初始化函数参数为Bottom因为它们在编译时是未知的
for (auto arg : func->getArguments()) {
valueState[arg] = SSAPValue(LatticeVal::Bottom);
if (DEBUG) {
std::cout << "Initializing function argument " << arg->getName() << " to Bottom" << std::endl;
}
}
// 标记入口块为可执行 // 标记入口块为可执行
if (!func->getBasicBlocks().empty()) { if (!func->getBasicBlocks().empty()) {
MarkBlockExecutable(func->getEntryBlock()); MarkBlockExecutable(func->getEntryBlock());
} }
// 主循环:处理工作列表直到不动点 // 主循环:标准的SCCP工作列表算法
// 交替处理边工作列表和指令工作列表直到不动点
while (!instWorkList.empty() || !edgeWorkList.empty()) { while (!instWorkList.empty() || !edgeWorkList.empty()) {
// 处理所有待处理的CFG边
while (!edgeWorkList.empty()) { while (!edgeWorkList.empty()) {
ProcessEdge(edgeWorkList.front()); ProcessEdge(edgeWorkList.front());
edgeWorkList.pop(); edgeWorkList.pop();
} }
// 处理所有待处理的指令
while (!instWorkList.empty()) { while (!instWorkList.empty()) {
Instruction *inst = instWorkList.front(); Instruction *inst = instWorkList.front();
instWorkList.pop(); instWorkList.pop();

View File

@@ -42,7 +42,7 @@ bool SysYCFGOptUtils::SysYDelInstAfterBr(Function *func) {
++Branchiter; ++Branchiter;
while (Branchiter != instructions.end()) { while (Branchiter != instructions.end()) {
changed = true; changed = true;
Branchiter = instructions.erase(Branchiter); Branchiter = SysYIROptUtils::usedelete(Branchiter); // 删除指令
} }
if (Branch) { // 更新前驱后继关系 if (Branch) { // 更新前驱后继关系
@@ -77,6 +77,11 @@ bool SysYCFGOptUtils::SysYBlockMerge(Function *func) {
bool changed = false; bool changed = false;
for (auto blockiter = func->getBasicBlocks().begin(); blockiter != func->getBasicBlocks().end();) { for (auto blockiter = func->getBasicBlocks().begin(); blockiter != func->getBasicBlocks().end();) {
// 检查当前块是是不是entry块
if( blockiter->get() == func->getEntryBlock() ) {
blockiter++;
continue; // 跳过入口块
}
if (blockiter->get()->getNumSuccessors() == 1) { if (blockiter->get()->getNumSuccessors() == 1) {
// 如果当前块只有一个后继块 // 如果当前块只有一个后继块
// 且后继块只有一个前驱块 // 且后继块只有一个前驱块
@@ -86,7 +91,7 @@ bool SysYCFGOptUtils::SysYBlockMerge(Function *func) {
BasicBlock *block = blockiter->get(); BasicBlock *block = blockiter->get();
BasicBlock *nextBlock = blockiter->get()->getSuccessors()[0]; BasicBlock *nextBlock = blockiter->get()->getSuccessors()[0];
// auto nextarguments = nextBlock->getArguments(); // auto nextarguments = nextBlock->getArguments();
// 删除br指令 // 删除block的br指令
if (block->getNumInstructions() != 0) { if (block->getNumInstructions() != 0) {
auto thelastinstinst = block->terminator(); auto thelastinstinst = block->terminator();
if (thelastinstinst->get()->isUnconditional()) { if (thelastinstinst->get()->isUnconditional()) {
@@ -98,14 +103,21 @@ bool SysYCFGOptUtils::SysYBlockMerge(Function *func) {
if (brinst->getThenBlock() == brinst->getElseBlock()) { if (brinst->getThenBlock() == brinst->getElseBlock()) {
thelastinstinst = SysYIROptUtils::usedelete(thelastinstinst); thelastinstinst = SysYIROptUtils::usedelete(thelastinstinst);
} }
else{
assert(false && "SysYBlockMerge: unexpected conditional branch with different then and else blocks");
}
} }
} }
// 将后继块的指令移动到当前块 // 将后继块的指令移动到当前块
// 并将后继块的父指针改为当前块 // 并将后继块的父指针改为当前块
for (auto institer = nextBlock->begin(); institer != nextBlock->end();) { for (auto institer = nextBlock->begin(); institer != nextBlock->end();) {
institer->get()->setParent(block); // institer->get()->setParent(block);
block->getInstructions().emplace_back(institer->release()); // block->getInstructions().emplace_back(institer->release());
institer = nextBlock->getInstructions().erase(institer); // 用usedelete删除会导致use关系被删除我只希望移动指令到当前块
// institer = SysYIROptUtils::usedelete(institer);
// institer = nextBlock->getInstructions().erase(institer);
institer = nextBlock->moveInst(institer, block->getInstructions().end(), block);
} }
// 更新前驱后继关系,类似树节点操作 // 更新前驱后继关系,类似树节点操作
block->removeSuccessor(nextBlock); block->removeSuccessor(nextBlock);
@@ -288,13 +300,12 @@ bool SysYCFGOptUtils::SysYDelEmptyBlock(Function *func, IRBuilder *pBuilder) {
continue; continue;
} }
std::function<Value *(Value *, BasicBlock *)> getUltimateSourceValue = [&](Value *val, std::function<Value *(Value *, BasicBlock *)> getUltimateSourceValue = [&](Value *val, BasicBlock *currentDefBlock) -> Value * {
BasicBlock *currentDefBlock) -> Value * {
// 如果值不是指令,例如常量或函数参数,则它本身就是最终来源 if(!dynamic_cast<Instruction *>(val)) {
if (auto instr = dynamic_cast<Instruction *>(val)) { // Assuming Value* has a method to check if it's an instruction // 如果 val 不是指令,直接返回它
return val; return val;
} }
Instruction *inst = dynamic_cast<Instruction *>(val); Instruction *inst = dynamic_cast<Instruction *>(val);
// 如果定义指令不在任何空块中,它就是最终来源 // 如果定义指令不在任何空块中,它就是最终来源
if (!emptyBlockRedirectMap.count(currentDefBlock)) { if (!emptyBlockRedirectMap.count(currentDefBlock)) {

View File

@@ -389,26 +389,7 @@ void SysYIRGenerator::compute() {
case BinaryOp::ADD: resultValue = builder.createAddInst(lhs, rhs); break; case BinaryOp::ADD: resultValue = builder.createAddInst(lhs, rhs); break;
case BinaryOp::SUB: resultValue = builder.createSubInst(lhs, rhs); break; case BinaryOp::SUB: resultValue = builder.createSubInst(lhs, rhs); break;
case BinaryOp::MUL: resultValue = builder.createMulInst(lhs, rhs); break; case BinaryOp::MUL: resultValue = builder.createMulInst(lhs, rhs); break;
case BinaryOp::DIV: { case BinaryOp::DIV: resultValue = builder.createDivInst(lhs, rhs); break;
ConstantInteger *rhsConst = dynamic_cast<ConstantInteger *>(rhs);
if (rhsConst) {
int divisor = rhsConst->getInt();
if (divisor > 0 && (divisor & (divisor - 1)) == 0) {
int shift = 0;
int temp = divisor;
while (temp > 1) {
temp >>= 1;
shift++;
}
resultValue = builder.createSRAInst(lhs, ConstantInteger::get(shift));
} else {
resultValue = builder.createDivInst(lhs, rhs);
}
} else {
resultValue = builder.createDivInst(lhs, rhs);
}
break;
}
case BinaryOp::MOD: resultValue = builder.createRemInst(lhs, rhs); break; case BinaryOp::MOD: resultValue = builder.createRemInst(lhs, rhs); break;
} }
} else if (commonType == Type::getFloatType()) { } else if (commonType == Type::getFloatType()) {
@@ -1210,15 +1191,25 @@ std::any SysYIRGenerator::visitFuncDef(SysYParser::FuncDefContext *ctx){
for(int i = 0; i < paramActualTypes.size(); ++i) { for(int i = 0; i < paramActualTypes.size(); ++i) {
Argument* arg = new Argument(paramActualTypes[i], function, i, paramNames[i]); Argument* arg = new Argument(paramActualTypes[i], function, i, paramNames[i]);
function->insertArgument(arg); function->insertArgument(arg);
}
// 先将所有参数名字注册到符号表中确保alloca不会使用相同的名字
for (int i = 0; i < paramNames.size(); ++i) {
// 预先注册参数名字这样addVariable就会使用不同的后缀
module->registerParameterName(paramNames[i]);
} }
auto funcArgs = function->getArguments(); auto funcArgs = function->getArguments();
std::vector<AllocaInst *> allocas; std::vector<AllocaInst *> allocas;
for (int i = 0; i < paramActualTypes.size(); ++i) { for (int i = 0; i < paramActualTypes.size(); ++i) {
AllocaInst *alloca = builder.createAllocaInst(Type::getPointerType(paramActualTypes[i]), paramNames[i]); // 使用函数特定的前缀来确保参数alloca名字唯一
std::string allocaName = name + "_param_" + paramNames[i];
AllocaInst *alloca = builder.createAllocaInst(Type::getPointerType(paramActualTypes[i]), allocaName);
// 直接设置唯一名字不依赖addVariable的命名逻辑
alloca->setName(allocaName);
allocas.push_back(alloca); allocas.push_back(alloca);
module->addVariable(paramNames[i], alloca); // 直接添加到符号表,使用原参数名作为查找键
module->addVariableDirectly(paramNames[i], alloca);
} }
for(int i = 0; i < paramActualTypes.size(); ++i) { for(int i = 0; i < paramActualTypes.size(); ++i) {
@@ -1287,6 +1278,45 @@ std::any SysYIRGenerator::visitAssignStmt(SysYParser::AssignStmtContext *ctx) {
if (dynamic_cast<AllocaInst*>(variable) || dynamic_cast<GlobalValue*>(variable)) { if (dynamic_cast<AllocaInst*>(variable) || dynamic_cast<GlobalValue*>(variable)) {
LValue = variable; LValue = variable;
} }
// 标量变量的类型推断
Type* LType = builder.getIndexedType(variable->getType(), indices);
Value* RValue = computeExp(ctx->exp(), LType); // 右值计算
Type* RType = RValue->getType();
// TODO:computeExp处理了类型转换可以考虑删除判断逻辑
if (LType != RType) {
ConstantValue *constValue = dynamic_cast<ConstantValue *>(RValue);
if (constValue != nullptr) {
if (LType == Type::getFloatType()) {
if(dynamic_cast<ConstantInteger *>(constValue)) {
// 如果是整型常量,转换为浮点型
RValue = ConstantFloating::get(static_cast<float>(constValue->getInt()));
} else if (dynamic_cast<ConstantFloating *>(constValue)) {
// 如果是浮点型常量,直接使用
RValue = ConstantFloating::get(static_cast<float>(constValue->getFloat()));
}
} else { // 假设如果不是浮点型,就是整型
if(dynamic_cast<ConstantFloating *>(constValue)) {
// 如果是浮点型常量,转换为整型
RValue = ConstantInteger::get(static_cast<int>(constValue->getFloat()));
} else if (dynamic_cast<ConstantInteger *>(constValue)) {
// 如果是整型常量,直接使用
RValue = ConstantInteger::get(static_cast<int>(constValue->getInt()));
}
}
} else {
if (LType == Type::getFloatType() && RType != Type::getFloatType()) {
RValue = builder.createItoFInst(RValue);
} else if (LType != Type::getFloatType() && RType == Type::getFloatType()) {
RValue = builder.createFtoIInst(RValue);
}
// 如果两者都是同一类型,就不需要转换
}
}
builder.createStoreInst(RValue, LValue);
} }
else { else {
// 对于数组或多维数组的左值处理 // 对于数组或多维数组的左值处理
@@ -1324,15 +1354,9 @@ std::any SysYIRGenerator::visitAssignStmt(SysYParser::AssignStmtContext *ctx) {
} }
// 左值为地址 // 左值为地址
LValue = getGEPAddressInst(gepBasePointer, gepIndices); LValue = getGEPAddressInst(gepBasePointer, gepIndices);
}
// Value* RValue = std::any_cast<Value *>(visitExp(ctx->exp())); // 右值 // 数组变量的类型推断使用gepIndices和gepBasePointer的类型
Type* LType = builder.getIndexedType(gepBasePointer->getType(), gepIndices);
// 先推断 LValue 的类型
// 如果 LValue 是指向数组的指针,则需要根据 indices 获取正确的类型
// 如果 LValue 是标量,则直接使用其类型
// 注意LValue 的类型可能是指向数组的指针 (e.g., int(*)[3]) 或者指向标量的指针 (e.g., int*) 也能推断
Type* LType = builder.getIndexedType(variable->getType(), indices);
Value* RValue = computeExp(ctx->exp(), LType); // 右值计算 Value* RValue = computeExp(ctx->exp(), LType); // 右值计算
Type* RType = RValue->getType(); Type* RType = RValue->getType();
@@ -1356,19 +1380,21 @@ std::any SysYIRGenerator::visitAssignStmt(SysYParser::AssignStmtContext *ctx) {
} else if (dynamic_cast<ConstantInteger *>(constValue)) { } else if (dynamic_cast<ConstantInteger *>(constValue)) {
// 如果是整型常量,直接使用 // 如果是整型常量,直接使用
RValue = ConstantInteger::get(static_cast<int>(constValue->getInt())); RValue = ConstantInteger::get(static_cast<int>(constValue->getInt()));
} }
} }
} else { } else {
if (LType == Type::getFloatType()) { if (LType == Type::getFloatType() && RType != Type::getFloatType()) {
RValue = builder.createItoFInst(RValue); RValue = builder.createItoFInst(RValue);
} else { // 假设如果不是浮点型,就是整型 } else if (LType != Type::getFloatType() && RType == Type::getFloatType()) {
RValue = builder.createFtoIInst(RValue); RValue = builder.createFtoIInst(RValue);
} }
// 如果两者都是同一类型,就不需要转换
} }
} }
builder.createStoreInst(RValue, LValue); builder.createStoreInst(RValue, LValue);
}
invalidateExpressionsOnStore(LValue); invalidateExpressionsOnStore(LValue);
return std::any(); return std::any();
} }
@@ -1535,7 +1561,7 @@ std::any SysYIRGenerator::visitWhileStmt(SysYParser::WhileStmtContext *ctx) {
} }
builder.createUncondBrInst(headBlock); builder.createUncondBrInst(headBlock);
BasicBlock::conectBlocks(builder.getBasicBlock(), exitBlock); BasicBlock::conectBlocks(builder.getBasicBlock(), headBlock);
builder.popBreakBlock(); builder.popBreakBlock();
builder.popContinueBlock(); builder.popContinueBlock();
@@ -1652,11 +1678,19 @@ std::any SysYIRGenerator::visitLValue(SysYParser::LValueContext *ctx) {
break; break;
} }
} }
if (allIndicesConstant) { // 如果是常量变量且所有索引都是常量,并且不是数组名单独出现的情况
if (allIndicesConstant && !dims.empty()) {
// 如果是常量变量且所有索引都是常量,直接通过 getByIndices 获取编译时值 // 如果是常量变量且所有索引都是常量,直接通过 getByIndices 获取编译时值
// 这个方法会根据索引深度返回最终的标量值或指向子数组的指针 (作为 ConstantValue/Variable) // 这个方法会根据索引深度返回最终的标量值或指向子数组的指针 (作为 ConstantValue/Variable)
return constVar->getByIndices(dims); return constVar->getByIndices(dims);
} }
// 如果dims为空检查是否是常量标量
if (dims.empty() && declaredNumDims == 0) {
// 常量标量,直接返回其值
// 默认传入空索引列表,表示访问标量本身
return constVar->getByIndices(dims);
}
// 如果dims为空但不是标量数组名单独出现需要走GEP路径来实现数组到指针的退化
} }
// 3. 处理可变变量 (AllocaInst/GlobalValue) 或带非常量索引的常量变量 // 3. 处理可变变量 (AllocaInst/GlobalValue) 或带非常量索引的常量变量
@@ -1666,7 +1700,8 @@ std::any SysYIRGenerator::visitLValue(SysYParser::LValueContext *ctx) {
if (dims.empty() && declaredNumDims == 0) { if (dims.empty() && declaredNumDims == 0) {
if (dynamic_cast<AllocaInst*>(variable) || dynamic_cast<GlobalValue*>(variable)) { if (dynamic_cast<AllocaInst*>(variable) || dynamic_cast<GlobalValue*>(variable)) {
targetAddress = variable; targetAddress = variable;
} else { }
else {
assert(false && "Unhandled scalar variable type in LValue access."); assert(false && "Unhandled scalar variable type in LValue access.");
return static_cast<Value*>(nullptr); return static_cast<Value*>(nullptr);
} }
@@ -1681,16 +1716,39 @@ std::any SysYIRGenerator::visitLValue(SysYParser::LValueContext *ctx) {
} else { } else {
gepBasePointer = alloc; gepBasePointer = alloc;
gepIndices.push_back(ConstantInteger::get(0)); gepIndices.push_back(ConstantInteger::get(0));
if (dims.empty() && declaredNumDims > 0) {
// 数组名单独出现没有索引在SysY中多维数组名应该退化为指向第一行的指针
// 对于二维数组 T[M][N],退化为 T(*)[N]需要GEP: getelementptr T[M][N], T[M][N]* ptr, i32 0, i32 0
// 第一个i32 0: 选择数组本身第二个i32 0: 选择第0行
// 结果类型: T[N]*
gepIndices.push_back(ConstantInteger::get(0));
} else {
// 正常的数组元素访问
gepIndices.insert(gepIndices.end(), dims.begin(), dims.end()); gepIndices.insert(gepIndices.end(), dims.begin(), dims.end());
} }
}
} else if (GlobalValue *glob = dynamic_cast<GlobalValue *>(variable)) { } else if (GlobalValue *glob = dynamic_cast<GlobalValue *>(variable)) {
gepBasePointer = glob; gepBasePointer = glob;
gepIndices.push_back(ConstantInteger::get(0)); gepIndices.push_back(ConstantInteger::get(0));
if (dims.empty() && declaredNumDims > 0) {
// 全局数组名单独出现(没有索引):应该退化为指向第一行的指针
// 需要添加一个额外的i32 0索引
gepIndices.push_back(ConstantInteger::get(0));
} else {
// 正常的数组元素访问
gepIndices.insert(gepIndices.end(), dims.begin(), dims.end()); gepIndices.insert(gepIndices.end(), dims.begin(), dims.end());
}
} else if (ConstantVariable *constV = dynamic_cast<ConstantVariable *>(variable)) { } else if (ConstantVariable *constV = dynamic_cast<ConstantVariable *>(variable)) {
gepBasePointer = constV; gepBasePointer = constV;
gepIndices.push_back(ConstantInteger::get(0)); gepIndices.push_back(ConstantInteger::get(0));
if (dims.empty() && declaredNumDims > 0) {
// 常量数组名单独出现(没有索引):应该退化为指向第一行的指针
// 需要添加一个额外的i32 0索引
gepIndices.push_back(ConstantInteger::get(0));
} else {
// 正常的数组元素访问
gepIndices.insert(gepIndices.end(), dims.begin(), dims.end()); gepIndices.insert(gepIndices.end(), dims.begin(), dims.end());
}
} else { } else {
assert(false && "LValue variable type not supported for GEP base pointer."); assert(false && "LValue variable type not supported for GEP base pointer.");
return static_cast<Value *>(nullptr); return static_cast<Value *>(nullptr);
@@ -1772,10 +1830,10 @@ std::any SysYIRGenerator::visitCall(SysYParser::CallContext *ctx) {
// 获取形参列表。`getArguments()` 返回的是 `Argument*` 的集合, // 获取形参列表。`getArguments()` 返回的是 `Argument*` 的集合,
// 每个 `Argument` 代表一个函数形参,其 `getType()` 就是指向形参的类型的指针类型。 // 每个 `Argument` 代表一个函数形参,其 `getType()` 就是指向形参的类型的指针类型。
auto formalParams = function->getArguments(); const auto& formalParams = function->getArguments();
// 检查实参和形参数量是否匹配。 // 检查实参和形参数量是否匹配。
if (args.size() != formalParams.size()) { if (args.size() != function->getNumArguments()) {
std::cerr << "Error: Function call argument count mismatch for function '" << funcName << "'." << std::endl; std::cerr << "Error: Function call argument count mismatch for function '" << funcName << "'." << std::endl;
assert(false && "Function call argument count mismatch!"); assert(false && "Function call argument count mismatch!");
} }
@@ -1807,15 +1865,27 @@ std::any SysYIRGenerator::visitCall(SysYParser::CallContext *ctx) {
} else if (formalParamExpectedValueType->isFloat() && actualArgType->isInt()) { } else if (formalParamExpectedValueType->isFloat() && actualArgType->isInt()) {
args[i] = builder.createItoFInst(args[i]); args[i] = builder.createItoFInst(args[i]);
} }
// 2. 指针类型转换 (例如数组退化:`[N x T]*` 到 `T*`,或兼容指针类型之间) TODO不清楚有没有这种样例 // 2. 指针类型转换 (例如数组退化:`[N x T]*` 到 `T*`,或兼容指针类型之间)
// 这种情况常见于数组参数,实参可能是一个更具体的数组指针类型, // 这种情况常见于数组参数,实参可能是一个更具体的数组指针类型,
// 而形参是其退化后的基础指针类型。LLVM 的 `bitcast` 指令可以用于 // 而形参是其退化后的基础指针类型。
// 在相同大小的指针类型之间进行转换,这对于数组退化至关重要。 else if (formalParamExpectedValueType->isPointer() && actualArgType->isPointer()) {
// else if (formalParamType->isPointer() && actualArgType->isPointer()) { // 检查是否是数组指针到元素指针的decay
// 检查指针基类型是否兼容,或者是否是数组退化导致的类型不同。 // 例如:[N x T]* -> T*
// 使用 bitcast auto formalPtrType = formalParamExpectedValueType->as<PointerType>();
// args[i] = builder.createBitCastInst(args[i], formalParamType); auto actualPtrType = actualArgType->as<PointerType>();
// }
if (formalPtrType && actualPtrType && actualPtrType->getBaseType()->isArray()) {
auto actualArrayType = actualPtrType->getBaseType()->as<ArrayType>();
if (actualArrayType &&
formalPtrType->getBaseType() == actualArrayType->getElementType()) {
// 这是数组decay的情况添加GEP来获取数组的第一个元素
std::vector<Value*> indices;
indices.push_back(ConstantInteger::get(0)); // 第一个索引:解引用指针
indices.push_back(ConstantInteger::get(0)); // 第二个索引:获取数组第一个元素
args[i] = getGEPAddressInst(args[i], indices);
}
}
}
// 3. 其他未预期的类型不匹配 // 3. 其他未预期的类型不匹配
// 如果代码执行到这里,说明存在编译器前端未处理的类型不兼容或错误。 // 如果代码执行到这里,说明存在编译器前端未处理的类型不兼容或错误。
else { else {
@@ -2199,15 +2269,23 @@ void Utils::createExternalFunction(
const std::vector<std::string> &paramNames, const std::vector<std::string> &paramNames,
const std::vector<std::vector<Value *>> &paramDims, Type *returnType, const std::vector<std::vector<Value *>> &paramDims, Type *returnType,
const std::string &funcName, Module *pModule, IRBuilder *pBuilder) { const std::string &funcName, Module *pModule, IRBuilder *pBuilder) {
auto funcType = Type::getFunctionType(returnType, paramTypes); // 根据paramDims调整参数类型数组参数需要转换为指针类型
std::vector<Type *> adjustedParamTypes = paramTypes;
for (int i = 0; i < paramTypes.size() && i < paramDims.size(); ++i) {
if (!paramDims[i].empty()) {
// 如果参数有维度信息,说明是数组参数,转换为指针类型
adjustedParamTypes[i] = Type::getPointerType(paramTypes[i]);
}
}
auto funcType = Type::getFunctionType(returnType, adjustedParamTypes);
auto function = pModule->createExternalFunction(funcName, funcType); auto function = pModule->createExternalFunction(funcName, funcType);
auto entry = function->getEntryBlock(); auto entry = function->getEntryBlock();
pBuilder->setPosition(entry, entry->end()); pBuilder->setPosition(entry, entry->end());
for (int i = 0; i < paramTypes.size(); ++i) { for (int i = 0; i < paramTypes.size(); ++i) {
auto arg = new Argument(paramTypes[i], function, i, paramNames[i]); auto arg = new Argument(adjustedParamTypes[i], function, i, paramNames[i]);
auto alloca = pBuilder->createAllocaInst( auto alloca = pBuilder->createAllocaInst(
Type::getPointerType(paramTypes[i]), paramNames[i]); Type::getPointerType(adjustedParamTypes[i]), paramNames[i]);
function->insertArgument(arg); function->insertArgument(arg);
auto store = pBuilder->createStoreInst(arg, alloca); auto store = pBuilder->createStoreInst(arg, alloca);
pModule->addVariable(paramNames[i], alloca); pModule->addVariable(paramNames[i], alloca);

View File

@@ -110,6 +110,7 @@ int main(int argc, char **argv) {
// 如果指定停止在 AST 阶段,则打印并退出 // 如果指定停止在 AST 阶段,则打印并退出
if (argStopAfter == "ast") { if (argStopAfter == "ast") {
cout << moduleAST->toStringTree(true) << '\n'; cout << moduleAST->toStringTree(true) << '\n';
sysy::cleanupIRPools(); // 清理内存池
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
@@ -132,7 +133,7 @@ int main(int argc, char **argv) {
if (DEBUG) { if (DEBUG) {
cout << "=== Init IR ===\n"; cout << "=== Init IR ===\n";
SysYPrinter(moduleIR).printIR(); // 临时打印器用于调试 moduleIR->print(cout); // 使用新实现的print方法直接打印IR
} }
// 创建 Pass 管理器并运行优化管道 // 创建 Pass 管理器并运行优化管道
@@ -144,10 +145,26 @@ int main(int argc, char **argv) {
// a) 如果指定停止在 IR 阶段,则打印最终 IR 并退出 // a) 如果指定停止在 IR 阶段,则打印最终 IR 并退出
if (argStopAfter == "ir" || argStopAfter == "ird") { if (argStopAfter == "ir" || argStopAfter == "ird") {
// 打印最终 IR // 打印最终 IR
cout << "=== Final IR ===\n"; if (DEBUG) cerr << "=== Final IR ===\n";
SysYPrinter printer(moduleIR); // 在这里创建打印器,因为可能之前调试时用过临时打印器 if (!argOutputFilename.empty()) {
printer.printIR(); // 输出到指定文件
ofstream fout(argOutputFilename);
if (not fout.is_open()) {
cerr << "Failed to open output file: " << argOutputFilename << endl;
moduleIR->cleanup(); // 清理模块
sysy::cleanupIRPools(); // 清理内存池
return EXIT_FAILURE;
}
moduleIR->print(fout);
fout.close();
} else {
// 输出到标准输出
moduleIR->print(cout);
}
moduleIR->cleanup(); // 清理模块
sysy::cleanupIRPools(); // 清理内存池
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
// b) 如果未停止在 IR 阶段,则继续生成汇编 (后端) // b) 如果未停止在 IR 阶段,则继续生成汇编 (后端)
@@ -166,6 +183,8 @@ int main(int argc, char **argv) {
ofstream fout(argOutputFilename); ofstream fout(argOutputFilename);
if (not fout.is_open()) { if (not fout.is_open()) {
cerr << "Failed to open output file: " << argOutputFilename << endl; cerr << "Failed to open output file: " << argOutputFilename << endl;
moduleIR->cleanup(); // 清理模块
sysy::cleanupIRPools(); // 清理内存池
return EXIT_FAILURE; return EXIT_FAILURE;
} }
fout << asmCode << endl; fout << asmCode << endl;
@@ -173,6 +192,8 @@ int main(int argc, char **argv) {
} else { } else {
cout << asmCode << endl; cout << asmCode << endl;
} }
moduleIR->cleanup(); // 清理模块
sysy::cleanupIRPools(); // 清理内存池
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
@@ -181,5 +202,7 @@ int main(int argc, char **argv) {
cout << "Compilation completed. No output specified (neither -s nor -S). Exiting.\n"; cout << "Compilation completed. No output specified (neither -s nor -S). Exiting.\n";
// return EXIT_SUCCESS; // 或者这里调用一个链接器生成可执行文件 // return EXIT_SUCCESS; // 或者这里调用一个链接器生成可执行文件
moduleIR->cleanup(); // 清理模块
sysy::cleanupIRPools(); // 清理内存池
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }