feat(mir): 添加栈偏移操作数类型并重构内存访问表示

- 将 MIR 中的 SCC_MIR_OP_MEM 替换为更精确的 SCC_MIR_OP_STACK_SLOT 和
  SCC_MIR_OP_STACK_OFFSET 类型
- 在 x86_64 指令选择中更新相应的内存操作数处理逻辑
- 修改寄存器分配器中的栈槽操作数类型检查
- 更新 IR 转机器码过程中的内存操作数转换

refactor(hir): 使用 tree_dump_node 输出函数节点

- 将 hir_dump 中的函数名输出从 append 改为 node 类型

refactor(frame-layout): 重构栈帧布局传递实现结构

- 引入函数指针实现方式替代直接函数实现
- 将栈帧分配功能集成到 MIR 传递流程中
- 移除独立的 frame_layout 实现文件

refactor(prolog-epilog): 添加函数序言/尾声传递框架

- 为 Windows x64 平台初始化序言/尾声生成器
- 在 MIR 传递阶段添加序言/尾声处理步骤

refactor(win64): 更新 Windows x64 目标平台接口

- 重命名寄存器分配填充函数为 scc_win_pc_x64_reg_alloc_fill
- 添加栈帧分配和序言/尾声初始化函数
- 修正参数传递中的栈槽操作数类型

refactor(dump): 改进 MIR 输出格式

- 将基本块显示改为分支节点类型
- 更新操作数类型的显示处理

chore: 添加 x86 编码相关数据结构定义

- 新增 scc_x86_encode.h 头文件包含内存操作数和指令编码接口定义
This commit is contained in:
zzy
2026-05-13 18:47:44 +08:00
parent 68eac24152
commit 3df858fb85
20 changed files with 662 additions and 85 deletions

View File

@@ -3,10 +3,15 @@
#include "../scc_mir_module.h"
typedef struct scc_frame_layout {
scc_mir_func_t *func;
} scc_frame_layout_t;
struct scc_frame_layout;
typedef struct scc_frame_layout scc_frame_layout_t;
typedef void (*scc_frame_layout_impl_fn)(scc_frame_layout_t *ctx,
scc_mir_module_t *mir_module,
scc_mir_func_t *mir_func);
void scc_frame_layout(scc_frame_layout_t *ctx, scc_mir_module_t *module);
struct scc_frame_layout {
scc_frame_layout_impl_fn impl_fn;
int offset;
};
#endif /* __SCC_FRAME_LAYOUT_H__ */

View File

@@ -1,8 +1,9 @@
#ifndef __SCC_PROLOG_EPILOG_H__
#define __SCC_PROLOG_EPILOG_H__
#include "../scc_mir_module.h"
#include <scc_lir_module.h>
#include <scc_mir_module.h>
typedef void (*scc_prolog_epilog_fn)(void *userdata,
const scc_mir_func_t *func);

View File

