#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]; if (node->instr8 || (instr->flags & fIsArgInit)) 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); CError_ASSERT(128, instr->args[0].kind == PCOp_REGISTER); 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: CError_FATAL(187); } memclrw(&operand, sizeof(Operand)); operand.optype = OpndType_Symbol; operand.object = node->spillTemporary; CError_ASSERT(222, node->spillTemporary->datatype == DLOCAL); coerce_to_addressable(&operand); CError_ASSERT(233, operand.optype == OpndType_GPR_ImmOffset); CError_ASSERT(237, node->spillTemporary->datatype == DLOCAL); 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: CError_ASSERT(253, node->spillTemporary->datatype == DLOCAL); 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: CError_ASSERT(320, node->spillTemporary->datatype == DLOCAL); 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: CError_FATAL(333); } } 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: CError_FATAL(391); } 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: CError_FATAL(527); } } 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++) { CError_ASSERT(563, instr->block != NULL); 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) { if (node->instr8 || (instr->flags & fIsArgInit)) 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: CError_FATAL(771); } 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]++; if (instr->flags & fIsMove) spillcopy(block, instr); else if (instr->flags & fIsCall) spillcall(block, instr); else spillinstruction(block, instr); } } } }