feat(mir): 添加寄存器分配 pass 支持
- 实现了核心寄存器分配接口 scc_reg_alloc.h - 添加 x86_64 架构特定的寄存器分配实现 - 实现基础的虚拟寄存器到物理寄存器分配逻辑 - 支持栈槽分配和重载/存储指令生成 fix(argparse): 修复帮助信息打印中的枚举类型显示 - 将固定大小缓冲区改为动态字符串处理 - 正确显示枚举类型的选项值和可选值列表 - 使用 | 分隔符展示多个可选值 refactor(main): 调整编译流程中的 pass 阶段支持 - 更新配置文件中各编译阶段的名称定义 - 添加对寄存器分配、帧布局等中间表示 pass 的支持 - 重构主流程以支持不同 MIR 阶段的代码输出
This commit is contained in:
86
libs/ir/mir/src/arch/x86_64_reg_alloc.c
Normal file
86
libs/ir/mir/src/arch/x86_64_reg_alloc.c
Normal 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,
|
||||
};
|
||||
111
libs/ir/mir/src/reg_alloc/reg_alloc.c
Normal file
111
libs/ir/mir/src/reg_alloc/reg_alloc.c
Normal 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));
|
||||
}
|
||||
}
|
||||
@@ -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(®_alloc_ctx, mir_module);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user