feat(mir): 添加寄存器分配 pass 支持

- 实现了核心寄存器分配接口 scc_reg_alloc.h
- 添加 x86_64 架构特定的寄存器分配实现
- 实现基础的虚拟寄存器到物理寄存器分配逻辑
- 支持栈槽分配和重载/存储指令生成

fix(argparse): 修复帮助信息打印中的枚举类型显示

- 将固定大小缓冲区改为动态字符串处理
- 正确显示枚举类型的选项值和可选值列表
- 使用 | 分隔符展示多个可选值

refactor(main): 调整编译流程中的 pass 阶段支持

- 更新配置文件中各编译阶段的名称定义
- 添加对寄存器分配、帧布局等中间表示 pass 的支持
- 重构主流程以支持不同 MIR 阶段的代码输出
This commit is contained in:
zzy
2026-05-11 13:18:58 +08:00
parent 902ee6dea3
commit 4f40f0d5e4
8 changed files with 311 additions and 19 deletions

View File

@@ -0,0 +1,45 @@
#ifndef __SCC_REG_ALLOC_H__
#define __SCC_REG_ALLOC_H__
#include "../scc_mir_module.h"
typedef enum {
SCC_REG_ALLOC_OP_ACCESS_READ = 0,
SCC_REG_ALLOC_OP_ACCESS_WRITE = 1,
SCC_REG_ALLOC_OP_ACCESS_READWRITE = 2,
} scc_op_access_t;
typedef struct scc_reg_alloc_op {
// ---- 指令生成(纯动作,无状态) ----
// preg → [slot]
void (*emit_spill)(scc_mir_instr_vec_t *ctx, int preg, int slot);
// [slot] → preg
void (*emit_reload)(scc_mir_instr_vec_t *ctx, int preg, int slot);
// preg → preg
void (*emit_copy)(scc_mir_instr_vec_t *ctx, int dst_preg, int src_preg,
int size);
// 通用寄存器申请 / 释放
int (*acquire_reg)(void *ctx); // 返回一个物理寄存器编号
void (*release_reg)(void *ctx, int preg); // 归还该寄存器
// 显式标记某个寄存器已占用 / 未占用(用于隐式寄存器、固定分配等)
void (*mark_reg_used)(void *ctx, int preg);
void (*mark_reg_unused)(void *ctx, int preg);
// ---- 指令信息查询(只读) ----
scc_op_access_t (*get_operand_access)(void *ctx, int opcode, int op_idx);
void (*get_implicit_regs)(void *ctx, int opcode, const int **out_uses,
const int **out_defs);
} scc_reg_alloc_op_t;
typedef struct scc_reg_alloc_ctx {
const scc_reg_alloc_op_t *ops;
scc_mir_func_meta_t *func_meta;
scc_mir_instr_vec_t *instrs;
} scc_reg_alloc_ctx_t;
void scc_reg_alloc(scc_reg_alloc_ctx_t *ctx, scc_mir_module_t *module);
#endif /* __SCC_REG_ALLOC__ */

View File