@@ -7,12 +7,13 @@
typedef enum {
SCC_MIR_OP_NONE,
SCC_MIR_OP_MEM, // 内存访问
SCC_MIR_OP_VREG, // 虚拟寄存器
SCC_MIR_OP_PREG, // 物理寄存器
SCC_MIR_OP_IMM, // 立即数
SCC_MIR_OP_SYMBOL, // 符号地址(用于重定位)
SCC_MIR_OP_BLOCK // 基本块引用(label)
SCC_MIR_OP_STACK_OFFSET, // 已分配的内存
SCC_MIR_OP_STACK_SLOT, // 栈空间
SCC_MIR_OP_VREG, // 虚拟寄存器
SCC_MIR_OP_PREG, // 物理寄存器
SCC_MIR_OP_IMM, // 立即数
SCC_MIR_OP_SYMBOL, // 符号地址(用于重定位)
SCC_MIR_OP_BLOCK // 基本块引用(label)
} scc_mir_op_kind_t;
typedef struct scc_mir_operand {
@@ -23,6 +24,7 @@ typedef struct scc_mir_operand {
i64 imm; // 立即数
const char *symbol; // 符号名
int stack_slot; // 栈槽 ID (由 FrameLayout 分配)
int stack_offset; // 栈偏移
scc_lir_bblock_id_t block_id; // 目标基本块
};
} scc_mir_operand_t;
@@ -78,5 +80,10 @@ void scc_mir_vreg_op(const scc_mir_func_t *func, int vreg,
void scc_mir_vreg_map2preg(scc_mir_func_t *func, int vreg, int preg);
int scc_mir_vreg_map2slot(scc_mir_func_t *func, int vreg, int size, int align);
static inline scc_mir_stack_slot_t *
scc_mir_unsafe_slot(const scc_mir_func_t *func, int slot) {
Assert(slot > 0);
return &scc_vec_at(SCC_MIR_FUNC_META(func)->stack_slots, slot);
}
#endif /* __SCC_MIR_H__ */

View File

@@ -2,8 +2,13 @@
#define __SCC_WIN64_H__
#include "../arch/x86_64_isel.h"
#include "../core_pass/scc_frame_layout.h"
#include "../core_pass/scc_prolog_epilog.h"
#include "../core_pass/scc_reg_alloc.h"
void scc_win_pc_x64_abi_lowering(scc_abi_lowering_t *abi_lowering);
void scc_reg_alloc_fill_win_x64(scc_reg_alloc_op_t *ops);
void scc_win_pc_x64_reg_alloc_fill(scc_reg_alloc_op_t *ops);
void scc_win_pc_x64_frame_alloc_init(scc_frame_layout_t *ctx);
void scc_win_pc_x64_prolog_epilog_init(scc_prolog_epilog_t *ctx);
#endif

View File

@@ -36,9 +36,11 @@ void scc_x86_instr_dump(scc_tree_dump_t *td, const scc_mir_instr_t *instr) {
case SCC_MIR_OP_SYMBOL:
scc_tree_dump_append_fmt(td, "@%s", op->symbol);
break;
case SCC_MIR_OP_MEM:
case SCC_MIR_OP_STACK_SLOT:
scc_tree_dump_append_fmt(td, "[%d]", op->stack_slot);
break;
case SCC_MIR_OP_STACK_OFFSET:
scc_tree_dump_append_fmt(td, "[sp, %d]", op->stack_offset);
default:
break;
}
@@ -99,12 +101,12 @@ void scc_x86_emit_move(scc_x86_64_isel_t *isel, scc_mir_operand_t dst,
dst, src);
} else if (src.kind == SCC_MIR_OP_SYMBOL) {
add_instr_2(isel, SCC_X86_IFORM_MOV_GPRV_IMMZ, dst, src);
} else if (src.kind == SCC_MIR_OP_MEM) {
} else if (src.kind == SCC_MIR_OP_STACK_SLOT) {
add_instr_2(isel, SCC_X86_IFORM_MOV_GPRV_MEMV, dst, src);
} else {
UNREACHABLE();
}
} else if (dst.kind == SCC_MIR_OP_MEM) {
} else if (dst.kind == SCC_MIR_OP_STACK_SLOT) {
if (src.kind == SCC_MIR_OP_VREG || src.kind == SCC_MIR_OP_PREG) {
add_instr_2(isel, SCC_X86_IFORM_MOV_MEMV_GPRV, dst, src);
} else if (src.kind == SCC_MIR_OP_IMM) {
@@ -113,7 +115,7 @@ void scc_x86_emit_move(scc_x86_64_isel_t *isel, scc_mir_operand_t dst,
scc_mir_operand_t temp = new_vreg_temp(isel);
add_instr_2(isel, SCC_X86_IFORM_MOV_GPRV_IMMZ, temp, src);
add_instr_2(isel, SCC_X86_IFORM_MOV_MEMV_GPRV, dst, temp);
} else if (src.kind == SCC_MIR_OP_MEM) {
} else if (src.kind == SCC_MIR_OP_STACK_SLOT) {
scc_mir_operand_t temp = new_vreg_temp(isel);
scc_x86_emit_move(isel, temp, src, size);
scc_x86_emit_move(isel, dst, temp, size);
@@ -209,7 +211,8 @@ static void emit_binary_op(scc_x86_64_isel_t *isel, scc_lir_op_t op,
}
static scc_mir_operand_t stack_slot_op(int offset) {
return (scc_mir_operand_t){.kind = SCC_MIR_OP_MEM, .stack_slot = offset};
return (scc_mir_operand_t){.kind = SCC_MIR_OP_STACK_SLOT,
.stack_slot = offset};
}
static void emit_spill_load(scc_x86_64_isel_t *isel, int vreg, int offset) {
@@ -281,6 +284,11 @@ static void sel_mir(scc_x86_64_isel_t *isel, const scc_lir_instr_t *instr) {
case SCC_LIR_MUL:
// imul dst, src0, src1 → 需要 mov + imul
emit_copy_if_needed(isel, dst, src0, size);
if (src1.kind == SCC_MIR_OP_IMM) {
scc_mir_operand_t op = new_vreg_temp(isel);
scc_x86_emit_move(isel, op, src1, size);
src1 = op;
}
add_instr_2(isel, SCC_X86_IFORM_IMUL_GPRV_GPRV, dst, src1);
break;
@@ -421,6 +429,8 @@ static void sel_mir(scc_x86_64_isel_t *isel, const scc_lir_instr_t *instr) {
.imm = instr->size,
});
Assert(op.kind == SCC_MIR_OP_VREG);
// FIXME
scc_mir_vreg_map2slot(isel->func, op.vreg, instr->size, 8);
break;
/* ---- 其他(占位) ---- */

View File

@@ -5,7 +5,7 @@ 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},
.operands = {{.kind = SCC_MIR_OP_STACK_SLOT, .stack_slot = slot},
{.kind = SCC_MIR_OP_PREG, .preg = preg}}};
scc_vec_push(*ctx, ins);
}
@@ -15,7 +15,7 @@ static void x86_emit_reload(scc_mir_instr_vec_t *ctx, int preg, int slot) {
.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}}};
{.kind = SCC_MIR_OP_STACK_SLOT, .stack_slot = slot}}};
scc_vec_push(*ctx, ins);
}

