Compare commits
39 Commits
backend-la
...
midend-llv
| Author | SHA1 | Date | |
|---|---|---|---|
| a1cca3c95a | |||
| 1361156b0d | |||
| 46179e3866 | |||
| 038552f58b | |||
| 4d0e2d73ea | |||
| ad19a6715f | |||
| d1ba140657 | |||
| 2c5e4cead1 | |||
| 6b92020bc4 | |||
| c867bda9b4 | |||
| 6b9ad0566d | |||
| c507b98199 | |||
| ba21bb3203 | |||
| 8aa5ba692f | |||
| d732800149 | |||
| 37f2a01783 | |||
| 5d343f42a5 | |||
| a4406e0112 | |||
| 08fcda939b | |||
| 5f63554ca3 | |||
| 4db3cc3fb5 | |||
| 17f1bed310 | |||
| b848ffca5a | |||
| 603506d142 | |||
| 0179c13cf4 | |||
| 7e5f6800b7 | |||
| 64ba25a77e | |||
| 208d5528b5 | |||
| a269366ac5 | |||
| 1b9a7a4827 | |||
| b2c2f3289d | |||
| 0ecd47f0ac | |||
| f24cc7ec88 | |||
| c8a8bf9a37 | |||
| 446a6a6fcb | |||
| d8b004e5e5 | |||
| cd814de495 | |||
| 58c8cd53f5 | |||
| 91f755959b |
@ -2,66 +2,67 @@
|
||||
|
||||
# runit-single.sh - 用于编译和测试单个或少量 SysY 程序的脚本
|
||||
# 模仿 runit.sh 的功能,但以具体文件路径作为输入。
|
||||
# 此脚本应该位于 mysysy/script/
|
||||
|
||||
export ASAN_OPTIONS=detect_leaks=0
|
||||
|
||||
# --- 配置区 ---
|
||||
# 请根据你的环境修改这些路径
|
||||
# 假设此脚本位于你的项目根目录或一个脚本目录中
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
||||
# 默认寻找项目根目录下的 build 和 lib
|
||||
BUILD_BIN_DIR="${SCRIPT_DIR}/../build/bin"
|
||||
LIB_DIR="${SCRIPT_DIR}/../lib"
|
||||
# 临时文件会存储在脚本所在目录的 tmp 子目录中
|
||||
TMP_DIR="${SCRIPT_DIR}/tmp"
|
||||
|
||||
# 定义编译器和模拟器
|
||||
SYSYC="${BUILD_BIN_DIR}/sysyc"
|
||||
LLC_CMD="llc-19" # 新增
|
||||
GCC_RISCV64="riscv64-linux-gnu-gcc"
|
||||
QEMU_RISCV64="qemu-riscv64"
|
||||
|
||||
# --- 初始化变量 ---
|
||||
EXECUTE_MODE=false
|
||||
IR_EXECUTE_MODE=false # 新增
|
||||
CLEAN_MODE=false
|
||||
OPTIMIZE_FLAG="" # 用于存储 -O1 标志
|
||||
SYSYC_TIMEOUT=10 # sysyc 编译超时 (秒)
|
||||
GCC_TIMEOUT=10 # gcc 编译超时 (秒)
|
||||
EXEC_TIMEOUT=5 # qemu 自动化执行超时 (秒)
|
||||
MAX_OUTPUT_LINES=50 # 对比失败时显示的最大行数
|
||||
SY_FILES=() # 存储用户提供的 .sy 文件列表
|
||||
OPTIMIZE_FLAG=""
|
||||
SYSYC_TIMEOUT=30
|
||||
LLC_TIMEOUT=10 # 新增
|
||||
GCC_TIMEOUT=10
|
||||
EXEC_TIMEOUT=30
|
||||
MAX_OUTPUT_LINES=20
|
||||
SY_FILES=()
|
||||
PASSED_CASES=0
|
||||
FAILED_CASES_LIST=""
|
||||
INTERRUPTED=false # 新增
|
||||
|
||||
# =================================================================
|
||||
# --- 函数定义 ---
|
||||
# =================================================================
|
||||
show_help() {
|
||||
echo "用法: $0 [文件1.sy] [文件2.sy] ... [选项]"
|
||||
echo "编译并测试指定的 .sy 文件。"
|
||||
echo ""
|
||||
echo "如果找到对应的 .in/.out 文件,则进行自动化测试。否则,进入交互模式。"
|
||||
echo "编译并测试指定的 .sy 文件。必须提供 -e 或 -eir 之一。"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " -e, --executable 编译为可执行文件并运行测试 (必须)。"
|
||||
echo " -e 通过汇编运行测试 (sysyc -> gcc -> qemu)。"
|
||||
echo " -eir 通过IR运行测试 (sysyc -> llc -> gcc -> qemu)。"
|
||||
echo " -c, --clean 清理 tmp 临时目录下的所有文件。"
|
||||
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 " -et N 设置 qemu 自动化执行超时为 N 秒 (默认: 5)。"
|
||||
echo " -ml N, --max-lines N 当输出对比失败时,最多显示 N 行内容 (默认: 50)。"
|
||||
echo " -et N 设置 qemu 自动化执行超时为 N 秒 (默认: 30)。"
|
||||
echo " -ml N, --max-lines N 当输出对比失败时,最多显示 N 行内容 (默认: 20)。"
|
||||
echo " -h, --help 显示此帮助信息并退出。"
|
||||
echo ""
|
||||
echo "可在任何时候按 Ctrl+C 来中断测试并显示当前已完成的测例总结。"
|
||||
}
|
||||
|
||||
# --- 新增功能: 显示文件内容并根据行数截断 ---
|
||||
display_file_content() {
|
||||
local file_path="$1"
|
||||
local title="$2"
|
||||
local max_lines="$3"
|
||||
|
||||
if [ ! -f "$file_path" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [ ! -f "$file_path" ]; then return; fi
|
||||
echo -e "$title"
|
||||
local line_count
|
||||
line_count=$(wc -l < "$file_path")
|
||||
|
||||
if [ "$line_count" -gt "$max_lines" ]; then
|
||||
head -n "$max_lines" "$file_path"
|
||||
echo -e "\e[33m[... 输出已截断,共 ${line_count} 行 ...]\e[0m"
|
||||
@ -70,55 +71,79 @@ display_file_content() {
|
||||
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
|
||||
case "$1" in
|
||||
-e|--executable)
|
||||
EXECUTE_MODE=true
|
||||
shift # 消耗选项
|
||||
;;
|
||||
-c|--clean)
|
||||
CLEAN_MODE=true
|
||||
shift # 消耗选项
|
||||
;;
|
||||
-O1)
|
||||
OPTIMIZE_FLAG="-O1"
|
||||
shift # 消耗选项
|
||||
;;
|
||||
-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
|
||||
;;
|
||||
*) # 其他参数被视为文件路径
|
||||
-e|--executable) EXECUTE_MODE=true; shift ;;
|
||||
-eir) IR_EXECUTE_MODE=true; shift ;; # 新增
|
||||
-c|--clean) CLEAN_MODE=true; shift ;;
|
||||
-O1) OPTIMIZE_FLAG="-O1"; shift ;;
|
||||
-lct) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then LLC_TIMEOUT="$2"; shift 2; else echo "错误: -lct 需要一个正整数参数。" >&2; exit 1; fi ;; # 新增
|
||||
-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
|
||||
SY_FILES+=("$1")
|
||||
else
|
||||
echo "警告: 无效文件或不是 .sy 文件,已忽略: $1"
|
||||
fi
|
||||
shift # 消耗文件参数
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
if ${CLEAN_MODE}; then
|
||||
echo "检测到 -c/--clean 选项,正在清空 ${TMP_DIR}..."
|
||||
if [ -d "${TMP_DIR}" ]; then
|
||||
@ -127,19 +152,22 @@ if ${CLEAN_MODE}; then
|
||||
else
|
||||
echo "临时目录 ${TMP_DIR} 不存在,无需清理。"
|
||||
fi
|
||||
|
||||
if [ ${#SY_FILES[@]} -eq 0 ] && ! ${EXECUTE_MODE}; then
|
||||
if [ ${#SY_FILES[@]} -eq 0 ] && ! ${EXECUTE_MODE} && ! ${IR_EXECUTE_MODE}; then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- 主逻辑开始 ---
|
||||
if ! ${EXECUTE_MODE}; then
|
||||
echo "错误: 请提供 -e 或 --executable 选项来运行测试。"
|
||||
if ! ${EXECUTE_MODE} && ! ${IR_EXECUTE_MODE}; then
|
||||
echo "错误: 请提供 -e 或 -eir 选项来运行测试。"
|
||||
show_help
|
||||
exit 1
|
||||
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
|
||||
echo "错误: 未提供任何 .sy 文件作为输入。"
|
||||
show_help
|
||||
@ -151,18 +179,17 @@ TOTAL_CASES=${#SY_FILES[@]}
|
||||
|
||||
echo "SysY 单例测试运行器启动..."
|
||||
if [ -n "$OPTIMIZE_FLAG" ]; then echo "优化等级: ${OPTIMIZE_FLAG}"; fi
|
||||
echo "超时设置: sysyc=${SYSYC_TIMEOUT}s, gcc=${GCC_TIMEOUT}s, qemu=${EXEC_TIMEOUT}s"
|
||||
echo "失败输出最大行数: ${MAX_OUTPUT_LINES}"
|
||||
echo "超时设置: sysyc=${SYSYC_TIMEOUT}s, llc=${LLC_TIMEOUT}s, gcc=${GCC_TIMEOUT}s, qemu=${EXEC_TIMEOUT}s"
|
||||
echo ""
|
||||
|
||||
for sy_file in "${SY_FILES[@]}"; do
|
||||
is_passed=1
|
||||
compilation_ok=1
|
||||
base_name=$(basename "${sy_file}" .sy)
|
||||
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_debug_file="${TMP_DIR}/${base_name}_d.s"
|
||||
executable_file="${TMP_DIR}/${base_name}"
|
||||
input_file="${source_dir}/${base_name}.in"
|
||||
output_reference_file="${source_dir}/${base_name}.out"
|
||||
@ -171,47 +198,39 @@ for sy_file in "${SY_FILES[@]}"; do
|
||||
echo "======================================================================"
|
||||
echo "正在处理: ${sy_file}"
|
||||
|
||||
# --- 本次修改点: 拷贝源文件到 tmp 目录 ---
|
||||
echo " 拷贝源文件到 ${TMP_DIR}..."
|
||||
cp "${sy_file}" "${TMP_DIR}/$(basename "${sy_file}")"
|
||||
if [ -f "${input_file}" ]; then
|
||||
cp "${input_file}" "${TMP_DIR}/$(basename "${input_file}")"
|
||||
fi
|
||||
if [ -f "${output_reference_file}" ]; then
|
||||
cp "${output_reference_file}" "${TMP_DIR}/$(basename "${output_reference_file}")"
|
||||
fi
|
||||
|
||||
# 步骤 1: sysyc 编译
|
||||
echo " 使用 sysyc 编译 (超时 ${SYSYC_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
|
||||
# --- 编译阶段 ---
|
||||
if ${IR_EXECUTE_MODE}; then
|
||||
# 路径1: sysyc -> llc -> gcc
|
||||
echo " [1/3] 使用 sysyc 编译为 IR (超时 ${SYSYC_TIMEOUT}s)..."
|
||||
timeout -s KILL ${SYSYC_TIMEOUT} "${SYSYC}" -s ir "${sy_file}" ${OPTIMIZE_FLAG} -o "${ir_file}"
|
||||
if [ $? -ne 0 ]; then echo -e "\e[31m错误: SysY (IR) 编译失败或超时。\e[0m"; compilation_ok=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
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "\e[31m错误: GCC 编译失败或超时。\e[0m"
|
||||
is_passed=0
|
||||
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
|
||||
|
||||
if [ "$compilation_ok" -eq 1 ]; then
|
||||
echo " [3/3] 使用 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
|
||||
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
|
||||
|
||||
# 步骤 3: 执行与测试
|
||||
if [ "$is_passed" -eq 1 ]; then
|
||||
# 检查是自动化测试还是交互模式
|
||||
# --- 执行与测试阶段 (公共逻辑) ---
|
||||
if [ "$compilation_ok" -eq 1 ]; then
|
||||
if [ -f "${input_file}" ] || [ -f "${output_reference_file}" ]; then
|
||||
# --- 自动化测试模式 ---
|
||||
echo " 检测到 .in/.out 文件,进入自动化测试模式..."
|
||||
@ -234,24 +253,26 @@ for sy_file in "${SY_FILES[@]}"; do
|
||||
EXPECTED_RETURN_CODE="$LAST_LINE_TRIMMED"
|
||||
EXPECTED_STDOUT_FILE="${TMP_DIR}/${base_name}.expected_stdout"
|
||||
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
|
||||
echo -e "\e[31m 标准输出测试失败。\e[0m"
|
||||
is_passed=0
|
||||
echo -e "\e[31m 标准输出测试失败。\e[0m"; out_ok=0
|
||||
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}"
|
||||
echo -e " \e[36m----------------\e[0m"
|
||||
fi
|
||||
|
||||
if [ "$ret_ok" -eq 1 ] && [ "$out_ok" -eq 1 ]; then echo -e "\e[32m 返回码与标准输出测试成功。\e[0m"; else is_passed=0; fi
|
||||
|
||||
else
|
||||
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"
|
||||
else
|
||||
echo -e "\e[31m 标准输出测试失败。\e[0m"
|
||||
is_passed=0
|
||||
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_actual_file}" " \e[36m--- 实际输出 ---\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
echo -e " \e[36m----------------\e[0m"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
@ -260,20 +281,16 @@ for sy_file in "${SY_FILES[@]}"; do
|
||||
fi
|
||||
else
|
||||
# --- 交互模式 ---
|
||||
echo -e "\e[33m"
|
||||
echo " **********************************************************"
|
||||
echo " ** 未找到 .in 或 .out 文件,进入交互模式。 **"
|
||||
echo " ** 程序即将运行,你可以直接在终端中输入。 **"
|
||||
echo " ** 按下 Ctrl+D (EOF) 或以其他方式结束程序以继续。 **"
|
||||
echo " **********************************************************"
|
||||
echo -e "\e[0m"
|
||||
echo -e "\e[33m\n 未找到 .in 或 .out 文件,进入交互模式...\e[0m"
|
||||
"${QEMU_RISCV64}" "${executable_file}"
|
||||
INTERACTIVE_RET_CODE=$?
|
||||
echo -e "\e[33m\n 交互模式执行完毕,程序返回码: ${INTERACTIVE_RET_CODE}\e[0m"
|
||||
echo " 注意: 交互模式的结果未经验证。"
|
||||
echo -e "\e[33m\n 交互模式执行完毕,程序返回码: ${INTERACTIVE_RET_CODE} (此结果未经验证)\e[0m"
|
||||
fi
|
||||
else
|
||||
is_passed=0
|
||||
fi
|
||||
|
||||
# --- 状态总结 ---
|
||||
if [ "$is_passed" -eq 1 ]; then
|
||||
echo -e "\e[32m状态: 通过\e[0m"
|
||||
((PASSED_CASES++))
|
||||
@ -284,20 +301,4 @@ for sy_file in "${SY_FILES[@]}"; do
|
||||
done
|
||||
|
||||
# --- 打印最终总结 ---
|
||||
echo "======================================================================"
|
||||
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
|
||||
print_summary
|
||||
484
script/runit.sh
484
script/runit.sh
@ -1,31 +1,41 @@
|
||||
#!/bin/bash
|
||||
|
||||
# runit.sh - 用于编译和测试 SysY 程序的脚本
|
||||
# 此脚本应该位于 mysysy/test_script/
|
||||
# 此脚本应该位于 mysysy/script/
|
||||
|
||||
export ASAN_OPTIONS=detect_leaks=0
|
||||
|
||||
# 定义相对于脚本位置的目录
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
||||
TESTDATA_DIR="${SCRIPT_DIR}/../testdata"
|
||||
BUILD_BIN_DIR="${SCRIPT_DIR}/../build/bin"
|
||||
LIB_DIR="${SCRIPT_DIR}/../lib"
|
||||
# TMP_DIR="${SCRIPT_DIR}/tmp"
|
||||
TMP_DIR="${SCRIPT_DIR}/tmp"
|
||||
|
||||
# 定义编译器和模拟器
|
||||
SYSYC="${BUILD_BIN_DIR}/sysyc"
|
||||
LLC_CMD="llc-19"
|
||||
GCC_RISCV64="riscv64-linux-gnu-gcc"
|
||||
QEMU_RISCV64="qemu-riscv64"
|
||||
|
||||
# --- 状态变量 ---
|
||||
EXECUTE_MODE=false
|
||||
OPTIMIZE_FLAG="" # 用于存储 -O1 标志
|
||||
SYSYC_TIMEOUT=10 # sysyc 编译超时 (秒)
|
||||
GCC_TIMEOUT=10 # gcc 编译超时 (秒)
|
||||
EXEC_TIMEOUT=5 # qemu 执行超时 (秒)
|
||||
MAX_OUTPUT_LINES=50 # 对比失败时显示的最大行数
|
||||
TEST_SETS=() # 用于存储要运行的测试集
|
||||
IR_EXECUTE_MODE=false
|
||||
OPTIMIZE_FLAG=""
|
||||
SYSYC_TIMEOUT=30
|
||||
LLC_TIMEOUT=10
|
||||
GCC_TIMEOUT=10
|
||||
EXEC_TIMEOUT=30
|
||||
MAX_OUTPUT_LINES=20
|
||||
TEST_SETS=()
|
||||
TOTAL_CASES=0
|
||||
PASSED_CASES=0
|
||||
FAILED_CASES_LIST="" # 用于存储未通过的测例列表
|
||||
FAILED_CASES_LIST=""
|
||||
INTERRUPTED=false # 新增:用于标记是否被中断
|
||||
|
||||
# =================================================================
|
||||
# --- 函数定义 ---
|
||||
# =================================================================
|
||||
|
||||
# 显示帮助信息的函数
|
||||
show_help() {
|
||||
@ -33,31 +43,32 @@ show_help() {
|
||||
echo "此脚本用于按文件名前缀数字升序编译和测试 .sy 文件。"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " -e, --executable 编译为可执行文件并运行测试。"
|
||||
echo " -e, --executable 编译为汇编并运行测试 (sysyc -> gcc -> qemu)。"
|
||||
echo " -eir 通过IR编译为可执行文件并运行测试 (sysyc -> llc -> gcc -> qemu)。"
|
||||
echo " -c, --clean 清理 'tmp' 目录下的所有生成文件。"
|
||||
echo " -O1 启用 sysyc 的 -O1 优化。"
|
||||
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 " -et N 设置 qemu 执行超时为 N 秒 (默认: 5)。"
|
||||
echo " -ml N, --max-lines N 当输出对比失败时,最多显示 N 行内容 (默认: 50)。"
|
||||
echo " -et N 设置 qemu 执行超时为 N 秒 (默认: 30)。"
|
||||
echo " -ml N, --max-lines N 当输出对比失败时,最多显示 N 行内容 (默认: 20)。"
|
||||
echo " -h, --help 显示此帮助信息并退出。"
|
||||
echo ""
|
||||
echo "注意: 默认行为 (无 -e 或 -eir) 是将 .sy 文件同时编译为 .s (汇编) 和 .ll (IR),不执行。"
|
||||
echo " 可在任何时候按 Ctrl+C 来中断测试并显示当前已完成的测例总结。"
|
||||
}
|
||||
|
||||
|
||||
# 显示文件内容并根据行数截断的函数
|
||||
display_file_content() {
|
||||
local file_path="$1"
|
||||
local title="$2"
|
||||
local max_lines="$3"
|
||||
|
||||
if [ ! -f "$file_path" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [ ! -f "$file_path" ]; then return; fi
|
||||
echo -e "$title"
|
||||
local line_count
|
||||
line_count=$(wc -l < "$file_path")
|
||||
|
||||
if [ "$line_count" -gt "$max_lines" ]; then
|
||||
head -n "$max_lines" "$file_path"
|
||||
echo -e "\e[33m[... 输出已截断,共 ${line_count} 行 ...]\e[0m"
|
||||
@ -72,63 +83,90 @@ clean_tmp() {
|
||||
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}"
|
||||
|
||||
# 解析命令行参数
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-e|--executable)
|
||||
EXECUTE_MODE=true
|
||||
shift
|
||||
;;
|
||||
-c|--clean)
|
||||
clean_tmp
|
||||
exit 0
|
||||
;;
|
||||
-O1)
|
||||
OPTIMIZE_FLAG="-O1"
|
||||
shift
|
||||
;;
|
||||
-e|--executable) EXECUTE_MODE=true; shift ;;
|
||||
-eir) IR_EXECUTE_MODE=true; shift ;;
|
||||
-c|--clean) clean_tmp; exit 0 ;;
|
||||
-O1) OPTIMIZE_FLAG="-O1"; shift ;;
|
||||
-set)
|
||||
shift # 移过 '-set'
|
||||
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
|
||||
shift
|
||||
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 ;;
|
||||
-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
|
||||
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
|
||||
SET_MAP[f]="functional"
|
||||
SET_MAP[h]="h_functional"
|
||||
SET_MAP[p]="performance"
|
||||
|
||||
SEARCH_PATHS=()
|
||||
|
||||
if [ ${#TEST_SETS[@]} -eq 0 ] || [[ " ${TEST_SETS[@]} " =~ " all " ]]; then
|
||||
SEARCH_PATHS+=("${TESTDATA_DIR}")
|
||||
else
|
||||
@ -150,9 +188,21 @@ echo "SysY 测试运行器启动..."
|
||||
if [ -n "$OPTIMIZE_FLAG" ]; then echo "优化等级: ${OPTIMIZE_FLAG}"; fi
|
||||
echo "输入目录: ${SEARCH_PATHS[@]}"
|
||||
echo "临时目录: ${TMP_DIR}"
|
||||
echo "执行模式: ${EXECUTE_MODE}"
|
||||
if ${EXECUTE_MODE}; then
|
||||
echo "超时设置: sysyc=${SYSYC_TIMEOUT}s, gcc=${GCC_TIMEOUT}s, qemu=${EXEC_TIMEOUT}s"
|
||||
|
||||
RUN_MODE_INFO=""
|
||||
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}"
|
||||
fi
|
||||
echo ""
|
||||
@ -165,132 +215,228 @@ fi
|
||||
TOTAL_CASES=$(echo "$sy_files" | wc -w)
|
||||
|
||||
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%.*}")
|
||||
output_base_name=$(echo "${relative_path_no_ext}" | tr '/' '_')
|
||||
|
||||
assembly_file="${TMP_DIR}/${output_base_name}_sysyc_riscv64.s"
|
||||
executable_file="${TMP_DIR}/${output_base_name}_sysyc_riscv64"
|
||||
assembly_file_S="${TMP_DIR}/${output_base_name}_sysyc_S.s"
|
||||
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"
|
||||
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 " 使用 sysyc 编译 (超时 ${SYSYC_TIMEOUT}s)..."
|
||||
timeout -s KILL ${SYSYC_TIMEOUT} "${SYSYC}" -S "${sy_file}" -o "${assembly_file}" ${OPTIMIZE_FLAG}
|
||||
SYSYC_STATUS=$?
|
||||
if [ $SYSYC_STATUS -eq 124 ]; then
|
||||
echo -e "\e[31m错误: SysY 编译 ${sy_file} 超时\e[0m"
|
||||
is_passed=0
|
||||
elif [ $SYSYC_STATUS -ne 0 ]; then
|
||||
echo -e "\e[31m错误: SysY 编译 ${sy_file} 失败,退出码: ${SYSYC_STATUS}\e[0m"
|
||||
is_passed=0
|
||||
fi
|
||||
|
||||
if ${EXECUTE_MODE} && [ "$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
|
||||
GCC_STATUS=$?
|
||||
if [ $GCC_STATUS -eq 124 ]; then
|
||||
echo -e "\e[31m错误: GCC 编译 ${assembly_file} 超时\e[0m"
|
||||
is_passed=0
|
||||
elif [ $GCC_STATUS -ne 0 ]; then
|
||||
echo -e "\e[31m错误: GCC 编译 ${assembly_file} 失败,退出码: ${GCC_STATUS}\e[0m"
|
||||
is_passed=0
|
||||
# --- 模式 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=$?
|
||||
if [ $SYSYC_STATUS -ne 0 ]; then
|
||||
[ $SYSYC_STATUS -eq 124 ] && echo -e "\e[31m错误: SysY (IR) 编译超时\e[0m" || echo -e "\e[31m错误: SysY (IR) 编译失败,退出码: ${SYSYC_STATUS}\e[0m"
|
||||
step_failed=1
|
||||
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
|
||||
|
||||
if [ "$is_passed" -eq 1 ]; then
|
||||
echo " 正在执行 (超时 ${EXEC_TIMEOUT}s)..."
|
||||
|
||||
exec_cmd="${QEMU_RISCV64} \"${executable_file}\""
|
||||
if [ -f "${input_file}" ]; then
|
||||
exec_cmd+=" < \"${input_file}\""
|
||||
fi
|
||||
exec_cmd+=" > \"${output_actual_file}\""
|
||||
|
||||
eval "timeout -s KILL ${EXEC_TIMEOUT} ${exec_cmd}"
|
||||
ACTUAL_RETURN_CODE=$?
|
||||
|
||||
if [ "$ACTUAL_RETURN_CODE" -eq 124 ]; then
|
||||
echo -e "\e[31m 执行超时: ${sy_file} 运行超过 ${EXEC_TIMEOUT} 秒\e[0m"
|
||||
is_passed=0
|
||||
else
|
||||
if [ -f "${output_reference_file}" ]; then
|
||||
LAST_LINE_TRIMMED=$(tail -n 1 "${output_reference_file}" | tr -d '[:space:]')
|
||||
|
||||
if [[ "$LAST_LINE_TRIMMED" =~ ^[-+]?[0-9]+$ ]]; then
|
||||
EXPECTED_RETURN_CODE="$LAST_LINE_TRIMMED"
|
||||
EXPECTED_STDOUT_FILE="${TMP_DIR}/${output_base_name}_sysyc_riscv64.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"
|
||||
is_passed=0
|
||||
fi
|
||||
|
||||
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"
|
||||
is_passed=0
|
||||
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}"
|
||||
echo -e " \e[36m------------------------------\e[0m"
|
||||
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}") <(tr -d '[:space:]' < "${output_reference_file}") >/dev/null 2>&1; then
|
||||
echo -e "\e[32m 成功: 输出与参考输出匹配\e[0m"
|
||||
else
|
||||
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_actual_file}" " \e[36m---------- 实际输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
echo -e " \e[36m------------------------------\e[0m"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo " 无参考输出文件。程序返回码: ${ACTUAL_RETURN_CODE}"
|
||||
if [ "$step_failed" -eq 0 ]; then
|
||||
echo " [2/4] 使用 llc-19 编译为汇编 (超时 ${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_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=$?
|
||||
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 " [4/4] 正在执行 (超时 ${EXEC_TIMEOUT}s)..."
|
||||
exec_cmd="${QEMU_RISCV64} \"${executable_file_from_ir}\""
|
||||
[ -f "${input_file}" ] && exec_cmd+=" < \"${input_file}\""
|
||||
exec_cmd+=" > \"${output_actual_file_from_ir}\""
|
||||
|
||||
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}_from_ir.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_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"
|
||||
display_file_content "${EXPECTED_STDOUT_FILE}" " \e[36m---------- 期望输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
display_file_content "${output_actual_file_from_ir}" " \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_from_ir}") <(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_from_ir}" " \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
|
||||
|
||||
# --- 模式 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
|
||||
((PASSED_CASES++))
|
||||
else
|
||||
# 确保 FAILED_CASES_LIST 的每一项都以换行符结尾
|
||||
FAILED_CASES_LIST+="${relative_path_no_ext}.sy\n"
|
||||
fi
|
||||
echo ""
|
||||
done <<< "$sy_files"
|
||||
|
||||
echo "========================================"
|
||||
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
|
||||
# --- 修改:调用总结函数 ---
|
||||
print_summary
|
||||
@ -105,16 +105,17 @@ void PrologueEpilogueInsertionPass::runOnMachineFunction(MachineFunction* mfunc)
|
||||
// 4.4. 保存所有使用到的被调用者保存寄存器
|
||||
int next_available_offset = -(16 + frame_info.locals_size + frame_info.spill_size);
|
||||
for (const auto& reg : frame_info.callee_saved_regs_to_store) {
|
||||
// 采用“先使用,后更新”逻辑
|
||||
// 改为“先更新,后使用”逻辑
|
||||
next_available_offset -= 8; // 先为当前寄存器分配下一个可用槽位
|
||||
RVOpcodes store_op = isFPR(reg) ? RVOpcodes::FSD : RVOpcodes::SD;
|
||||
auto save_cs_reg = std::make_unique<MachineInstr>(store_op);
|
||||
save_cs_reg->addOperand(std::make_unique<RegOperand>(reg));
|
||||
save_cs_reg->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::S0),
|
||||
std::make_unique<ImmOperand>(next_available_offset) // 使用当前偏移
|
||||
std::make_unique<ImmOperand>(next_available_offset) // 使用新计算出的正确偏移
|
||||
));
|
||||
prologue_instrs.push_back(std::move(save_cs_reg));
|
||||
next_available_offset -= 8; // 为下一个寄存器准备偏移
|
||||
// 不再需要在循环末尾递减
|
||||
}
|
||||
|
||||
// 4.5. 将所有生成的序言指令一次性插入到函数入口
|
||||
@ -131,6 +132,7 @@ void PrologueEpilogueInsertionPass::runOnMachineFunction(MachineFunction* mfunc)
|
||||
// 5.1. 恢复被调用者保存寄存器
|
||||
int next_available_offset_restore = -(16 + frame_info.locals_size + frame_info.spill_size);
|
||||
for (const auto& reg : frame_info.callee_saved_regs_to_store) {
|
||||
next_available_offset_restore -= 8; // 为下一个寄存器准备偏移
|
||||
RVOpcodes load_op = isFPR(reg) ? RVOpcodes::FLD : RVOpcodes::LD;
|
||||
auto restore_cs_reg = std::make_unique<MachineInstr>(load_op);
|
||||
restore_cs_reg->addOperand(std::make_unique<RegOperand>(reg));
|
||||
@ -139,7 +141,6 @@ void PrologueEpilogueInsertionPass::runOnMachineFunction(MachineFunction* mfunc)
|
||||
std::make_unique<ImmOperand>(next_available_offset_restore) // 使用当前偏移
|
||||
));
|
||||
epilogue_instrs.push_back(std::move(restore_cs_reg));
|
||||
next_available_offset_restore -= 8; // 为下一个寄存器准备偏移
|
||||
}
|
||||
|
||||
// 5.2. 恢复 ra 和 s0
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
#include "RISCv64AsmPrinter.h"
|
||||
#include "RISCv64ISel.h"
|
||||
#include <stdexcept>
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
namespace sysy {
|
||||
|
||||
// 检查是否为内存加载/存储指令,以处理特殊的打印格式
|
||||
@ -236,4 +237,30 @@ std::string RISCv64AsmPrinter::regToString(PhysicalReg reg) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string RISCv64AsmPrinter::formatInstr(const MachineInstr* instr) {
|
||||
if (!instr) return "(null instr)";
|
||||
|
||||
// 使用 stringstream 作为临时的输出目标
|
||||
std::stringstream ss;
|
||||
|
||||
// 关键: 临时将类成员 'OS' 指向我们的 stringstream
|
||||
std::ostream* old_os = this->OS;
|
||||
this->OS = &ss;
|
||||
|
||||
// 修正: 调用正确的内部打印函数 printMachineInstr
|
||||
printInstruction(const_cast<MachineInstr*>(instr), false);
|
||||
|
||||
// 恢复旧的 ostream 指针
|
||||
this->OS = old_os;
|
||||
|
||||
// 获取stringstream的内容并做一些清理
|
||||
std::string result = ss.str();
|
||||
size_t endpos = result.find_last_not_of(" \t\n\r");
|
||||
if (std::string::npos != endpos) {
|
||||
result = result.substr(0, endpos + 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -139,7 +139,29 @@ std::string RISCv64CodeGen::module_gen() {
|
||||
ss << ".type " << global->getName() << ", @object\n";
|
||||
ss << ".size " << global->getName() << ", " << total_size << "\n";
|
||||
ss << global->getName() << ":\n";
|
||||
printInitializer(ss, global->getInitValues());
|
||||
bool is_all_zeros = true;
|
||||
const auto& init_values = global->getInitValues();
|
||||
if (init_values.getValues().empty()) {
|
||||
is_all_zeros = true;
|
||||
} else {
|
||||
for (auto val : init_values.getValues()) {
|
||||
if (auto const_val = dynamic_cast<ConstantValue*>(val)) {
|
||||
if (!const_val->isZero()) {
|
||||
is_all_zeros = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
is_all_zeros = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_all_zeros) {
|
||||
ss << " .zero " << total_size << "\n";
|
||||
} else {
|
||||
// 对于有非零初始值的变量,保持原有的打印逻辑。
|
||||
printInitializer(ss, global->getInitValues());
|
||||
}
|
||||
}
|
||||
|
||||
// b. 处理全局常量 (ConstantVariable)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "RISCv64ISel.h"
|
||||
#include "IR.h" // For GlobalValue
|
||||
#include <stdexcept>
|
||||
#include <set>
|
||||
#include <functional>
|
||||
@ -167,33 +168,6 @@ void RISCv64ISel::selectBasicBlock(BasicBlock* bb) {
|
||||
select_recursive(node_to_select);
|
||||
}
|
||||
}
|
||||
|
||||
if (CurMBB == MFunc->getBlocks().front().get()) { // 只对入口块操作
|
||||
auto keepalive = std::make_unique<MachineInstr>(RVOpcodes::PSEUDO_KEEPALIVE);
|
||||
for (Argument* arg : F->getArguments()) {
|
||||
keepalive->addOperand(std::make_unique<RegOperand>(getVReg(arg)));
|
||||
}
|
||||
|
||||
auto& instrs = CurMBB->getInstructions();
|
||||
auto insert_pos = instrs.end();
|
||||
|
||||
// 关键:检查基本块是否以一个“终止指令”结尾
|
||||
if (!instrs.empty()) {
|
||||
RVOpcodes last_op = instrs.back()->getOpcode();
|
||||
// 扩充了判断条件,涵盖所有可能的终止指令
|
||||
if (last_op == RVOpcodes::J || last_op == RVOpcodes::RET ||
|
||||
last_op == RVOpcodes::BEQ || last_op == RVOpcodes::BNE ||
|
||||
last_op == RVOpcodes::BLT || last_op == RVOpcodes::BGE ||
|
||||
last_op == RVOpcodes::BLTU || last_op == RVOpcodes::BGEU)
|
||||
{
|
||||
// 如果是,插入点就在这个终止指令之前
|
||||
insert_pos = std::prev(instrs.end());
|
||||
}
|
||||
}
|
||||
|
||||
// 在计算出的正确位置插入伪指令
|
||||
instrs.insert(insert_pos, std::move(keepalive));
|
||||
}
|
||||
}
|
||||
|
||||
// 核心函数:为DAG节点选择并生成MachineInstr (已修复和增强的完整版本)
|
||||
@ -209,8 +183,12 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
case DAGNode::CONSTANT:
|
||||
case DAGNode::ALLOCA_ADDR:
|
||||
if (node->value) {
|
||||
// 确保它有一个关联的虚拟寄存器即可,不生成代码。
|
||||
getVReg(node->value);
|
||||
// GlobalValue objects (global variables) should not get virtual registers
|
||||
// since they represent memory addresses, not register-allocated values
|
||||
if (dynamic_cast<GlobalValue*>(node->value) == nullptr) {
|
||||
// 确保它有一个关联的虚拟寄存器即可,不生成代码。
|
||||
getVReg(node->value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -767,11 +745,83 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
case Instruction::kFtoI: { // 浮点 to 整数
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FCVT_W_S);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg)); // 目标是整数vreg
|
||||
instr->addOperand(std::make_unique<RegOperand>(src_vreg)); // 源是浮点vreg
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
case Instruction::kFtoI: { // 浮点 to 整数 (带向下取整)
|
||||
// 目标:实现 floor(x) 的效果, C/C++中浮点转整数是截断(truncate)
|
||||
// 对于正数,floor(x) == truncate(x)
|
||||
// RISC-V的 fcvt.w.s 默认是“四舍五入到偶数”
|
||||
// 我们需要手动实现截断逻辑
|
||||
// 逻辑:
|
||||
// temp_i = fcvt.w.s(x) // 四舍五入
|
||||
// temp_f = fcvt.s.w(temp_i) // 转回浮点
|
||||
// if (x < temp_f) { // 如果原数更小,说明被“五入”了
|
||||
// result = temp_i - 1
|
||||
// } else {
|
||||
// result = temp_i
|
||||
// }
|
||||
|
||||
auto temp_i_vreg = getNewVReg(Type::getIntType());
|
||||
auto temp_f_vreg = getNewVReg(Type::getFloatType());
|
||||
auto cmp_vreg = getNewVReg(Type::getIntType());
|
||||
|
||||
// 1. fcvt.w.s temp_i_vreg, src_vreg
|
||||
auto fcvt_w = std::make_unique<MachineInstr>(RVOpcodes::FCVT_W_S);
|
||||
fcvt_w->addOperand(std::make_unique<RegOperand>(temp_i_vreg));
|
||||
fcvt_w->addOperand(std::make_unique<RegOperand>(src_vreg));
|
||||
CurMBB->addInstruction(std::move(fcvt_w));
|
||||
|
||||
// 2. fcvt.s.w temp_f_vreg, temp_i_vreg
|
||||
auto fcvt_s = std::make_unique<MachineInstr>(RVOpcodes::FCVT_S_W);
|
||||
fcvt_s->addOperand(std::make_unique<RegOperand>(temp_f_vreg));
|
||||
fcvt_s->addOperand(std::make_unique<RegOperand>(temp_i_vreg));
|
||||
CurMBB->addInstruction(std::move(fcvt_s));
|
||||
|
||||
// 3. flt.s cmp_vreg, src_vreg, temp_f_vreg
|
||||
auto flt = std::make_unique<MachineInstr>(RVOpcodes::FLT_S);
|
||||
flt->addOperand(std::make_unique<RegOperand>(cmp_vreg));
|
||||
flt->addOperand(std::make_unique<RegOperand>(src_vreg));
|
||||
flt->addOperand(std::make_unique<RegOperand>(temp_f_vreg));
|
||||
CurMBB->addInstruction(std::move(flt));
|
||||
|
||||
// 创建标签
|
||||
int unique_id = this->local_label_counter++;
|
||||
std::string rounded_up_label = MFunc->getName() + "_ftoi_rounded_up_" + std::to_string(unique_id);
|
||||
std::string done_label = MFunc->getName() + "_ftoi_done_" + std::to_string(unique_id);
|
||||
|
||||
// 4. bne cmp_vreg, x0, rounded_up_label
|
||||
auto bne = std::make_unique<MachineInstr>(RVOpcodes::BNE);
|
||||
bne->addOperand(std::make_unique<RegOperand>(cmp_vreg));
|
||||
bne->addOperand(std::make_unique<RegOperand>(PhysicalReg::ZERO));
|
||||
bne->addOperand(std::make_unique<LabelOperand>(rounded_up_label));
|
||||
CurMBB->addInstruction(std::move(bne));
|
||||
|
||||
// 5. else 分支: mv dest_vreg, temp_i_vreg
|
||||
auto mv = std::make_unique<MachineInstr>(RVOpcodes::MV);
|
||||
mv->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
mv->addOperand(std::make_unique<RegOperand>(temp_i_vreg));
|
||||
CurMBB->addInstruction(std::move(mv));
|
||||
|
||||
// 6. j done_label
|
||||
auto j = std::make_unique<MachineInstr>(RVOpcodes::J);
|
||||
j->addOperand(std::make_unique<LabelOperand>(done_label));
|
||||
CurMBB->addInstruction(std::move(j));
|
||||
|
||||
// 7. rounded_up_label:
|
||||
auto label_up = std::make_unique<MachineInstr>(RVOpcodes::LABEL);
|
||||
label_up->addOperand(std::make_unique<LabelOperand>(rounded_up_label));
|
||||
CurMBB->addInstruction(std::move(label_up));
|
||||
|
||||
// 8. addiw dest_vreg, temp_i_vreg, -1
|
||||
auto addi = std::make_unique<MachineInstr>(RVOpcodes::ADDIW);
|
||||
addi->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
addi->addOperand(std::make_unique<RegOperand>(temp_i_vreg));
|
||||
addi->addOperand(std::make_unique<ImmOperand>(-1));
|
||||
CurMBB->addInstruction(std::move(addi));
|
||||
|
||||
// 9. done_label:
|
||||
auto label_done = std::make_unique<MachineInstr>(RVOpcodes::LABEL);
|
||||
label_done->addOperand(std::make_unique<LabelOperand>(done_label));
|
||||
CurMBB->addInstruction(std::move(label_done));
|
||||
|
||||
break;
|
||||
}
|
||||
case Instruction::kFNeg: { // 浮点取负
|
||||
@ -1659,4 +1709,8 @@ void RISCv64ISel::print_dag(const std::vector<std::unique_ptr<DAGNode>>& dag, co
|
||||
std::cerr << "======================================\n\n";
|
||||
}
|
||||
|
||||
unsigned int RISCv64ISel::getVRegCounter() const {
|
||||
return vreg_counter;
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -55,12 +55,41 @@ void RISCv64RegAlloc::run() {
|
||||
|
||||
if (DEBUG) std::cerr << "===== Running Graph Coloring Register Allocation for function: " << MFunc->getName() << " =====\n";
|
||||
|
||||
while (true) {
|
||||
const int MAX_ITERATIONS = 50;
|
||||
int iteration = 0;
|
||||
|
||||
while (iteration++ < MAX_ITERATIONS) {
|
||||
if (doAllocation()) {
|
||||
break;
|
||||
} else {
|
||||
rewriteProgram();
|
||||
if (DEBUG) std::cerr << "--- Spilling detected, re-running allocation ---\n";
|
||||
if (DEBUG) std::cerr << "--- Spilling detected, re-running allocation (iteration " << iteration << ") ---\n";
|
||||
|
||||
if (iteration >= MAX_ITERATIONS) {
|
||||
std::cerr << "ERROR: Register allocation failed to converge after " << MAX_ITERATIONS << " iterations\n";
|
||||
std::cerr << " Spill worklist size: " << spillWorklist.size() << "\n";
|
||||
std::cerr << " Total nodes: " << (initial.size() + coloredNodes.size()) << "\n";
|
||||
|
||||
// Emergency spill remaining nodes to break the loop
|
||||
std::cerr << " Emergency spilling remaining spill worklist nodes...\n";
|
||||
for (unsigned node : spillWorklist) {
|
||||
spilledNodes.insert(node);
|
||||
}
|
||||
|
||||
// Also spill any nodes that didn't get colors
|
||||
std::set<unsigned> uncolored;
|
||||
for (unsigned node : initial) {
|
||||
if (color_map.find(node) == color_map.end()) {
|
||||
uncolored.insert(node);
|
||||
}
|
||||
}
|
||||
for (unsigned node : uncolored) {
|
||||
spilledNodes.insert(node);
|
||||
}
|
||||
|
||||
// Force completion
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,30 +151,6 @@ void RISCv64RegAlloc::precolorByCallingConvention() {
|
||||
}
|
||||
}
|
||||
|
||||
// // --- 部分2:为CALL指令的返回值预着色 ---
|
||||
// for (auto& mbb : MFunc->getBlocks()) {
|
||||
// for (auto& instr : mbb->getInstructions()) {
|
||||
// if (instr->getOpcode() == RVOpcodes::CALL) {
|
||||
// if (!instr->getOperands().empty() &&
|
||||
// instr->getOperands().front()->getKind() == MachineOperand::KIND_REG)
|
||||
// {
|
||||
// auto reg_op = static_cast<RegOperand*>(instr->getOperands().front().get());
|
||||
// if (reg_op->isVirtual()) {
|
||||
// unsigned ret_vreg = reg_op->getVRegNum();
|
||||
// assert(vreg_to_value_map.count(ret_vreg) && "Return vreg not found!");
|
||||
// Value* ret_val = vreg_to_value_map.at(ret_vreg);
|
||||
|
||||
// if (ret_val->getType()->isFloat()) {
|
||||
// color_map[ret_vreg] = PhysicalReg::F10; // fa0
|
||||
// } else {
|
||||
// color_map[ret_vreg] = PhysicalReg::A0; // a0
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// 将所有预着色的vreg视为已着色节点
|
||||
for(const auto& pair : color_map) {
|
||||
coloredNodes.insert(pair.first);
|
||||
@ -402,14 +407,32 @@ void RISCv64RegAlloc::build() {
|
||||
|
||||
// --- 规则 3: Live_Out 集合内部的【虚拟寄存器】形成完全图 ---
|
||||
// 使用更高效的遍历,避免重复调用 addEdge(A,B) 和 addEdge(B,A)
|
||||
for (auto it1 = live_out.begin(); it1 != live_out.end(); ++it1) {
|
||||
unsigned l1 = *it1;
|
||||
// 只为虚拟寄存器 l1 添加边
|
||||
if (precolored.count(l1)) continue;
|
||||
// 添加限制以防止过度密集的图
|
||||
const size_t MAX_LIVE_OUT_SIZE = 32; // 限制最大活跃变量数
|
||||
if (live_out.size() > MAX_LIVE_OUT_SIZE) {
|
||||
// 对于大量活跃变量,使用更保守的边添加策略
|
||||
// 只添加必要的边,而不是完全图
|
||||
for (auto it1 = live_out.begin(); it1 != live_out.end(); ++it1) {
|
||||
unsigned l1 = *it1;
|
||||
if (precolored.count(l1)) continue;
|
||||
|
||||
// 只添加与定义变量相关的边
|
||||
for (unsigned d : def) {
|
||||
if (d != l1 && !precolored.count(d)) {
|
||||
addEdge(l1, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 对于较小的集合,使用原来的完全图方法
|
||||
for (auto it1 = live_out.begin(); it1 != live_out.end(); ++it1) {
|
||||
unsigned l1 = *it1;
|
||||
if (precolored.count(l1)) continue;
|
||||
|
||||
for (auto it2 = std::next(it1); it2 != live_out.end(); ++it2) {
|
||||
unsigned l2 = *it2;
|
||||
addEdge(l1, l2);
|
||||
for (auto it2 = std::next(it1); it2 != live_out.end(); ++it2) {
|
||||
unsigned l2 = *it2;
|
||||
addEdge(l1, l2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -656,7 +679,7 @@ void RISCv64RegAlloc::rewriteProgram() {
|
||||
StackFrameInfo& frame_info = MFunc->getFrameInfo();
|
||||
// 使用 EFI Pass 确定的 locals_end_offset 作为溢出分配的基准。
|
||||
// locals_end_offset 本身是负数,代表局部变量区域的下边界地址。
|
||||
int spill_current_offset = frame_info.locals_end_offset;
|
||||
int spill_current_offset = frame_info.locals_end_offset - frame_info.spill_size;
|
||||
|
||||
// 保存溢出区域的起始点,用于最后计算总的 spill_size
|
||||
const int spill_start_offset = frame_info.locals_end_offset;
|
||||
@ -787,6 +810,11 @@ void RISCv64RegAlloc::rewriteProgram() {
|
||||
std::make_unique<ImmOperand>(frame_info.spill_offsets.at(old_vreg))
|
||||
));
|
||||
new_instructions.push_back(std::move(store));
|
||||
if (DEEPERDEBUG) {
|
||||
std::cerr << "[Spill] Inserted spill store for %vreg" << old_vreg
|
||||
<< " at offset " << frame_info.spill_offsets.at(old_vreg)
|
||||
<< " with new temp vreg " << regIdToString(new_temp_vreg) << ".\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
mbb->getInstructions() = std::move(new_instructions);
|
||||
@ -1352,9 +1380,9 @@ void RISCv64RegAlloc::applyColoring() {
|
||||
// 使用 setPReg 将虚拟寄存器转换为物理寄存器
|
||||
reg_op->setPReg(color_map.at(vreg));
|
||||
} else {
|
||||
// 如果一个vreg在成功分配后仍然没有颜色,这是一个错误
|
||||
std::cerr << "FATAL: Virtual register %vreg" << vreg << " has no color after allocation!\n";
|
||||
assert(false && "Virtual register has no color after allocation!");
|
||||
// 如果一个vreg在成功分配后仍然没有颜色,可能是紧急溢出
|
||||
// std::cerr << "WARNING: Virtual register %vreg" << vreg << " has no color after allocation, treating as spilled\n";
|
||||
// 在紧急溢出情况下,使用临时寄存器
|
||||
reg_op->setPReg(PhysicalReg::T6);
|
||||
}
|
||||
}
|
||||
@ -1366,7 +1394,7 @@ void RISCv64RegAlloc::applyColoring() {
|
||||
if (color_map.count(vreg)) {
|
||||
reg_op->setPReg(color_map.at(vreg));
|
||||
} else {
|
||||
assert(false && "Virtual register in memory operand has no color!");
|
||||
// std::cerr << "WARNING: Virtual register in memory operand has no color, using T6\n";
|
||||
reg_op->setPReg(PhysicalReg::T6);
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,8 @@ public:
|
||||
void setStream(std::ostream& os) { OS = &os; }
|
||||
// 辅助函数
|
||||
std::string regToString(PhysicalReg reg);
|
||||
std::string formatInstr(const MachineInstr *instr);
|
||||
|
||||
private:
|
||||
// 打印各个部分
|
||||
void printBasicBlock(MachineBasicBlock* mbb, bool debug = false);
|
||||
|
||||
@ -3,6 +3,12 @@
|
||||
|
||||
#include "RISCv64LLIR.h"
|
||||
|
||||
// Forward declarations
|
||||
namespace sysy {
|
||||
class GlobalValue;
|
||||
class Value;
|
||||
}
|
||||
|
||||
extern int DEBUG;
|
||||
extern int DEEPDEBUG;
|
||||
|
||||
@ -17,7 +23,8 @@ public:
|
||||
// 公开接口,以便后续模块(如RegAlloc)可以查询或创建vreg
|
||||
unsigned getVReg(Value* val);
|
||||
unsigned getNewVReg() { return vreg_counter++; }
|
||||
unsigned getNewVReg(Type* type);
|
||||
unsigned getNewVReg(Type* type);
|
||||
unsigned getVRegCounter() const;
|
||||
// 获取 vreg_map 的公共接口
|
||||
const std::map<Value*, unsigned>& getVRegMap() const { return vreg_map; }
|
||||
const std::map<unsigned, Value*>& getVRegValueMap() const { return vreg_to_value_map; }
|
||||
|
||||
@ -20,6 +20,10 @@
|
||||
#include <algorithm>
|
||||
|
||||
namespace sysy {
|
||||
|
||||
// Global cleanup function to release all statically allocated IR objects
|
||||
void cleanupIRPools();
|
||||
|
||||
/**
|
||||
* \defgroup type Types
|
||||
* @brief Sysy的类型系统
|
||||
@ -83,6 +87,7 @@ class Type {
|
||||
auto as() const -> std::enable_if_t<std::is_base_of_v<Type, T>, T *> {
|
||||
return dynamic_cast<T *>(const_cast<Type *>(this));
|
||||
}
|
||||
virtual void print(std::ostream& os) const;
|
||||
};
|
||||
|
||||
class PointerType : public Type {
|
||||
@ -94,6 +99,9 @@ class PointerType : public Type {
|
||||
|
||||
public:
|
||||
static PointerType* get(Type *baseType); ///< 获取指向baseType的Pointer类型
|
||||
|
||||
// Cleanup method to release all cached pointer types (call at program exit)
|
||||
static void cleanup();
|
||||
|
||||
public:
|
||||
Type* getBaseType() const { return baseType; } ///< 获取指向的类型
|
||||
@ -111,6 +119,9 @@ class FunctionType : public Type {
|
||||
public:
|
||||
/// 获取返回值类型为returnType, 形参类型列表为paramTypes的Function类型
|
||||
static FunctionType* get(Type *returnType, const std::vector<Type *> ¶mTypes = {});
|
||||
|
||||
// Cleanup method to release all cached function types (call at program exit)
|
||||
static void cleanup();
|
||||
|
||||
public:
|
||||
Type* getReturnType() const { return returnType; } ///< 获取返回值类信息
|
||||
@ -123,6 +134,9 @@ class ArrayType : public Type {
|
||||
// elements:数组的元素类型 (例如,int[3] 的 elementType 是 int)
|
||||
// numElements:该维度的大小 (例如,int[3] 的 numElements 是 3)
|
||||
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; }
|
||||
unsigned getNumElements() const { return numElements; }
|
||||
@ -202,9 +216,11 @@ class Use {
|
||||
|
||||
public:
|
||||
unsigned getIndex() const { return index; } ///< 返回value在User操作数中的位置
|
||||
void setIndex(int newIndex) { index = newIndex; } ///< 设置value在User操作数中的位置
|
||||
User* getUser() const { return user; } ///< 返回使用者
|
||||
Value* getValue() const { return value; } ///< 返回被使用的值
|
||||
void setValue(Value *newValue) { value = newValue; } ///< 将被使用的值设置为newValue
|
||||
void print(std::ostream& os) const;
|
||||
};
|
||||
|
||||
//! The base class of all value types
|
||||
@ -229,7 +245,15 @@ class Value {
|
||||
std::list<std::shared_ptr<Use>>& getUses() { return uses; } ///< 获取使用关系列表
|
||||
void addUse(const std::shared_ptr<Use> &use) { uses.push_back(use); } ///< 添加使用关系
|
||||
void replaceAllUsesWith(Value *value); ///< 将原来使用该value的使用者全变为使用给定参数value并修改相应use关系
|
||||
void removeUse(const std::shared_ptr<Use> &use) { uses.remove(use); } ///< 删除使用关系use
|
||||
void removeUse(const std::shared_ptr<Use> &use) {
|
||||
assert(use != nullptr && "Use cannot be null");
|
||||
assert(use->getValue() == this && "Use being removed does NOT point to this Value!");
|
||||
auto it = std::find(uses.begin(), uses.end(), use);
|
||||
assert(it != uses.end() && "Use not found in Value's uses");
|
||||
uses.remove(use);
|
||||
} ///< 删除使用关系use
|
||||
void removeAllUses();
|
||||
virtual void print(std::ostream& os) const = 0; ///< 输出值信息到输出流
|
||||
};
|
||||
|
||||
/**
|
||||
@ -356,6 +380,9 @@ public:
|
||||
|
||||
// Static factory method to get a canonical ConstantValue from the pool
|
||||
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
|
||||
int getInt() const {
|
||||
@ -394,6 +421,7 @@ public:
|
||||
|
||||
virtual bool isZero() const = 0;
|
||||
virtual bool isOne() const = 0;
|
||||
void print(std::ostream& os) const = 0;
|
||||
};
|
||||
|
||||
class ConstantInteger : public ConstantValue {
|
||||
@ -420,6 +448,7 @@ public:
|
||||
|
||||
bool isZero() const override { return constVal == 0; }
|
||||
bool isOne() const override { return constVal == 1; }
|
||||
void print(std::ostream& os) const;
|
||||
};
|
||||
|
||||
class ConstantFloating : public ConstantValue {
|
||||
@ -446,6 +475,7 @@ public:
|
||||
|
||||
bool isZero() const override { return constFVal == 0.0f; }
|
||||
bool isOne() const override { return constFVal == 1.0f; }
|
||||
void print(std::ostream& os) const;
|
||||
};
|
||||
|
||||
class UndefinedValue : public ConstantValue {
|
||||
@ -460,6 +490,9 @@ protected:
|
||||
|
||||
public:
|
||||
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 {
|
||||
return std::hash<Type*>{}(getType());
|
||||
@ -477,6 +510,7 @@ public:
|
||||
|
||||
bool isZero() const override { return false; }
|
||||
bool isOne() const override { return false; }
|
||||
void print(std::ostream& os) const;
|
||||
};
|
||||
|
||||
// --- End of refactored ConstantValue and related classes ---
|
||||
@ -617,6 +651,11 @@ public:
|
||||
}
|
||||
} ///< 移除指定位置的指令
|
||||
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
|
||||
@ -633,21 +672,6 @@ class User : public Value {
|
||||
explicit User(Type *type, const std::string &name = "") : Value(type, name) {}
|
||||
|
||||
public:
|
||||
// ~User() override {
|
||||
// // 当 User 对象被销毁时(例如,LoadInst 或 StoreInst 被删除时),
|
||||
// // 它必须通知它所使用的所有 Value,将对应的 Use 关系从它们的 uses 列表中移除。
|
||||
// // 这样可以防止 Value 的 uses 列表中出现悬空的 Use 对象。
|
||||
// for (const auto &use_ptr : operands) {
|
||||
// // 确保 use_ptr 非空,并且其内部指向的 Value* 也非空
|
||||
// // (虽然通常情况下不会为空,但为了健壮性考虑)
|
||||
// if (use_ptr && use_ptr->getValue()) {
|
||||
// use_ptr->getValue()->removeUse(use_ptr);
|
||||
// }
|
||||
// }
|
||||
// // operands 向量本身是 std::vector<std::shared_ptr<Use>>,
|
||||
// // 在此析构函数结束后,operands 向量会被销毁,其内部的 shared_ptr 也会被释放,
|
||||
// // 如果 shared_ptr 引用计数降为0,Use 对象本身也会被销毁。
|
||||
// }
|
||||
unsigned getNumOperands() const { return operands.size(); } ///< 获取操作数数量
|
||||
auto operand_begin() const { return operands.begin(); } ///< 返回操作数列表的开头迭代器
|
||||
auto operand_end() const { return operands.end(); } ///< 返回操作数列表的结尾迭代器
|
||||
@ -657,11 +681,7 @@ class User : public Value {
|
||||
operands.emplace_back(std::make_shared<Use>(operands.size(), this, value));
|
||||
value->addUse(operands.back());
|
||||
} ///< 增加操作数
|
||||
void removeOperand(unsigned index) {
|
||||
auto value = getOperand(index);
|
||||
value->removeUse(operands[index]);
|
||||
operands.erase(operands.begin() + index);
|
||||
} ///< 移除操作数
|
||||
void removeOperand(unsigned index);
|
||||
template <typename ContainerT>
|
||||
void addOperands(const ContainerT &newoperands) {
|
||||
for (auto value : newoperands) {
|
||||
@ -670,6 +690,9 @@ class User : public Value {
|
||||
} ///< 增加多个操作数
|
||||
void replaceOperand(unsigned index, Value *value); ///< 替换操作数
|
||||
void setOperand(unsigned index, Value *value); ///< 设置操作数
|
||||
|
||||
/// 清理用户的所有操作数使用关系
|
||||
void cleanup();
|
||||
};
|
||||
|
||||
/*!
|
||||
@ -747,57 +770,57 @@ public:
|
||||
std::string getKindString() const{
|
||||
switch (kind) {
|
||||
case kInvalid:
|
||||
return "Invalid";
|
||||
return "invalid";
|
||||
case kAdd:
|
||||
return "Add";
|
||||
return "add";
|
||||
case kSub:
|
||||
return "Sub";
|
||||
return "sub";
|
||||
case kMul:
|
||||
return "Mul";
|
||||
return "mul";
|
||||
case kDiv:
|
||||
return "Div";
|
||||
return "sdiv";
|
||||
case kRem:
|
||||
return "Rem";
|
||||
return "srem";
|
||||
case kICmpEQ:
|
||||
return "ICmpEQ";
|
||||
return "icmp eq";
|
||||
case kICmpNE:
|
||||
return "ICmpNE";
|
||||
return "icmp ne";
|
||||
case kICmpLT:
|
||||
return "ICmpLT";
|
||||
return "icmp slt";
|
||||
case kICmpGT:
|
||||
return "ICmpGT";
|
||||
return "icmp sgt";
|
||||
case kICmpLE:
|
||||
return "ICmpLE";
|
||||
return "icmp sle";
|
||||
case kICmpGE:
|
||||
return "ICmpGE";
|
||||
return "icmp sge";
|
||||
case kFAdd:
|
||||
return "FAdd";
|
||||
return "fadd";
|
||||
case kFSub:
|
||||
return "FSub";
|
||||
return "fsub";
|
||||
case kFMul:
|
||||
return "FMul";
|
||||
return "fmul";
|
||||
case kFDiv:
|
||||
return "FDiv";
|
||||
return "fdiv";
|
||||
case kFCmpEQ:
|
||||
return "FCmpEQ";
|
||||
return "fcmp oeq";
|
||||
case kFCmpNE:
|
||||
return "FCmpNE";
|
||||
return "fcmp one";
|
||||
case kFCmpLT:
|
||||
return "FCmpLT";
|
||||
return "fcmp olt";
|
||||
case kFCmpGT:
|
||||
return "FCmpGT";
|
||||
return "fcmp ogt";
|
||||
case kFCmpLE:
|
||||
return "FCmpLE";
|
||||
return "fcmp ole";
|
||||
case kFCmpGE:
|
||||
return "FCmpGE";
|
||||
return "fcmp oge";
|
||||
case kAnd:
|
||||
return "And";
|
||||
return "and";
|
||||
case kOr:
|
||||
return "Or";
|
||||
return "or";
|
||||
case kNeg:
|
||||
return "Neg";
|
||||
return "neg";
|
||||
case kNot:
|
||||
return "Not";
|
||||
return "not";
|
||||
case kFNeg:
|
||||
return "FNeg";
|
||||
case kFNot:
|
||||
@ -805,33 +828,35 @@ public:
|
||||
case kFtoI:
|
||||
return "FtoI";
|
||||
case kItoF:
|
||||
return "IToF";
|
||||
return "iToF";
|
||||
case kCall:
|
||||
return "Call";
|
||||
return "call";
|
||||
case kCondBr:
|
||||
return "CondBr";
|
||||
return "condBr";
|
||||
case kBr:
|
||||
return "Br";
|
||||
return "br";
|
||||
case kReturn:
|
||||
return "Return";
|
||||
return "return";
|
||||
case kUnreachable:
|
||||
return "unreachable";
|
||||
case kAlloca:
|
||||
return "Alloca";
|
||||
return "alloca";
|
||||
case kLoad:
|
||||
return "Load";
|
||||
return "load";
|
||||
case kStore:
|
||||
return "Store";
|
||||
return "store";
|
||||
case kGetElementPtr:
|
||||
return "GetElementPtr";
|
||||
return "getElementPtr";
|
||||
case kMemset:
|
||||
return "Memset";
|
||||
return "memset";
|
||||
case kPhi:
|
||||
return "Phi";
|
||||
return "phi";
|
||||
case kBitItoF:
|
||||
return "BitItoF";
|
||||
case kBitFtoI:
|
||||
return "BitFtoI";
|
||||
case kSRA:
|
||||
return "SRA";
|
||||
return "ashr";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
@ -898,6 +923,10 @@ public:
|
||||
static constexpr uint64_t DefineOpMask = kAlloca | kStore | kPhi;
|
||||
return (kind & DefineOpMask) != 0U;
|
||||
}
|
||||
|
||||
virtual ~Instruction() = default;
|
||||
|
||||
virtual void print(std::ostream& os) const = 0;
|
||||
}; // class Instruction
|
||||
|
||||
class Function;
|
||||
@ -919,58 +948,56 @@ class PhiInst : public Instruction {
|
||||
const std::string &name = "")
|
||||
: Instruction(Kind::kPhi, type, parent, name), vsize(rhs.size()) {
|
||||
assert(rhs.size() == Blocks.size() && "PhiInst: rhs and Blocks must have the same size");
|
||||
for(size_t i = 0; i < rhs.size(); ++i) {
|
||||
for(size_t i = 0; i < vsize; ++i) {
|
||||
addOperand(rhs[i]);
|
||||
addOperand(Blocks[i]);
|
||||
blk2val[Blocks[i]] = rhs[i];
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
Value* getValue(unsigned k) const {return getOperand(2 * k);} ///< 获取位置为k的值
|
||||
BasicBlock* getBlock(unsigned k) const {return dynamic_cast<BasicBlock*>(getOperand(2 * k + 1));}
|
||||
//增加llvm同名方法实现获取value和block
|
||||
Value* getIncomingValue(unsigned k) const {return getOperand(2 * k);} ///< 获取位置为k的值
|
||||
BasicBlock* getIncomingBlock(unsigned k) const {return dynamic_cast<BasicBlock*>(getOperand(2 * k + 1));}
|
||||
|
||||
Value* getIncomingValue(BasicBlock* blk) const {
|
||||
return getvalfromBlk(blk);
|
||||
} ///< 获取指定基本块的传入值
|
||||
|
||||
BasicBlock* getIncomingBlock(Value* val) const {
|
||||
return getBlkfromVal(val);
|
||||
} ///< 获取指定值的传入基本块
|
||||
|
||||
void replaceIncoming(BasicBlock *oldBlock, BasicBlock *newBlock, Value *newValue){
|
||||
delBlk(oldBlock);
|
||||
addIncoming(newValue, newBlock);
|
||||
}
|
||||
|
||||
auto& getincomings() const {return blk2val;} ///< 获取所有的基本块和对应的值
|
||||
|
||||
Value* getvalfromBlk(BasicBlock* blk) const ;
|
||||
BasicBlock* getBlkfromVal(Value* val) const ;
|
||||
|
||||
unsigned getNumIncomingValues() const { return vsize; } ///< 获取传入值的数量
|
||||
Value *getIncomingValue(unsigned Idx) const { return getOperand(Idx * 2); } ///< 获取指定位置的传入值
|
||||
BasicBlock *getIncomingBlock(unsigned Idx) const {return dynamic_cast<BasicBlock *>(getOperand(Idx * 2 + 1)); } ///< 获取指定位置的传入基本块
|
||||
|
||||
Value* getValfromBlk(BasicBlock* block);
|
||||
BasicBlock* getBlkfromVal(Value* value);
|
||||
|
||||
void addIncoming(Value *value, BasicBlock *block) {
|
||||
assert(value && block && "PhiInst: value and block must not be null");
|
||||
assert(value && block && "PhiInst: value and block cannot be null");
|
||||
addOperand(value);
|
||||
addOperand(block);
|
||||
blk2val[block] = value;
|
||||
vsize++;
|
||||
} ///< 添加传入值和对应的基本块
|
||||
|
||||
void removeIncoming(BasicBlock *block){
|
||||
delBlk(block);
|
||||
}
|
||||
|
||||
void delValue(Value* val);
|
||||
void delBlk(BasicBlock* blk);
|
||||
|
||||
void replaceBlk(BasicBlock* newBlk, unsigned k);
|
||||
void replaceold2new(BasicBlock* oldBlk, BasicBlock* newBlk);
|
||||
void refreshB2VMap();
|
||||
|
||||
void removeIncoming(unsigned Idx) {
|
||||
assert(Idx < vsize && "PhiInst: Index out of bounds");
|
||||
auto blk = getIncomingBlock(Idx);
|
||||
removeOperand(Idx * 2 + 1); // Remove block
|
||||
removeOperand(Idx * 2); // Remove value
|
||||
blk2val.erase(blk);
|
||||
vsize--;
|
||||
} ///< 移除指定位置的传入值和对应的基本块
|
||||
// 移除指定的传入值或基本块
|
||||
void removeIncomingValue(Value *value);
|
||||
void removeIncomingBlock(BasicBlock *block);
|
||||
// 设置指定位置的传入值或基本块
|
||||
void setIncomingValue(unsigned Idx, Value *value);
|
||||
void setIncomingBlock(unsigned Idx, BasicBlock *block);
|
||||
// 替换指定位置的传入值或基本块(原理是删除再添加)保留旧块或者旧值
|
||||
void replaceIncomingValue(Value *oldValue, Value *newValue);
|
||||
void replaceIncomingBlock(BasicBlock *oldBlock, BasicBlock *newBlock);
|
||||
// 替换指定位置的传入值或基本块(原理是删除再添加)
|
||||
void replaceIncomingValue(Value *oldValue, Value *newValue, BasicBlock *newBlock);
|
||||
void replaceIncomingBlock(BasicBlock *oldBlock, BasicBlock *newBlock, Value *newValue);
|
||||
void refreshMap() {
|
||||
blk2val.clear();
|
||||
for (unsigned i = 0; i < vsize; ++i) {
|
||||
blk2val[getIncomingBlock(i)] = getIncomingValue(i);
|
||||
}
|
||||
} ///< 刷新块到值的映射关系
|
||||
auto getValues() { return make_range(std::next(operand_begin()), operand_end()); }
|
||||
void print(std::ostream& os) const override;
|
||||
};
|
||||
|
||||
|
||||
@ -979,16 +1006,14 @@ class CallInst : public Instruction {
|
||||
friend class IRBuilder;
|
||||
|
||||
protected:
|
||||
CallInst(Function *callee, const std::vector<Value *> &args = {},
|
||||
BasicBlock *parent = nullptr, const std::string &name = "");
|
||||
|
||||
CallInst(Function *callee, const std::vector<Value *> &args, BasicBlock *parent = nullptr, const std::string &name = "");
|
||||
|
||||
public:
|
||||
Function* getCallee() const;
|
||||
Function *getCallee() const;
|
||||
auto getArguments() const {
|
||||
return make_range(std::next(operand_begin()), operand_end());
|
||||
}
|
||||
|
||||
void print(std::ostream& os) const override;
|
||||
}; // class CallInst
|
||||
|
||||
//! Unary instruction, includes '!', '-' and type conversion.
|
||||
@ -1006,7 +1031,7 @@ protected:
|
||||
|
||||
public:
|
||||
Value* getOperand() const { return User::getOperand(0); }
|
||||
|
||||
void print(std::ostream& os) const override;
|
||||
}; // class UnaryInst
|
||||
|
||||
//! Binary instruction, e.g., arithmatic, relation, logic, etc.
|
||||
@ -1085,6 +1110,7 @@ public:
|
||||
// 后端处理数组访存操作时需要创建计算地址的指令,需要在外部构造 BinaryInst 对象
|
||||
return new BinaryInst(kind, type, lhs, rhs, parent, name);
|
||||
}
|
||||
void print(std::ostream& os) const override;
|
||||
}; // class BinaryInst
|
||||
|
||||
//! The return statement
|
||||
@ -1105,6 +1131,7 @@ class ReturnInst : public Instruction {
|
||||
Value* getReturnValue() const {
|
||||
return hasReturnValue() ? getOperand(0) : nullptr;
|
||||
}
|
||||
void print(std::ostream& os) const override;
|
||||
};
|
||||
|
||||
//! Unconditional branch
|
||||
@ -1134,7 +1161,7 @@ public:
|
||||
}
|
||||
return succs;
|
||||
}
|
||||
|
||||
void print(std::ostream& os) const override;
|
||||
}; // class UncondBrInst
|
||||
|
||||
//! Conditional branch
|
||||
@ -1174,7 +1201,7 @@ public:
|
||||
}
|
||||
return succs;
|
||||
}
|
||||
|
||||
void print(std::ostream& os) const override;
|
||||
}; // class CondBrInst
|
||||
|
||||
class UnreachableInst : public Instruction {
|
||||
@ -1182,7 +1209,7 @@ public:
|
||||
// 构造函数:设置指令类型为 kUnreachable
|
||||
explicit UnreachableInst(const std::string& name, BasicBlock *parent = nullptr)
|
||||
: Instruction(kUnreachable, Type::getVoidType(), parent, "") {}
|
||||
|
||||
void print(std::ostream& os) const { os << "unreachable"; }
|
||||
};
|
||||
|
||||
//! Allocate memory for stack variables, used for non-global variable declartion
|
||||
@ -1200,7 +1227,7 @@ public:
|
||||
Type* getAllocatedType() const {
|
||||
return getType()->as<PointerType>()->getBaseType();
|
||||
} ///< 获取分配的类型
|
||||
|
||||
void print(std::ostream& os) const override;
|
||||
}; // class AllocaInst
|
||||
|
||||
|
||||
@ -1238,6 +1265,7 @@ public:
|
||||
BasicBlock *parent = nullptr, const std::string &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
|
||||
@ -1255,7 +1283,7 @@ protected:
|
||||
|
||||
public:
|
||||
Value* getPointer() const { return getOperand(0); }
|
||||
|
||||
void print(std::ostream& os) const override;
|
||||
}; // class LoadInst
|
||||
|
||||
//! Store a value to memory address specified by a pointer value
|
||||
@ -1274,7 +1302,7 @@ protected:
|
||||
public:
|
||||
Value* getValue() const { return getOperand(0); }
|
||||
Value* getPointer() const { return getOperand(1); }
|
||||
|
||||
void print(std::ostream& os) const override;
|
||||
}; // class StoreInst
|
||||
|
||||
//! Memset instruction
|
||||
@ -1304,7 +1332,7 @@ public:
|
||||
Value* getBegin() const { return getOperand(1); }
|
||||
Value* getSize() const { return getOperand(2); }
|
||||
Value* getValue() const { return getOperand(3); }
|
||||
|
||||
void print(std::ostream& os) const override;
|
||||
};
|
||||
|
||||
class GlobalValue;
|
||||
@ -1322,6 +1350,11 @@ public:
|
||||
public:
|
||||
Function* getParent() const { return func; }
|
||||
int getIndex() const { return index; }
|
||||
|
||||
/// 清理参数的使用关系
|
||||
void cleanup();
|
||||
|
||||
void print(std::ostream& os) const;
|
||||
};
|
||||
|
||||
|
||||
@ -1399,6 +1432,11 @@ protected:
|
||||
blocks.emplace_front(block);
|
||||
return block;
|
||||
}
|
||||
|
||||
/// 清理函数中的所有使用关系
|
||||
void cleanup();
|
||||
|
||||
void print(std::ostream& os) const;
|
||||
};
|
||||
|
||||
//! Global value declared at file scope
|
||||
@ -1464,6 +1502,7 @@ public:
|
||||
return getByIndex(index);
|
||||
} ///< 通过多维索引indices获取初始值
|
||||
const ValueCounter& getInitValues() const { return initValues; }
|
||||
void print(std::ostream& os) const;
|
||||
}; // class GlobalValue
|
||||
|
||||
|
||||
@ -1521,6 +1560,8 @@ class ConstantVariable : public Value {
|
||||
return getByIndex(index);
|
||||
} ///< 通过多维索引indices获取初始值
|
||||
const ValueCounter& getInitValues() const { return initValues; } ///< 获取初始值
|
||||
void print(std::ostream& os) const;
|
||||
void print_init(std::ostream& os) const;
|
||||
};
|
||||
|
||||
using SymbolTableNode = struct SymbolTableNode {
|
||||
@ -1543,6 +1584,8 @@ class SymbolTable {
|
||||
|
||||
Value* getVariable(const std::string &name) const; ///< 根据名字name以及当前作用域获取变量
|
||||
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(); ///< 获取全局变量列表
|
||||
const std::vector<std::unique_ptr<ConstantVariable>>& getConsts() const; ///< 获取全局常量列表
|
||||
void enterNewScope(); ///< 进入新的作用域
|
||||
@ -1550,6 +1593,9 @@ class SymbolTable {
|
||||
bool isInGlobalScope() const; ///< 是否位于全局作用域
|
||||
void enterGlobalScope(); ///< 进入全局作用域
|
||||
bool isCurNodeNull() { return curNode == nullptr; }
|
||||
|
||||
/// 清理符号表中的所有内容
|
||||
void cleanup();
|
||||
};
|
||||
|
||||
//! IR unit for representing a SysY compile unit
|
||||
@ -1602,6 +1648,12 @@ class Module {
|
||||
void addVariable(const std::string &name, AllocaInst *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) {
|
||||
return variableTable.getVariable(name);
|
||||
} ///< 根据名字name和当前作用域获取变量
|
||||
@ -1614,7 +1666,7 @@ class Module {
|
||||
} ///< 获取函数
|
||||
Function* getExternalFunction(const std::string &name) const {
|
||||
auto result = externalFunctions.find(name);
|
||||
if (result == functions.end()) {
|
||||
if (result == externalFunctions.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return result->second.get();
|
||||
@ -1634,6 +1686,11 @@ class Module {
|
||||
void leaveScope() { variableTable.leaveScope(); } ///< 离开作用域
|
||||
|
||||
bool isInGlobalArea() const { return variableTable.isInGlobalScope(); } ///< 是否位于全局作用域
|
||||
|
||||
/// 清理模块中的所有对象,包括函数、基本块、指令等
|
||||
void cleanup();
|
||||
|
||||
void print(std::ostream& os) const;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
||||
@ -350,38 +350,31 @@ class IRBuilder {
|
||||
Type *currentWalkType = pointerType->as<PointerType>()->getBaseType();
|
||||
|
||||
// 遍历所有索引来深入类型层次结构。
|
||||
// `indices` 向量包含了所有 GEP 索引,包括由 `visitLValue` 等函数添加的初始 `0` 索引。
|
||||
// 重要:第一个索引总是用于"解引用"指针,后续索引才用于数组/结构体的索引
|
||||
for (int i = 0; i < indices.size(); ++i) {
|
||||
if (currentWalkType->isArray()) {
|
||||
// 情况一:当前遍历类型是 `ArrayType`。
|
||||
// 索引用于选择数组元素,`currentWalkType` 更新为数组的元素类型。
|
||||
currentWalkType = currentWalkType->as<ArrayType>()->getElementType();
|
||||
} else if (currentWalkType->isPointer()) {
|
||||
// 情况二:当前遍历类型是 `PointerType`。
|
||||
// 这意味着我们正在通过一个指针来访问其指向的内存。
|
||||
// 索引用于选择该指针所指向的“数组”的元素。
|
||||
// `currentWalkType` 更新为该指针所指向的基础类型。
|
||||
// 例如:如果 `currentWalkType` 是 `i32*`,它将变为 `i32`。
|
||||
// 如果 `currentWalkType` 是 `[10 x i32]*`,它将变为 `[10 x i32]`。
|
||||
currentWalkType = currentWalkType->as<PointerType>()->getBaseType();
|
||||
if (i == 0) {
|
||||
// 第一个索引:总是用于"解引用"基指针,不改变currentWalkType
|
||||
// 例如:对于 `[4 x i32]* ptr, i32 0`,第一个0只是说"访问ptr指向的对象"
|
||||
// currentWalkType 保持为 `[4 x i32]`
|
||||
continue;
|
||||
} 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) {
|
||||
assert(false && "Invalid GEP indexing: attempting to index into a non-aggregate/non-pointer type with further indices.");
|
||||
return nullptr; // 返回空指针表示类型推断失败
|
||||
// 后续索引:用于实际的数组/结构体索引
|
||||
if (currentWalkType->isArray()) {
|
||||
// 数组索引:选择数组中的元素
|
||||
currentWalkType = currentWalkType->as<ArrayType>()->getElementType();
|
||||
} else if (currentWalkType->isPointer()) {
|
||||
// 指针索引:解引用指针并继续
|
||||
currentWalkType = currentWalkType->as<PointerType>()->getBaseType();
|
||||
} else {
|
||||
// 标量类型:不能进一步索引
|
||||
if (i < indices.size() - 1) {
|
||||
assert(false && "Invalid GEP indexing: attempting to index into a non-aggregate/non-pointer type with further indices.");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
// 如果是最后一个索引,且当前类型是标量,则类型保持不变,这是合法的。
|
||||
// 循环会自然结束,返回正确的 `currentWalkType`。
|
||||
}
|
||||
}
|
||||
|
||||
// 所有索引处理完毕后,`currentWalkType` 就是 GEP 指令最终计算出的地址所指向的元素的类型。
|
||||
return currentWalkType;
|
||||
}
|
||||
|
||||
@ -48,13 +48,6 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
// 清空 User 的 operands 向量。这会递减 User 持有的 shared_ptr<Use> 的引用计数。
|
||||
// 当引用计数降为 0 时,Use 对象本身将被销毁。
|
||||
// User::operands.clear(); // 这个步骤会在 Instruction 的析构函数中自动完成,因为它是 vector 成员
|
||||
// 或者我们可以在 User::removeOperand 方法中确保 Use 对象从 operands 中移除。
|
||||
// 实际上,只要 Value::removeUse(use_ptr) 被调用了,
|
||||
// 当 Instruction 所在的 unique_ptr 销毁时,它的 operands vector 也会被销毁。
|
||||
// 所以这里不需要显式 clear()
|
||||
}
|
||||
static void usedelete(Instruction *inst) {
|
||||
assert(inst && "Instruction to delete cannot be null.");
|
||||
@ -75,7 +68,7 @@ public:
|
||||
// 步骤3: 物理删除指令
|
||||
// 这会导致 Instruction 对象的 unique_ptr 销毁,从而调用其析构函数链。
|
||||
parentBlock->removeInst(inst);
|
||||
}
|
||||
}
|
||||
|
||||
static BasicBlock::iterator usedelete(BasicBlock::iterator inst_it) {
|
||||
Instruction *inst_to_delete = inst_it->get();
|
||||
@ -92,7 +85,7 @@ public:
|
||||
|
||||
// 步骤3: 物理删除指令并返回下一个迭代器
|
||||
return parentBlock->removeInst(inst_it);
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否是全局变量
|
||||
static bool isGlobal(Value *val) {
|
||||
|
||||
@ -86,7 +86,60 @@ private:
|
||||
case LPAREN: case RPAREN: return 0; // Parentheses have lowest precedence for stack logic
|
||||
default: return -1; // Unknown operator
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ExpKey {
|
||||
BinaryOp op; ///< 操作符
|
||||
Value *left; ///< 左操作数
|
||||
Value *right; ///< 右操作数
|
||||
ExpKey(BinaryOp op, Value *left, Value *right) : op(op), left(left), right(right) {}
|
||||
|
||||
bool operator<(const ExpKey &other) const {
|
||||
if (op != other.op)
|
||||
return op < other.op; ///< 比较操作符
|
||||
if (left != other.left)
|
||||
return left < other.left; ///< 比较左操作
|
||||
return right < other.right; ///< 比较右操作数
|
||||
} ///< 重载小于运算符用于比较ExpKey
|
||||
};
|
||||
|
||||
struct UnExpKey {
|
||||
BinaryOp op; ///< 一元操作符
|
||||
Value *operand; ///< 操作数
|
||||
UnExpKey(BinaryOp op, Value *operand) : op(op), operand(operand) {}
|
||||
|
||||
bool operator<(const UnExpKey &other) const {
|
||||
if (op != other.op)
|
||||
return op < other.op; ///< 比较操作符
|
||||
return operand < other.operand; ///< 比较操作数
|
||||
} ///< 重载小于运算符用于比较UnExpKey
|
||||
};
|
||||
|
||||
struct GEPKey {
|
||||
Value *basePointer;
|
||||
std::vector<Value *> indices;
|
||||
|
||||
// 为 std::map 定义比较运算符,使得 GEPKey 可以作为键
|
||||
bool operator<(const GEPKey &other) const {
|
||||
if (basePointer != other.basePointer) {
|
||||
return basePointer < other.basePointer;
|
||||
}
|
||||
// 逐个比较索引,确保顺序一致
|
||||
if (indices.size() != other.indices.size()) {
|
||||
return indices.size() < other.indices.size();
|
||||
}
|
||||
for (size_t i = 0; i < indices.size(); ++i) {
|
||||
if (indices[i] != other.indices[i]) {
|
||||
return indices[i] < other.indices[i];
|
||||
}
|
||||
}
|
||||
return false; // 如果 basePointer 和所有索引都相同,则认为相等
|
||||
}
|
||||
};
|
||||
std::map<GEPKey, Value*> availableGEPs; ///< 用于存储 GEP 的缓存
|
||||
std::map<ExpKey, Value*> availableBinaryExpressions;
|
||||
std::map<UnExpKey, Value*> availableUnaryExpressions;
|
||||
std::map<Value*, Value*> availableLoads;
|
||||
|
||||
public:
|
||||
SysYIRGenerator() = default;
|
||||
@ -167,6 +220,15 @@ public:
|
||||
Value* computeExp(SysYParser::ExpContext *ctx, Type* targetType = nullptr);
|
||||
Value* computeAddExp(SysYParser::AddExpContext *ctx, Type* targetType = nullptr);
|
||||
void compute();
|
||||
|
||||
// 参数是发生 store 操作的目标地址/变量的 Value*
|
||||
void invalidateExpressionsOnStore(Value* storedAddress);
|
||||
|
||||
// 清除因函数调用而失效的表达式缓存(保守策略)
|
||||
void invalidateExpressionsOnCall();
|
||||
|
||||
// 在进入新的基本块时清空所有表达式缓存
|
||||
void enterNewBasicBlock();
|
||||
public:
|
||||
// 获取GEP指令的地址
|
||||
Value* getGEPAddressInst(Value* basePointer, const std::vector<Value*>& indices);
|
||||
|
||||
1509
src/midend/IR.cpp
1509
src/midend/IR.cpp
File diff suppressed because it is too large
Load Diff
@ -52,14 +52,16 @@ bool LargeArrayToGlobalPass::runOnModule(Module *M, AnalysisManager &AM) {
|
||||
|
||||
// Calculate the size of the allocated type
|
||||
unsigned size = calculateTypeSize(allocatedType);
|
||||
|
||||
// Debug: print size information
|
||||
std::cout << "LargeArrayToGlobalPass: Found alloca with size " << size
|
||||
if(DEBUG){
|
||||
// Debug: print size information
|
||||
std::cout << "LargeArrayToGlobalPass: Found alloca with size " << size
|
||||
<< " for type " << typeToString(allocatedType) << std::endl;
|
||||
}
|
||||
|
||||
// Convert arrays of 1KB (1024 bytes) or larger to global variables
|
||||
if (size >= 1024) {
|
||||
std::cout << "LargeArrayToGlobalPass: Converting array of size " << size << " to global" << std::endl;
|
||||
if(DEBUG)
|
||||
std::cout << "LargeArrayToGlobalPass: Converting array of size " << size << " to global" << std::endl;
|
||||
allocasToConvert.emplace_back(alloca, F);
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,8 +148,8 @@ void Reg2MemContext::rewritePhis(Function *func) {
|
||||
// 1. 为 Phi 指令的每个入边,在前驱块的末尾插入 Store 指令
|
||||
// PhiInst 假设有 getIncomingValues() 和 getIncomingBlocks()
|
||||
for (unsigned i = 0; i < phiInst->getNumIncomingValues(); ++i) { // 假设 PhiInst 是通过操作数来管理入边的
|
||||
Value *incomingValue = phiInst->getValue(i); // 获取入值
|
||||
BasicBlock *incomingBlock = phiInst->getBlock(i); // 获取对应的入块
|
||||
Value *incomingValue = phiInst->getIncomingValue(i); // 获取入值
|
||||
BasicBlock *incomingBlock = phiInst->getIncomingBlock(i); // 获取对应的入块
|
||||
|
||||
// 在入块的跳转指令之前插入 StoreInst
|
||||
// 需要找到 incomingBlock 的终结指令 (Terminator Instruction)
|
||||
|
||||
@ -280,6 +280,22 @@ void SCCPContext::ProcessInstruction(Instruction *inst) {
|
||||
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()) {
|
||||
case Instruction::kAdd:
|
||||
case Instruction::kSub:
|
||||
@ -398,6 +414,7 @@ void SCCPContext::ProcessInstruction(Instruction *inst) {
|
||||
newState = SSAPValue(); // 保持 Top
|
||||
break;
|
||||
case Instruction::kCall:
|
||||
// TODO: 处理 Call 指令根据副作用分析可以推断的常量
|
||||
// 大多数 Call 指令都假定为 Bottom,除非是纯函数且所有参数都是常量
|
||||
newState = SSAPValue(LatticeVal::Bottom);
|
||||
break;
|
||||
@ -417,19 +434,71 @@ void SCCPContext::ProcessInstruction(Instruction *inst) {
|
||||
}
|
||||
case Instruction::kPhi: {
|
||||
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
|
||||
|
||||
bool hasAnyExecutablePred = false;
|
||||
|
||||
for (unsigned i = 0; i < phi->getNumIncomingValues(); ++i) {
|
||||
Value *incomingVal = phi->getIncomingValue(i);
|
||||
BasicBlock *incomingBlock = phi->getIncomingBlock(i);
|
||||
|
||||
if (executableBlocks.count(incomingBlock)) { // 仅考虑可执行前驱
|
||||
phiResult = Meet(phiResult, GetValueState(incomingVal));
|
||||
if (phiResult.state == LatticeVal::Bottom)
|
||||
break; // 如果已经 Bottom,则提前退出
|
||||
|
||||
if (executableBlocks.count(incomingBlock)) {
|
||||
hasAnyExecutablePred = true;
|
||||
Value *incomingVal = phi->getIncomingValue(i);
|
||||
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;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
newState = phiResult;
|
||||
break;
|
||||
}
|
||||
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 *toBB = edge.second;
|
||||
|
||||
// 检查目标块是否已经可执行
|
||||
bool wasAlreadyExecutable = executableBlocks.count(toBB) > 0;
|
||||
|
||||
// 标记目标块为可执行(如果还不是的话)
|
||||
MarkBlockExecutable(toBB);
|
||||
|
||||
// 对于目标块中的所有 Phi 指令,重新评估其值,因为可能有新的前驱被激活
|
||||
for (auto &inst_ptr : toBB->getInstructions()) {
|
||||
if (dynamic_cast<PhiInst *>(inst_ptr.get())) {
|
||||
instWorkList.push(inst_ptr.get());
|
||||
|
||||
// 如果目标块之前就已经可执行,那么需要重新处理其中的phi节点
|
||||
// 因为现在有新的前驱变为可执行,phi节点的值可能需要更新
|
||||
if (wasAlreadyExecutable) {
|
||||
for (auto &inst_ptr : toBB->getInstructions()) {
|
||||
if (dynamic_cast<PhiInst *>(inst_ptr.get())) {
|
||||
instWorkList.push(inst_ptr.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果目标块是新变为可执行的,MarkBlockExecutable已经添加了所有指令
|
||||
}
|
||||
|
||||
// 阶段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()) {
|
||||
MarkBlockExecutable(func->getEntryBlock());
|
||||
}
|
||||
|
||||
// 主循环:处理工作列表直到不动点
|
||||
// 主循环:标准的SCCP工作列表算法
|
||||
// 交替处理边工作列表和指令工作列表直到不动点
|
||||
while (!instWorkList.empty() || !edgeWorkList.empty()) {
|
||||
// 处理所有待处理的CFG边
|
||||
while (!edgeWorkList.empty()) {
|
||||
ProcessEdge(edgeWorkList.front());
|
||||
edgeWorkList.pop();
|
||||
}
|
||||
|
||||
// 处理所有待处理的指令
|
||||
while (!instWorkList.empty()) {
|
||||
Instruction *inst = instWorkList.front();
|
||||
instWorkList.pop();
|
||||
@ -845,7 +949,7 @@ void SCCPContext::RemovePhiIncoming(BasicBlock *phiParentBB, BasicBlock *removed
|
||||
|
||||
for (Instruction *inst : insts_to_check) {
|
||||
if (auto phi = dynamic_cast<PhiInst *>(inst)) {
|
||||
phi->delBlk(removedPred);
|
||||
phi->removeIncomingBlock(removedPred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ bool SysYCFGOptUtils::SysYDelInstAfterBr(Function *func) {
|
||||
++Branchiter;
|
||||
while (Branchiter != instructions.end()) {
|
||||
changed = true;
|
||||
Branchiter = instructions.erase(Branchiter);
|
||||
Branchiter = SysYIROptUtils::usedelete(Branchiter); // 删除指令
|
||||
}
|
||||
|
||||
if (Branch) { // 更新前驱后继关系
|
||||
@ -77,6 +77,11 @@ bool SysYCFGOptUtils::SysYBlockMerge(Function *func) {
|
||||
bool changed = false;
|
||||
|
||||
for (auto blockiter = func->getBasicBlocks().begin(); blockiter != func->getBasicBlocks().end();) {
|
||||
// 检查当前块是是不是entry块
|
||||
if( blockiter->get() == func->getEntryBlock() ) {
|
||||
blockiter++;
|
||||
continue; // 跳过入口块
|
||||
}
|
||||
if (blockiter->get()->getNumSuccessors() == 1) {
|
||||
// 如果当前块只有一个后继块
|
||||
// 且后继块只有一个前驱块
|
||||
@ -86,7 +91,7 @@ bool SysYCFGOptUtils::SysYBlockMerge(Function *func) {
|
||||
BasicBlock *block = blockiter->get();
|
||||
BasicBlock *nextBlock = blockiter->get()->getSuccessors()[0];
|
||||
// auto nextarguments = nextBlock->getArguments();
|
||||
// 删除br指令
|
||||
// 删除block的br指令
|
||||
if (block->getNumInstructions() != 0) {
|
||||
auto thelastinstinst = block->terminator();
|
||||
if (thelastinstinst->get()->isUnconditional()) {
|
||||
@ -98,14 +103,21 @@ bool SysYCFGOptUtils::SysYBlockMerge(Function *func) {
|
||||
if (brinst->getThenBlock() == brinst->getElseBlock()) {
|
||||
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();) {
|
||||
institer->get()->setParent(block);
|
||||
block->getInstructions().emplace_back(institer->release());
|
||||
institer = nextBlock->getInstructions().erase(institer);
|
||||
// institer->get()->setParent(block);
|
||||
// block->getInstructions().emplace_back(institer->release());
|
||||
// 用usedelete删除会导致use关系被删除我只希望移动指令到当前块
|
||||
// institer = SysYIROptUtils::usedelete(institer);
|
||||
// institer = nextBlock->getInstructions().erase(institer);
|
||||
institer = nextBlock->moveInst(institer, block->getInstructions().end(), block);
|
||||
|
||||
}
|
||||
// 更新前驱后继关系,类似树节点操作
|
||||
block->removeSuccessor(nextBlock);
|
||||
@ -189,7 +201,7 @@ bool SysYCFGOptUtils::SysYDelNoPreBLock(Function *func) {
|
||||
break;
|
||||
}
|
||||
// 将这个 Phi 节点中来自不可达前驱(unreachableBlock)的输入参数删除
|
||||
dynamic_cast<PhiInst *>(phiInstPtr.get())->delBlk(unreachableBlock);
|
||||
dynamic_cast<PhiInst *>(phiInstPtr.get())->removeIncomingBlock(unreachableBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -288,13 +300,12 @@ bool SysYCFGOptUtils::SysYDelEmptyBlock(Function *func, IRBuilder *pBuilder) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::function<Value *(Value *, BasicBlock *)> getUltimateSourceValue = [&](Value *val,
|
||||
BasicBlock *currentDefBlock) -> Value * {
|
||||
// 如果值不是指令,例如常量或函数参数,则它本身就是最终来源
|
||||
if (auto instr = dynamic_cast<Instruction *>(val)) { // Assuming Value* has a method to check if it's an instruction
|
||||
std::function<Value *(Value *, BasicBlock *)> getUltimateSourceValue = [&](Value *val, BasicBlock *currentDefBlock) -> Value * {
|
||||
|
||||
if(!dynamic_cast<Instruction *>(val)) {
|
||||
// 如果 val 不是指令,直接返回它
|
||||
return val;
|
||||
}
|
||||
|
||||
Instruction *inst = dynamic_cast<Instruction *>(val);
|
||||
// 如果定义指令不在任何空块中,它就是最终来源
|
||||
if (!emptyBlockRedirectMap.count(currentDefBlock)) {
|
||||
@ -311,7 +322,7 @@ bool SysYCFGOptUtils::SysYDelEmptyBlock(Function *func, IRBuilder *pBuilder) {
|
||||
// 找到在空块链中导致 currentDefBlock 的那个前驱块
|
||||
if (emptyBlockRedirectMap.count(incomingBlock) || incomingBlock == currentBlock) {
|
||||
// 递归追溯该传入值
|
||||
return getUltimateSourceValue(phi->getIncomingValue(incomingBlock), incomingBlock);
|
||||
return getUltimateSourceValue(phi->getValfromBlk(incomingBlock), incomingBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -354,7 +365,7 @@ bool SysYCFGOptUtils::SysYDelEmptyBlock(Function *func, IRBuilder *pBuilder) {
|
||||
|
||||
if (actualEmptyPredecessorOfS) {
|
||||
// 获取 Phi 节点原本从 actualEmptyPredecessorOfS 接收的值
|
||||
Value *valueFromEmptyPredecessor = phiInst->getIncomingValue(actualEmptyPredecessorOfS);
|
||||
Value *valueFromEmptyPredecessor = phiInst->getValfromBlk(actualEmptyPredecessorOfS);
|
||||
|
||||
// 追溯这个值,找到它在非空块中的最终来源
|
||||
// currentBlock 是 P
|
||||
@ -364,12 +375,13 @@ bool SysYCFGOptUtils::SysYDelEmptyBlock(Function *func, IRBuilder *pBuilder) {
|
||||
|
||||
// 替换 Phi 节点的传入块和传入值
|
||||
if (ultimateSourceValue) { // 确保成功追溯到有效来源
|
||||
phiInst->replaceIncoming(actualEmptyPredecessorOfS, currentBlock, ultimateSourceValue);
|
||||
// phiInst->replaceIncoming(actualEmptyPredecessorOfS, currentBlock, ultimateSourceValue);
|
||||
phiInst->replaceIncomingBlock(actualEmptyPredecessorOfS, currentBlock, ultimateSourceValue);
|
||||
} else {
|
||||
assert(false && "[DelEmptyBlock] Unable to trace a valid source for Phi instruction");
|
||||
// 无法追溯到有效来源,这可能是个错误或特殊情况
|
||||
// 此时可能需要移除该 Phi 项,或者插入一个 undef 值
|
||||
phiInst->removeIncoming(actualEmptyPredecessorOfS);
|
||||
phiInst->getValfromBlk(actualEmptyPredecessorOfS);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -421,7 +433,7 @@ bool SysYCFGOptUtils::SysYDelEmptyBlock(Function *func, IRBuilder *pBuilder) {
|
||||
|
||||
if (actualEmptyPredecessorOfS) {
|
||||
// 获取 Phi 节点原本从 actualEmptyPredecessorOfS 接收的值
|
||||
Value *valueFromEmptyPredecessor = phiInst->getIncomingValue(actualEmptyPredecessorOfS);
|
||||
Value *valueFromEmptyPredecessor = phiInst->getValfromBlk(actualEmptyPredecessorOfS);
|
||||
|
||||
// 追溯这个值,找到它在非空块中的最终来源
|
||||
// currentBlock 是 P
|
||||
@ -431,12 +443,13 @@ bool SysYCFGOptUtils::SysYDelEmptyBlock(Function *func, IRBuilder *pBuilder) {
|
||||
|
||||
// 替换 Phi 节点的传入块和传入值
|
||||
if (ultimateSourceValue) { // 确保成功追溯到有效来源
|
||||
phiInst->replaceIncoming(actualEmptyPredecessorOfS, currentBlock, ultimateSourceValue);
|
||||
// phiInst->replaceIncoming(actualEmptyPredecessorOfS, currentBlock, ultimateSourceValue);
|
||||
phiInst->replaceIncomingBlock(actualEmptyPredecessorOfS, currentBlock, ultimateSourceValue);
|
||||
} else {
|
||||
assert(false && "[DelEmptyBlock] Unable to trace a valid source for Phi instruction");
|
||||
// 无法追溯到有效来源,这可能是个错误或特殊情况
|
||||
// 此时可能需要移除该 Phi 项,或者插入一个 undef 值
|
||||
phiInst->removeIncoming(actualEmptyPredecessorOfS);
|
||||
phiInst->removeIncomingBlock(actualEmptyPredecessorOfS);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -481,7 +494,7 @@ bool SysYCFGOptUtils::SysYDelEmptyBlock(Function *func, IRBuilder *pBuilder) {
|
||||
|
||||
if (actualEmptyPredecessorOfS) {
|
||||
// 获取 Phi 节点原本从 actualEmptyPredecessorOfS 接收的值
|
||||
Value *valueFromEmptyPredecessor = phiInst->getIncomingValue(actualEmptyPredecessorOfS);
|
||||
Value *valueFromEmptyPredecessor = phiInst->getValfromBlk(actualEmptyPredecessorOfS);
|
||||
|
||||
// 追溯这个值,找到它在非空块中的最终来源
|
||||
// currentBlock 是 P
|
||||
@ -491,12 +504,13 @@ bool SysYCFGOptUtils::SysYDelEmptyBlock(Function *func, IRBuilder *pBuilder) {
|
||||
|
||||
// 替换 Phi 节点的传入块和传入值
|
||||
if (ultimateSourceValue) { // 确保成功追溯到有效来源
|
||||
phiInst->replaceIncoming(actualEmptyPredecessorOfS, currentBlock, ultimateSourceValue);
|
||||
// phiInst->replaceIncoming(actualEmptyPredecessorOfS, currentBlock, ultimateSourceValue);
|
||||
phiInst->replaceIncomingBlock(actualEmptyPredecessorOfS, currentBlock, ultimateSourceValue);
|
||||
} else {
|
||||
assert(false && "[DelEmptyBlock] Unable to trace a valid source for Phi instruction");
|
||||
// 无法追溯到有效来源,这可能是个错误或特殊情况
|
||||
// 此时可能需要移除该 Phi 项,或者插入一个 undef 值
|
||||
phiInst->removeIncoming(actualEmptyPredecessorOfS);
|
||||
phiInst->removeIncomingBlock(actualEmptyPredecessorOfS);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -647,7 +661,7 @@ bool SysYCFGOptUtils::SysYCondBr2Br(Function *func, IRBuilder *pBuilder) {
|
||||
break;
|
||||
}
|
||||
// 使用 delBlk 方法删除 basicblock.get() 对应的传入值
|
||||
dynamic_cast<PhiInst *>(phiinst.get())->removeIncoming(basicblock.get());
|
||||
dynamic_cast<PhiInst *>(phiinst.get())->removeIncomingBlock(basicblock.get());
|
||||
}
|
||||
|
||||
} else { // cond为false或0
|
||||
@ -665,7 +679,7 @@ bool SysYCFGOptUtils::SysYCondBr2Br(Function *func, IRBuilder *pBuilder) {
|
||||
break;
|
||||
}
|
||||
// 使用 delBlk 方法删除 basicblock.get() 对应的传入值
|
||||
dynamic_cast<PhiInst *>(phiinst.get())->removeIncoming(basicblock.get());
|
||||
dynamic_cast<PhiInst *>(phiinst.get())->removeIncomingBlock(basicblock.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,6 +82,10 @@ void PassManager::runOptimizationPipeline(Module* moduleIR, IRBuilder* builderIR
|
||||
this->addPass(&SysYAddReturnPass::ID);
|
||||
this->run();
|
||||
|
||||
this->clearPasses();
|
||||
this->addPass(&BuildCFG::ID);
|
||||
this->run();
|
||||
|
||||
if(DEBUG) {
|
||||
std::cout << "=== IR After CFGOpt Optimizations ===\n";
|
||||
printPasses();
|
||||
@ -122,7 +126,9 @@ void PassManager::runOptimizationPipeline(Module* moduleIR, IRBuilder* builderIR
|
||||
std::cout << "=== IR After Reg2Mem Optimizations ===\n";
|
||||
printPasses();
|
||||
}
|
||||
|
||||
this->clearPasses();
|
||||
this->addPass(&BuildCFG::ID);
|
||||
this->run();
|
||||
if (DEBUG) std::cout << "--- Custom optimization sequence finished ---\n";
|
||||
}
|
||||
|
||||
@ -137,6 +143,7 @@ void PassManager::runOptimizationPipeline(Module* moduleIR, IRBuilder* builderIR
|
||||
SysYPrinter printer(moduleIR);
|
||||
printer.printIR();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PassManager::clearPasses() {
|
||||
|
||||
@ -38,6 +38,116 @@ std::pair<long long, int> calculate_signed_magic(int d) {
|
||||
return {m, k};
|
||||
}
|
||||
|
||||
// 清除因函数调用而失效的表达式缓存(保守策略)
|
||||
void SysYIRGenerator::invalidateExpressionsOnCall() {
|
||||
availableBinaryExpressions.clear();
|
||||
availableUnaryExpressions.clear();
|
||||
availableLoads.clear();
|
||||
availableGEPs.clear();
|
||||
}
|
||||
|
||||
// 在进入新的基本块时清空所有表达式缓存
|
||||
void SysYIRGenerator::enterNewBasicBlock() {
|
||||
availableBinaryExpressions.clear();
|
||||
availableUnaryExpressions.clear();
|
||||
availableLoads.clear();
|
||||
availableGEPs.clear();
|
||||
}
|
||||
|
||||
// 清除因变量赋值而失效的表达式缓存
|
||||
// @param storedAddress: store 指令的目标地址 (例如 AllocaInst* 或 GEPInst*)
|
||||
void SysYIRGenerator::invalidateExpressionsOnStore(Value *storedAddress) {
|
||||
// 遍历二元表达式缓存,移除受影响的条目
|
||||
// 创建一个临时列表来存储要移除的键,避免在迭代时修改容器
|
||||
std::vector<ExpKey> binaryKeysToRemove;
|
||||
for (const auto &pair : availableBinaryExpressions) {
|
||||
// 检查左操作数
|
||||
// 如果左操作数是 LoadInst,并且它从 storedAddress 加载
|
||||
if (auto loadInst = dynamic_cast<LoadInst *>(pair.first.left)) {
|
||||
if (loadInst->getPointer() == storedAddress) {
|
||||
binaryKeysToRemove.push_back(pair.first);
|
||||
continue; // 这个表达式已标记为移除,跳到下一个
|
||||
}
|
||||
}
|
||||
// 如果左操作数本身就是被存储的地址 (例如,将一个地址值直接作为操作数,虽然不常见)
|
||||
if (pair.first.left == storedAddress) {
|
||||
binaryKeysToRemove.push_back(pair.first);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查右操作数,逻辑同左操作数
|
||||
if (auto loadInst = dynamic_cast<LoadInst *>(pair.first.right)) {
|
||||
if (loadInst->getPointer() == storedAddress) {
|
||||
binaryKeysToRemove.push_back(pair.first);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (pair.first.right == storedAddress) {
|
||||
binaryKeysToRemove.push_back(pair.first);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 实际移除条目
|
||||
for (const auto &key : binaryKeysToRemove) {
|
||||
availableBinaryExpressions.erase(key);
|
||||
}
|
||||
|
||||
// 遍历一元表达式缓存,移除受影响的条目
|
||||
std::vector<UnExpKey> unaryKeysToRemove;
|
||||
for (const auto &pair : availableUnaryExpressions) {
|
||||
// 检查操作数
|
||||
if (auto loadInst = dynamic_cast<LoadInst *>(pair.first.operand)) {
|
||||
if (loadInst->getPointer() == storedAddress) {
|
||||
unaryKeysToRemove.push_back(pair.first);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (pair.first.operand == storedAddress) {
|
||||
unaryKeysToRemove.push_back(pair.first);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 实际移除条目
|
||||
for (const auto &key : unaryKeysToRemove) {
|
||||
availableUnaryExpressions.erase(key);
|
||||
}
|
||||
availableLoads.erase(storedAddress);
|
||||
|
||||
std::vector<GEPKey> gepKeysToRemove;
|
||||
for (const auto &pair : availableGEPs) {
|
||||
// 检查 GEP 的基指针是否受存储影响
|
||||
if (auto loadInst = dynamic_cast<LoadInst *>(pair.first.basePointer)) {
|
||||
if (loadInst->getPointer() == storedAddress) {
|
||||
gepKeysToRemove.push_back(pair.first);
|
||||
continue; // 标记此GEP为移除,跳过后续检查
|
||||
}
|
||||
}
|
||||
// 如果基指针本身就是存储的目标地址 (不常见,但可能)
|
||||
if (pair.first.basePointer == storedAddress) {
|
||||
gepKeysToRemove.push_back(pair.first);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查 GEP 的每个索引是否受存储影响
|
||||
for (const auto &indexVal : pair.first.indices) {
|
||||
if (auto loadInst = dynamic_cast<LoadInst *>(indexVal)) {
|
||||
if (loadInst->getPointer() == storedAddress) {
|
||||
gepKeysToRemove.push_back(pair.first);
|
||||
break; // 标记此GEP为移除,并跳出内部循环
|
||||
}
|
||||
}
|
||||
// 如果索引本身就是存储的目标地址
|
||||
if (indexVal == storedAddress) {
|
||||
gepKeysToRemove.push_back(pair.first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 实际移除条目
|
||||
for (const auto &key : gepKeysToRemove) {
|
||||
availableGEPs.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
// std::vector<Value*> BinaryValueStack; ///< 用于存储value的栈
|
||||
// std::vector<int> BinaryOpStack; ///< 用于存储二元表达式的操作符栈
|
||||
@ -267,46 +377,37 @@ void SysYIRGenerator::compute() {
|
||||
}
|
||||
} else {
|
||||
// 否则,创建相应的IR指令
|
||||
if (commonType == Type::getIntType()) {
|
||||
switch (op) {
|
||||
case BinaryOp::ADD: resultValue = builder.createAddInst(lhs, rhs); break;
|
||||
case BinaryOp::SUB: resultValue = builder.createSubInst(lhs, rhs); break;
|
||||
case BinaryOp::MUL: resultValue = builder.createMulInst(lhs, rhs); break;
|
||||
case BinaryOp::DIV: {
|
||||
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);
|
||||
ExpKey currentExpKey(static_cast<BinaryOp>(op), lhs, rhs);
|
||||
auto it = availableBinaryExpressions.find(currentExpKey);
|
||||
|
||||
if (it != availableBinaryExpressions.end()) {
|
||||
// 在缓存中找到,重用结果
|
||||
resultValue = it->second;
|
||||
} else {
|
||||
if (commonType == Type::getIntType()) {
|
||||
switch (op) {
|
||||
case BinaryOp::ADD: resultValue = builder.createAddInst(lhs, rhs); break;
|
||||
case BinaryOp::SUB: resultValue = builder.createSubInst(lhs, rhs); break;
|
||||
case BinaryOp::MUL: resultValue = builder.createMulInst(lhs, rhs); break;
|
||||
case BinaryOp::DIV: resultValue = builder.createDivInst(lhs, rhs); break;
|
||||
case BinaryOp::MOD: resultValue = builder.createRemInst(lhs, rhs); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BinaryOp::MOD: resultValue = builder.createRemInst(lhs, rhs); break;
|
||||
}
|
||||
} else if (commonType == Type::getFloatType()) {
|
||||
switch (op) {
|
||||
case BinaryOp::ADD: resultValue = builder.createFAddInst(lhs, rhs); break;
|
||||
case BinaryOp::SUB: resultValue = builder.createFSubInst(lhs, rhs); break;
|
||||
case BinaryOp::MUL: resultValue = builder.createFMulInst(lhs, rhs); break;
|
||||
case BinaryOp::DIV: resultValue = builder.createFDivInst(lhs, rhs); break;
|
||||
case BinaryOp::MOD:
|
||||
std::cerr << "Error: Modulo operator not supported for float types." << std::endl;
|
||||
} else if (commonType == Type::getFloatType()) {
|
||||
switch (op) {
|
||||
case BinaryOp::ADD: resultValue = builder.createFAddInst(lhs, rhs); break;
|
||||
case BinaryOp::SUB: resultValue = builder.createFSubInst(lhs, rhs); break;
|
||||
case BinaryOp::MUL: resultValue = builder.createFMulInst(lhs, rhs); break;
|
||||
case BinaryOp::DIV: resultValue = builder.createFDivInst(lhs, rhs); break;
|
||||
case BinaryOp::MOD:
|
||||
std::cerr << "Error: Modulo operator not supported for float types." << std::endl;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Error: Unsupported type for binary instruction." << std::endl;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Error: Unsupported type for binary instruction." << std::endl;
|
||||
return;
|
||||
// 将新创建的指令结果添加到缓存
|
||||
availableBinaryExpressions[currentExpKey] = resultValue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -358,36 +459,45 @@ void SysYIRGenerator::compute() {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// 否则,创建相应的IR指令
|
||||
switch (op) {
|
||||
case BinaryOp::PLUS:
|
||||
resultValue = operand; // 一元加指令通常直接返回操作数
|
||||
break;
|
||||
case BinaryOp::NEG: {
|
||||
if (commonType == sysy::Type::getIntType()) {
|
||||
resultValue = builder.createNegInst(operand);
|
||||
} else if (commonType == sysy::Type::getFloatType()) {
|
||||
resultValue = builder.createFNegInst(operand);
|
||||
} else {
|
||||
std::cerr << "Error: Negation not supported for operand type." << std::endl;
|
||||
return;
|
||||
// 否则,创建相应的IR指令 (在这里应用CSE)
|
||||
UnExpKey currentUnExpKey(static_cast<BinaryOp>(op), operand);
|
||||
auto it = availableUnaryExpressions.find(currentUnExpKey);
|
||||
if (it != availableUnaryExpressions.end()) {
|
||||
// 在缓存中找到,重用结果
|
||||
resultValue = it->second;
|
||||
} else {
|
||||
switch (op) {
|
||||
case BinaryOp::PLUS:
|
||||
resultValue = operand; // 一元加指令通常直接返回操作数
|
||||
break;
|
||||
case BinaryOp::NEG: {
|
||||
if (commonType == sysy::Type::getIntType()) {
|
||||
resultValue = builder.createNegInst(operand);
|
||||
} else if (commonType == sysy::Type::getFloatType()) {
|
||||
resultValue = builder.createFNegInst(operand);
|
||||
} else {
|
||||
std::cerr << "Error: Negation not supported for operand type." << std::endl;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BinaryOp::NOT:
|
||||
// 逻辑非
|
||||
if (commonType == sysy::Type::getIntType()) {
|
||||
resultValue = builder.createNotInst(operand);
|
||||
} else if (commonType == sysy::Type::getFloatType()) {
|
||||
resultValue = builder.createFNotInst(operand);
|
||||
} else {
|
||||
std::cerr << "Error: Logical NOT not supported for operand type." << std::endl;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
std::cerr << "Error: Unknown unary operator for instructions: " << op << std::endl;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BinaryOp::NOT:
|
||||
// 逻辑非
|
||||
if (commonType == sysy::Type::getIntType()) {
|
||||
resultValue = builder.createNotInst(operand);
|
||||
} else if (commonType == sysy::Type::getFloatType()) {
|
||||
resultValue = builder.createFNotInst(operand);
|
||||
} else {
|
||||
std::cerr << "Error: Logical NOT not supported for operand type." << std::endl;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
std::cerr << "Error: Unknown unary operator for instructions: " << op << std::endl;
|
||||
return;
|
||||
// 将新创建的指令结果添加到缓存
|
||||
availableUnaryExpressions[currentUnExpKey] = resultValue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -529,7 +639,19 @@ Value* SysYIRGenerator::getGEPAddressInst(Value* basePointer, const std::vector<
|
||||
// `indices` 向量现在由调用方(如 visitLValue, visitVarDecl, visitAssignStmt)负责完整准备,
|
||||
// 包括是否需要添加初始的 `0` 索引。
|
||||
// 所以这里直接将其传递给 `builder.createGetElementPtrInst`。
|
||||
return builder.createGetElementPtrInst(basePointer, indices);
|
||||
GEPKey key = {basePointer, indices};
|
||||
|
||||
// 尝试从缓存中查找
|
||||
auto it = availableGEPs.find(key);
|
||||
if (it != availableGEPs.end()) {
|
||||
return it->second; // 缓存命中,返回已有的 GEPInst*
|
||||
}
|
||||
|
||||
// 缓存未命中,创建新的 GEPInst
|
||||
Value* gepInst = builder.createGetElementPtrInst(basePointer, indices); // 假设 builder 提供了 createGEPInst 方法
|
||||
availableGEPs[key] = gepInst; // 将新的 GEPInst* 加入缓存
|
||||
|
||||
return gepInst;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -628,7 +750,13 @@ std::any SysYIRGenerator::visitConstDecl(SysYParser::ConstDeclContext *ctx) {
|
||||
|
||||
// 显式地为局部常量在栈上分配空间
|
||||
// alloca 的类型将是指针指向常量类型,例如 `int*` 或 `int[2][3]*`
|
||||
// 将alloca全部集中到entry中
|
||||
auto entry = builder.getBasicBlock()->getParent()->getEntryBlock();
|
||||
auto it = builder.getPosition();
|
||||
auto nowblk = builder.getBasicBlock();
|
||||
builder.setPosition(entry, entry->terminator());
|
||||
AllocaInst *alloca = builder.createAllocaInst(Type::getPointerType(variableType), name);
|
||||
builder.setPosition(nowblk, it);
|
||||
|
||||
ArrayValueTree *root = std::any_cast<ArrayValueTree *>(constDef->constInitVal()->accept(this));
|
||||
ValueCounter values;
|
||||
@ -785,8 +913,12 @@ std::any SysYIRGenerator::visitVarDecl(SysYParser::VarDeclContext *ctx) {
|
||||
|
||||
// 对于数组,alloca 的类型将是指针指向数组类型,例如 `int[2][3]*`
|
||||
// 对于标量,alloca 的类型将是指针指向标量类型,例如 `int*`
|
||||
AllocaInst* alloca =
|
||||
builder.createAllocaInst(Type::getPointerType(variableType), name);
|
||||
auto entry = builder.getBasicBlock()->getParent()->getEntryBlock();
|
||||
auto it = builder.getPosition();
|
||||
auto nowblk = builder.getBasicBlock();
|
||||
builder.setPosition(entry, entry->terminator());
|
||||
AllocaInst *alloca = builder.createAllocaInst(Type::getPointerType(variableType), name);
|
||||
builder.setPosition(nowblk, it);
|
||||
|
||||
if (varDef->initVal() != nullptr) {
|
||||
ValueCounter values;
|
||||
@ -988,6 +1120,8 @@ std::any SysYIRGenerator::visitFuncType(SysYParser::FuncTypeContext *ctx) {
|
||||
std::any SysYIRGenerator::visitFuncDef(SysYParser::FuncDefContext *ctx){
|
||||
// 更新作用域
|
||||
module->enterNewScope();
|
||||
// 清除CSE缓存
|
||||
enterNewBasicBlock();
|
||||
|
||||
auto name = ctx->Ident()->getText();
|
||||
std::vector<Type *> paramActualTypes;
|
||||
@ -1057,15 +1191,25 @@ std::any SysYIRGenerator::visitFuncDef(SysYParser::FuncDefContext *ctx){
|
||||
for(int i = 0; i < paramActualTypes.size(); ++i) {
|
||||
Argument* arg = new Argument(paramActualTypes[i], function, i, paramNames[i]);
|
||||
function->insertArgument(arg);
|
||||
}
|
||||
|
||||
// 先将所有参数名字注册到符号表中,确保alloca不会使用相同的名字
|
||||
for (int i = 0; i < paramNames.size(); ++i) {
|
||||
// 预先注册参数名字,这样addVariable就会使用不同的后缀
|
||||
module->registerParameterName(paramNames[i]);
|
||||
}
|
||||
|
||||
auto funcArgs = function->getArguments();
|
||||
std::vector<AllocaInst *> allocas;
|
||||
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);
|
||||
module->addVariable(paramNames[i], alloca);
|
||||
// 直接添加到符号表,使用原参数名作为查找键
|
||||
module->addVariableDirectly(paramNames[i], alloca);
|
||||
}
|
||||
|
||||
for(int i = 0; i < paramActualTypes.size(); ++i) {
|
||||
@ -1134,6 +1278,45 @@ std::any SysYIRGenerator::visitAssignStmt(SysYParser::AssignStmtContext *ctx) {
|
||||
if (dynamic_cast<AllocaInst*>(variable) || dynamic_cast<GlobalValue*>(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 {
|
||||
// 对于数组或多维数组的左值处理
|
||||
@ -1143,7 +1326,16 @@ std::any SysYIRGenerator::visitAssignStmt(SysYParser::AssignStmtContext *ctx) {
|
||||
if (AllocaInst *alloc = dynamic_cast<AllocaInst *>(variable)) {
|
||||
Type* allocatedType = alloc->getType()->as<PointerType>()->getBaseType();
|
||||
if (allocatedType->isPointer()) {
|
||||
gepBasePointer = builder.createLoadInst(alloc);
|
||||
// 尝试从缓存中获取 builder.createLoadInst(alloc) 的结果
|
||||
auto it = availableLoads.find(alloc);
|
||||
if (it != availableLoads.end()) {
|
||||
gepBasePointer = it->second; // 缓存命中,重用
|
||||
} else {
|
||||
gepBasePointer = builder.createLoadInst(alloc); // 缓存未命中,创建新的 LoadInst
|
||||
availableLoads[alloc] = gepBasePointer; // 将结果加入缓存
|
||||
}
|
||||
// --- CSE 结束 ---
|
||||
// gepBasePointer = builder.createLoadInst(alloc);
|
||||
gepIndices = indices;
|
||||
} else {
|
||||
gepBasePointer = alloc;
|
||||
@ -1162,52 +1354,48 @@ std::any SysYIRGenerator::visitAssignStmt(SysYParser::AssignStmtContext *ctx) {
|
||||
}
|
||||
// 左值为地址
|
||||
LValue = getGEPAddressInst(gepBasePointer, gepIndices);
|
||||
}
|
||||
|
||||
// 数组变量的类型推断,使用gepIndices和gepBasePointer的类型
|
||||
Type* LType = builder.getIndexedType(gepBasePointer->getType(), gepIndices);
|
||||
|
||||
Value* RValue = computeExp(ctx->exp(), LType); // 右值计算
|
||||
Type* RType = RValue->getType();
|
||||
|
||||
// Value* RValue = std::any_cast<Value *>(visitExp(ctx->exp())); // 右值
|
||||
|
||||
// 先推断 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); // 右值计算
|
||||
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()));
|
||||
// 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(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);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (LType == Type::getFloatType()) {
|
||||
RValue = builder.createItoFInst(RValue);
|
||||
} else { // 假设如果不是浮点型,就是整型
|
||||
RValue = builder.createFtoIInst(RValue);
|
||||
// 如果两者都是同一类型,就不需要转换
|
||||
}
|
||||
}
|
||||
|
||||
builder.createStoreInst(RValue, LValue);
|
||||
}
|
||||
|
||||
builder.createStoreInst(RValue, LValue);
|
||||
|
||||
invalidateExpressionsOnStore(LValue);
|
||||
return std::any();
|
||||
}
|
||||
|
||||
@ -1244,7 +1432,9 @@ std::any SysYIRGenerator::visitIfStmt(SysYParser::IfStmtContext *ctx) {
|
||||
labelstring.str("");
|
||||
function->addBasicBlock(thenBlock);
|
||||
builder.setPosition(thenBlock, thenBlock->end());
|
||||
|
||||
// CSE清除缓存
|
||||
enterNewBasicBlock();
|
||||
|
||||
auto block = dynamic_cast<SysYParser::BlockStmtContext *>(ctx->stmt(0));
|
||||
// 如果是块语句,直接访问
|
||||
// 否则访问语句
|
||||
@ -1263,7 +1453,9 @@ std::any SysYIRGenerator::visitIfStmt(SysYParser::IfStmtContext *ctx) {
|
||||
labelstring.str("");
|
||||
function->addBasicBlock(elseBlock);
|
||||
builder.setPosition(elseBlock, elseBlock->end());
|
||||
|
||||
// CSE清除缓存
|
||||
enterNewBasicBlock();
|
||||
|
||||
block = dynamic_cast<SysYParser::BlockStmtContext *>(ctx->stmt(1));
|
||||
if (block != nullptr) {
|
||||
visitBlockStmt(block);
|
||||
@ -1280,7 +1472,9 @@ std::any SysYIRGenerator::visitIfStmt(SysYParser::IfStmtContext *ctx) {
|
||||
labelstring.str("");
|
||||
function->addBasicBlock(exitBlock);
|
||||
builder.setPosition(exitBlock, exitBlock->end());
|
||||
|
||||
// CSE清除缓存
|
||||
enterNewBasicBlock();
|
||||
|
||||
} else {
|
||||
builder.pushTrueBlock(thenBlock);
|
||||
builder.pushFalseBlock(exitBlock);
|
||||
@ -1293,7 +1487,9 @@ std::any SysYIRGenerator::visitIfStmt(SysYParser::IfStmtContext *ctx) {
|
||||
labelstring.str("");
|
||||
function->addBasicBlock(thenBlock);
|
||||
builder.setPosition(thenBlock, thenBlock->end());
|
||||
|
||||
// CSE清除缓存
|
||||
enterNewBasicBlock();
|
||||
|
||||
auto block = dynamic_cast<SysYParser::BlockStmtContext *>(ctx->stmt(0));
|
||||
if (block != nullptr) {
|
||||
visitBlockStmt(block);
|
||||
@ -1310,6 +1506,9 @@ std::any SysYIRGenerator::visitIfStmt(SysYParser::IfStmtContext *ctx) {
|
||||
labelstring.str("");
|
||||
function->addBasicBlock(exitBlock);
|
||||
builder.setPosition(exitBlock, exitBlock->end());
|
||||
// CSE清除缓存
|
||||
enterNewBasicBlock();
|
||||
|
||||
}
|
||||
return std::any();
|
||||
}
|
||||
@ -1327,7 +1526,9 @@ std::any SysYIRGenerator::visitWhileStmt(SysYParser::WhileStmtContext *ctx) {
|
||||
builder.createUncondBrInst(headBlock);
|
||||
BasicBlock::conectBlocks(curBlock, headBlock);
|
||||
builder.setPosition(headBlock, headBlock->end());
|
||||
|
||||
// CSE清除缓存
|
||||
enterNewBasicBlock();
|
||||
|
||||
BasicBlock* bodyBlock = new BasicBlock(function);
|
||||
BasicBlock* exitBlock = new BasicBlock(function);
|
||||
|
||||
@ -1343,6 +1544,8 @@ std::any SysYIRGenerator::visitWhileStmt(SysYParser::WhileStmtContext *ctx) {
|
||||
labelstring.str("");
|
||||
function->addBasicBlock(bodyBlock);
|
||||
builder.setPosition(bodyBlock, bodyBlock->end());
|
||||
// CSE清除缓存
|
||||
enterNewBasicBlock();
|
||||
|
||||
builder.pushBreakBlock(exitBlock);
|
||||
builder.pushContinueBlock(headBlock);
|
||||
@ -1358,7 +1561,7 @@ std::any SysYIRGenerator::visitWhileStmt(SysYParser::WhileStmtContext *ctx) {
|
||||
}
|
||||
|
||||
builder.createUncondBrInst(headBlock);
|
||||
BasicBlock::conectBlocks(builder.getBasicBlock(), exitBlock);
|
||||
BasicBlock::conectBlocks(builder.getBasicBlock(), headBlock);
|
||||
builder.popBreakBlock();
|
||||
builder.popContinueBlock();
|
||||
|
||||
@ -1367,7 +1570,9 @@ std::any SysYIRGenerator::visitWhileStmt(SysYParser::WhileStmtContext *ctx) {
|
||||
labelstring.str("");
|
||||
function->addBasicBlock(exitBlock);
|
||||
builder.setPosition(exitBlock, exitBlock->end());
|
||||
|
||||
// CSE清除缓存
|
||||
enterNewBasicBlock();
|
||||
|
||||
return std::any();
|
||||
}
|
||||
|
||||
@ -1473,90 +1678,101 @@ std::any SysYIRGenerator::visitLValue(SysYParser::LValueContext *ctx) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allIndicesConstant) {
|
||||
// 如果是常量变量且所有索引都是常量,并且不是数组名单独出现的情况
|
||||
if (allIndicesConstant && !dims.empty()) {
|
||||
// 如果是常量变量且所有索引都是常量,直接通过 getByIndices 获取编译时值
|
||||
// 这个方法会根据索引深度返回最终的标量值或指向子数组的指针 (作为 ConstantValue/Variable)
|
||||
return constVar->getByIndices(dims);
|
||||
}
|
||||
// 如果dims为空,检查是否是常量标量
|
||||
if (dims.empty() && declaredNumDims == 0) {
|
||||
// 常量标量,直接返回其值
|
||||
// 默认传入空索引列表,表示访问标量本身
|
||||
return constVar->getByIndices(dims);
|
||||
}
|
||||
// 如果dims为空但不是标量(数组名单独出现),需要走GEP路径来实现数组到指针的退化
|
||||
}
|
||||
|
||||
// 3. 处理可变变量 (AllocaInst/GlobalValue) 或带非常量索引的常量变量
|
||||
// 这里区分标量访问和数组元素/子数组访问
|
||||
|
||||
Value *targetAddress = nullptr;
|
||||
// 检查是否是访问标量变量本身(没有索引,且声明维度为0)
|
||||
if (dims.empty() && declaredNumDims == 0) {
|
||||
// 对于标量变量,直接加载其值。
|
||||
// variable 本身就是指向标量的指针 (e.g., int* %a)
|
||||
if (dynamic_cast<AllocaInst*>(variable) || dynamic_cast<GlobalValue*>(variable)) {
|
||||
value = builder.createLoadInst(variable);
|
||||
} else {
|
||||
// 如果走到这里且不是AllocaInst/GlobalValue,但dims为空且declaredNumDims为0,
|
||||
// 且又不是ConstantVariable (前面已处理),则可能是错误情况。
|
||||
targetAddress = variable;
|
||||
}
|
||||
else {
|
||||
assert(false && "Unhandled scalar variable type in LValue access.");
|
||||
return static_cast<Value*>(nullptr);
|
||||
}
|
||||
} else {
|
||||
// 访问数组元素或子数组(有索引,或变量本身是数组/多维指针)
|
||||
Value* gepBasePointer = nullptr;
|
||||
std::vector<Value*> gepIndices; // 准备传递给 getGEPAddressInst 的索引列表
|
||||
// GEP 的基指针就是变量本身(它是一个指向内存的指针)
|
||||
std::vector<Value*> gepIndices;
|
||||
if (AllocaInst *alloc = dynamic_cast<AllocaInst *>(variable)) {
|
||||
// 情况 A: 局部变量 (AllocaInst)
|
||||
// 获取 AllocaInst 分配的内存的实际类型。
|
||||
// 例如:对于 `int b[10][20];`,`allocatedType` 是 `[10 x [20 x i32]]`。
|
||||
// 对于 `int b[][20]` 的函数参数,其 AllocaInst 存储的是一个指针,
|
||||
// 此时 `allocatedType` 是 `[20 x i32]*`。
|
||||
Type* allocatedType = alloc->getType()->as<PointerType>()->getBaseType();
|
||||
|
||||
if (allocatedType->isPointer()) {
|
||||
// 如果 AllocaInst 分配的是一个指针类型 (例如,用于存储函数参数的指针,如 int b[][20] 中的 b)
|
||||
// 即 `allocatedType` 是一个指向数组指针的指针 (e.g., [20 x i32]**)
|
||||
// 那么 GEP 的基指针是加载这个指针变量的值。
|
||||
gepBasePointer = builder.createLoadInst(alloc); // 加载出实际的指针值 (e.g., [20 x i32]*)
|
||||
// 对于这种参数指针,用户提供的索引直接作用于它。不需要额外的 0。
|
||||
gepBasePointer = builder.createLoadInst(alloc);
|
||||
gepIndices = dims;
|
||||
} else {
|
||||
// 如果 AllocaInst 分配的是实际的数组数据 (例如,int b[10][20] 中的 b)
|
||||
// 那么 AllocaInst 本身就是 GEP 的基指针。
|
||||
// 这里的 `alloc` 是指向数组的指针 (e.g., [10 x [20 x i32]]*)
|
||||
gepBasePointer = alloc; // 类型是 [10 x [20 x i32]]*
|
||||
// 对于这种完整的数组分配,GEP 的第一个索引必须是 0,用于“步过”整个数组。
|
||||
gepBasePointer = alloc;
|
||||
gepIndices.push_back(ConstantInteger::get(0));
|
||||
gepIndices.insert(gepIndices.end(), dims.begin(), dims.end());
|
||||
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());
|
||||
}
|
||||
}
|
||||
} else if (GlobalValue *glob = dynamic_cast<GlobalValue *>(variable)) {
|
||||
// 情况 B: 全局变量 (GlobalValue)
|
||||
// GlobalValue 总是指向全局数据的指针。
|
||||
gepBasePointer = glob; // 类型是 [61 x [67 x i32]]*
|
||||
// 对于全局数组,GEP 的第一个索引必须是 0,用于“步过”整个数组。
|
||||
gepBasePointer = glob;
|
||||
gepIndices.push_back(ConstantInteger::get(0));
|
||||
gepIndices.insert(gepIndices.end(), dims.begin(), dims.end());
|
||||
if (dims.empty() && declaredNumDims > 0) {
|
||||
// 全局数组名单独出现(没有索引):应该退化为指向第一行的指针
|
||||
// 需要添加一个额外的i32 0索引
|
||||
gepIndices.push_back(ConstantInteger::get(0));
|
||||
} else {
|
||||
// 正常的数组元素访问
|
||||
gepIndices.insert(gepIndices.end(), dims.begin(), dims.end());
|
||||
}
|
||||
} else if (ConstantVariable *constV = dynamic_cast<ConstantVariable *>(variable)) {
|
||||
// 情况 C: 常量变量 (ConstantVariable),如果它代表全局数组常量
|
||||
// 假设 ConstantVariable 可以直接作为 GEP 的基指针。
|
||||
gepBasePointer = constV;
|
||||
// 对于常量数组,也需要 0 索引来“步过”整个数组。
|
||||
// 这里可以进一步检查 constV->getType()->as<PointerType>()->getBaseType()->isArray()
|
||||
// 但为了简洁,假设所有 ConstantVariable 作为 GEP 基指针时都需要此 0。
|
||||
gepIndices.push_back(ConstantInteger::get(0));
|
||||
gepIndices.insert(gepIndices.end(), dims.begin(), dims.end());
|
||||
if (dims.empty() && declaredNumDims > 0) {
|
||||
// 常量数组名单独出现(没有索引):应该退化为指向第一行的指针
|
||||
// 需要添加一个额外的i32 0索引
|
||||
gepIndices.push_back(ConstantInteger::get(0));
|
||||
} else {
|
||||
// 正常的数组元素访问
|
||||
gepIndices.insert(gepIndices.end(), dims.begin(), dims.end());
|
||||
}
|
||||
} else {
|
||||
assert(false && "LValue variable type not supported for GEP base pointer.");
|
||||
return static_cast<Value *>(nullptr);
|
||||
}
|
||||
|
||||
// 现在调用 getGEPAddressInst,传入正确准备的基指针和索引列表
|
||||
Value *targetAddress = getGEPAddressInst(gepBasePointer, gepIndices);
|
||||
targetAddress = getGEPAddressInst(gepBasePointer, gepIndices);
|
||||
|
||||
// 如果提供的索引数量少于声明的维度数量,则表示访问的是子数组,返回其地址
|
||||
if (dims.size() < declaredNumDims) {
|
||||
value = targetAddress;
|
||||
}
|
||||
|
||||
// 如果提供的索引数量少于声明的维度数量,则表示访问的是子数组,返回其地址 (无需加载)
|
||||
if (dims.size() < declaredNumDims) {
|
||||
value = targetAddress;
|
||||
} else {
|
||||
// value = builder.createLoadInst(targetAddress);
|
||||
auto it = availableLoads.find(targetAddress);
|
||||
if (it != availableLoads.end()) {
|
||||
value = it->second; // 缓存命中,重用已有的 LoadInst 结果
|
||||
} else {
|
||||
// 否则,表示访问的是最终的标量元素,加载其值
|
||||
// 假设 createLoadInst 接受 Value* pointer
|
||||
value = builder.createLoadInst(targetAddress);
|
||||
// 缓存未命中,创建新的 LoadInst
|
||||
value = builder.createLoadInst(targetAddress);
|
||||
availableLoads[targetAddress] = value; // 将新的 LoadInst 结果加入缓存
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@ -1614,10 +1830,10 @@ std::any SysYIRGenerator::visitCall(SysYParser::CallContext *ctx) {
|
||||
|
||||
// 获取形参列表。`getArguments()` 返回的是 `Argument*` 的集合,
|
||||
// 每个 `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;
|
||||
assert(false && "Function call argument count mismatch!");
|
||||
}
|
||||
@ -1649,15 +1865,27 @@ std::any SysYIRGenerator::visitCall(SysYParser::CallContext *ctx) {
|
||||
} else if (formalParamExpectedValueType->isFloat() && actualArgType->isInt()) {
|
||||
args[i] = builder.createItoFInst(args[i]);
|
||||
}
|
||||
// 2. 指针类型转换 (例如数组退化:`[N x T]*` 到 `T*`,或兼容指针类型之间) TODO:不清楚有没有这种样例
|
||||
// 2. 指针类型转换 (例如数组退化:`[N x T]*` 到 `T*`,或兼容指针类型之间)
|
||||
// 这种情况常见于数组参数,实参可能是一个更具体的数组指针类型,
|
||||
// 而形参是其退化后的基础指针类型。LLVM 的 `bitcast` 指令可以用于
|
||||
// 在相同大小的指针类型之间进行转换,这对于数组退化至关重要。
|
||||
// else if (formalParamType->isPointer() && actualArgType->isPointer()) {
|
||||
// 检查指针基类型是否兼容,或者是否是数组退化导致的类型不同。
|
||||
// 使用 bitcast,
|
||||
// args[i] = builder.createBitCastInst(args[i], formalParamType);
|
||||
// }
|
||||
// 而形参是其退化后的基础指针类型。
|
||||
else if (formalParamExpectedValueType->isPointer() && actualArgType->isPointer()) {
|
||||
// 检查是否是数组指针到元素指针的decay
|
||||
// 例如:[N x T]* -> T*
|
||||
auto formalPtrType = formalParamExpectedValueType->as<PointerType>();
|
||||
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. 其他未预期的类型不匹配
|
||||
// 如果代码执行到这里,说明存在编译器前端未处理的类型不兼容或错误。
|
||||
else {
|
||||
@ -1676,6 +1904,7 @@ std::any SysYIRGenerator::visitUnaryExp(SysYParser::UnaryExpContext *ctx) {
|
||||
visitPrimaryExp(ctx->primaryExp());
|
||||
} else if (ctx->call() != nullptr) {
|
||||
BinaryExpStack.push_back(std::any_cast<Value *>(visitCall(ctx->call())));BinaryExpLenStack.back()++;
|
||||
invalidateExpressionsOnCall();
|
||||
} else if (ctx->unaryOp() != nullptr) {
|
||||
// 遇到一元操作符,将其压入 BinaryExpStack
|
||||
auto opNode = dynamic_cast<antlr4::tree::TerminalNode*>(ctx->unaryOp()->children[0]);
|
||||
@ -2040,15 +2269,23 @@ void Utils::createExternalFunction(
|
||||
const std::vector<std::string> ¶mNames,
|
||||
const std::vector<std::vector<Value *>> ¶mDims, Type *returnType,
|
||||
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 entry = function->getEntryBlock();
|
||||
pBuilder->setPosition(entry, entry->end());
|
||||
|
||||
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(
|
||||
Type::getPointerType(paramTypes[i]), paramNames[i]);
|
||||
Type::getPointerType(adjustedParamTypes[i]), paramNames[i]);
|
||||
function->insertArgument(arg);
|
||||
auto store = pBuilder->createStoreInst(arg, alloca);
|
||||
pModule->addVariable(paramNames[i], alloca);
|
||||
|
||||
@ -299,7 +299,12 @@ void SysYPrinter::printInst(Instruction *pInst) {
|
||||
|
||||
// Types and operands
|
||||
std::cout << " ";
|
||||
printType(binInst->getType());
|
||||
// For comparison operations, print operand types instead of result type
|
||||
if (pInst->getKind() >= Kind::kICmpEQ && pInst->getKind() <= Kind::kFCmpGE) {
|
||||
printType(binInst->getLhs()->getType());
|
||||
} else {
|
||||
printType(binInst->getType());
|
||||
}
|
||||
std::cout << " ";
|
||||
printValue(binInst->getLhs());
|
||||
std::cout << ", ";
|
||||
@ -512,9 +517,9 @@ void SysYPrinter::printInst(Instruction *pInst) {
|
||||
if (!firstPair) std::cout << ", ";
|
||||
firstPair = false;
|
||||
std::cout << "[ ";
|
||||
printValue(phiInst->getValue(i));
|
||||
printValue(phiInst->getIncomingValue(i));
|
||||
std::cout << ", %";
|
||||
printBlock(phiInst->getBlock(i));
|
||||
printBlock(phiInst->getIncomingBlock(i));
|
||||
std::cout << " ]";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
@ -110,6 +110,7 @@ int main(int argc, char **argv) {
|
||||
// 如果指定停止在 AST 阶段,则打印并退出
|
||||
if (argStopAfter == "ast") {
|
||||
cout << moduleAST->toStringTree(true) << '\n';
|
||||
sysy::cleanupIRPools(); // 清理内存池
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -132,7 +133,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
if (DEBUG) {
|
||||
cout << "=== Init IR ===\n";
|
||||
SysYPrinter(moduleIR).printIR(); // 临时打印器用于调试
|
||||
moduleIR->print(cout); // 使用新实现的print方法直接打印IR
|
||||
}
|
||||
|
||||
// 创建 Pass 管理器并运行优化管道
|
||||
@ -144,10 +145,26 @@ int main(int argc, char **argv) {
|
||||
// a) 如果指定停止在 IR 阶段,则打印最终 IR 并退出
|
||||
if (argStopAfter == "ir" || argStopAfter == "ird") {
|
||||
// 打印最终 IR
|
||||
cout << "=== Final IR ===\n";
|
||||
SysYPrinter printer(moduleIR); // 在这里创建打印器,因为可能之前调试时用过临时打印器
|
||||
printer.printIR();
|
||||
if (DEBUG) cerr << "=== Final IR ===\n";
|
||||
if (!argOutputFilename.empty()) {
|
||||
// 输出到指定文件
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
// b) 如果未停止在 IR 阶段,则继续生成汇编 (后端)
|
||||
@ -166,6 +183,8 @@ int main(int argc, char **argv) {
|
||||
ofstream fout(argOutputFilename);
|
||||
if (not fout.is_open()) {
|
||||
cerr << "Failed to open output file: " << argOutputFilename << endl;
|
||||
moduleIR->cleanup(); // 清理模块
|
||||
sysy::cleanupIRPools(); // 清理内存池
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
fout << asmCode << endl;
|
||||
@ -173,6 +192,8 @@ int main(int argc, char **argv) {
|
||||
} else {
|
||||
cout << asmCode << endl;
|
||||
}
|
||||
moduleIR->cleanup(); // 清理模块
|
||||
sysy::cleanupIRPools(); // 清理内存池
|
||||
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";
|
||||
// return EXIT_SUCCESS; // 或者这里调用一个链接器生成可执行文件
|
||||
|
||||
moduleIR->cleanup(); // 清理模块
|
||||
sysy::cleanupIRPools(); // 清理内存池
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user