@@ -0,0 +1,86 @@
#include <arch/x86_64_isel.h>
#include <core_pass/scc_reg_alloc.h>
static void x86_emit_spill(scc_mir_instr_vec_t *ctx, int preg, int slot) {
scc_mir_instr_t ins = {
.opcode = SCC_X86_IFORM_MOV_MEMV_GPRV,
.num_operands = 2,
.operands = {{.kind = SCC_MIR_OP_MEM, .stack_slot = slot},
{.kind = SCC_MIR_OP_PREG, .preg = preg}}};
scc_vec_push(*ctx, ins);
}
static void x86_emit_reload(scc_mir_instr_vec_t *ctx, int preg, int slot) {
scc_mir_instr_t ins = {
.opcode = SCC_X86_IFORM_MOV_GPRV_MEMV,
.num_operands = 2,
.operands = {{.kind = SCC_MIR_OP_PREG, .preg = preg},
{.kind = SCC_MIR_OP_MEM, .stack_slot = slot}}};
scc_vec_push(*ctx, ins);
}
static void x86_emit_copy(scc_mir_instr_vec_t *ctx, int dst_preg, int src_preg,
int size) {
scc_mir_instr_t ins = {
.opcode = SCC_X86_IFORM_MOV_GPRV_GPRV_89,
.num_operands = 2,
.operands = {{.kind = SCC_MIR_OP_PREG, .preg = dst_preg},
{.kind = SCC_MIR_OP_PREG, .preg = src_preg}}};
scc_vec_push(*ctx, ins);
}
/* ---- 临时寄存器 ---- */
static int x86_acquire_temp_reg(void *vctx) { return SCC_X86_REG_R11; }
static void x86_release_temp_reg(void *vctx, int preg) { /* 简单模式无需操作 */
}
/* ---- 操作数读写属性 ---- */
static scc_op_access_t x86_get_operand_access(void *vctx, int opcode,
int op_idx) {
switch (opcode) {
default:
return SCC_REG_ALLOC_OP_ACCESS_READWRITE; // 保守
}
}
/* ---- 隐式寄存器 ---- */
static void x86_get_implicit_regs(void *vctx, int opcode, const int **uses,
const int **defs) {
static const int empty[] = {-1};
static const int rax[] = {SCC_X86_REG_RAX, -1};
static const int rdx[] = {SCC_X86_REG_RDX, -1};
static const int rax_rdx[] = {SCC_X86_REG_RAX, SCC_X86_REG_RDX, -1};
static const int cl[] = {SCC_X86_REG_CL, -1};
switch (opcode) {
case SCC_X86_IFORM_IDIV_GPRV:
case SCC_X86_IFORM_DIV_GPRV:
*uses = rax_rdx;
*defs = rax_rdx;
break;
case SCC_X86_IFORM_CQO:
*uses = rax;
*defs = rax_rdx;
break;
case SCC_X86_IFORM_SAR_GPRV_CL:
*uses = cl;
*defs = empty;
break;
default:
*uses = empty;
*defs = empty;
break;
}
}
/* 组装 hooks 表 */
scc_reg_alloc_op_t x86_reg_alloc_hooks = {
.emit_spill = x86_emit_spill,
.emit_reload = x86_emit_reload,
.emit_copy = x86_emit_copy,
.acquire_reg = x86_acquire_temp_reg,
.release_reg = x86_release_temp_reg,
.get_operand_access = x86_get_operand_access,
.get_implicit_regs = x86_get_implicit_regs,
};

View File