View File

@@ -1,3 +0,0 @@
#include <core_pass/scc_frame_layout.h>
void scc_frame_layout(scc_frame_layout_t *ctx, scc_mir_module_t *module) {}

View File

@@ -118,7 +118,7 @@ static void alloc_instr(scc_reg_alloc_ctx_t *ctx,
slot = scc_mir_vreg_map2slot(ctx->func, vreg, 8, 8);
} else {
// 已经是 MEM直接用它的 slot
Assert(op->kind == SCC_MIR_OP_MEM);
Assert(op->kind == SCC_MIR_OP_STACK_SLOT);
slot = op->stack_slot;
}
@@ -192,6 +192,11 @@ static void alloc_bb(scc_reg_alloc_ctx_t *ctx, scc_mir_module_t *module,
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_mir_vreg_op(ctx->func, ins.operands[0].vreg, &ins.operands[0]);
scc_vec_push(new_instrs, ins);
continue;
}
alloc_instr(ctx, &new_instrs, &ins);
}

View File

@@ -28,7 +28,7 @@ void scc_mir_vreg_op(const scc_mir_func_t *func, int vreg,
out->kind = SCC_MIR_OP_PREG;
out->preg = (int)-idx;
} else {
out->kind = SCC_MIR_OP_MEM;
out->kind = SCC_MIR_OP_STACK_SLOT;
Assert(idx < scc_vec_size(meta->stack_slots));
out->stack_slot = (int)(usize)idx;
}

View File

