2022-12-14 00:16:59 +00:00
|
|
|
#include "compiler/SpillCode.h"
|
|
|
|
#include "compiler/CError.h"
|
|
|
|
#include "compiler/CMachine.h"
|
|
|
|
#include "compiler/CParser.h"
|
|
|
|
#include "compiler/CodeGen.h"
|
|
|
|
#include "compiler/CompilerTools.h"
|
|
|
|
#include "compiler/Coloring.h"
|
|
|
|
#include "compiler/InterferenceGraph.h"
|
|
|
|
#include "compiler/Operands.h"
|
|
|
|
#include "compiler/PCode.h"
|
|
|
|
#include "compiler/PCodeUtilities.h"
|
|
|
|
#include "compiler/Registers.h"
|
|
|
|
#include "compiler/RegisterInfo.h"
|
|
|
|
#include "compiler/StackFrame.h"
|
|
|
|
#include "compiler/objects.h"
|
|
|
|
|
|
|
|
static int last_unused_vreg_before_spilling;
|
|
|
|
static short rTEMP_for_VR_spill;
|
|
|
|
|
|
|
|
void estimatespillcosts(void) {
|
|
|
|
PCodeBlock *block;
|
|
|
|
PCode *instr;
|
|
|
|
IGNode *node;
|
|
|
|
PCodeArg *op;
|
|
|
|
int i;
|
|
|
|
int weight;
|
|
|
|
|
|
|
|
for (block = pcbasicblocks; block; block = block->nextBlock) {
|
|
|
|
if (copts.optimize_for_size)
|
|
|
|
weight = 1;
|
|
|
|
else
|
|
|
|
weight = block->loopWeight;
|
|
|
|
|
|
|
|
for (instr = block->firstPCode; instr; instr = instr->nextPCode) {
|
|
|
|
op = instr->args;
|
|
|
|
i = instr->argCount;
|
|
|
|
while (i--) {
|
|
|
|
if (PC_OP_IS_READ_ANY_REGISTER(op, coloring_class)) {
|
|
|
|
node = interferencegraph[op->data.reg.reg];
|
|
|
|
if (node->instr8 || copts.optimize_for_size)
|
|
|
|
node->spillCost += weight;
|
|
|
|
else
|
|
|
|
node->spillCost += weight * 2;
|
|
|
|
}
|
|
|
|
op++;
|
|
|
|
}
|
|
|
|
|
|
|
|
op = instr->args;
|
|
|
|
i = instr->argCount;
|
|
|
|
while (i--) {
|
|
|
|
if (PC_OP_IS_WRITE_ANY_REGISTER(op, coloring_class)) {
|
|
|
|
node = interferencegraph[op->data.reg.reg];
|
2023-01-11 22:29:53 +00:00
|
|
|
if (node->instr8 || (instr->flags & fIsArgInit))
|
2022-12-14 00:16:59 +00:00
|
|
|
node->spillCost -= weight;
|
|
|
|
else
|
|
|
|
node->spillCost += weight;
|
|
|
|
}
|
|
|
|
op++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Object *makespilltemporary(Type *type) {
|
|
|
|
Object *obj = lalloc(sizeof(Object));
|
|
|
|
memclrw(obj, sizeof(Object));
|
|
|
|
|
|
|
|
obj->otype = OT_OBJECT;
|
|
|
|
obj->access = ACCESSPUBLIC;
|
|
|
|
obj->datatype = DLOCAL;
|
|
|
|
obj->type = type;
|
|
|
|
obj->name = CParser_GetUniqueName();
|
|
|
|
obj->u.var.info = CodeGen_GetNewVarInfo();
|
|
|
|
obj->u.var.uid = 0;
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PCode *rematerialize_spilled_register(short reg, IGNode *node) {
|
|
|
|
PCode *instr = copypcode(node->instr8);
|
|
|
|
|
2022-12-29 12:32:55 +00:00
|
|
|
CError_ASSERT(128, instr->args[0].kind == PCOp_REGISTER);
|
2022-12-14 00:16:59 +00:00
|
|
|
|
|
|
|
instr->args[0].data.reg.reg = reg;
|
|
|
|
return instr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void insert_load_spilled_register(PCode *instr, short reg, IGNode *node) {
|
|
|
|
Type *type;
|
|
|
|
Opcode opcode;
|
|
|
|
Object *object;
|
|
|
|
PCode *newInstr;
|
|
|
|
PCode *newInstr2;
|
|
|
|
SInt32 offset;
|
|
|
|
Operand operand;
|
|
|
|
|
|
|
|
type = node->spillTemporary->type;
|
|
|
|
switch (coloring_class) {
|
|
|
|
case RegClass_CRFIELD:
|
|
|
|
case RegClass_GPR:
|
|
|
|
switch (type->size) {
|
|
|
|
case 1:
|
|
|
|
opcode = PC_LBZ;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
opcode = is_unsigned(type) ? PC_LHZ : PC_LHA;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
opcode = PC_LWZ;
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
opcode = PC_LWZ;
|
|
|
|
break;
|
|
|
|
default:
|
2022-12-29 12:32:55 +00:00
|
|
|
CError_FATAL(187);
|
2022-12-14 00:16:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
memclrw(&operand, sizeof(Operand));
|
|
|
|
operand.optype = OpndType_Symbol;
|
|
|
|
operand.object = node->spillTemporary;
|
2022-12-29 12:32:55 +00:00
|
|
|
CError_ASSERT(222, node->spillTemporary->datatype == DLOCAL);
|
2022-12-14 00:16:59 +00:00
|
|
|
|
|
|
|
coerce_to_addressable(&operand);
|
|
|
|
|
2022-12-29 12:32:55 +00:00
|
|
|
CError_ASSERT(233, operand.optype == OpndType_GPR_ImmOffset);
|
2022-12-14 00:16:59 +00:00
|
|
|
|
2022-12-29 12:32:55 +00:00
|
|
|
CError_ASSERT(237, node->spillTemporary->datatype == DLOCAL);
|
2022-12-14 00:16:59 +00:00
|
|
|
|
|
|
|
if (node->flags & fPairLow)
|
|
|
|
offset = low_offset;
|
|
|
|
else if (node->flags & fPairHigh)
|
|
|
|
offset = high_offset;
|
|
|
|
else
|
|
|
|
offset = 0;
|
|
|
|
insertpcodebefore(instr, makepcode(opcode, reg, operand.reg, operand.object, operand.immOffset + offset));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RegClass_FPR:
|
2022-12-29 12:32:55 +00:00
|
|
|
CError_ASSERT(253, node->spillTemporary->datatype == DLOCAL);
|
2022-12-14 00:16:59 +00:00
|
|
|
|
|
|
|
if (node->flags & fPairLow)
|
|
|
|
offset = low_offset;
|
|
|
|
else if (node->flags & fPairHigh)
|
|
|
|
offset = high_offset;
|
|
|
|
else
|
|
|
|
offset = 0;
|
|
|
|
|
|
|
|
object = node->spillTemporary;
|
|
|
|
insertpcodebefore(
|
|
|
|
instr,
|
|
|
|
makepcode(
|
|
|
|
(type->size == 8) ? PC_LFD : PC_LFS,
|
|
|
|
reg,
|
|
|
|
local_base_register(object),
|
|
|
|
object,
|
|
|
|
offset
|
|
|
|
)
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RegClass_VR:
|
2022-12-29 12:32:55 +00:00
|
|
|
CError_ASSERT(320, node->spillTemporary->datatype == DLOCAL);
|
2022-12-14 00:16:59 +00:00
|
|
|
|
|
|
|
object = node->spillTemporary;
|
|
|
|
newInstr = makepcode(PC_ADDI, rTEMP_for_VR_spill, local_base_register(object), object, 0);
|
|
|
|
newInstr2 = makepcode(PC_LVX, reg, 0, rTEMP_for_VR_spill);
|
|
|
|
insertpcodebefore(instr, newInstr);
|
|
|
|
insertpcodeafter(newInstr, newInstr2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2022-12-29 12:32:55 +00:00
|
|
|
CError_FATAL(333);
|
2022-12-14 00:16:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void insert_store_spilled_register(PCode *instr, Boolean flag, short reg, IGNode *node) {
|
|
|
|
Object *object; // r31
|
|
|
|
Opcode opcode; // r30
|
|
|
|
SInt32 offset; // r26
|
|
|
|
PCode *newInstr2; // r26
|
|
|
|
PCode *newInstr; // r25
|
|
|
|
Type *type; // r25
|
|
|
|
|
|
|
|
object = node->spillTemporary;
|
|
|
|
type = object->type;
|
|
|
|
|
|
|
|
switch (coloring_class) {
|
|
|
|
case RegClass_CRFIELD:
|
|
|
|
case RegClass_GPR:
|
|
|
|
switch (type->size) {
|
|
|
|
case 1:
|
|
|
|
opcode = PC_STB;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
opcode = PC_STH;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
opcode = PC_STW;
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
opcode = PC_STW;
|
|
|
|
break;
|
|
|
|
default:
|
2022-12-29 12:32:55 +00:00
|
|
|
CError_FATAL(391);
|
2022-12-14 00:16:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (node->flags & fPairLow)
|
|
|
|
offset = low_offset;
|
|
|
|
else if (node->flags & fPairHigh)
|
|
|
|
offset = high_offset;
|
|
|
|
else
|
|
|
|
offset = 0;
|
|
|
|
|
|
|
|
newInstr = makepcode(opcode, reg, local_base_register(object), object, offset);
|
|
|
|
if (flag)
|
|
|
|
insertpcodebefore(instr, newInstr);
|
|
|
|
else
|
|
|
|
insertpcodeafter(instr, newInstr);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RegClass_FPR:
|
|
|
|
newInstr = makepcode((type->size == 8) ? PC_STFD : PC_STFS, reg, local_base_register(object), object, 0);
|
|
|
|
if (flag)
|
|
|
|
insertpcodebefore(instr, newInstr);
|
|
|
|
else
|
|
|
|
insertpcodeafter(instr, newInstr);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RegClass_VR:
|
|
|
|
newInstr = makepcode(PC_ADDI, rTEMP_for_VR_spill, local_base_register(object), object, 0);
|
|
|
|
newInstr2 = makepcode(PC_STVX, reg, 0, rTEMP_for_VR_spill);
|
|
|
|
if (flag)
|
|
|
|
insertpcodebefore(instr, newInstr);
|
|
|
|
else
|
|
|
|
insertpcodeafter(instr, newInstr);
|
|
|
|
insertpcodeafter(newInstr, newInstr2);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2022-12-29 12:32:55 +00:00
|
|
|
CError_FATAL(527);
|
2022-12-14 00:16:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void spillinstruction(PCodeBlock *block, PCode *instr) {
|
|
|
|
int reg;
|
|
|
|
int reg2;
|
|
|
|
int regs;
|
|
|
|
IGNode *node;
|
|
|
|
PCodeArg *op;
|
|
|
|
int i;
|
|
|
|
PCodeArg *op2;
|
|
|
|
int j;
|
|
|
|
int readCounter;
|
|
|
|
int writeCounter;
|
|
|
|
Boolean flag;
|
|
|
|
|
|
|
|
regs = used_virtual_registers[coloring_class];
|
|
|
|
flag = 0;
|
|
|
|
for (i = 0, op = instr->args; i < instr->argCount; i++, op++) {
|
2022-12-29 12:32:55 +00:00
|
|
|
CError_ASSERT(563, instr->block != NULL);
|
2022-12-14 00:16:59 +00:00
|
|
|
|
|
|
|
if (
|
|
|
|
PC_OP_IS_ANY_REGISTER(op, coloring_class) &&
|
|
|
|
(reg = op->data.reg.reg) < regs &&
|
|
|
|
((node = interferencegraph[op->data.reg.reg])->flags & fSpilled)
|
|
|
|
)
|
|
|
|
{
|
|
|
|
reg2 = used_virtual_registers[coloring_class]++;
|
|
|
|
readCounter = 0;
|
|
|
|
writeCounter = 0;
|
|
|
|
|
|
|
|
for (j = i, op2 = op; j < instr->argCount; j++, op2++) {
|
|
|
|
if (PC_OP_IS_REGISTER(op2, coloring_class, reg)) {
|
|
|
|
if (op2->data.reg.effect & EffectRead)
|
|
|
|
readCounter++;
|
|
|
|
if (op2->data.reg.effect & EffectWrite)
|
|
|
|
writeCounter++;
|
|
|
|
op2->data.reg.reg = reg2;
|
|
|
|
op2->data.reg.effect |= Effect40;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (readCounter) {
|
|
|
|
if (node->instr8)
|
|
|
|
insertpcodebefore(instr, rematerialize_spilled_register(reg2, node));
|
|
|
|
else
|
|
|
|
insert_load_spilled_register(instr, reg2, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (writeCounter) {
|
2023-01-11 22:29:53 +00:00
|
|
|
if (node->instr8 || (instr->flags & fIsArgInit))
|
2022-12-14 00:16:59 +00:00
|
|
|
flag = 1;
|
|
|
|
else
|
|
|
|
insert_store_spilled_register(instr, 0, reg2, node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flag)
|
|
|
|
deletepcode(instr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void spillcopy(PCodeBlock *block, PCode *instr) {
|
|
|
|
IGNode *node1;
|
|
|
|
IGNode *node2;
|
|
|
|
int reg;
|
|
|
|
|
|
|
|
node1 = interferencegraph[instr->args[1].data.reg.reg];
|
|
|
|
node2 = interferencegraph[instr->args[0].data.reg.reg];
|
|
|
|
|
|
|
|
if (node1->flags & fSpilled) {
|
|
|
|
if (node2->flags & fSpilled) {
|
|
|
|
reg = used_virtual_registers[coloring_class]++;
|
|
|
|
if (node1->instr8)
|
|
|
|
insertpcodebefore(instr, rematerialize_spilled_register(reg, node1));
|
|
|
|
else
|
|
|
|
insert_load_spilled_register(instr, reg, node1);
|
|
|
|
insert_store_spilled_register(instr, 1, reg, node2);
|
|
|
|
} else {
|
|
|
|
if (node1->instr8)
|
|
|
|
insertpcodebefore(instr, rematerialize_spilled_register(instr->args[0].data.reg.reg, node1));
|
|
|
|
else
|
|
|
|
insert_load_spilled_register(instr, instr->args[0].data.reg.reg, node1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
insert_store_spilled_register(instr, 1, instr->args[1].data.reg.reg, node2);
|
|
|
|
}
|
|
|
|
|
|
|
|
deletepcode(instr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void spillcall(PCodeBlock *block, PCode *instr) {
|
|
|
|
PCodeArg *opSrc;
|
|
|
|
PCodeArg *opDst;
|
|
|
|
int opCount;
|
|
|
|
int volatileCount;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
opCount = instr->argCount;
|
|
|
|
volatileCount = branch_count_volatiles();
|
|
|
|
|
|
|
|
opDst = instr->args + volatileCount;
|
|
|
|
opSrc = instr->args + volatileCount;
|
|
|
|
for (i = volatileCount; i < opCount; i++) {
|
|
|
|
if (
|
|
|
|
PC_OP_IS_ANY_REGISTER(opSrc, coloring_class) &&
|
|
|
|
opSrc->data.reg.reg >= n_real_registers[coloring_class] &&
|
|
|
|
(interferencegraph[opSrc->data.reg.reg]->flags & fSpilled)
|
|
|
|
)
|
|
|
|
{
|
|
|
|
instr->argCount--;
|
|
|
|
} else {
|
|
|
|
*opDst = *opSrc;
|
|
|
|
opDst++;
|
|
|
|
}
|
|
|
|
opSrc++;
|
|
|
|
}
|
|
|
|
|
|
|
|
spillinstruction(block, instr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void assign_spill_locations(void) {
|
|
|
|
UInt32 i;
|
|
|
|
IGNode *node;
|
|
|
|
Type *type;
|
|
|
|
|
|
|
|
last_unused_vreg_before_spilling = used_virtual_registers[coloring_class];
|
|
|
|
for (i = n_real_registers[coloring_class]; i < last_unused_vreg_before_spilling; i++) {
|
|
|
|
node = interferencegraph[i];
|
|
|
|
if (node->flags & fCoalesced)
|
|
|
|
continue;
|
|
|
|
if (!(node->flags & fSpilled))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!node->spillTemporary) {
|
|
|
|
switch (coloring_class) {
|
|
|
|
case RegClass_GPR:
|
|
|
|
type = TYPE(&stunsignedlong);
|
|
|
|
break;
|
|
|
|
case RegClass_CRFIELD:
|
|
|
|
type = TYPE(&stunsignedlong);
|
|
|
|
break;
|
|
|
|
case RegClass_FPR:
|
|
|
|
type = TYPE(&stunsignedlong);
|
|
|
|
break;
|
|
|
|
case RegClass_VR:
|
|
|
|
type = TYPE(&stvectorunsignedchar);
|
|
|
|
break;
|
|
|
|
default:
|
2022-12-29 12:32:55 +00:00
|
|
|
CError_FATAL(771);
|
2022-12-14 00:16:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
node->spillTemporary = makespilltemporary(type);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node->spillTemporary->datatype == DLOCAL && !(node->spillTemporary->u.var.info->flags & VarInfoFlag1))
|
|
|
|
assign_local_memory(node->spillTemporary);
|
|
|
|
|
|
|
|
if (node->flags & fPairHigh)
|
|
|
|
Registers_GetVarInfo(node->spillTemporary)->regHi = Register0;
|
|
|
|
else
|
|
|
|
Registers_GetVarInfo(node->spillTemporary)->reg = Register0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void insertspillcode(void) {
|
|
|
|
PCodeBlock *block;
|
|
|
|
PCode *instr;
|
|
|
|
PCode *nextInstr;
|
|
|
|
PCodeArg *op;
|
|
|
|
UInt32 i;
|
|
|
|
int flag;
|
|
|
|
|
|
|
|
rTEMP_for_VR_spill = 0;
|
|
|
|
assign_spill_locations();
|
|
|
|
|
|
|
|
for (block = pcbasicblocks; block; block = block->nextBlock) {
|
|
|
|
for (instr = block->firstPCode; instr; instr = nextInstr) {
|
|
|
|
nextInstr = instr->nextPCode;
|
|
|
|
flag = 0;
|
|
|
|
op = instr->args;
|
|
|
|
i = instr->argCount;
|
|
|
|
while (i--) {
|
|
|
|
if (
|
|
|
|
PC_OP_IS_ANY_REGISTER(op, coloring_class) &&
|
|
|
|
op->data.reg.reg < last_unused_vreg_before_spilling &&
|
|
|
|
(interferencegraph[op->data.reg.reg]->flags & fSpilled)
|
|
|
|
)
|
|
|
|
{
|
|
|
|
flag = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
op++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flag) {
|
|
|
|
if (coloring_class == RegClass_VR && rTEMP_for_VR_spill == 0)
|
|
|
|
rTEMP_for_VR_spill = used_virtual_registers[RegClass_GPR]++;
|
|
|
|
|
2023-01-11 22:29:53 +00:00
|
|
|
if (instr->flags & fIsMove)
|
2022-12-14 00:16:59 +00:00
|
|
|
spillcopy(block, instr);
|
2023-01-11 22:29:53 +00:00
|
|
|
else if (instr->flags & fIsCall)
|
2022-12-14 00:16:59 +00:00
|
|
|
spillcall(block, instr);
|
|
|
|
else
|
|
|
|
spillinstruction(block, instr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|