@@ -0,0 +1,111 @@
#include <core_pass/scc_reg_alloc.h>
#include <scc_hashtable.h>
#include <scc_mir_module.h>
static int ensure_slot(scc_reg_alloc_ctx_t *ctx, int vreg) {
void *key = (void *)(usize)vreg;
void *val = scc_hashtable_get(&ctx->func_meta->vreg2slot, key);
if (val != NULL)
return (int)(usize)val;
int new_slot = scc_vec_size(ctx->func_meta->stack_slots) + 1; // 1-based
scc_mir_stack_slot_t s = {
.slot_id = new_slot, .size = 8, .alignment = 8, .offset = 0};
scc_vec_push(ctx->func_meta->stack_slots, s);
scc_hashtable_set(&ctx->func_meta->vreg2slot, key, (void *)(usize)new_slot);
return new_slot;
}
static void alloc_instr(scc_reg_alloc_ctx_t *ctx,
scc_mir_instr_vec_t *instr_vec,
scc_mir_instr_t *instr) {
const scc_reg_alloc_op_t *ops = ctx->ops;
scc_mir_instr_vec_t before_instrs;
scc_vec_init(before_instrs);
scc_mir_instr_vec_t after_instrs;
scc_vec_init(after_instrs);
for (int i = 0; i < instr->num_operands; i += 1) {
if (instr->operands[i].kind != SCC_MIR_OP_VREG) {
continue;
}
int vreg = instr->operands[i].vreg;
scc_op_access_t op_access =
ops->get_operand_access(ctx, instr->opcode, i);
int slot = ensure_slot(ctx, vreg);
int preg = -1;
// if (implicit_uses && implicit_uses[0] != -1)
// preg = implicit_uses[0];
// else
preg = ops->acquire_reg(ctx);
if (op_access == SCC_REG_ALLOC_OP_ACCESS_READ ||
op_access == SCC_REG_ALLOC_OP_ACCESS_READWRITE) {
ops->emit_reload(&before_instrs, preg, slot);
}
instr->operands[i].kind = SCC_MIR_OP_PREG;
instr->operands[i].preg = preg;
if (op_access == SCC_REG_ALLOC_OP_ACCESS_WRITE ||
op_access == SCC_REG_ALLOC_OP_ACCESS_READWRITE) {
ops->emit_spill(&after_instrs, preg, slot);
}
ops->release_reg(ctx, preg);
}
scc_vec_foreach(before_instrs, i) {
scc_vec_push(*instr_vec, scc_vec_at(before_instrs, i));
}
scc_vec_push(*instr_vec, *instr);
// TODO reverse it
scc_vec_foreach(after_instrs, i) {
scc_vec_push(*instr_vec, scc_vec_at(after_instrs, i));
}
}
/* 对一个基本块执行分配 */
static void alloc_bb(scc_reg_alloc_ctx_t *ctx, scc_mir_module_t *module,
scc_cfg_bblock_id_t bb_id) {
scc_cfg_bblock_t *bb =
scc_cfg_module_unsafe_get_bblock(&module->cfg_module, bb_id);
Assert(bb != nullptr);
scc_mir_instr_vec_t *old_instrs = SCC_MIR_BBLOCK_VALUES(bb);
scc_mir_instr_vec_t new_instrs;
scc_vec_init(new_instrs);
scc_vec_foreach(*old_instrs, i) {
scc_mir_instr_t ins = scc_vec_at(*old_instrs, i);
if (ins.opcode == SCC_MIR_PSUEDO_ALLOCA) {
scc_vec_push(new_instrs, ins);
continue;
}
alloc_instr(ctx, &new_instrs, &ins);
}
scc_vec_free(*old_instrs);
*old_instrs = new_instrs;
}
/* 对一个函数运行分配 */
static void alloc_func(scc_reg_alloc_ctx_t *ctx, scc_mir_module_t *module,
scc_mir_func_t *func) {
ctx->func_meta = SCC_MIR_FUNC_META(func);
scc_vec_foreach(func->bblocks, i) {
scc_cfg_bblock_id_t bb_id = scc_vec_at(func->bblocks, i);
alloc_bb(ctx, module, bb_id);
}
ctx->func_meta = nullptr;
}
/* 公开入口 */
void scc_reg_alloc(scc_reg_alloc_ctx_t *ctx, scc_mir_module_t *module) {
Assert(ctx != nullptr && module != nullptr);
scc_vec_foreach(module->cfg_module.funcs, i) {
if (i == 0)
continue;
alloc_func(ctx, module, &scc_vec_at(module->cfg_module.funcs, i));
}
}

View File

@@ -1,8 +1,11 @@
#include <core_pass/scc_reg_alloc.h>
#include <scc_mir_module.h>
#include <scc_mir_pass.h>
extern scc_reg_alloc_t x86_reg_alloc_hooks;
extern scc_reg_alloc_op_t x86_reg_alloc_hooks;
void scc_mir_pass(scc_mir_module_t *mir_module) {
scc_reg_alloc(&x86_reg_alloc_hooks, mir_module);
void scc_mir_pass(scc_mir_module_t *mir_module, scc_mir_pass_stage_t stage) {
scc_reg_alloc_ctx_t reg_alloc_ctx = {
.func_meta = nullptr, .instrs = nullptr, .ops = &x86_reg_alloc_hooks};
scc_reg_alloc(&reg_alloc_ctx, mir_module);
}