clarenceleo's picture
Add ir.c - IR builder, AST-to-IR lowering, and printing
dabb8de verified
Raw
History Blame Contribute Delete
30.9 kB
/*
* par2serial-cc: ir.c - IR construction and AST→IR lowering
*/
#include "ir.h"
/* ── IR Module/Func/Block creation ───────────────────────── */
IRModule *ir_module_create(Arena *arena) {
IRModule *mod = (IRModule *)arena_alloc(arena, sizeof(IRModule));
memset(mod, 0, sizeof(IRModule));
mod->arena = arena;
vec_init(&mod->preproc_lines);
vec_init(&mod->global_vars);
return mod;
}
IRFunc *ir_func_create(IRModule *mod, const char *name) {
IRFunc *f = (IRFunc *)arena_alloc(mod->arena, sizeof(IRFunc));
memset(f, 0, sizeof(IRFunc));
f->name = name;
vec_init(&f->blocks);
f->reg_count = 0;
f->label_count = 0;
f->next = mod->functions;
mod->functions = f;
mod->func_count++;
return f;
}
IRBlock *ir_block_create(IRFunc *func, const char *name) {
IRBlock *bb = (IRBlock *)calloc(1, sizeof(IRBlock));
bb->id = (int)func->blocks.len;
bb->name = name;
vec_init(&bb->insts);
vec_push(&func->blocks, bb);
return bb;
}
/* ── Value constructors ──────────────────────────────────── */
IRValue ir_reg(int reg, TypeKind type) {
IRValue v = {0};
v.kind = VAL_REG;
v.reg = reg;
v.type = type;
return v;
}
IRValue ir_int(int64_t val) {
IRValue v = {0};
v.kind = VAL_INT;
v.int_val = val;
v.type = TYPE_INT;
return v;
}
IRValue ir_float(double val) {
IRValue v = {0};
v.kind = VAL_FLOAT;
v.float_val = val;
v.type = TYPE_FLOAT;
return v;
}
IRValue ir_label(int id) {
IRValue v = {0};
v.kind = VAL_LABEL;
v.label_id = id;
return v;
}
IRValue ir_name(const char *name) {
IRValue v = {0};
v.kind = VAL_NAME;
v.name = name;
return v;
}
IRValue ir_none(void) {
IRValue v = {0};
v.kind = VAL_NONE;
return v;
}
IRValue ir_simd_reg(int reg, TypeKind type, int width) {
IRValue v = ir_reg(reg, type);
v.simd_width = width;
return v;
}
/* ── Instruction emission ────────────────────────────────── */
int ir_new_reg(IRFunc *func) {
return func->reg_count++;
}
int ir_new_label(IRFunc *func) {
return func->label_count++;
}
IRInst *ir_emit(Arena *arena, IRBlock *bb, IROp op,
IRValue dst, IRValue src1, IRValue src2) {
IRInst *inst = (IRInst *)arena_alloc(arena, sizeof(IRInst));
memset(inst, 0, sizeof(IRInst));
inst->op = op;
inst->dst = dst;
inst->src1 = src1;
inst->src2 = src2;
inst->src3 = ir_none();
vec_push(&bb->insts, inst);
return inst;
}
IRInst *ir_emit_comment(Arena *arena, IRBlock *bb, const char *comment) {
IRInst *inst = (IRInst *)arena_alloc(arena, sizeof(IRInst));
memset(inst, 0, sizeof(IRInst));
inst->op = IR_COMMENT;
inst->comment = comment;
inst->dst = inst->src1 = inst->src2 = inst->src3 = ir_none();
vec_push(&bb->insts, inst);
return inst;
}
/* ══════════════════════════════════════════════════════════
* AST → IR Lowering
* ══════════════════════════════════════════════════════════ */
typedef struct {
Arena *arena;
IRModule *mod;
IRFunc *cur_func;
IRBlock *cur_block;
HashMap *var_regs; /* variable name → register number */
} IRBuilder;
static IRValue lower_expr(IRBuilder *b, ASTNode *expr);
static void lower_stmt(IRBuilder *b, ASTNode *stmt);
static int lookup_var(IRBuilder *b, const char *name) {
void *val = hashmap_get(b->var_regs, name);
if (val) return (int)(intptr_t)val;
/* allocate new register for this variable */
int reg = ir_new_reg(b->cur_func);
hashmap_put(b->var_regs, name, (void *)(intptr_t)(reg + 1)); /* +1 to avoid NULL */
return reg;
}
static TypeKind infer_type(ASTNode *expr) {
if (!expr) return TYPE_INT;
switch (expr->type) {
case NODE_FLOAT_LIT: return TYPE_FLOAT;
case NODE_INT_LIT: return TYPE_INT;
default: return TYPE_INT;
}
}
static IRValue lower_expr(IRBuilder *b, ASTNode *expr) {
if (!expr) return ir_int(0);
switch (expr->type) {
case NODE_INT_LIT: {
int r = ir_new_reg(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_LOAD_IMM,
ir_reg(r, TYPE_INT), ir_int(expr->int_lit.value), ir_none());
return ir_reg(r, TYPE_INT);
}
case NODE_FLOAT_LIT: {
int r = ir_new_reg(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_LOAD_FIMM,
ir_reg(r, TYPE_FLOAT), ir_float(expr->float_lit.value), ir_none());
return ir_reg(r, TYPE_FLOAT);
}
case NODE_IDENT: {
int r = lookup_var(b, expr->ident.name);
return ir_reg(r, TYPE_INT);
}
case NODE_STRING_LIT: {
return ir_name(expr->string_lit.value);
}
case NODE_BINARY: {
IRValue left = lower_expr(b, expr->binary.left);
IRValue right = lower_expr(b, expr->binary.right);
int r = ir_new_reg(b->cur_func);
bool is_float = (left.type == TYPE_FLOAT || left.type == TYPE_DOUBLE ||
right.type == TYPE_FLOAT || right.type == TYPE_DOUBLE);
IROp op;
switch (expr->binary.op) {
case OP_ADD: op = is_float ? IR_FADD : IR_ADD; break;
case OP_SUB: op = is_float ? IR_FSUB : IR_SUB; break;
case OP_MUL: op = is_float ? IR_FMUL : IR_MUL; break;
case OP_DIV: op = is_float ? IR_FDIV : IR_DIV; break;
case OP_MOD: op = IR_MOD; break;
case OP_AND: op = IR_AND; break;
case OP_OR: op = IR_OR; break;
case OP_XOR: op = IR_XOR; break;
case OP_LSHIFT: op = IR_SHL; break;
case OP_RSHIFT: op = IR_SHR; break;
case OP_EQ: op = is_float ? IR_FEQ : IR_EQ; break;
case OP_NEQ: op = is_float ? IR_FNE : IR_NE; break;
case OP_LT: op = is_float ? IR_FLT : IR_LT; break;
case OP_GT: op = is_float ? IR_FGT : IR_GT; break;
case OP_LE: op = is_float ? IR_FLE : IR_LE; break;
case OP_GE: op = is_float ? IR_FGE : IR_GE; break;
case OP_LAND: op = IR_AND; break;
case OP_LOR: op = IR_OR; break;
default: op = IR_ADD; break;
}
TypeKind rtype = is_float ? TYPE_FLOAT : TYPE_INT;
ir_emit(b->arena, b->cur_block, op,
ir_reg(r, rtype), left, right);
return ir_reg(r, rtype);
}
case NODE_UNARY: {
IRValue operand = lower_expr(b, expr->unary.operand);
int r = ir_new_reg(b->cur_func);
switch (expr->unary.op) {
case OP_NEG:
ir_emit(b->arena, b->cur_block, IR_NEG,
ir_reg(r, operand.type), operand, ir_none());
break;
case OP_NOT:
ir_emit(b->arena, b->cur_block, IR_NOT,
ir_reg(r, TYPE_INT), operand, ir_none());
break;
case OP_LNOT: {
ir_emit(b->arena, b->cur_block, IR_EQ,
ir_reg(r, TYPE_INT), operand, ir_int(0));
break;
}
case OP_INC: {
ir_emit(b->arena, b->cur_block, IR_ADD,
ir_reg(r, operand.type), operand, ir_int(1));
if (expr->unary.operand->type == NODE_IDENT) {
int vr = lookup_var(b, expr->unary.operand->ident.name);
ir_emit(b->arena, b->cur_block, IR_MOVE,
ir_reg(vr, operand.type), ir_reg(r, operand.type), ir_none());
}
break;
}
case OP_DEC: {
ir_emit(b->arena, b->cur_block, IR_SUB,
ir_reg(r, operand.type), operand, ir_int(1));
if (expr->unary.operand->type == NODE_IDENT) {
int vr = lookup_var(b, expr->unary.operand->ident.name);
ir_emit(b->arena, b->cur_block, IR_MOVE,
ir_reg(vr, operand.type), ir_reg(r, operand.type), ir_none());
}
break;
}
default:
ir_emit(b->arena, b->cur_block, IR_MOVE,
ir_reg(r, operand.type), operand, ir_none());
}
return ir_reg(r, operand.type);
}
case NODE_ASSIGN: {
IRValue val = lower_expr(b, expr->assign.value);
if (expr->assign.target->type == NODE_IDENT) {
int vr = lookup_var(b, expr->assign.target->ident.name);
if (expr->assign.op == OP_ASSIGN) {
ir_emit(b->arena, b->cur_block, IR_MOVE,
ir_reg(vr, val.type), val, ir_none());
} else {
IRValue cur = ir_reg(vr, val.type);
int tmp = ir_new_reg(b->cur_func);
IROp op;
switch (expr->assign.op) {
case OP_ADD_ASSIGN: op = IR_ADD; break;
case OP_SUB_ASSIGN: op = IR_SUB; break;
case OP_MUL_ASSIGN: op = IR_MUL; break;
case OP_DIV_ASSIGN: op = IR_DIV; break;
case OP_MOD_ASSIGN: op = IR_MOD; break;
default: op = IR_ADD; break;
}
ir_emit(b->arena, b->cur_block, op,
ir_reg(tmp, val.type), cur, val);
ir_emit(b->arena, b->cur_block, IR_MOVE,
ir_reg(vr, val.type), ir_reg(tmp, val.type), ir_none());
}
return ir_reg(vr, val.type);
}
if (expr->assign.target->type == NODE_INDEX) {
/* array[idx] = val → STORE */
IRValue base = lower_expr(b, expr->assign.target->index.array);
IRValue idx = lower_expr(b, expr->assign.target->index.index);
int addr = ir_new_reg(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_INDEX,
ir_reg(addr, TYPE_POINTER), base, idx);
ir_emit(b->arena, b->cur_block, IR_STORE,
ir_reg(addr, TYPE_POINTER), val, ir_none());
return val;
}
return val;
}
case NODE_INDEX: {
IRValue base = lower_expr(b, expr->index.array);
IRValue idx = lower_expr(b, expr->index.index);
int addr = ir_new_reg(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_INDEX,
ir_reg(addr, TYPE_POINTER), base, idx);
int r = ir_new_reg(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_LOAD,
ir_reg(r, TYPE_FLOAT), ir_reg(addr, TYPE_POINTER), ir_none());
return ir_reg(r, TYPE_FLOAT);
}
case NODE_CALL: {
/* Lower arguments */
IRValue func_val = ir_none();
if (expr->call.func->type == NODE_IDENT)
func_val = ir_name(expr->call.func->ident.name);
/* emit args then call */
for (size_t i = 0; i < expr->call.args.len; i++)
lower_expr(b, expr->call.args.data[i]);
int r = ir_new_reg(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_CALL,
ir_reg(r, TYPE_INT), func_val, ir_int((int64_t)expr->call.args.len));
return ir_reg(r, TYPE_INT);
}
case NODE_CAST: {
IRValue inner = lower_expr(b, expr->cast.expr);
int r = ir_new_reg(b->cur_func);
TypeKind target = expr->cast.target_type->kind;
if ((target == TYPE_FLOAT || target == TYPE_DOUBLE) &&
inner.type != TYPE_FLOAT && inner.type != TYPE_DOUBLE) {
ir_emit(b->arena, b->cur_block, IR_INT_TO_FLOAT,
ir_reg(r, TYPE_FLOAT), inner, ir_none());
} else if (inner.type == TYPE_FLOAT && target == TYPE_INT) {
ir_emit(b->arena, b->cur_block, IR_FLOAT_TO_INT,
ir_reg(r, TYPE_INT), inner, ir_none());
} else {
ir_emit(b->arena, b->cur_block, IR_MOVE,
ir_reg(r, target), inner, ir_none());
}
return ir_reg(r, target);
}
case NODE_POSTFIX: {
IRValue operand = lower_expr(b, expr->postfix.operand);
int old_val = ir_new_reg(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_MOVE,
ir_reg(old_val, operand.type), operand, ir_none());
int new_val = ir_new_reg(b->cur_func);
IROp op = (expr->postfix.op == OP_POST_INC) ? IR_ADD : IR_SUB;
ir_emit(b->arena, b->cur_block, op,
ir_reg(new_val, operand.type), operand, ir_int(1));
if (expr->postfix.operand->type == NODE_IDENT) {
int vr = lookup_var(b, expr->postfix.operand->ident.name);
ir_emit(b->arena, b->cur_block, IR_MOVE,
ir_reg(vr, operand.type), ir_reg(new_val, operand.type), ir_none());
}
return ir_reg(old_val, operand.type);
}
case NODE_TERNARY: {
IRValue cond_val = lower_expr(b, expr->ternary.cond);
int lbl_then = ir_new_label(b->cur_func);
int lbl_else = ir_new_label(b->cur_func);
int lbl_end = ir_new_label(b->cur_func);
int result = ir_new_reg(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_BRANCH, ir_label(lbl_then), cond_val, ir_none());
ir_emit(b->arena, b->cur_block, IR_JUMP, ir_label(lbl_else), ir_none(), ir_none());
/* then */
ir_emit(b->arena, b->cur_block, IR_LABEL, ir_label(lbl_then), ir_none(), ir_none());
IRValue then_val = lower_expr(b, expr->ternary.then_expr);
ir_emit(b->arena, b->cur_block, IR_MOVE,
ir_reg(result, then_val.type), then_val, ir_none());
ir_emit(b->arena, b->cur_block, IR_JUMP, ir_label(lbl_end), ir_none(), ir_none());
/* else */
ir_emit(b->arena, b->cur_block, IR_LABEL, ir_label(lbl_else), ir_none(), ir_none());
IRValue else_val = lower_expr(b, expr->ternary.else_expr);
ir_emit(b->arena, b->cur_block, IR_MOVE,
ir_reg(result, else_val.type), else_val, ir_none());
ir_emit(b->arena, b->cur_block, IR_LABEL, ir_label(lbl_end), ir_none(), ir_none());
return ir_reg(result, then_val.type);
}
case NODE_ADDR_OF: {
if (expr->unary_expr.operand->type == NODE_IDENT) {
int r = ir_new_reg(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_LOAD_ADDR,
ir_reg(r, TYPE_POINTER),
ir_name(expr->unary_expr.operand->ident.name), ir_none());
return ir_reg(r, TYPE_POINTER);
}
return lower_expr(b, expr->unary_expr.operand);
}
case NODE_DEREF: {
IRValue ptr = lower_expr(b, expr->unary_expr.operand);
int r = ir_new_reg(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_LOAD,
ir_reg(r, TYPE_INT), ptr, ir_none());
return ir_reg(r, TYPE_INT);
}
default:
return ir_int(0);
}
}
static void lower_stmt(IRBuilder *b, ASTNode *stmt) {
if (!stmt) return;
switch (stmt->type) {
case NODE_BLOCK:
for (size_t i = 0; i < stmt->block.stmts.len; i++)
lower_stmt(b, stmt->block.stmts.data[i]);
break;
case NODE_EXPR_STMT:
lower_expr(b, stmt->expr_stmt.expr);
break;
case NODE_VAR_DECL: {
int r = lookup_var(b, stmt->var.name);
if (stmt->var.init) {
IRValue val = lower_expr(b, stmt->var.init);
ir_emit(b->arena, b->cur_block, IR_MOVE,
ir_reg(r, val.type), val, ir_none());
}
break;
}
case NODE_IF: {
IRValue cond = lower_expr(b, stmt->if_stmt.cond);
int lbl_then = ir_new_label(b->cur_func);
int lbl_else = ir_new_label(b->cur_func);
int lbl_end = ir_new_label(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_BRANCH,
ir_label(lbl_then), cond, ir_none());
ir_emit(b->arena, b->cur_block, IR_JUMP,
ir_label(stmt->if_stmt.else_body ? lbl_else : lbl_end),
ir_none(), ir_none());
ir_emit(b->arena, b->cur_block, IR_LABEL,
ir_label(lbl_then), ir_none(), ir_none());
lower_stmt(b, stmt->if_stmt.then_body);
ir_emit(b->arena, b->cur_block, IR_JUMP,
ir_label(lbl_end), ir_none(), ir_none());
if (stmt->if_stmt.else_body) {
ir_emit(b->arena, b->cur_block, IR_LABEL,
ir_label(lbl_else), ir_none(), ir_none());
lower_stmt(b, stmt->if_stmt.else_body);
}
ir_emit(b->arena, b->cur_block, IR_LABEL,
ir_label(lbl_end), ir_none(), ir_none());
break;
}
case NODE_WHILE: {
int lbl_cond = ir_new_label(b->cur_func);
int lbl_body = ir_new_label(b->cur_func);
int lbl_end = ir_new_label(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_LABEL,
ir_label(lbl_cond), ir_none(), ir_none());
IRValue cond = lower_expr(b, stmt->while_stmt.cond);
ir_emit(b->arena, b->cur_block, IR_BRANCH_FALSE,
ir_label(lbl_end), cond, ir_none());
lower_stmt(b, stmt->while_stmt.body);
ir_emit(b->arena, b->cur_block, IR_JUMP,
ir_label(lbl_cond), ir_none(), ir_none());
ir_emit(b->arena, b->cur_block, IR_LABEL,
ir_label(lbl_end), ir_none(), ir_none());
break;
}
case NODE_FOR: {
int lbl_cond = ir_new_label(b->cur_func);
int lbl_body = ir_new_label(b->cur_func);
int lbl_end = ir_new_label(b->cur_func);
if (stmt->for_stmt.init)
lower_stmt(b, stmt->for_stmt.init);
ir_emit(b->arena, b->cur_block, IR_LABEL,
ir_label(lbl_cond), ir_none(), ir_none());
if (stmt->for_stmt.cond) {
IRValue cond = lower_expr(b, stmt->for_stmt.cond);
ir_emit(b->arena, b->cur_block, IR_BRANCH_FALSE,
ir_label(lbl_end), cond, ir_none());
}
lower_stmt(b, stmt->for_stmt.body);
if (stmt->for_stmt.step)
lower_expr(b, stmt->for_stmt.step);
ir_emit(b->arena, b->cur_block, IR_JUMP,
ir_label(lbl_cond), ir_none(), ir_none());
ir_emit(b->arena, b->cur_block, IR_LABEL,
ir_label(lbl_end), ir_none(), ir_none());
break;
}
case NODE_RETURN: {
IRValue val = ir_none();
if (stmt->ret.value) val = lower_expr(b, stmt->ret.value);
ir_emit(b->arena, b->cur_block, IR_RET, ir_none(), val, ir_none());
break;
}
/* ── Parallel constructs → IR parallel markers ──── */
case NODE_PARALLEL_FOR: {
ir_emit_comment(b->arena, b->cur_block, "=== PARALLEL_FOR begin ===");
int iter_reg = lookup_var(b, stmt->par_for.iter_var);
IRValue lo = lower_expr(b, stmt->par_for.lo);
IRValue hi = lower_expr(b, stmt->par_for.hi);
IRInst *begin = ir_emit(b->arena, b->cur_block, IR_PAR_FOR_BEGIN,
ir_reg(iter_reg, TYPE_INT), lo, hi);
begin->par.iter_var = stmt->par_for.iter_var;
begin->par.lo_reg = lo.reg;
begin->par.hi_reg = hi.reg;
/* Initialize iterator */
ir_emit(b->arena, b->cur_block, IR_MOVE,
ir_reg(iter_reg, TYPE_INT), lo, ir_none());
int lbl_cond = ir_new_label(b->cur_func);
int lbl_end = ir_new_label(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_LABEL,
ir_label(lbl_cond), ir_none(), ir_none());
/* cond: iter < hi */
int cmp_reg = ir_new_reg(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_LT,
ir_reg(cmp_reg, TYPE_INT), ir_reg(iter_reg, TYPE_INT), hi);
ir_emit(b->arena, b->cur_block, IR_BRANCH_FALSE,
ir_label(lbl_end), ir_reg(cmp_reg, TYPE_INT), ir_none());
/* body */
lower_stmt(b, stmt->par_for.body);
/* step */
int next = ir_new_reg(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_ADD,
ir_reg(next, TYPE_INT), ir_reg(iter_reg, TYPE_INT), ir_int(1));
ir_emit(b->arena, b->cur_block, IR_MOVE,
ir_reg(iter_reg, TYPE_INT), ir_reg(next, TYPE_INT), ir_none());
ir_emit(b->arena, b->cur_block, IR_JUMP,
ir_label(lbl_cond), ir_none(), ir_none());
ir_emit(b->arena, b->cur_block, IR_LABEL,
ir_label(lbl_end), ir_none(), ir_none());
ir_emit(b->arena, b->cur_block, IR_PAR_FOR_END,
ir_none(), ir_none(), ir_none());
ir_emit_comment(b->arena, b->cur_block, "=== PARALLEL_FOR end ===");
break;
}
case NODE_PARALLEL_REDUCE: {
ir_emit_comment(b->arena, b->cur_block, "=== PARALLEL_REDUCE begin ===");
int accum_reg = lookup_var(b, stmt->par_reduce.accum_var);
int iter_reg = lookup_var(b, stmt->par_reduce.iter_var);
IRValue lo = lower_expr(b, stmt->par_reduce.lo);
IRValue hi = lower_expr(b, stmt->par_reduce.hi);
IRInst *begin = ir_emit(b->arena, b->cur_block, IR_PAR_REDUCE_BEGIN,
ir_reg(accum_reg, TYPE_FLOAT), lo, hi);
begin->par.iter_var = stmt->par_reduce.iter_var;
begin->par.reduce_op = stmt->par_reduce.op;
begin->par.accum_reg = accum_reg;
/* Loop: for(iter = lo; iter < hi; iter++) */
ir_emit(b->arena, b->cur_block, IR_MOVE,
ir_reg(iter_reg, TYPE_INT), lo, ir_none());
int lbl_cond = ir_new_label(b->cur_func);
int lbl_end = ir_new_label(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_LABEL,
ir_label(lbl_cond), ir_none(), ir_none());
int cmp_reg = ir_new_reg(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_LT,
ir_reg(cmp_reg, TYPE_INT), ir_reg(iter_reg, TYPE_INT), hi);
ir_emit(b->arena, b->cur_block, IR_BRANCH_FALSE,
ir_label(lbl_end), ir_reg(cmp_reg, TYPE_INT), ir_none());
lower_stmt(b, stmt->par_reduce.body);
int next = ir_new_reg(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_ADD,
ir_reg(next, TYPE_INT), ir_reg(iter_reg, TYPE_INT), ir_int(1));
ir_emit(b->arena, b->cur_block, IR_MOVE,
ir_reg(iter_reg, TYPE_INT), ir_reg(next, TYPE_INT), ir_none());
ir_emit(b->arena, b->cur_block, IR_JUMP,
ir_label(lbl_cond), ir_none(), ir_none());
ir_emit(b->arena, b->cur_block, IR_LABEL,
ir_label(lbl_end), ir_none(), ir_none());
ir_emit(b->arena, b->cur_block, IR_PAR_REDUCE_END,
ir_reg(accum_reg, TYPE_FLOAT), ir_none(), ir_none());
ir_emit_comment(b->arena, b->cur_block, "=== PARALLEL_REDUCE end ===");
break;
}
case NODE_PARALLEL_SCAN: {
ir_emit_comment(b->arena, b->cur_block, "=== PARALLEL_SCAN begin ===");
/* Lower as sequential scan with markers */
int iter_reg = lookup_var(b, stmt->par_scan.iter_var);
IRValue lo = lower_expr(b, stmt->par_scan.lo);
IRValue hi = lower_expr(b, stmt->par_scan.hi);
ir_emit(b->arena, b->cur_block, IR_PAR_SCAN_BEGIN,
ir_none(), lo, hi);
ir_emit(b->arena, b->cur_block, IR_MOVE,
ir_reg(iter_reg, TYPE_INT), lo, ir_none());
int lbl_cond = ir_new_label(b->cur_func);
int lbl_end = ir_new_label(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_LABEL,
ir_label(lbl_cond), ir_none(), ir_none());
int cmp = ir_new_reg(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_LT,
ir_reg(cmp, TYPE_INT), ir_reg(iter_reg, TYPE_INT), hi);
ir_emit(b->arena, b->cur_block, IR_BRANCH_FALSE,
ir_label(lbl_end), ir_reg(cmp, TYPE_INT), ir_none());
lower_stmt(b, stmt->par_scan.body);
int next = ir_new_reg(b->cur_func);
ir_emit(b->arena, b->cur_block, IR_ADD,
ir_reg(next, TYPE_INT), ir_reg(iter_reg, TYPE_INT), ir_int(1));
ir_emit(b->arena, b->cur_block, IR_MOVE,
ir_reg(iter_reg, TYPE_INT), ir_reg(next, TYPE_INT), ir_none());
ir_emit(b->arena, b->cur_block, IR_JUMP,
ir_label(lbl_cond), ir_none(), ir_none());
ir_emit(b->arena, b->cur_block, IR_LABEL,
ir_label(lbl_end), ir_none(), ir_none());
ir_emit(b->arena, b->cur_block, IR_PAR_SCAN_END,
ir_none(), ir_none(), ir_none());
ir_emit_comment(b->arena, b->cur_block, "=== PARALLEL_SCAN end ===");
break;
}
case NODE_BARRIER:
ir_emit(b->arena, b->cur_block, IR_BARRIER, ir_none(), ir_none(), ir_none());
break;
case NODE_TILE_HINT:
case NODE_SIMD_HINT:
case NODE_MEMORY_LAYOUT:
/* These are hints consumed by optimization passes, stored as comments */
ir_emit_comment(b->arena, b->cur_block,
node_type_str(stmt->type));
break;
case NODE_PREPROC:
/* Pass through */
break;
default:
/* Try as expression */
lower_expr(b, stmt);
break;
}
}
/* ── Top-level lowering ──────────────────────────────────── */
IRModule *ir_build_from_ast(Arena *arena, ASTNode *program) {
assert(program->type == NODE_PROGRAM);
IRModule *mod = ir_module_create(arena);
for (size_t i = 0; i < program->program.decls.len; i++) {
ASTNode *decl = program->program.decls.data[i];
if (decl->type == NODE_PREPROC) {
vec_push(&mod->preproc_lines, decl);
continue;
}
if (decl->type == NODE_VAR_DECL) {
vec_push(&mod->global_vars, decl);
continue;
}
if (decl->type == NODE_FUNC_DECL) {
IRFunc *func = ir_func_create(mod, decl->func.name);
func->param_count = (int)decl->func.params.len;
IRBlock *entry = ir_block_create(func, "entry");
IRBuilder builder = {0};
builder.arena = arena;
builder.mod = mod;
builder.cur_func = func;
builder.cur_block = entry;
builder.var_regs = hashmap_create();
/* Map parameters to registers */
for (size_t j = 0; j < decl->func.params.len; j++) {
ASTNode *param = decl->func.params.data[j];
if (param->param.name) {
int r = ir_new_reg(func);
hashmap_put(builder.var_regs, param->param.name,
(void *)(intptr_t)(r + 1));
}
}
if (decl->func.body)
lower_stmt(&builder, decl->func.body);
hashmap_destroy(builder.var_regs);
}
}
return mod;
}
/* ══════════════════════════════════════════════════════════
* IR Printing
* ══════════════════════════════════════════════════════════ */
static const char *op_names[] = {
[IR_ADD]="add", [IR_SUB]="sub", [IR_MUL]="mul", [IR_DIV]="div", [IR_MOD]="mod",
[IR_NEG]="neg",
[IR_FADD]="fadd", [IR_FSUB]="fsub", [IR_FMUL]="fmul", [IR_FDIV]="fdiv",
[IR_AND]="and", [IR_OR]="or", [IR_XOR]="xor", [IR_NOT]="not",
[IR_SHL]="shl", [IR_SHR]="shr",
[IR_EQ]="eq", [IR_NE]="ne", [IR_LT]="lt", [IR_GT]="gt", [IR_LE]="le", [IR_GE]="ge",
[IR_FEQ]="feq", [IR_FNE]="fne", [IR_FLT]="flt", [IR_FGT]="fgt", [IR_FLE]="fle", [IR_FGE]="fge",
[IR_LOAD]="load", [IR_STORE]="store", [IR_MOVE]="move",
[IR_LOAD_IMM]="load_imm", [IR_LOAD_FIMM]="load_fimm",
[IR_LOAD_ADDR]="load_addr", [IR_INDEX]="index",
[IR_LABEL]="label", [IR_JUMP]="jump", [IR_BRANCH]="branch",
[IR_BRANCH_FALSE]="branch_false",
[IR_CALL]="call", [IR_RET]="ret",
[IR_INT_TO_FLOAT]="i2f", [IR_FLOAT_TO_INT]="f2i",
[IR_WIDEN]="widen", [IR_NARROW]="narrow",
[IR_PAR_FOR_BEGIN]="par_for_begin", [IR_PAR_FOR_END]="par_for_end",
[IR_PAR_REDUCE_BEGIN]="par_reduce_begin", [IR_PAR_REDUCE_END]="par_reduce_end",
[IR_PAR_SCAN_BEGIN]="par_scan_begin", [IR_PAR_SCAN_END]="par_scan_end",
[IR_BARRIER]="barrier", [IR_ATOMIC_ADD]="atomic_add",
[IR_SIMD_LOAD]="simd_load", [IR_SIMD_STORE]="simd_store",
[IR_SIMD_BROADCAST]="simd_bcast", [IR_SIMD_ADD]="simd_add",
[IR_SIMD_SUB]="simd_sub", [IR_SIMD_MUL]="simd_mul",
[IR_SIMD_FMA]="simd_fma", [IR_SIMD_HADD]="simd_hadd",
[IR_SIMD_MIN]="simd_min", [IR_SIMD_MAX]="simd_max",
[IR_PREFETCH]="prefetch", [IR_ALLOCA]="alloca",
[IR_NOP]="nop", [IR_PHI]="phi", [IR_COMMENT]="comment",
};
const char *ir_op_name(IROp op) {
if (op >= 0 && op < IR_OP_COUNT && op_names[op])
return op_names[op];
return "unknown";
}
void ir_print_value(IRValue v) {
switch (v.kind) {
case VAL_REG:
if (v.simd_width > 0) printf("v%d<%d>", v.reg, v.simd_width);
else printf("%%t%d", v.reg);
break;
case VAL_INT: printf("%ld", (long)v.int_val); break;
case VAL_FLOAT: printf("%g", v.float_val); break;
case VAL_LABEL: printf("L%d", v.label_id); break;
case VAL_NAME: printf("@%s", v.name); break;
case VAL_NONE: break;
}
}
void ir_print_module(IRModule *mod) {
printf("=== IR Module (%d functions) ===\n\n", mod->func_count);
for (IRFunc *f = mod->functions; f; f = f->next) {
printf("function @%s (%d params, %d regs):\n", f->name, f->param_count, f->reg_count);
for (size_t i = 0; i < f->blocks.len; i++) {
IRBlock *bb = f->blocks.data[i];
printf(" %s:\n", bb->name);
for (size_t j = 0; j < bb->insts.len; j++) {
IRInst *inst = bb->insts.data[j];
if (inst->op == IR_COMMENT) {
printf(" ; %s\n", inst->comment);
continue;
}
if (inst->op == IR_LABEL) {
printf(" L%d:\n", inst->dst.label_id);
continue;
}
printf(" %-18s ", ir_op_name(inst->op));
if (inst->dst.kind != VAL_NONE) { ir_print_value(inst->dst); printf(" "); }
if (inst->src1.kind != VAL_NONE) { printf(", "); ir_print_value(inst->src1); }
if (inst->src2.kind != VAL_NONE) { printf(", "); ir_print_value(inst->src2); }
if (inst->src3.kind != VAL_NONE) { printf(", "); ir_print_value(inst->src3); }
printf("\n");
}
}
printf("\n");
}
}