diff --git a/ir.h b/ir.h index 7e6bf1c..5eb5690 100644 --- a/ir.h +++ b/ir.h @@ -486,21 +486,32 @@ typedef enum _ir_fold_action { IR_FOLD_DO_CONST } ir_fold_action; +typedef ir_ref ir_live_pos; typedef struct _ir_use_pos ir_use_pos; typedef struct _ir_live_range ir_live_range; typedef struct _ir_live_interval ir_live_interval; +#define IR_LIVE_POS_TO_REF(pos) ((pos) / 4) +#define IR_LIVE_POS_TO_SUB_REF(pos) ((pos) % 4) + +#define IR_LIVE_POS_FROM_REF(ref) ((ref) * 4) + +#define IR_START_LIVE_POS_FROM_REF(ref) ((ref) * 4) +#define IR_GAP_LIVE_POS_FROM_REF(ref) ((ref) * 4 + 1) +#define IR_USE_LIVE_POS_FROM_REF(ref) ((ref) * 4 + 2) +#define IR_DEF_LIVE_POS_FROM_REF(ref) ((ref) * 4 + 3) +#define IR_END_LIVE_POS_FROM_REF(ref) ((ref) * 4 + 3) + struct _ir_use_pos { - uint16_t op_num; + uint16_t op_num; /* 0 - means result */ int8_t hint; - int8_t reg; - ir_ref pos; + ir_live_pos pos; ir_use_pos *next; }; struct _ir_live_range { - ir_ref start; - ir_ref end; + ir_live_pos start; /* inclusive */ + ir_live_pos end; /* exclusive */ ir_live_range *next; }; @@ -510,6 +521,8 @@ struct _ir_live_interval { int32_t stack_spill_pos; ir_live_range range; ir_use_pos *use_pos; + ir_live_interval *top; + ir_live_interval *next; }; typedef struct _ir_ctx { diff --git a/ir_dump.c b/ir_dump.c index 9b11c3a..dd22c31 100644 --- a/ir_dump.c +++ b/ir_dump.c @@ -263,7 +263,7 @@ void ir_dump_live_ranges(ir_ctx *ctx, FILE *f) ir_live_interval *ival = ctx->live_intervals[i]; if (ival) { - ir_live_range *p = &ival->range;; + ir_live_range *p; ir_use_pos *use_pos; for (j = 1; j < ctx->insns_count; j++) { @@ -278,34 +278,71 @@ void ir_dump_live_ranges(ir_ctx *ctx, FILE *f) } } fprintf(f, ")"); - if (ival->reg >= 0) { - fprintf(f, " [%%%s]", ir_reg_name(ival->reg, ival->type)); + if (ival->next) { + fprintf(f, "\n\t"); + } else if (ival->reg >= 0) { + fprintf(f, " "); } - fprintf(f, ": [%d-%d%c", p->start / 2, p->end / 2, p->end % 2 == 0 ? '[' : ']'); + do { + if (ival->reg >= 0) { + fprintf(f, "[%%%s]", ir_reg_name(ival->reg, ival->type)); + } + p = &ival->range; + fprintf(f, ": [%d.%d-%d.%d)", + IR_LIVE_POS_TO_REF(p->start), IR_LIVE_POS_TO_SUB_REF(p->start), + IR_LIVE_POS_TO_REF(p->end), IR_LIVE_POS_TO_SUB_REF(p->end)); + p = p->next; + while (p) { + fprintf(f, ", [%d.%d-%d.%d)", + IR_LIVE_POS_TO_REF(p->start), IR_LIVE_POS_TO_SUB_REF(p->start), + IR_LIVE_POS_TO_REF(p->end), IR_LIVE_POS_TO_SUB_REF(p->end)); + + p = p->next; + } + use_pos = ival->use_pos; + while (use_pos) { + if (!use_pos->op_num) { + fprintf(f, ", DEF(%d.%d", + IR_LIVE_POS_TO_REF(use_pos->pos), IR_LIVE_POS_TO_SUB_REF(use_pos->pos)); + } else { + fprintf(f, ", USE(%d.%d/%d", + IR_LIVE_POS_TO_REF(use_pos->pos), IR_LIVE_POS_TO_SUB_REF(use_pos->pos), + use_pos->op_num); + } + if (use_pos->hint >= 0) { + fprintf(f, ", hint=%%%s", ir_reg_name(use_pos->hint, ival->type)); + } + fprintf(f, ")"); + use_pos = use_pos->next; + } + if (ival->next) { + fprintf(f, "\n\t"); + } + ival = ival->next; + } while (ival); + fprintf(f, "\n"); + } + } +#if 1 + for (i = ctx->vregs_count + 1; i < ctx->vregs_count + 1 +32; i++) { + ir_live_interval *ival = ctx->live_intervals[i]; + + if (ival) { + ir_live_range *p = &ival->range; + fprintf(f, "[%%%s] : [%d.%d-%d.%d)", + ir_reg_name(ival->reg, IR_ADDR), + IR_LIVE_POS_TO_REF(p->start), IR_LIVE_POS_TO_SUB_REF(p->start), + IR_LIVE_POS_TO_REF(p->end), IR_LIVE_POS_TO_SUB_REF(p->end)); p = p->next; while (p) { - fprintf(f, ", [%d-%d%c", p->start / 2, p->end / 2, p->end % 2 == 0 ? '[' : ']'); - + fprintf(f, ", [%d.%d-%d.%d)", + IR_LIVE_POS_TO_REF(p->start), IR_LIVE_POS_TO_SUB_REF(p->start), + IR_LIVE_POS_TO_REF(p->end), IR_LIVE_POS_TO_SUB_REF(p->end)); p = p->next; } - use_pos = ival->use_pos; - while (use_pos) { - if (!use_pos->op_num) { - fprintf(f, ", DEF(%d", use_pos->pos / 2); - } else { - fprintf(f, ", USE(%d/%d", use_pos->pos / 2, use_pos->op_num); - } - if (use_pos->hint >= 0) { - fprintf(f, ", hint=%%%s", ir_reg_name(use_pos->hint, ival->type)); - } - fprintf(f, ")"); - if (use_pos->reg >= 0) { - fprintf(f, " [%%%s]", ir_reg_name(use_pos->reg, ival->type)); - } - use_pos = use_pos->next; - } fprintf(f, "\n"); } } +#endif fprintf(f, "}\n"); } diff --git a/ir_ra.c b/ir_ra.c index ea5f075..95ca2b5 100644 --- a/ir_ra.c +++ b/ir_ra.c @@ -31,7 +31,7 @@ int ir_assign_virtual_registers(ir_ctx *ctx) if ((flags & IR_OP_FLAG_DATA) || ((flags & IR_OP_FLAG_MEM) && insn->type != IR_VOID)) { if ((insn->op == IR_PARAM || insn->op == IR_VAR) && ctx->use_lists[i].count == 0) { /* pass */ - } else if (!ctx->rules || ir_needs_def_reg(ctx, i)) { + } else if (!ctx->rules || ir_needs_vreg(ctx, i)) { vregs[i] = ++vregs_count; } } @@ -70,7 +70,7 @@ int ir_assign_virtual_registers(ir_ctx *ctx) * See "Linear Scan Register Allocation on SSA Form", Christian Wimmer and * Michael Franz, CGO'10 (2010), Figure 4. */ -static void ir_add_live_range(ir_ctx *ctx, int v, uint8_t type, ir_ref start, ir_ref end) +static void ir_add_live_range(ir_ctx *ctx, int v, uint8_t type, ir_live_pos start, ir_live_pos end) { ir_live_interval *ival = ctx->live_intervals[v]; ir_live_range *p, *q, *next, *prev; @@ -79,12 +79,16 @@ static void ir_add_live_range(ir_ctx *ctx, int v, uint8_t type, ir_ref start, ir ival = ir_mem_malloc(sizeof(ir_live_interval)); IR_ASSERT(type != IR_VOID); ival->type = type; - ival->reg = -1; // IR_REG_NONE + ival->reg = IR_REG_NONE; ival->stack_spill_pos = 0; // not allocated ival->range.start = start; ival->range.end = end; ival->range.next = NULL; ival->use_pos = NULL; + + ival->top = ival; + ival->next = NULL; + ctx->live_intervals[v] = ival; return; } @@ -92,8 +96,8 @@ static void ir_add_live_range(ir_ctx *ctx, int v, uint8_t type, ir_ref start, ir IR_ASSERT(type == IR_VOID || type == ival->type); p = &ival->range; prev = NULL; - while (p && end >= p->start - 1) { - if (start < p->end + 1) { + while (p && end + 1 >= p->start) { + if (p->end + 1 >= start) { if (start < p->start) { p->start = start; } @@ -101,7 +105,7 @@ static void ir_add_live_range(ir_ctx *ctx, int v, uint8_t type, ir_ref start, ir p->end = end; /* merge with next */ next = p->next; - while (next && p->end >= next->start - 1) { + while (next && p->end + 1 >= next->start) { if (next->end > p->end) { p->end = next->end; } @@ -138,12 +142,10 @@ static void ir_add_live_range(ir_ctx *ctx, int v, uint8_t type, ir_ref start, ir q->next = p; } -static void ir_add_fixed_live_range(ir_ctx *ctx, ir_reg reg, ir_ref start, ir_ref end) +static void ir_add_fixed_live_range(ir_ctx *ctx, ir_reg reg, ir_live_pos start, ir_live_pos end) { int v = ctx->vregs_count + 1 + reg; ir_live_interval *ival = ctx->live_intervals[v]; - ir_live_range *p, *q, *prev; - if (!ival) { ival = ir_mem_malloc(sizeof(ir_live_interval)); ival->type = IR_VOID; @@ -153,39 +155,17 @@ static void ir_add_fixed_live_range(ir_ctx *ctx, ir_reg reg, ir_ref start, ir_re ival->range.end = end; ival->range.next = NULL; ival->use_pos = NULL; + + ival->top = ival; + ival->next = NULL; + ctx->live_intervals[v] = ival; return; } - - IR_ASSERT(ival->type == IR_VOID); - p = &ival->range; - prev = NULL; - while (p && end >= p->start) { - prev = p; - p = prev->next; - } - if (ctx->unused_live_ranges) { - /* reuse */ - q = ctx->unused_live_ranges; - ctx->unused_live_ranges = q->next; - } else { - q = ir_mem_malloc(sizeof(ir_live_range)); - } - if (prev) { - prev->next = q; - } else { - q->start = ival->range.start; - q->end = ival->range.end; - q->next = ival->range.next; - p = q; - q = &ival->range; - } - q->start = start; - q->end = end; - q->next = p; + ir_add_live_range(ctx, v, IR_VOID, start, end); } -static void ir_fix_live_range(ir_ctx *ctx, int v, ir_ref old_start, ir_ref new_start) +static void ir_fix_live_range(ir_ctx *ctx, int v, ir_live_pos old_start, ir_live_pos new_start) { ir_live_range *p = &ctx->live_intervals[v]->range; @@ -217,14 +197,13 @@ static void ir_add_use_pos(ir_ctx *ctx, int v, ir_use_pos *use_pos) } } -static void ir_add_use(ir_ctx *ctx, int v, int op_num, ir_ref pos, ir_reg hint) +static void ir_add_use(ir_ctx *ctx, int v, int op_num, ir_live_pos pos, ir_reg hint) { ir_use_pos *use_pos; use_pos = ir_mem_malloc(sizeof(ir_use_pos)); use_pos->op_num = op_num; use_pos->hint = hint; - use_pos->reg = IR_REG_NONE; use_pos->pos = pos; ir_add_use_pos(ctx, v, use_pos); @@ -284,7 +263,9 @@ int ir_compute_live_ranges(ir_ctx *ctx) ir_bitset_incl(live, ctx->vregs[insn->ops[k]]); // TODO: ir_add_live_range() is used just to set ival->type /* intervals[phi.inputOf(b)].addRange(b.from, b.to) */ - ir_add_live_range(ctx, ctx->vregs[insn->ops[k]], insn->type, bb->start * 2, bb->end * 2 + 1); + ir_add_live_range(ctx, ctx->vregs[insn->ops[k]], insn->type, + IR_START_LIVE_POS_FROM_REF(bb->start), + IR_END_LIVE_POS_FROM_REF(bb->end)); } } } @@ -294,7 +275,9 @@ int ir_compute_live_ranges(ir_ctx *ctx) /* for each opd in live */ IR_BITSET_FOREACH(live, len, i) { /* intervals[opd].addRange(b.from, b.to) */ - ir_add_live_range(ctx, i, IR_VOID, bb->start * 2, bb->end * 2 + 1); + ir_add_live_range(ctx, i, IR_VOID, + IR_START_LIVE_POS_FROM_REF(bb->start), + IR_END_LIVE_POS_FROM_REF(bb->end)); } IR_BITSET_FOREACH_END(); /* for each operation op of b in reverse order */ @@ -304,64 +287,74 @@ int ir_compute_live_ranges(ir_ctx *ctx) if ((flags & IR_OP_FLAG_DATA) || ((flags & IR_OP_FLAG_MEM) && insn->type != IR_VOID)) { if (ctx->vregs[i] && ir_bitset_in(live, ctx->vregs[i])) { if (insn->op != IR_PHI) { - /* intervals[opd].setFrom(op.id) */ - ir_fix_live_range(ctx, ctx->vregs[i], bb->start * 2, i * 2); - } - reg = ctx->rules ? ir_uses_fixed_reg(ctx, i, 0) : IR_REG_NONE; - if (reg != IR_REG_NONE) { - if (insn->op == IR_PARAM) { - /* parameter register must be kept before it's copied */ - ir_add_fixed_live_range(ctx, reg, 2, i * 2); + ir_live_pos def_pos; + + reg = ctx->rules ? ir_uses_fixed_reg(ctx, i, 0) : IR_REG_NONE; + if (reg != IR_REG_NONE) { + def_pos = IR_GAP_LIVE_POS_FROM_REF(i); + if (insn->op == IR_PARAM) { + /* parameter register must be kept before it's copied */ + ir_add_fixed_live_range(ctx, reg, + IR_START_LIVE_POS_FROM_REF(bb->start), def_pos); + } else { + ir_add_fixed_live_range(ctx, reg, + IR_START_LIVE_POS_FROM_REF(i), def_pos); + } + } else if (ctx->rules && ir_result_reuses_op1_reg(ctx, i)) { + def_pos = IR_GAP_LIVE_POS_FROM_REF(i); } else { - ir_add_fixed_live_range(ctx, reg, i * 2, i * 2); + def_pos = IR_DEF_LIVE_POS_FROM_REF(i); } + /* intervals[opd].setFrom(op.id) */ + ir_fix_live_range(ctx, ctx->vregs[i], + IR_START_LIVE_POS_FROM_REF(bb->start), def_pos); + ir_add_use(ctx, ctx->vregs[i], 0, def_pos, reg); + } else { + ir_add_use(ctx, ctx->vregs[i], 0, IR_DEF_LIVE_POS_FROM_REF(i), reg); } - ir_add_use(ctx, ctx->vregs[i], 0, i * 2, reg); /* live.remove(opd) */ ir_bitset_excl(live, ctx->vregs[i]); } } if (insn->op != IR_PHI) { - int l = 0; - n = ir_input_edges_count(ctx, insn); for (j = 1; j <= n; j++) { if (IR_OPND_KIND(flags, j) == IR_OPND_DATA) { ir_ref input = insn->ops[j]; if (input > 0 && ctx->vregs[input]) { - /* intervals[opd].addRange(b.from, op.id) */ - ir_add_live_range(ctx, ctx->vregs[input], ctx->ir_base[input].type, bb->start * 2, i * 2 + l); - reg = ctx->rules ? ir_uses_fixed_reg(ctx, i, j) : IR_REG_NONE; - if (reg != IR_REG_NONE && !ir_result_reuses_op1(ctx, i)) { - ir_add_fixed_live_range(ctx, reg, i * 2 + 1, i * 2 + 1); + ir_live_pos use_pos; + + if (ctx->rules && j == 1 && ir_result_reuses_op1_reg(ctx, i)) { + use_pos = IR_START_LIVE_POS_FROM_REF(i); // TODO: ??? + } else { + reg = ctx->rules ? ir_uses_fixed_reg(ctx, i, j) : IR_REG_NONE; + if (reg != IR_REG_NONE) { + use_pos = IR_GAP_LIVE_POS_FROM_REF(i); + ir_add_fixed_live_range(ctx, reg, + IR_USE_LIVE_POS_FROM_REF(i), IR_DEF_LIVE_POS_FROM_REF(i)); + } else { + use_pos = IR_USE_LIVE_POS_FROM_REF(i); + } } - ir_add_use(ctx, ctx->vregs[input], j, i * 2 + l, reg); + /* intervals[opd].addRange(b.from, op.id) */ + ir_add_live_range(ctx, ctx->vregs[input], ctx->ir_base[input].type, + IR_START_LIVE_POS_FROM_REF(bb->start), use_pos); + ir_add_use(ctx, ctx->vregs[input], j, use_pos, reg); /* live.add(opd) */ ir_bitset_incl(live, ctx->vregs[input]); } - if (ctx->rules && j == 1 && ir_result_reuses_op1(ctx, i)) { - l = 1; /* TODO: op2 may overlap with result */ - } } } /* CPU specific constraints */ if (ctx->rules) { - ir_regset regset = IR_REGSET_EMPTY; + ir_regset regset = ir_get_scratch_regset(ctx, i); - if (insn->op == IR_CALL) { - regset = IR_REGSET_SCRATCH; - if (regset != IR_REGSET_EMPTY) { - IR_REGSET_FOREACH(regset, reg) { - ir_add_fixed_live_range(ctx, reg, i * 2, i * 2); - } IR_REGSET_FOREACH_END(); - } - } else if (ctx->rules[i] > IR_LAST_OP) { - regset = ir_get_fixed_regset(ctx, i); - if (regset != IR_REGSET_EMPTY) { - IR_REGSET_FOREACH(regset, reg) { - ir_add_fixed_live_range(ctx, reg, i * 2, i * 2 + 1); - } IR_REGSET_FOREACH_END(); - } + if (regset != IR_REGSET_EMPTY) { + IR_REGSET_FOREACH(regset, reg) { + ir_add_fixed_live_range(ctx, reg, + IR_GAP_LIVE_POS_FROM_REF(i), + IR_DEF_LIVE_POS_FROM_REF(i)); + } IR_REGSET_FOREACH_END(); } } } @@ -389,7 +382,9 @@ int ir_compute_live_ranges(ir_ctx *ctx) child_bb = &ctx->cfg_blocks[child]; IR_BITSET_FOREACH(live, len, i) { - ir_add_live_range(ctx, i, IR_VOID, child_bb->start * 2, child_bb->end * 2 + 1); + ir_add_live_range(ctx, i, IR_VOID, + IR_START_LIVE_POS_FROM_REF(child_bb->start), + IR_END_LIVE_POS_FROM_REF(child_bb->end)); } IR_BITSET_FOREACH_END(); child = child_bb->dom_child; @@ -452,7 +447,7 @@ void ir_free_live_intervals(ir_live_interval **live_intervals, int count) /* Live Ranges coalescing */ -static bool ir_vregs_overlap(ir_ctx *ctx, uint32_t r1, uint32_t r2) +static ir_live_pos ir_vregs_overlap(ir_ctx *ctx, uint32_t r1, uint32_t r2) { ir_live_range *lrg1 = &ctx->live_intervals[r1]->range; ir_live_range *lrg2 = &ctx->live_intervals[r2]->range; @@ -780,7 +775,7 @@ typedef struct _ir_lsra_data { uint32_t stack_frame_size; } ir_lsra_data; -static ir_ref ir_live_range_end(ir_live_interval *ival) +static ir_live_pos ir_live_range_end(ir_live_interval *ival) { ir_live_range *live_range = &ival->range; @@ -790,7 +785,7 @@ static ir_ref ir_live_range_end(ir_live_interval *ival) return live_range->end; } -static bool ir_live_range_covers(ir_live_interval *ival, ir_ref position) +static bool ir_live_range_covers(ir_live_interval *ival, ir_live_pos position) { ir_live_range *live_range = &ival->range; @@ -804,7 +799,68 @@ static bool ir_live_range_covers(ir_live_interval *ival, ir_ref position) return 0; } -static ir_ref ir_try_allocate_preferred_reg(ir_live_interval *ival, ir_ref *freeUntilPos) +static ir_live_interval *ir_split_interval_at(ir_live_interval *ival, ir_live_pos pos) +{ + ir_live_interval *child; + ir_live_range *p, *prev; + ir_use_pos *use_pos, *prev_use_pos; + + IR_ASSERT(pos > ival->range.start); + + p = &ival->range; + prev = NULL; + while (p && pos >= p->end) { + prev = p; + p = prev->next; + } + IR_ASSERT(p); + + use_pos = ival->use_pos; + prev_use_pos = NULL; + while (use_pos && pos > use_pos->pos) { + prev_use_pos = use_pos; + use_pos = use_pos->next; + } + + child = ir_mem_malloc(sizeof(ir_live_interval)); + child->type = ival->type; + child->reg = IR_REG_NONE; + child->stack_spill_pos = 0; // not allocated + child->range.start = pos; + child->range.end = p->end; + child->range.next = p->next; + child->use_pos = prev_use_pos ? prev_use_pos->next : use_pos; + + child->top = ival->top; + child->next = NULL; + ival->next = child; + + if (pos == p->start) { + prev->next = NULL; + } else { + p->end = pos; + p->next = NULL; + } + if (prev_use_pos) { + prev_use_pos->next = NULL; + } else { + ival->use_pos = NULL; + } + + return child; +} + +static void ir_allocate_spill_slot(ir_ctx *ctx, int current, ir_lsra_data *data) +{ + ir_live_interval *ival = ctx->live_intervals[current]->top; + + if (ival->stack_spill_pos == 0) { + data->stack_frame_size += 8; // ir_type_size[insn->type]; // TODO: alignment + ival->stack_spill_pos = data->stack_frame_size; + } +} + +static ir_reg ir_try_allocate_preferred_reg(ir_live_interval *ival, ir_live_pos *freeUntilPos) { ir_use_pos *use_pos; @@ -821,17 +877,34 @@ static ir_ref ir_try_allocate_preferred_reg(ir_live_interval *ival, ir_ref *free return IR_REG_NONE; } -static ir_reg ir_try_allocate_free_reg(ir_ctx *ctx, int current, uint32_t len, ir_bitset active, ir_bitset inactive) +static void ir_add_to_unhandled(ir_ctx *ctx, ir_list *unhandled, int current) { - ir_ref freeUntilPos[IR_REG_NUM]; + ir_live_pos pos = ctx->live_intervals[current]->range.start; + + if (ir_list_len(unhandled) == 0 || pos <= ctx->live_intervals[ir_list_peek(unhandled)]->range.start) { + ir_list_push(unhandled, current); + } else { + uint32_t i = ir_list_len(unhandled); + while (i > 0) { + i--; + if (pos <= ctx->live_intervals[ir_list_at(unhandled, i)]->range.start) { + break; + } + } + ir_list_insert(unhandled, i, current); + } +} + +static ir_reg ir_try_allocate_free_reg(ir_ctx *ctx, int current, uint32_t len, ir_bitset active, ir_bitset inactive, ir_list *unhandled) +{ + ir_live_pos freeUntilPos[IR_REG_NUM]; int i, reg; - ir_ref pos, next; + ir_live_pos pos, next; ir_live_interval *ival = ctx->live_intervals[current]; ir_regset available; if (IR_IS_TYPE_FP(ival->type)) { available = IR_REGSET_FP; - //available = IR_REGSET_INTERVAL(IR_REG_XMM2, IR_REG_XMM7); //TODO: spill-testing /* set freeUntilPos of all physical registers to maxInt */ for (i = IR_REG_FP_FIRST; i <= IR_REG_FP_LAST; i++) { freeUntilPos[i] = 0x7fffffff; @@ -881,6 +954,7 @@ static ir_reg ir_try_allocate_free_reg(ir_ctx *ctx, int current, uint32_t len, i return reg; } + /* reg = register with highest freeUntilPos */ reg = IR_REGSET_FIRST(available); IR_REGSET_EXCL(available, reg); pos = freeUntilPos[reg]; @@ -899,25 +973,142 @@ static ir_reg ir_try_allocate_free_reg(ir_ctx *ctx, int current, uint32_t len, i ival->reg = reg; return reg; } else { +#if 1 /* register available for the first part of the interval */ - // regs[current] = reg; - // split current before freeUntilPos[reg] + ival->reg = reg; + /* split current before freeUntilPos[reg] */ + ir_live_interval *child = ir_split_interval_at(ival, pos); + ctx->live_intervals[current] = child; + ir_add_to_unhandled(ctx, unhandled, current); + + //ir_allocate_spill_slot(ctx, current, ctx->data); + return reg; +#else return IR_REG_NONE; +#endif } } -static ir_reg ir_allocate_blocked_reg(ir_ctx *ctx) -{ - return IR_REG_NONE; -} - -static void ir_allocate_spill_slot(ir_ctx *ctx, int current, ir_lsra_data *data) +static ir_reg ir_allocate_blocked_reg(ir_ctx *ctx, int current, uint32_t len, ir_bitset active, ir_bitset inactive, ir_list *unhandled) { + ir_live_pos nextUsePos[IR_REG_NUM]; + int i, reg; + ir_live_pos pos, next_use_pos; ir_live_interval *ival = ctx->live_intervals[current]; + ir_use_pos *use_pos; + ir_regset available; - IR_ASSERT(ival->stack_spill_pos == 0); - data->stack_frame_size += 8; // ir_type_size[insn->type]; // TODO: alignment - ival->stack_spill_pos = data->stack_frame_size; + use_pos = ival->use_pos; + if (use_pos->pos > ival->range.start) { + next_use_pos = use_pos->pos; + } else { + while (use_pos && use_pos->pos <= ival->range.start) { + use_pos = use_pos->next; + } + if (!use_pos) { + /* spill */ + return IR_REG_NONE; + } + next_use_pos = use_pos->pos; + } + + if (IR_IS_TYPE_FP(ival->type)) { + available = IR_REGSET_FP; + /* set nextUsePos of all physical registers to maxInt */ + for (i = IR_REG_FP_FIRST; i <= IR_REG_FP_LAST; i++) { + nextUsePos[i] = 0x7fffffff; + } + } else { + available = IR_REGSET_GP; + if (ctx->flags & IR_USE_FRAME_POINTER) { + IR_REGSET_EXCL(available, IR_REG_FRAME_POINTER); + } + /* set nextUsePos of all physical registers to maxInt */ + for (i = IR_REG_GP_FIRST; i <= IR_REG_GP_LAST; i++) { + nextUsePos[i] = 0x7fffffff; + } + } + + /* for each interval it in active */ + IR_BITSET_FOREACH(active, len, i) { + /* nextUsePos[it.reg] = next use of it after start of current */ + reg = ctx->live_intervals[i]->reg; + IR_ASSERT(reg >= 0); + if (IR_REGSET_IN(available, reg)) { + use_pos = ctx->live_intervals[i]->use_pos; + while (use_pos && use_pos->pos < ival->range.start) { + use_pos = use_pos->next; + } + IR_ASSERT(use_pos); + nextUsePos[reg] = use_pos->pos; + } + } IR_BITSET_FOREACH_END(); + + /* for each interval it in inactive intersecting with current */ + IR_BITSET_FOREACH(inactive, len, i) { + /* freeUntilPos[it.reg] = next intersection of it with current */ + reg = ctx->live_intervals[i]->reg; + IR_ASSERT(reg >= 0); + if (IR_REGSET_IN(available, reg)) { + if (ir_vregs_overlap(ctx, current, i)) { + use_pos = ctx->live_intervals[i]->use_pos; + while (use_pos && use_pos->pos < ival->range.start) { + use_pos = use_pos->next; + } + IR_ASSERT(use_pos); + if (use_pos->pos < nextUsePos[reg]) { + nextUsePos[reg] = use_pos->pos; + } + } + } + } IR_BITSET_FOREACH_END(); + + // TODO: support for register hinting + // TODO: support for fixed register and intervals that cannot be spilled + + /* reg = register with highest nextUsePos */ + reg = IR_REGSET_FIRST(available); + IR_REGSET_EXCL(available, reg); + pos = nextUsePos[reg]; + IR_REGSET_FOREACH(available, i) { + if (nextUsePos[i] > pos) { + pos = nextUsePos[i]; + reg = i; + } + } IR_REGSET_FOREACH_END(); + + /* if first usage of current is after nextUsePos[reg] then */ + if (next_use_pos > pos) { + /* all other intervals are used before current, so it is best to spill current itself */ + /* assign spill slot to current */ + /* split current before its first use position that requires a register */ +// IR_ASSERT(0 && "spill between"); + ir_live_interval *child = ir_split_interval_at(ival, next_use_pos - 2); // TODO: split between + ctx->live_intervals[current] = child; + ir_add_to_unhandled(ctx, unhandled, current); + return IR_REG_NONE; + } else { + /* spill intervals that currently block reg */ + IR_BITSET_FOREACH(active, len, i) { + if (reg == ctx->live_intervals[i]->reg) { + /* current.reg = reg */ + ival->reg = reg; + /* split active interval for reg at position */ + ir_live_interval *child = ir_split_interval_at(ctx->live_intervals[i], ival->range.start); + ctx->live_intervals[i] = child; + ir_add_to_unhandled(ctx, unhandled, i); + ir_bitset_excl(active, i); + return reg; + } + } IR_BITSET_FOREACH_END(); + // TODO: split any inactive interval for reg at the end of its lifetime hole + } + + /* make sure that current does not intersect with the fixed interval for reg */ + // if current intersects with the fixed interval for reg then + // split current before this intersection + + return IR_REG_NONE; } static int ir_live_range_cmp(const void *r1, const void *r2, void *data) @@ -933,14 +1124,19 @@ static int ir_fix_dessa_tmps(ir_ctx *ctx, uint8_t type, int from, int to) { if (to == 0) { ir_block *bb = ctx->data; + ir_reg reg; if (IR_IS_TYPE_INT(type)) { - ir_add_fixed_live_range(ctx, IR_REG_R0, bb->end * 2, bb->end * 2); // TODO: Temporary register + reg = IR_REG_R0; // TODO: Temporary register } else if (IR_IS_TYPE_FP(type)) { - ir_add_fixed_live_range(ctx, IR_REG_XMM0, bb->end * 2, bb->end * 2); // TODO: Temporary register + reg = IR_REG_XMM0; // TODO: Temporary register } else { IR_ASSERT(0); + return 0; } + ir_add_fixed_live_range(ctx, reg, + IR_START_LIVE_POS_FROM_REF(bb->end), + IR_END_LIVE_POS_FROM_REF(bb->end)); } return 1; } @@ -949,12 +1145,12 @@ static int ir_linear_scan(ir_ctx *ctx) { int b; ir_block *bb; - ir_worklist unhandled; + ir_list unhandled; ir_bitset active, inactive; ir_live_interval *ival; int current, i; uint32_t len; - ir_ref position; + ir_live_pos position; ir_reg reg; ir_lsra_data data; @@ -972,14 +1168,14 @@ static int ir_linear_scan(ir_ctx *ctx) ctx->data = &data; data.stack_frame_size = 0; - ir_worklist_init(&unhandled, ctx->vregs_count + 1); + ir_list_init(&unhandled, ctx->vregs_count + 1); len = ir_bitset_len(ctx->vregs_count + 1 + IR_REG_NUM); active = ir_bitset_malloc(ctx->vregs_count + 1 + IR_REG_NUM); inactive = ir_bitset_malloc(ctx->vregs_count + 1 + IR_REG_NUM); for (i = 1; i <= ctx->vregs_count; i++) { if (ctx->live_intervals[i]) { - ir_worklist_push(&unhandled, i); + ir_list_push(&unhandled, i); } } @@ -989,10 +1185,10 @@ static int ir_linear_scan(ir_ctx *ctx) } } - qsort_r(unhandled.l.a.refs, ir_worklist_len(&unhandled), sizeof(ir_ref), ir_live_range_cmp, ctx); + qsort_r(unhandled.a.refs, ir_list_len(&unhandled), sizeof(ir_ref), ir_live_range_cmp, ctx); - while (ir_worklist_len(&unhandled) != 0) { - current = ir_worklist_pop(&unhandled); + while (ir_list_len(&unhandled) != 0) { + current = ir_list_pop(&unhandled); position = ctx->live_intervals[current]->range.start; /* for each interval i in active */ @@ -1021,12 +1217,16 @@ static int ir_linear_scan(ir_ctx *ctx) } } IR_BITSET_FOREACH_END(); - reg = ir_try_allocate_free_reg(ctx, current, len, active, inactive); + reg = ir_try_allocate_free_reg(ctx, current, len, active, inactive, &unhandled); if (reg == IR_REG_NONE) { - reg = ir_allocate_blocked_reg(ctx); +#if 1 + reg = ir_allocate_blocked_reg(ctx, current, len, active, inactive, &unhandled); +#endif } if (reg != IR_REG_NONE) { - ir_bitset_incl(active, current); + if (ctx->live_intervals[current]->reg != IR_REG_NONE) { + ir_bitset_incl(active, current); + } } else { ir_allocate_spill_slot(ctx, current, &data); } @@ -1034,7 +1234,18 @@ static int ir_linear_scan(ir_ctx *ctx) ir_mem_free(inactive); ir_mem_free(active); - ir_worklist_free(&unhandled); + ir_list_free(&unhandled); + + for (i = 1; i <= ctx->vregs_count; i++) { + ival = ctx->live_intervals[i]; + if (ival) { + ival = ival->top; + ctx->live_intervals[i] = ival; + if (ival->next || ival->reg == IR_REG_NONE) { + ir_allocate_spill_slot(ctx, current, &data); + } + } + } return 1; } diff --git a/ir_x86.dasc b/ir_x86.dasc index 541fba6..0d3dd42 100644 --- a/ir_x86.dasc +++ b/ir_x86.dasc @@ -796,24 +796,25 @@ typedef enum _ir_rule { /* instruction selection */ -bool ir_needs_def_reg(ir_ctx *ctx, ir_ref ref) +bool ir_needs_vreg(ir_ctx *ctx, ir_ref ref) { IR_ASSERT(ctx->rules); return ctx->rules[ref] != IR_SKIP; } -ir_regset ir_get_fixed_regset(ir_ctx *ctx, ir_ref ref) +ir_regset ir_get_scratch_regset(ir_ctx *ctx, ir_ref ref) { ir_ref rule; rule = ctx->rules[ref]; -// if (rule == IR_SHIFT) { -// return IR_REGSET(IR_REG_RCX); -// if (rule == IR_MUL_INT || rule == IR_DIV_INT || rule == IR_MOD_INT) { -// return IR_REGSET(IR_REG_RAX) | IR_REGSET(IR_REG_RDX); -// } - if (rule == IR_MOD_INT && IR_IS_CONST_REF(ctx->ir_base[ref].op1)) { - return IR_REGSET(IR_REG_RAX); + if (rule == IR_SHIFT) { + return IR_REGSET(IR_REG_RCX); + } else if (rule == IR_MUL_INT || rule == IR_DIV_INT) { + return IR_REGSET(IR_REG_RDX); /* %rax - used as result */ + } else if (rule == IR_MOD_INT) { + return IR_REGSET(IR_REG_RAX); /* %rdx - used as result */ + } else if (rule == IR_CALL) { + return IR_REGSET_SCRATCH; } return IR_REGSET_EMPTY; } @@ -896,7 +897,7 @@ ir_reg ir_uses_fixed_reg(ir_ctx *ctx, ir_ref ref, int op_num) return IR_REG_NONE; } -bool ir_result_reuses_op1(ir_ctx *ctx, ir_ref ref) +bool ir_result_reuses_op1_reg(ir_ctx *ctx, ir_ref ref) { ir_ref rule; @@ -908,7 +909,6 @@ bool ir_result_reuses_op1(ir_ctx *ctx, ir_ref ref) case IR_SHIFT_CONST: case IR_MUL_INT: case IR_DIV_INT: - case IR_MOD_INT: return 1; } return 0; diff --git a/ir_x86.h b/ir_x86.h index a667e16..92d72c9 100644 --- a/ir_x86.h +++ b/ir_x86.h @@ -275,9 +275,11 @@ uint32_t __inline __ir_clz(uint32_t value) { #endif -bool ir_needs_def_reg(ir_ctx *ctx, ir_ref ref); -ir_regset ir_get_fixed_regset(ir_ctx *ctx, ir_ref ref); +bool ir_needs_vreg(ir_ctx *ctx, ir_ref ref); + +/* Registers modified by the given instruction */ +ir_regset ir_get_scratch_regset(ir_ctx *ctx, ir_ref ref); ir_reg ir_uses_fixed_reg(ir_ctx *ctx, ir_ref ref, int op_num); -bool ir_result_reuses_op1(ir_ctx *ctx, ir_ref ref); +bool ir_result_reuses_op1_reg(ir_ctx *ctx, ir_ref ref); #endif /* IR_X86_H */ diff --git a/tests/020.irt b/tests/020.irt index 8697134..6385612 100644 --- a/tests/020.irt +++ b/tests/020.irt @@ -68,7 +68,7 @@ BB4: dom_depth=1 } { # LIVE-RANGES (vregs_count=3) -R1 (d_2, d_11): [2-5], [8-9], [10-12[, DEF(2), DEF(11), USE(12/2) -R2 (d_3): [3-7], DEF(3) -R3 (d_4): [4-5[, DEF(4), USE(5/2) +R1 (d_2, d_11): [2.3-5.3), [8.0-12.2), DEF(2.3), DEF(11.3), USE(12.2/2) +R2 (d_3): [3.3-7.3), DEF(3.3) +R3 (d_4): [4.3-5.2), DEF(4.3), USE(5.2/2) } diff --git a/tests/021.irt b/tests/021.irt index 9b58b4b..efb5d6d 100644 --- a/tests/021.irt +++ b/tests/021.irt @@ -85,6 +85,6 @@ BB5: dom_depth=3 } { # LIVE-RANGES (vregs_count=2) -R1 (d_2, d_5): [2-3], [4-7], [10-13[, DEF(2), DEF(5), USE(6/1), USE(13/2) -R2 (d_6): [6-9], DEF(6), USE(7/2) +R1 (d_2, d_5): [2.3-7.3), [10.0-13.2), DEF(2.3), DEF(5.3), USE(6.2/1), USE(13.2/2) +R2 (d_6): [6.3-9.3), DEF(6.3), USE(7.2/2) } diff --git a/tests/022.irt b/tests/022.irt index 5ff2d52..42533f9 100644 --- a/tests/022.irt +++ b/tests/022.irt @@ -93,7 +93,7 @@ BB5: dom_depth=3 } { # LIVE-RANGES (vregs_count=3) -R1 (d_2, d_7): [2-5], [6-15[, DEF(2), DEF(7), USE(15/2) -R2 (d_3, d_8): [3-5], [6-11], DEF(3), DEF(8) -R3 (d_4): [4-9], [10-11], DEF(4), USE(9/2) +R1 (d_2, d_7): [2.3-15.2), DEF(2.3), DEF(7.3), USE(15.2/2) +R2 (d_3, d_8): [3.3-11.3), DEF(3.3), DEF(8.3) +R3 (d_4): [4.3-11.3), DEF(4.3), USE(9.2/2) } diff --git a/tests/x86_64/not_002.irt b/tests/x86_64/not_002.irt new file mode 100644 index 0000000..2d3cf44 --- /dev/null +++ b/tests/x86_64/not_002.irt @@ -0,0 +1,16 @@ +--TEST-- +002: not function +--ARGS-- +-S +--CODE-- +{ + l_1 = START(l_4); + bool x = PARAM(l_1, "x", 1); + bool ret = NOT(x); + l_4 = RETURN(l_1, ret); +} +--EXPECT-- +test: + testb %dil, %dil + setne %al + retq diff --git a/tests/x86_64/ra_001.irt b/tests/x86_64/ra_001.irt new file mode 100644 index 0000000..8f8fd7b --- /dev/null +++ b/tests/x86_64/ra_001.irt @@ -0,0 +1,21 @@ +--TEST-- +001: Register Allocation (MUL + MUL) +--ARGS-- +-S +--CODE-- +{ + l_1 = START(l_4); + uint32_t x_1 = PARAM(l_1, "x", 1); + uint32_t y_1 = PARAM(l_1, "y", 2); + uint32_t z_1 = PARAM(l_1, "z", 3); + uint32_t x_2 = MUL(x_1, y_1); + uint32_t x_3 = MUL(x_2, z_1); + l_4 = RETURN(l_1, x_3); +} +--EXPECT-- +test: + movl %edx, %ecx + movl %esi, %eax + mull %edi + mull %ecx + retq diff --git a/tests/x86_64/ra_002.irt b/tests/x86_64/ra_002.irt new file mode 100644 index 0000000..2cf9808 --- /dev/null +++ b/tests/x86_64/ra_002.irt @@ -0,0 +1,22 @@ +--TEST-- +002: Register Allocation (MUL + DIV) +--ARGS-- +-S +--CODE-- +{ + l_1 = START(l_4); + uint32_t x_1 = PARAM(l_1, "x", 1); + uint32_t y_1 = PARAM(l_1, "y", 2); + uint32_t z_1 = PARAM(l_1, "z", 3); + uint32_t x_2 = MUL(x_1, y_1); + uint32_t x_3 = DIV(x_2, z_1); + l_4 = RETURN(l_1, x_3); +} +--EXPECT-- +test: + movl %edx, %ecx + movl %esi, %eax + mull %edi + xorl %edx, %edx + divl %ecx + retq diff --git a/tests/x86_64/ra_003.irt b/tests/x86_64/ra_003.irt new file mode 100644 index 0000000..a47bd23 --- /dev/null +++ b/tests/x86_64/ra_003.irt @@ -0,0 +1,23 @@ +--TEST-- +003: Register Allocation (MUL + MOD) +--ARGS-- +-S +--CODE-- +{ + l_1 = START(l_4); + uint32_t x_1 = PARAM(l_1, "x", 1); + uint32_t y_1 = PARAM(l_1, "y", 2); + uint32_t z_1 = PARAM(l_1, "z", 3); + uint32_t x_2 = MUL(x_1, y_1); + uint32_t x_3 = MOD(x_2, z_1); + l_4 = RETURN(l_1, x_3); +} +--EXPECT-- +test: + movl %edx, %ecx + movl %esi, %eax + mull %edi + xorl %edx, %edx + divl %ecx + movl %edx, %eax + retq diff --git a/tests/x86_64/ra_004.irt b/tests/x86_64/ra_004.irt new file mode 100644 index 0000000..a715ccf --- /dev/null +++ b/tests/x86_64/ra_004.irt @@ -0,0 +1,22 @@ +--TEST-- +004: Register Allocation (DIV + MUL) +--ARGS-- +-S +--CODE-- +{ + l_1 = START(l_4); + uint32_t x_1 = PARAM(l_1, "x", 1); + uint32_t y_1 = PARAM(l_1, "y", 2); + uint32_t z_1 = PARAM(l_1, "z", 3); + uint32_t x_2 = DIV(x_1, y_1); + uint32_t x_3 = MUL(x_2, z_1); + l_4 = RETURN(l_1, x_3); +} +--EXPECT-- +test: + movl %edx, %ecx + movl %edi, %eax + xorl %edx, %edx + divl %esi + mull %ecx + retq diff --git a/tests/x86_64/ra_005.irt b/tests/x86_64/ra_005.irt new file mode 100644 index 0000000..a347b37 --- /dev/null +++ b/tests/x86_64/ra_005.irt @@ -0,0 +1,23 @@ +--TEST-- +005: Register Allocation (DIV + DIV) +--ARGS-- +-S +--CODE-- +{ + l_1 = START(l_4); + uint32_t x_1 = PARAM(l_1, "x", 1); + uint32_t y_1 = PARAM(l_1, "y", 2); + uint32_t z_1 = PARAM(l_1, "z", 3); + uint32_t x_2 = DIV(x_1, y_1); + uint32_t x_3 = DIV(x_2, z_1); + l_4 = RETURN(l_1, x_3); +} +--EXPECT-- +test: + movl %edx, %ecx + movl %edi, %eax + xorl %edx, %edx + divl %esi + xorl %edx, %edx + divl %ecx + retq diff --git a/tests/x86_64/ra_006.irt b/tests/x86_64/ra_006.irt new file mode 100644 index 0000000..e4ac78f --- /dev/null +++ b/tests/x86_64/ra_006.irt @@ -0,0 +1,24 @@ +--TEST-- +006: Register Allocation (DIV + MOD) +--ARGS-- +-S +--CODE-- +{ + l_1 = START(l_4); + uint32_t x_1 = PARAM(l_1, "x", 1); + uint32_t y_1 = PARAM(l_1, "y", 2); + uint32_t z_1 = PARAM(l_1, "z", 3); + uint32_t x_2 = DIV(x_1, y_1); + uint32_t x_3 = MOD(x_2, z_1); + l_4 = RETURN(l_1, x_3); +} +--EXPECT-- +test: + movl %edx, %ecx + movl %edi, %eax + xorl %edx, %edx + divl %esi + xorl %edx, %edx + divl %ecx + movl %edx, %eax + retq diff --git a/tests/x86_64/ra_007.irt b/tests/x86_64/ra_007.irt new file mode 100644 index 0000000..5ca8325 --- /dev/null +++ b/tests/x86_64/ra_007.irt @@ -0,0 +1,23 @@ +--TEST-- +007: Register Allocation (MOD + MUL) +--ARGS-- +-S +--CODE-- +{ + l_1 = START(l_4); + uint32_t x_1 = PARAM(l_1, "x", 1); + uint32_t y_1 = PARAM(l_1, "y", 2); + uint32_t z_1 = PARAM(l_1, "z", 3); + uint32_t x_2 = MOD(x_1, y_1); + uint32_t x_3 = MUL(x_2, z_1); + l_4 = RETURN(l_1, x_3); +} +--EXPECT-- +test: + movl %edx, %ecx + movl %edi, %eax + xorl %edx, %edx + divl %esi + movl %edx, %eax + mull %ecx + retq diff --git a/tests/x86_64/ra_008.irt b/tests/x86_64/ra_008.irt new file mode 100644 index 0000000..64dd065 --- /dev/null +++ b/tests/x86_64/ra_008.irt @@ -0,0 +1,24 @@ +--TEST-- +008: Register Allocation (MOD + DIV) +--ARGS-- +-S +--CODE-- +{ + l_1 = START(l_4); + uint32_t x_1 = PARAM(l_1, "x", 1); + uint32_t y_1 = PARAM(l_1, "y", 2); + uint32_t z_1 = PARAM(l_1, "z", 3); + uint32_t x_2 = MOD(x_1, y_1); + uint32_t x_3 = DIV(x_2, z_1); + l_4 = RETURN(l_1, x_3); +} +--EXPECT-- +test: + movl %edx, %ecx + movl %edi, %eax + xorl %edx, %edx + divl %esi + movl %edx, %eax + xorl %edx, %edx + divl %ecx + retq diff --git a/tests/x86_64/ra_009.irt b/tests/x86_64/ra_009.irt new file mode 100644 index 0000000..945d4c2 --- /dev/null +++ b/tests/x86_64/ra_009.irt @@ -0,0 +1,15 @@ +--TEST-- +009: Register Allocation (MOD + MOD) +--ARGS-- +-S +--CODE-- +{ + l_1 = START(l_4); + uint32_t x_1 = PARAM(l_1, "x", 1); + uint32_t y_1 = PARAM(l_1, "y", 2); + uint32_t z_1 = PARAM(l_1, "z", 3); + uint32_t x_2 = MOD(x_1, y_1); + uint32_t x_3 = MOD(x_2, z_1); + l_4 = RETURN(l_1, x_3); +} +--EXPECT-- diff --git a/tests/x86_64/ra_010.irt b/tests/x86_64/ra_010.irt new file mode 100644 index 0000000..3a8f959 --- /dev/null +++ b/tests/x86_64/ra_010.irt @@ -0,0 +1,20 @@ +--TEST-- +010: Register Allocation (ADD + MUL) +--ARGS-- +-S +--CODE-- +{ + l_1 = START(l_4); + uint32_t x_1 = PARAM(l_1, "x", 1); + uint32_t y_1 = PARAM(l_1, "y", 2); + uint32_t z_1 = PARAM(l_1, "z", 3); + uint32_t x_2 = ADD(x_1, y_1); + uint32_t x_3 = MUL(x_2, z_1); + l_4 = RETURN(l_1, x_3); +} +--EXPECT-- +test: + movl %edx, %ecx + leal (%rsi, %rdi), %eax + mull %ecx + retq diff --git a/tests/x86_64/ra_011.irt b/tests/x86_64/ra_011.irt new file mode 100644 index 0000000..4244794 --- /dev/null +++ b/tests/x86_64/ra_011.irt @@ -0,0 +1,21 @@ +--TEST-- +011: Register Allocation (ADD + DIV) +--ARGS-- +-S +--CODE-- +{ + l_1 = START(l_4); + uint32_t x_1 = PARAM(l_1, "x", 1); + uint32_t y_1 = PARAM(l_1, "y", 2); + uint32_t z_1 = PARAM(l_1, "z", 3); + uint32_t x_2 = ADD(x_1, y_1); + uint32_t x_3 = DIV(x_2, z_1); + l_4 = RETURN(l_1, x_3); +} +--EXPECT-- +test: + movl %edx, %ecx + leal (%rsi, %rdi), %eax + xorl %edx, %edx + divl %ecx + retq diff --git a/tests/x86_64/ra_012.irt b/tests/x86_64/ra_012.irt new file mode 100644 index 0000000..cbbed40 --- /dev/null +++ b/tests/x86_64/ra_012.irt @@ -0,0 +1,22 @@ +--TEST-- +012: Register Allocation (ADD + MOD) +--ARGS-- +-S +--CODE-- +{ + l_1 = START(l_4); + uint32_t x_1 = PARAM(l_1, "x", 1); + uint32_t y_1 = PARAM(l_1, "y", 2); + uint32_t z_1 = PARAM(l_1, "z", 3); + uint32_t x_2 = ADD(x_1, y_1); + uint32_t x_3 = MOD(x_2, z_1); + l_4 = RETURN(l_1, x_3); +} +--EXPECT-- +test: + movl %edx, %ecx + leal (%rsi, %rdi), %eax + xorl %edx, %edx + divl %ecx + movl %edx, %eax + retq