@@ -16,7 +16,7 @@ void scc_mir_dump_instr(scc_mir_dump_ctx_t *ctx, const scc_mir_instr_t *ins) {
scc_tree_dump_append_fmt(td, "(%d)", ins->operands[1].imm);
if (ins->operands[0].kind == SCC_MIR_OP_VREG) {
scc_tree_dump_append_fmt(td, " %%%d", ins->operands[0].vreg);
} else if (ins->operands[0].kind == SCC_MIR_OP_MEM) {
} else if (ins->operands[0].kind == SCC_MIR_OP_STACK_SLOT) {
scc_tree_dump_append_fmt(td, " [%d]", ins->operands[0].stack_slot);
} else {
scc_tree_dump_append(td, "<alloced>");
@@ -33,7 +33,7 @@ void scc_mir_dump_bblock(scc_mir_dump_ctx_t *ctx, const scc_mir_bblock_t *bb) {
// 基本块头部
scc_tree_dump_begin_line(td);
scc_tree_dump_node(td, "#BB%zu", bb->id);
scc_tree_dump_branch(td, "#BB%zu", bb->id);
if (bb->name) {
scc_tree_dump_append_fmt(td, " (%s)", bb->name);
}

View File

@@ -1,27 +1,78 @@
#include <arch/x86_64_reg_alloc.h>
#include <target/scc_win64.h>
#include <core_pass/scc_frame_layout.h>
#include <core_pass/scc_reg_alloc.h>
// #include <core_pass/scc_frame_layout.h>
// #include <core_pass/scc_prolog_epilog.h>
// #include <core_pass/scc_reg_alloc.h>
#include <scc_mir_module.h>
#include <scc_mir_pass.h>
void scc_frame_layout(scc_frame_layout_t *ctx, scc_mir_module_t *module) {
Assert(ctx && ctx->impl_fn && module);
scc_vec_foreach(module->cfg_module.funcs, i) {
if (i == 0)
continue;
ctx->impl_fn(ctx, module, &scc_vec_at(module->cfg_module.funcs, i));
}
}
void scc_prolog_epilog(scc_prolog_epilog_t *ctx, scc_mir_module_t *module) {
Assert(ctx && ctx->epilog && ctx->prolog && module);
scc_vec_foreach(module->cfg_module.funcs, i) {
if (i == 0)
continue;
scc_mir_func_t *func = &scc_vec_at(module->cfg_module.funcs, i);
scc_vec_foreach(func->bblocks, i) {
scc_cfg_bblock_id_t bb_id = scc_vec_at(func->bblocks, i);
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);
if (i == 0) {
} else if (i == scc_vec_size(func->bblocks) - 1) {
// FIXME epilog it maybe used in ret instr
}
scc_vec_foreach(*old_instrs, i) {
scc_mir_instr_t ins = scc_vec_at(*old_instrs, i);
if (ins.opcode == SCC_MIR_PSUEDO_ALLOCA) {
continue;
}
scc_vec_push(new_instrs, ins);
}
scc_vec_free(*old_instrs);
*old_instrs = new_instrs;
}
}
}
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 = nullptr, .instrs = nullptr, .ops = {0}};
scc_reg_alloc_fill_arch_x86(&reg_alloc_ctx.ops);
scc_reg_alloc_fill_win_x64(&reg_alloc_ctx.ops);
scc_win_pc_x64_reg_alloc_fill(&reg_alloc_ctx.ops);
scc_reg_alloc(&reg_alloc_ctx, mir_module);
if (stage == SCC_MIR_STAGE_REGALLOC) {
return;
}
scc_frame_layout_t frame_layout_ctx = {0};
scc_win_pc_x64_frame_alloc_init(&frame_layout_ctx);
scc_frame_layout(&frame_layout_ctx, mir_module);
if (stage == SCC_MIR_STAGE_FRAME_LAYOUT) {
return;
}
scc_prolog_epilog_t prolog_epilog_ctx = {0};
scc_win_pc_x64_prolog_epilog_init(&prolog_epilog_ctx);
scc_prolog_epilog(&prolog_epilog_ctx, mir_module);
if (stage == SCC_MIR_STAGE_PROLOGUE_EPILOGUE) {
return;
}

View File

@@ -2,30 +2,23 @@
#include <x86/scc_x86_iform.h>
#include <arch/x86_64_isel.h>
#include <core_pass/scc_abi_lowering.h>
#include <core_pass/scc_prolog_epilog.h>
#include <target/scc_win64.h>
static const int WIN64_DEFAULT_ALIGN = 8;
static const int WIN64_STACK_ALIGN = 16;
typedef struct {
scc_hashtable_t *vreg2slot;
int offset;
} frame_layout_ctx_t;
static void transit_vreg(scc_mir_func_meta_t *func, scc_mir_operand_t *op) {
Assert(op->kind == SCC_MIR_OP_VREG);
}
static void frame_alloc_impl(frame_layout_ctx_t *ctx,
static void frame_alloc_impl(scc_frame_layout_t *ctx,
scc_mir_module_t *mir_module,
scc_mir_func_t *mir_func) {
/*
WIN ABI
*/
ctx->offset = 8; // called ret address
ctx->offset = 8;
scc_mir_func_meta_t *func_meta = SCC_MIR_FUNC_META(mir_func);
scc_vec_foreach(mir_func->bblocks, i) {
scc_cfg_bblock_id_t id = scc_vec_at(mir_func->bblocks, i);
@@ -35,8 +28,19 @@ static void frame_alloc_impl(frame_layout_ctx_t *ctx,
scc_vec_foreach(*instrs, j) {
scc_mir_instr_t *ins = &scc_vec_at(*instrs, j);
for (int k = 0; k < ins->num_operands; k += 1) {
if (ins->operands[k].kind == SCC_MIR_OP_VREG) {
transit_vreg(func_meta, &ins->operands[k]);
scc_mir_operand_t *op = &ins->operands[k];
if (op->kind == SCC_MIR_OP_VREG) {
Panic("vreg not supported in frame layout");
} else if (op->kind == SCC_MIR_OP_STACK_SLOT) {
scc_mir_stack_slot_t *slot =
scc_mir_unsafe_slot(mir_func, op->stack_slot);
op->kind = SCC_MIR_OP_STACK_OFFSET;
if (slot->offset == 0) {
// FIXME align
ctx->offset += slot->size;
slot->offset = ctx->offset;
}
op->stack_offset = slot->offset;
}
}
}
@@ -47,6 +51,11 @@ static void frame_alloc_impl(frame_layout_ctx_t *ctx,
// 16 字节栈对齐
ctx->offset =
(total_size + WIN64_STACK_ALIGN - 1) & ~(WIN64_STACK_ALIGN - 1);
func_meta->frame_size = ctx->offset;
}
void scc_win_pc_x64_frame_alloc_init(scc_frame_layout_t *ctx) {
ctx->impl_fn = frame_alloc_impl;
}
/*
@@ -99,6 +108,11 @@ static void epilogue(void *userdata, const scc_mir_func_t *func) {
add_instr_1(isel, SCC_X86_IFORM_POP_GPRV_58, reg_operand(SCC_X86_REG_RBP));
}
void scc_win_pc_x64_prolog_epilog_init(scc_prolog_epilog_t *ctx) {
ctx->prolog = prologue;
ctx->epilog = epilogue;
}
static void lower_call(void *userdata, const scc_lir_instr_t *instr) {
scc_x86_64_isel_t *isel = userdata;
/*
@@ -174,7 +188,7 @@ static scc_mir_operand_t lower_param(void *userdata, const scc_lir_val_t *val) {
case 3:
return reg_operand(SCC_X86_REG_R9);
default:
return (scc_mir_operand_t){.kind = SCC_MIR_OP_MEM,
return (scc_mir_operand_t){.kind = SCC_MIR_OP_STACK_SLOT,
.stack_slot = -val->data.arg};
}
}
@@ -231,14 +245,9 @@ static void mark_reg_used(void *ctx, int preg) {
}
static void clean_mark_regs(void *ctx) { reg_mask = 0; }
void scc_reg_alloc_fill_win_x64(scc_reg_alloc_op_t *ops) {
void scc_win_pc_x64_reg_alloc_fill(scc_reg_alloc_op_t *ops) {
ops->acquire_reg = acquire_reg;
ops->release_reg = release_reg;
ops->mark_reg_used = mark_reg_used;
ops->clean_mark_regs = clean_mark_regs;
}
void scc_win_pc_x64_prolog_epilog(scc_prolog_epilog_t *prolog_epilog) {
prolog_epilog->prolog = prologue;
prolog_epilog->epilog = epilogue;
}