| |
| |
| |
| #include "ir.h" |
|
|
| |
|
|
| 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; |
| } |
|
|
| |
|
|
| 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; |
| } |
|
|
| |
|
|
| 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; |
| } |
|
|
| |
| |
| |
|
|
| typedef struct { |
| Arena *arena; |
| IRModule *mod; |
| IRFunc *cur_func; |
| IRBlock *cur_block; |
| HashMap *var_regs; |
| } 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; |
| |
| int reg = ir_new_reg(b->cur_func); |
| hashmap_put(b->var_regs, name, (void *)(intptr_t)(reg + 1)); |
| 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) { |
| |
| 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: { |
| |
| IRValue func_val = ir_none(); |
| if (expr->call.func->type == NODE_IDENT) |
| func_val = ir_name(expr->call.func->ident.name); |
| |
| 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()); |
|
|
| |
| 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()); |
|
|
| |
| 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; |
| } |
|
|
| |
| 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; |
|
|
| |
| 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_for.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_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; |
|
|
| |
| 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 ==="); |
| |
| 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: |
| |
| ir_emit_comment(b->arena, b->cur_block, |
| node_type_str(stmt->type)); |
| break; |
|
|
| case NODE_PREPROC: |
| |
| break; |
|
|
| default: |
| |
| lower_expr(b, stmt); |
| break; |
| } |
| } |
|
|
| |
|
|
| 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(); |
|
|
| |
| 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; |
| } |
|
|
| |
| |
| |
|
|
| 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"); |
| } |
| } |
|
|