feat(ir2mcode): 实现AMD64代码生成器支持控制流和函数调用

- 实现了条件分支、无条件跳转和函数调用的机器码生成
- 添加了跳转目标地址回填机制,处理条件分支和跳转指令的偏移量
- 改进了寄存器分配逻辑,支持函数调用返回值的处理
- 重构了位置解析函数,从返回指针改为传入引用参数

fix(ast2ir): 移除无用的注释代码

- 删除了关于一元操作符映射的注释代码

test: 更新测试框架和测试用例

- 修改测试框架以支持新的可执行文件输出格式
- 添加了条件分支、循环和函数调用的测试用例
- 使用TOML配置文件管理期望的返回值
- 替换标准库头文件为自定义头文件以减少依赖
This commit is contained in:
zzy
2026-03-21 14:38:30 +08:00
parent 35a704a1cb
commit 097dbdcc2a
8 changed files with 340 additions and 101 deletions

View File

@@ -1,6 +1,6 @@
#include <stdio.h>
#include "scc_stdio.h"
int main(void) {
printf("hello world");
puts("hello world");
return 0;
}
}

13
tests/simple/expect.toml Normal file
View File

@@ -0,0 +1,13 @@
[return_val_cases]
"./01_return.c" = 65536
"./02_decl_expr.c" = 1
"./03_decl_init.c" = 11
"./04_if.c" = 1
"./05_else.c" = 2
"./06_fcall.c" = 3
"./07_while.c" = 10
"./08_do_while.c" = 128
"./09_for.c" = 10
"./10_main.c" = 3
"./11_recursive.c" = 120
[stdout_val_cases]

6
tests/simple/scc_stdio.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef __SCC_STDIO_H__
#define __SCC_STDIO_H__
extern int puts(const char *str);
#endif

View File

@@ -1,84 +1,89 @@
from pprint import PrettyPrinter
import subprocess
import os
from pathlib import Path
import tomllib
# 配置参数
TEST_DIR = Path(".")
CC_PATH = Path("../../src/smcc.exe")
VM_PATH = Path("../rv32-vm.exe")
WORKSPACE = Path(".") # 测试工作目录
WORKSPACE = Path(__file__).resolve().parent # 测试工作目录
TEST_DIR = Path(WORKSPACE)
CC_PATH = Path(WORKSPACE / "../../build/dev/scc")
# 测试用例映射表(示例)
TEST_CASE_MAP = {
"./01_return.c": 65536,
"./02_decl_expr.c": 1,
"./03_decl_init.c": 11,
"./04_if.c": 1,
"./05_else.c": 2,
"./06_fcall.c": 3,
"./07_while.c": 10,
"./08_do_while.c": 128,
"./09_for.c": 10,
"./10_main.c": 3,
"./11_recursive.c": 120,
}
def run_command(cmd, capture_stderr=True):
"""执行命令并捕获stderr"""
result = subprocess.run(
cmd,
cwd=WORKSPACE,
stderr=subprocess.PIPE if capture_stderr else None,
text=True,
timeout=1,
)
return result.stderr.strip() if capture_stderr else None
def run_command(cmd, capture_output=True):
"""执行命令并捕获 stdout"""
try:
result = subprocess.run(
cmd,
cwd=WORKSPACE,
stdout=subprocess.PIPE if capture_output else None,
stderr=subprocess.PIPE,
text=True,
timeout=5, # 增加超时时间以防虚拟机启动慢
)
# 返回 stdout 用于获取返回值,同时检查是否有运行时错误
return result.stdout.strip(), result.stderr.strip(), result.returncode
except subprocess.TimeoutExpired:
return None, "Timeout expired", -1
except Exception as e:
return None, str(e), -1
def run_test(test_file, expected):
print(f"\nTesting {test_file}...")
# 1. 编译生成flat.bin
compile_cmd = [str(CC_PATH), str(test_file)]
compile_err = run_command(compile_cmd)
# 1. 编译
compile_cmd = [str(CC_PATH), str(test_file), "-o", "test.exe"]
# 编译时关注 stderr 和返回码
_, compile_err, compile_ret = run_command(compile_cmd)
if not (WORKSPACE / "flat.bin").exists():
exe_path = WORKSPACE / "test.exe"
if not exe_path.exists() or compile_ret != 0:
print(f" Compilation failed: {compile_err}")
return False
# 2. 执行虚拟机
vm_cmd = [str(VM_PATH), "flat.bin"]
# 2. 执行虚拟机并获取输出
vm_cmd = [str(exe_path)]
actual_output, vm_err, vm_ret = run_command(vm_cmd)
# 3. 解析返回值(假设最后一行是返回值)
try:
vm_err = run_command(vm_cmd)
actual = int(vm_err.split()[-1])
except (ValueError, IndexError) as e:
print(f" Invalid VM output: {vm_err}")
return False
except subprocess.TimeoutExpired:
print(" Timeout expired")
# 如果存在 stderr 且返回码异常(例如负数表示信号终止),则视为运行时错误
if vm_err and vm_ret < 0:
print(f" Runtime error: {vm_err}")
return False
# 3. 获取返回值 (修改进程返回值而非 stdout)
actual = vm_ret
# 4. 验证结果
# 注意toml 中读取的 expected 可能是整数actual 也是整数,直接比较
if actual == expected:
print(f" PASSED {test_file}")
return True
else:
print(f" FAILED: Expected {expected}, got {actual}")
print(f" FAILED: Expected '{expected}', got '{actual}'")
return False
def main():
passed = 0
total = 0
config = {}
config_path = WORKSPACE / "expect.toml"
if not config_path.exists():
print(f"Config file not found: {config_path}")
return
for test_file, expected in TEST_CASE_MAP.items():
with open(config_path, "rb") as f:
config = tomllib.load(f)
PrettyPrinter().pprint(config)
for test_file, expected in config.get("return_val_cases", {}).items():
total += 1
if run_test(TEST_DIR / test_file, expected):
passed += 1
# 清理中间文件
if (WORKSPACE / "flat.bin").exists():
os.remove(WORKSPACE / "flat.bin")
exe_path = WORKSPACE / "test.exe"
if exe_path.exists():
os.remove(exe_path)
print(f"\nTest Summary: {passed}/{total} passed")