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

@@ -145,24 +145,42 @@ void scc_argparse_print_help(scc_argparse_t *parser, scc_argparse_cmd_t *cmd) {
continue; continue;
if (opt->short_name == 'h') if (opt->short_name == 'h')
opt->description = lines[ARGPARSE_SHOW_HELP_MSG]; opt->description = lines[ARGPARSE_SHOW_HELP_MSG];
char buf[64]; scc_str_t buf;
scc_str_init(&buf);
int pos = 0; int pos = 0;
if (opt->short_name) { if (opt->short_name) {
buf[pos++] = '-'; scc_str_append_ch(&buf, '-');
buf[pos++] = opt->short_name; scc_str_append_ch(&buf, opt->short_name);
if (opt->long_name) { if (opt->long_name) {
buf[pos++] = ','; scc_str_append_ch(&buf, ',');
buf[pos++] = ' '; scc_str_append_ch(&buf, ' ');
} }
} }
if (opt->long_name) { if (opt->long_name) {
buf[pos++] = '-'; scc_str_append_ch(&buf, '-');
buf[pos++] = '-'; scc_str_append_ch(&buf, '-');
for (const char *p = opt->long_name; *p; ++p) scc_str_append_cstr(&buf, opt->long_name,
buf[pos++] = *p; scc_strlen(opt->long_name));
} }
buf[pos] = '\0'; if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_ENUM) {
scc_printf(" %-25s %s", buf, scc_str_append_ch(&buf, '=');
scc_str_append_ch(&buf, '<');
for (int j = 0; j < opt->spec.choices.count; j += 1) {
if (opt->spec.choices.values[j] == 0) {
continue;
}
if (j != 0) {
scc_str_append_ch(&buf, '|');
}
scc_str_append_cstr(
&buf, opt->spec.choices.values[j],
scc_strlen(opt->spec.choices.values[j]));
}
scc_str_append_ch(&buf, '>');
}
scc_printf(" %-25s %s", scc_str_as_cstr(&buf),
opt->description ? opt->description : ""); opt->description ? opt->description : "");
// if (opt->spec.default_value) // if (opt->spec.default_value)
// scc_printf(" (default: %s)", opt->spec.default_value); // scc_printf(" (default: %s)", opt->spec.default_value);

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 <core_pass/scc_reg_alloc.h>
#include <scc_mir_module.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) { void scc_mir_pass(scc_mir_module_t *mir_module, scc_mir_pass_stage_t stage) {
scc_reg_alloc(&x86_reg_alloc_hooks, mir_module); 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);
} }

9
libs/mcode/cbuild.toml Normal file
View File

@@ -0,0 +1,9 @@
[package]
name = "mcode"
version = "0.1.0"
authors = []
description = ""
dependencies = [{ name = "scc_core", path = "../../runtime/scc_core" }]
# features = {}
# default_features = []

View File

@@ -161,9 +161,9 @@ void setup_argparse(scc_argparse_t *argparse, scc_config_t *config,
[SCC_EMIT_STAGE_LIR] = "lir", // -L [SCC_EMIT_STAGE_LIR] = "lir", // -L
[SCC_EMIT_STAGE_MIR] = "mir", // -M [SCC_EMIT_STAGE_MIR] = "mir", // -M
[SCC_EMIT_STAGE_MIR_PASS_REG_ALLOC] = "", [SCC_EMIT_STAGE_MIR_PASS_REG_ALLOC] = "reg_alloc",
[SCC_EMIT_STAGE_MIR_PASS_FLAME_LAYOUT] = "", [SCC_EMIT_STAGE_MIR_PASS_FLAME_LAYOUT] = "frame_layout",
[SCC_EMIT_STAGE_MIR_PASS_PROLOG_EPILOG] = "", [SCC_EMIT_STAGE_MIR_PASS_PROLOG_EPILOG] = "prolog_epilog",
[SCC_EMIT_STAGE_FLATBIN] = "flatbin", [SCC_EMIT_STAGE_FLATBIN] = "flatbin",
[SCC_EMIT_STAGE_SCCF] = "sccf", [SCC_EMIT_STAGE_SCCF] = "sccf",
[SCC_EMIT_STAGE_TARGET] = "target", [SCC_EMIT_STAGE_TARGET] = "target",

View File

@@ -11,6 +11,8 @@
#include <scc_lir_dump.h> #include <scc_lir_dump.h>
#include <scc_mir_dump.h> #include <scc_mir_dump.h>
#include <scc_mir_pass.h>
#include <scc_ir2mcode.h> #include <scc_ir2mcode.h>
#include <scc_ir2sccf.h> #include <scc_ir2sccf.h>
#include <sccf2pe.h> #include <sccf2pe.h>
@@ -271,7 +273,25 @@ sstream_drop:
scc_mir_module_t mir_module; scc_mir_module_t mir_module;
scc_mir_module_init(&mir_module); scc_mir_module_init(&mir_module);
scc_lir2mir(&mir_module, &lir_module); scc_lir2mir(&mir_module, &lir_module);
if (config.emit_stage == SCC_EMIT_STAGE_MIR) {
switch (config.emit_stage) {
case SCC_EMIT_STAGE_MIR:
goto mir_dump;
case SCC_EMIT_STAGE_MIR_PASS_REG_ALLOC:
scc_mir_pass(&mir_module, SCC_MIR_STAGE_REGALLOC);
goto mir_dump;
case SCC_EMIT_STAGE_MIR_PASS_FLAME_LAYOUT:
scc_mir_pass(&mir_module, SCC_MIR_STAGE_POST_REGALLOC);
goto mir_dump;
case SCC_EMIT_STAGE_MIR_PASS_PROLOG_EPILOG:
scc_mir_pass(&mir_module, SCC_MIR_STAGE_POST_REGALLOC);
goto mir_dump;
break;
default:
break;
}
do {
mir_dump:
scc_mir_dump_ctx_t mir_dump_ctx; scc_mir_dump_ctx_t mir_dump_ctx;
scc_tree_dump_t tree_dump; scc_tree_dump_t tree_dump;
if (fp == nullptr) { if (fp == nullptr) {
@@ -285,7 +305,7 @@ sstream_drop:
scc_tree_dump_flush(&tree_dump, tree_dump_output, GET_VALID_FP(fp)); scc_tree_dump_flush(&tree_dump, tree_dump_output, GET_VALID_FP(fp));
scc_tree_dump_drop(&tree_dump); scc_tree_dump_drop(&tree_dump);
return 0; return 0;
} } while (0);
if (config.emit_stage == SCC_EMIT_STAGE_FLATBIN) { if (config.emit_stage == SCC_EMIT_STAGE_FLATBIN) {
scc_mcode_t mcode = {0}; scc_mcode_t mcode = {0};