Split assign_regs() loop into two versions (with and without spilling).

This commit is contained in:
Dmitry Stogov 2023-06-20 08:34:54 +03:00
parent 0928b975be
commit 009e9c4a53
3 changed files with 163 additions and 57 deletions

141
ir_ra.c
View File

@ -3525,78 +3525,105 @@ static void assign_regs(ir_ctx *ctx)
memset(ctx->regs, IR_REG_NONE, sizeof(ir_regs) * ctx->insns_count); memset(ctx->regs, IR_REG_NONE, sizeof(ir_regs) * ctx->insns_count);
} }
for (i = 1; i <= ctx->vregs_count; i++) { if (!(ctx->flags & (IR_RA_HAVE_SPLITS|IR_RA_HAVE_SPILLS))) {
top_ival = ival = ctx->live_intervals[i]; for (i = 1; i <= ctx->vregs_count; i++) {
if (ival) { top_ival = ival = ctx->live_intervals[i];
do { if (ival) {
if (ival->reg != IR_REG_NONE) { do {
use_pos = ival->use_pos; if (ival->reg != IR_REG_NONE) {
while (use_pos) {
ref = IR_LIVE_POS_TO_REF(use_pos->pos);
reg = ival->reg; reg = ival->reg;
if (use_pos->op_num == 0 use_pos = ival->use_pos;
&& (use_pos->flags & IR_DEF_REUSES_OP1_REG) while (use_pos) {
&& ctx->regs[ref][1] != IR_REG_NONE ref = (use_pos->hint_ref < 0) ? -use_pos->hint_ref : IR_LIVE_POS_TO_REF(use_pos->pos);
&& IR_REG_SPILLED(ctx->regs[ref][1]) ir_set_alocated_reg(ctx, ref, use_pos->op_num, reg);
&& (ctx->regs[ref][2] == IR_REG_NONE || IR_REG_NUM(ctx->regs[ref][2]) != reg) use_pos = use_pos->next;
&& (ctx->regs[ref][3] == IR_REG_NONE || IR_REG_NUM(ctx->regs[ref][3]) != reg)) {
/* load op1 directly into result (valid only when op1 register is not reused) */
if (top_ival->flags & IR_LIVE_INTERVAL_SPILL_SPECIAL) {
ctx->regs[ref][1] = reg | IR_REG_SPILL_SPECIAL;
} else {
ctx->regs[ref][1] = reg | IR_REG_SPILL_LOAD;
}
} }
if (top_ival->flags & IR_LIVE_INTERVAL_SPILLED) { }
// TODO: Insert spill loads and stotres in optimal positons (resolution) ival = ival->next;
} while (ival);
}
}
} else {
for (i = 1; i <= ctx->vregs_count; i++) {
top_ival = ival = ctx->live_intervals[i];
if (ival) {
do {
if (ival->reg != IR_REG_NONE) {
use_pos = ival->use_pos;
while (use_pos) {
reg = ival->reg;
ref = (use_pos->hint_ref < 0) ? -use_pos->hint_ref : IR_LIVE_POS_TO_REF(use_pos->pos);
if (use_pos->op_num == 0
&& (use_pos->flags & IR_DEF_REUSES_OP1_REG)
&& ctx->regs[ref][1] != IR_REG_NONE
&& IR_REG_SPILLED(ctx->regs[ref][1])
&& IR_REG_NUM(ctx->regs[ref][1]) != reg
&& IR_REG_NUM(ctx->regs[ref][2]) != reg
&& IR_REG_NUM(ctx->regs[ref][3]) != reg) {
/* load op1 directly into result (valid only when op1 register is not reused) */
ir_reg old_reg = IR_REG_NUM(ctx->regs[ref][1]);
if (use_pos->op_num == 0) { if (ctx->live_intervals[ctx->vregs[ctx->ir_base[ref].op1]]->flags & IR_LIVE_INTERVAL_SPILL_SPECIAL) {
if (top_ival->flags & IR_LIVE_INTERVAL_SPILL_SPECIAL) { ctx->regs[ref][1] = reg | IR_REG_SPILL_SPECIAL;
reg |= IR_REG_SPILL_SPECIAL;
} else { } else {
reg |= IR_REG_SPILL_STORE; ctx->regs[ref][1] = reg | IR_REG_SPILL_LOAD;
} }
} else { if (IR_REG_NUM(ctx->regs[ref][2]) == old_reg) {
if ((use_pos->flags & IR_USE_MUST_BE_IN_REG) ctx->regs[ref][2] = reg;
|| ctx->ir_base[ref].op == IR_CALL }
|| ctx->ir_base[ref].op == IR_TAILCALL if (IR_REG_NUM(ctx->regs[ref][3]) == old_reg) {
|| ctx->ir_base[ref].op == IR_SNAPSHOT ctx->regs[ref][3] = reg;
|| (use_pos->op_num == 2 }
&& ctx->ir_base[ref].op1 == ctx->ir_base[ref].op2 }
&& IR_REG_NUM(ctx->regs[ref][1]) == reg)) { if (top_ival->flags & IR_LIVE_INTERVAL_SPILLED) {
// TODO: Insert spill loads and stotres in optimal positons (resolution)
if (use_pos->op_num == 0) {
if (top_ival->flags & IR_LIVE_INTERVAL_SPILL_SPECIAL) { if (top_ival->flags & IR_LIVE_INTERVAL_SPILL_SPECIAL) {
reg |= IR_REG_SPILL_SPECIAL; reg |= IR_REG_SPILL_SPECIAL;
} else { } else {
reg |= IR_REG_SPILL_LOAD; reg |= IR_REG_SPILL_STORE;
} }
} else { } else {
/* fuse spill load (valid only when register is not reused) */ if ((use_pos->flags & IR_USE_MUST_BE_IN_REG)
reg = IR_REG_NONE; || ctx->ir_base[ref].op == IR_CALL
|| ctx->ir_base[ref].op == IR_TAILCALL
|| ctx->ir_base[ref].op == IR_SNAPSHOT) {
if (top_ival->flags & IR_LIVE_INTERVAL_SPILL_SPECIAL) {
reg |= IR_REG_SPILL_SPECIAL;
} else {
reg |= IR_REG_SPILL_LOAD;
}
} else if (use_pos->op_num == 2
&& ctx->ir_base[ref].op1 == ctx->ir_base[ref].op2
&& IR_REG_NUM(ctx->regs[ref][1]) == reg) {
/* pass */
} else {
/* fuse spill load (valid only when register is not reused) */
reg = IR_REG_NONE;
}
} }
} }
}
if (use_pos->hint_ref < 0) {
ref = -use_pos->hint_ref;
}
ir_set_alocated_reg(ctx, ref, use_pos->op_num, reg);
use_pos = use_pos->next;
}
} else if ((top_ival->flags & IR_LIVE_INTERVAL_SPILLED)
&& !(top_ival->flags & IR_LIVE_INTERVAL_SPILL_SPECIAL)) {
use_pos = ival->use_pos;
while (use_pos) {
ref = IR_LIVE_POS_TO_REF(use_pos->pos);
if (ctx->ir_base[ref].op == IR_SNAPSHOT) {
/* A reference to a CPU spill slot */
reg = IR_REG_SPILL_STORE | IR_REG_STACK_POINTER;
ir_set_alocated_reg(ctx, ref, use_pos->op_num, reg); ir_set_alocated_reg(ctx, ref, use_pos->op_num, reg);
use_pos = use_pos->next;
}
} else if ((top_ival->flags & IR_LIVE_INTERVAL_SPILLED)
&& !(top_ival->flags & IR_LIVE_INTERVAL_SPILL_SPECIAL)) {
use_pos = ival->use_pos;
while (use_pos) {
ref = (use_pos->hint_ref < 0) ? -use_pos->hint_ref : IR_LIVE_POS_TO_REF(use_pos->pos);
if (ctx->ir_base[ref].op == IR_SNAPSHOT) {
/* A reference to a CPU spill slot */
reg = IR_REG_SPILL_STORE | IR_REG_STACK_POINTER;
ir_set_alocated_reg(ctx, ref, use_pos->op_num, reg);
}
use_pos = use_pos->next;
} }
use_pos = use_pos->next;
} }
} ival = ival->next;
ival = ival->next; } while (ival);
} while (ival); }
} }
} }

