feat(ir2mcode): 实现AMD64代码生成器支持控制流和函数调用
- 实现了条件分支、无条件跳转和函数调用的机器码生成 - 添加了跳转目标地址回填机制,处理条件分支和跳转指令的偏移量 - 改进了寄存器分配逻辑,支持函数调用返回值的处理 - 重构了位置解析函数,从返回指针改为传入引用参数 fix(ast2ir): 移除无用的注释代码 - 删除了关于一元操作符映射的注释代码 test: 更新测试框架和测试用例 - 修改测试框架以支持新的可执行文件输出格式 - 添加了条件分支、循环和函数调用的测试用例 - 使用TOML配置文件管理期望的返回值 - 替换标准库头文件为自定义头文件以减少依赖
This commit is contained in:
@@ -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
13
tests/simple/expect.toml
Normal 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
6
tests/simple/scc_stdio.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef __SCC_STDIO_H__
|
||||
#define __SCC_STDIO_H__
|
||||
|
||||
extern int puts(const char *str);
|
||||
|
||||
#endif
|
||||
@@ -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")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user