39
tests/debug/ra_002.irt Normal file
View File

@ -0,0 +1,39 @@
--TEST--
002: Register Allocation (spill load into result - movsd (%rsp), %xmm1)
--TARGET--
x86_64
--ARGS--
-S
--CODE--
{
uintptr_t fn = func(printf);
uintptr_t format = "hello\n";
l_1 = START(l_3);
double x = PARAM(l_1, "x", 1);
double y = PARAM(l_1, "y", 2);
double z = PARAM(l_1, "z", 2);
l_2 = CALL(l_1, fn, format);
double ret1 = SUB(x, y);
double ret2 = SUB(y, z);
double ret = ADD(ret1, ret2);
l_3 = RETURN(l_2, ret);
}
--EXPECT--
test:
subq $0x18, %rsp
movsd %xmm0, (%rsp)
movsd %xmm1, 8(%rsp)
movsd %xmm2, 0x10(%rsp)
leaq .L1(%rip), %rdi
movabsq $_IO_printf, %rax
callq *%rax
movsd (%rsp), %xmm1
subsd 8(%rsp), %xmm1
movsd 8(%rsp), %xmm0
subsd 0x10(%rsp), %xmm0
addsd %xmm1, %xmm0
addq $0x18, %rsp
retq
.rodata
.L1:
.db 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x00

40
tests/debug/ra_003.irt Normal file
View File

@ -0,0 +1,40 @@
--TEST--
003: Register Allocation (spill load into result - movsd (%rsp), %xmm1)
--TARGET--
x86_64
--ARGS--
-S
--CODE--
{
uintptr_t fn = func(printf);
uintptr_t format = "hello\n";
l_1 = START(l_3);
double x = PARAM(l_1, "x", 1);
double y = PARAM(l_1, "y", 2);
double z = PARAM(l_1, "z", 2);
l_2 = CALL(l_1, fn, format);
double ret1 = ADD(x, x);
double ret2 = SUB(y, z);
double ret = ADD(ret1, ret2);
l_3 = RETURN(l_2, ret);
}
--EXPECT--
test:
subq $0x18, %rsp
movsd %xmm0, (%rsp)
movsd %xmm1, 8(%rsp)
movsd %xmm2, 0x10(%rsp)
leaq .L1(%rip), %rdi
movabsq $_IO_printf, %rax
callq *%rax
movsd (%rsp), %xmm1
addsd %xmm1, %xmm1
movsd 8(%rsp), %xmm0
subsd 0x10(%rsp), %xmm0
addsd %xmm1, %xmm0
addq $0x18, %rsp
retq
.rodata
.db 0x90, 0x90
.L1:
.db 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x00