// Generator.cpp: implementation of the CGenerator class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "archimedes.h" #include "Generator.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif // note, to invoke objdump from command line use // objdump --target binary --architecture=i386 --disassemble-all dump.obj > dump.txt ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CGenerator::CGenerator() { TRACE("construct CGenerator \n"); defaultAreaSize = 1024; } CGenerator::~CGenerator() { } // // Takes a list of armlets forming a chunk, generates x86 code to // represent them and passes that to the code cache // // ??? define wrapping function to call a native support function void testInvokeWrapper() { //codeCache.testInvoke(); } void invokeIntCheck() { // ARM.context.arg1; TRACE("invoke intcheck"); } void CGenerator::generate(Armlet** aArmletChunk, int aTotalArmlets) { armletChunk = aArmletChunk; totalArmlets = aTotalArmlets; // create array to hold armlet number to area address mapping armletNumberToAreaAddress = new uint32[totalArmlets]; // create array to hold register allocations for each basic block basicBlockRA = new RegisterAllocation*[totalArmlets]; // blank array, this won't be necessary if can test for NULL ??? for(int eachArmlet = 0; eachArmletvarLocation[varCounter].location = locMEMORY; } for(int nativeReg=0; nativeRegregisterUse[nativeReg] = vUNUSED; } // create area to hold x86 code nativeChunk = new NativeChunk; // set up default values nativeChunk->area = (uint8*)malloc(defaultAreaSize); nativeChunk->allocatedSize = defaultAreaSize; nativeChunk->amountUsed = 0; // create linked list for forward-referencing-gotos to be backpatched gotoPatchList = new CLinkedList(); // for each armlet choose rule and generate code into code area for(armletCounter=0; armletCounterleader) { // ??? 000 // check for predefined reg allocation if(basicBlockRA[armletCounter] == NULL) { // if no predefined reg allocation // make copy of previous armlets reg allocation for this basic block regAl = copyRegisterAllocation(regAl); basicBlockRA[armletCounter] = regAl; } else { // there is a predefined reg allocation created by a branch // forward to this armlet (or by first armlet initialisation) // need to spill regs from armlet that comes before this one // and set up new regs in it's place (check it's not first armlet // though as there is no current reg al to spill then) if first // armlet just use the regAl there since it's just been // defined empty if(armletCounter > 0) allocateRegAllocation( regAl, basicBlockRA[armletCounter] ); } // ??? unnecessary to shed constant pool to code shedConstantPool(); } // ??? debugging CArmletDisassembler diss; TRACE("generating code @ 0x%x: %s \n", nativeChunk->amountUsed, diss.disassemble(armletCounter, armletChunk[armletCounter]) ); // keep record of where the armlet starts in the code area armletNumberToAreaAddress[armletCounter] = nativeChunk->amountUsed; // choose rule for generating single armlet chooseRule(armletChunk[armletCounter]); } // backpatch list of forward referencing gotos while( !gotoPatchList->empty() ) { // take next goto reference GotoBackpatch* patch = (GotoBackpatch*)gotoPatchList->removeHead(); // get address that goto points to uint32 destination = armletNumberToAreaAddress[patch->armletNumber]; // calculate offset // add 6 for the bytes taken up by the JMP instruction since // the x86 PC points to the location after the JMP instruction uint32 offset = destination - (patch->gotoPtr + 6); TRACE("backpatching: patch->gotoPtr=%d, destination=%d, offset=%d \n", patch->gotoPtr, destination, offset); // skip 2 bytes for opcode and fill in values, low bytes first nativeChunk->area[ patch->gotoPtr + 2 ] = offset & 0xff; nativeChunk->area[ patch->gotoPtr + 3 ] = (offset>>8) & 0xff; nativeChunk->area[ patch->gotoPtr + 4 ] = (offset>>16) & 0xff; nativeChunk->area[ patch->gotoPtr + 5 ] = (offset>>24) & 0xff; } // add native code to code cache //codeCache.addCodeToCache(address, nativeChunk); // dump area to file CFile dumpFile; CString dumpName = _T("D:\\Work\\Project\\dump.obj"); BOOL successfulDumpOpen = dumpFile.Open( dumpName, CFile::modeCreate | CFile::modeWrite | CFile::shareDenyWrite | CFile::typeBinary ); if( successfulDumpOpen ) { dumpFile.Write(nativeChunk->area, nativeChunk->amountUsed); dumpFile.Close(); // invoke external x86 disassembler system("D:\\work\\project\\objdump --target binary --architecture=i386 --disassemble-all D:\\work\\project\\dump.obj > D:\\work\\project\\x86dump.txt"); } else { TRACE("Dump file %s could not be opened \n", dumpName); } // clean up delete []armletNumberToAreaAddress; gotoPatchList->clear(); } // // Chooses the rule to translate a given armlet into x86 // void CGenerator::chooseRule(Armlet* armlet) { switch(armlet->opcode) { // no operands case irCLEARTRANS : chooseClearTrans(armlet); break; case irSETTRANS : chooseSetTrans(armlet); break; case irSETPC : chooseSetPC(armlet); break; // one variable and one value operand, emit1() case irMOVC : chooseMovc(armlet); break; case irGETPC : chooseGetPC(armlet); break; case irINTCHECK : chooseIntCheck(armlet); break; // two variable operands, emit2() case irMOV : chooseMov(armlet); break; case irMVN : chooseMvn(armlet); break; case irTST : chooseTst(armlet); break; case irTEQ : chooseTeq(armlet); break; case irCMP : chooseCmp(armlet); break; case irCMN : chooseCmn(armlet); break; case irRRX : chooseRrx(armlet); break; // three variable operands, emit3() case irAND : chooseAnd(armlet); break; case irEOR : chooseEor(armlet); break; case irSUB : chooseSub(armlet); break; case irADD : chooseAdd(armlet); break; case irADC : chooseAdc(armlet); break; case irSBC : chooseSbc(armlet); break; case irORR : chooseOrr(armlet); break; case irLSL : chooseLsl(armlet); break; case irLSR : chooseLsr(armlet); break; case irASR : chooseAsr(armlet); break; case irROR : chooseRor(armlet); break; case irMUL : chooseMul(armlet); break; case irLDB : chooseLdb(armlet); break; case irSTB : chooseStb(armlet); break; case irLDW : chooseLdw(armlet); break; case irSTW : chooseStw(armlet); break; // one value operand, emitV() case irGOTOEQ : chooseGoto(armlet, irGOTOEQ - irGOTOEQ); break; case irGOTONE : chooseGoto(armlet, irGOTONE - irGOTOEQ); break; case irGOTOCS : chooseGoto(armlet, irGOTOCS - irGOTOEQ); break; case irGOTOCC : chooseGoto(armlet, irGOTOCC - irGOTOEQ); break; case irGOTOMI : chooseGoto(armlet, irGOTOMI - irGOTOEQ); break; case irGOTOPL : chooseGoto(armlet, irGOTOPL - irGOTOEQ); break; case irGOTOVS : chooseGoto(armlet, irGOTOVS - irGOTOEQ); break; case irGOTOVC : chooseGoto(armlet, irGOTOVC - irGOTOEQ); break; case irGOTOHI : chooseGoto(armlet, irGOTOHI - irGOTOEQ); break; case irGOTOLS : chooseGoto(armlet, irGOTOLS - irGOTOEQ); break; case irGOTOGE : chooseGoto(armlet, irGOTOGE - irGOTOEQ); break; case irGOTOLT : chooseGoto(armlet, irGOTOLT - irGOTOEQ); break; case irGOTOGT : chooseGoto(armlet, irGOTOGT - irGOTOEQ); break; case irGOTOLE : chooseGoto(armlet, irGOTOLE - irGOTOEQ); break; case irGOTO : chooseGoto(armlet, irGOTO - irGOTOEQ); break; case irLEAVE : chooseLeave(armlet); break; default: TRACE("ERROR chooseRule() unable to decode armlet opcode 0x%x \n", armlet->opcode); } } void CGenerator::chooseClearTrans(Armlet *armlet) { } void CGenerator::chooseSetTrans(Armlet *armlet) { } void CGenerator::chooseSetPC(Armlet *armlet) { } void CGenerator::chooseMovc(Armlet *armlet) { // add value to constant pool if(armlet->outflags) { // ??? debugging TRACE("ERROR movc shouldn't be setting any outflags! opcode=0x%x \n", armlet->opcode ); } else { // ??? if variable is not an ARM processor flag since these are // made use of implicitly so must be updated eagerly // then can use lazy updating with constant pool addConstant(armlet->rd, armlet->value); TRACE("constant pool addition: v%d = 0x%x \n", armlet->rd, armlet->value ); } } void CGenerator::chooseGetPC(Armlet *armlet) { } void CGenerator::chooseIntCheck(Armlet *armlet) { // generate code to call int check function // note // address of returnVal context[194] // address of arg1 = context[198] // address of arg2 = context[202] // preflags preFlags(armlet->outflags, MOVdef | MOVundef | CALLdef | CALLundef ); // put intcheck argument into arg1 in context int mod = mod_DISP32; uint32 disp = 198; // arg1 address uint8 rm = EBP; uint32 imm = armlet->value; #define MODSIBDISP MODDISP32 assemble(0, MOVlmi, 0); #undef MODSIBDISP // allocate success variable for putting address of // invokeIntCheck() into and call intcheck function imm = (uint32)&invokeIntCheck; uint8 successVarReg = getVariableInRegister(armlet->rd); rm = successVarReg; assemble(0, MOVlri, CALLnar, 0); // move result from memory in context.returnVal into success variable mod = mod_DISP32; disp = 194; // returnVal address rm = EBP; int reg = successVarReg; #define MODSIBDISP MODDISP32 assemble(0, MOVlrm, 0); #undef MODSIBDISP // postflags preFlags(armlet->outflags, MOVundef | CALLundef ); } void CGenerator::chooseMov(Armlet *armlet) { CArmletDisassembler diss; TRACE("actually generating %s \n", diss.disassemble(armletCounter, armlet) ); if(constantValid[armlet->rx]) { // mov ra, # if(armlet->outflags) { // ??? flags need setting } else { // update constant pool constantPool[armlet->rd] = constantPool[armlet->rx]; constantValid[armlet->rd] = TRUE; } } else { if(armlet->rd == armlet->rx) { // mov ra, ra if(armlet->outflags) { genMov1(armlet); } else { // ??? generate a nop placeholder (mov r0,r0 is the recommended // nop on ARM) so that if an instruction were to branch // to the mov, there is something to go to, need to investigate // the use of nops in real ARM code. assemble(0, NOP, 0); } } else { // mov ra, rb genMov1(armlet); } } } void CGenerator::chooseMvn(Armlet *armlet) { } void CGenerator::chooseTst(Armlet *armlet) { } void CGenerator::chooseTeq(Armlet *armlet) { } void CGenerator::chooseCmp(Armlet *armlet) { // ??? same for CMP,CMN,TEQ,TST if(constantValid[armlet->rx]) { if(constantValid[armlet->ry]) { // cmp #,# // ??? set flags return; } else { // cmp #,ra return; } } else { if(constantValid[armlet->ry]) { // cmp ra, # genCmp2(armlet); return; } } if(armlet->rx == armlet->ry) { // cmp ra, ra } else { // cmp ra, rb genCmp1(armlet); } } void CGenerator::chooseCmn(Armlet *armlet) { } void CGenerator::chooseRrx(Armlet *armlet) { } void CGenerator::chooseAnd(Armlet *armlet) { } void CGenerator::chooseEor(Armlet *armlet) { } void CGenerator::chooseSub(Armlet *armlet) { // ??? similar for SBC? if(constantValid[armlet->rx]) { if(constantValid[armlet->ry]) { // sub ra # # 4 } else { // sub ra # ra 3 // sub ra # rb 3 } } else { if(armlet->rd == armlet->rx) { if(constantValid[armlet->ry]) { // sub ra ra # 2 } else { // sub ra ra ra 1 // sub ra ra rb 1 genSub1(armlet); } } else { if(constantValid[armlet->ry]) { // sub ra rb # 5 } else { // sub ra rb rb 6 // sub ra rb ra 6 // sub ra rb rc 6 genSub6(armlet); } } } } void CGenerator::chooseAdd(Armlet *armlet) { // ??? apply to and, or, eor too! // check for constants if(constantValid[armlet->rx]) { if(constantValid[armlet->ry]) { // add ra # # // put value in constant pool switch(armlet->opcode) { // ??? also ADC but can't be constant eval'd since C flag is not constant! case irADD : constantPool[armlet->rd] = constantPool[armlet->rx] + constantPool[armlet->ry]; break; case irEOR : constantPool[armlet->rd] = constantPool[armlet->rx] ^ constantPool[armlet->ry]; break; case irORR : constantPool[armlet->rd] = constantPool[armlet->rx] | constantPool[armlet->ry]; break; case irAND : constantPool[armlet->rd] = constantPool[armlet->rx] & constantPool[armlet->ry]; break; } constantValid[armlet->rd] = TRUE; // if any flags to be set then must calculate them too if(armlet->outflags) { // switch on operation as logic stuff just does N and Z switch(armlet->opcode) { case irADD : updateAddFlags(armlet->rd, armlet->rx, armlet->ry); break; case irEOR : // fall through to irAND case irORR : // fall through to irORR case irAND : updateNZFlags(armlet->rd); break; default : TRACE("ERROR, bad opcode being used in chooseAdd \n"); break; } } return; } else { if(armlet->rd == armlet->ry) { // add ra # ra (same as add ra ra #) // ??? complete switch table switch(armlet->opcode) { case irADD : genAdd1(armlet, constantPool[armlet->rx]); break; } } else { // add ra # rb (same as add ra rb #) switch(armlet->opcode) { case irADD : genAdd2(armlet, armlet->ry, constantPool[armlet->rx]); break; } } return; } } if(constantValid[armlet->ry]) { if(armlet->rd == armlet->rx) { // add ra ra # // ??? complete switch table switch(armlet->opcode) { case irADD : genAdd1(armlet, constantPool[armlet->ry]); break; } } else { // add ra rb # // ??? complete switch table switch(armlet->opcode) { case irADD : genAdd2(armlet, armlet->rx, constantPool[armlet->ry]); break; } } return; } if(armlet->rd == armlet->rx) { // add ra ra ra // add ra ra rb genAdd3(armlet, armlet->rd, armlet->ry); return; } else { if(armlet->rd == armlet->ry) { // add ra rb ra (same as add ra ra rb) genAdd3(armlet, armlet->rd, armlet->rx); return; } else { if(armlet->rx == armlet->ry) { // add ra rb rb genAdd4(armlet, armlet->rx, armlet->rx); } else { // add ra rb rc genAdd4(armlet, armlet->rx, armlet->ry); } } } } void CGenerator::chooseAdc(Armlet *armlet) { } void CGenerator::chooseSbc(Armlet *armlet) { } void CGenerator::chooseOrr(Armlet *armlet) { } void CGenerator::chooseLsl(Armlet *armlet) { } void CGenerator::chooseLsr(Armlet *armlet) { } void CGenerator::chooseAsr(Armlet *armlet) { } void CGenerator::chooseRor(Armlet *armlet) { } void CGenerator::chooseMul(Armlet *armlet) { } void CGenerator::chooseLdb(Armlet *armlet) { } void CGenerator::chooseStb(Armlet *armlet) { } void CGenerator::chooseLdw(Armlet *armlet) { } void CGenerator::chooseStw(Armlet *armlet) { } void CGenerator::chooseGoto(Armlet *armlet, uint8 conditionCode) { // end of basic block so: // ??? shed unused constant pool values to code since may be used in this basic block // ??? would be best if the constant pool were shed direct to memory (where suitable) // in this situation since the basic block has ended, some vars will be left in regs // though so not suitable for all to be shed direct to memory if I do that shedConstantPool(); // deal with reg allocation ready for the goto's destination regAllocateChangeBlock(armlet); // see if goto is forward/backward if(armlet->value <= armletCounter) { // goto to before this instruction // ??? pre flags preFlags(armlet->outflags, Jdef | Jundef); // get relative address // note, x86 PC appears to point to after jump so +6 for number of bytes in jmp instruction uint32 disp = armletNumberToAreaAddress[armlet->value] - (nativeChunk->amountUsed + 6); switch(conditionCode) { case ccEQ : assemble(0, JZnr, 0); break; case ccNE : assemble(0, JNZnr, 0); break; case ccCS : assemble(0, JCnr, 0); break; case ccCC : assemble(0, JNCnr, 0); break; case ccMI : assemble(0, JSnr, 0); break; case ccPL : assemble(0, JNSnr, 0); break; case ccVS : assemble(0, JOnr, 0); break; case ccVC : assemble(0, JNOnr, 0); break; case ccHI : assemble(0, JAnr, 0); break; case ccLS : assemble(0, JBEnr, 0); break; case ccGE : assemble(0, JGEnr, 0); break; case ccLT : assemble(0, JLnr, 0); break; case ccGT : assemble(0, JGnr, 0); break; case ccLE : assemble(0, JLEnr, 0); break; case ccAL : assemble(0, JMPnr, 0); break; } // ??? post flags postFlags(armlet->outflags, Jundef, 0); } else { // goto to this instruction or afterward preFlags(armlet->outflags, Jdef | Jundef); // get relative address uint32 disp = 0; // store reference to this goto for backpatching GotoBackpatch* patch = new GotoBackpatch; patch->armletNumber = armlet->value; patch->gotoPtr = (uint32)nativeChunk->amountUsed; gotoPatchList->addToTail(patch); // ??? debugging TRACE("add to list forwardref nativeChunk->amountUsed=%d armletNumber=%d \n", nativeChunk->amountUsed, armlet->value); switch(conditionCode) { case ccEQ : assemble(0, JZnr, 0); break; case ccNE : assemble(0, JNZnr, 0); break; case ccCS : assemble(0, JCnr, 0); break; case ccCC : assemble(0, JNCnr, 0); break; case ccMI : assemble(0, JSnr, 0); break; case ccPL : assemble(0, JNSnr, 0); break; case ccVS : assemble(0, JOnr, 0); break; case ccVC : assemble(0, JNOnr, 0); break; case ccHI : assemble(0, JAnr, 0); break; case ccLS : assemble(0, JBEnr, 0); break; case ccGE : assemble(0, JGEnr, 0); break; case ccLT : assemble(0, JLnr, 0); break; case ccGT : assemble(0, JGnr, 0); break; case ccLE : assemble(0, JLEnr, 0); break; case ccAL : assemble(0, JMPnr, 0); break; } // ??? post flags postFlags(armlet->outflags, Jundef, 0); } } void CGenerator::chooseLeave(Armlet *armlet) { // end of basic block so: // shed unused constant pool values to code since may be used in this basic block shedConstantPool(); // ??? spill all regs to memory spillAll(); // ??? preflagging is likely to be unnecessary if everything has // just been spilt to memory preFlags(armlet->outflags, MOVdef | MOVundef); // move leave argument to EAX to return it { uint8 rm = EAX; uint32 imm = armlet->value; assemble(0, MOVlri, 0); } // return assemble(0, RET, 0); } // // Assemble x86 instruction (variable argument list function) // Based on work done by Julian Brown in ARMphetamine // void CGenerator::assemble(const int wasteArgument, ... ) { // note, variable argument list functions must have at least 1 argument // that is always passed to them for them to work, hence wasted argument // set up ptr to point to arguments va_list ptr; va_start(ptr, wasteArgument); int bit = 0; int numBits; // total number of bits in this argument int value; // clear first byte in instruction (since later clearing is done at end of loop) nativeChunk->area[nativeChunk->amountUsed] = 0; // while there are more arguments to get (terminate list with 0) while( numBits = va_arg(ptr, uint8) ) { // new byte then clear it??? if(!bit) nativeChunk->area[nativeChunk->amountUsed] = 0; // get value of argument value = va_arg(ptr, uint32); // while there are still bits to output while(numBits > 0) { // calculate how many bits of the value are to be output this time uint8 chunk; if(numBits > 8) chunk = 8; else chunk = numBits; // ??? debugging // TRACE("asm: numBits=%d, chunk=%d, value=0x%x nativeChunk->amountUsed=%d \n", numBits, chunk, value, nativeChunk->amountUsed); // set the bits nativeChunk->area[nativeChunk->amountUsed] |= ((value << bit) & 0xff); // update details of what's been output already bit += chunk; value >>= chunk; numBits -= chunk; // if over into next byte, increment area index if( bit >= 8) { bit -= 8; nativeChunk->amountUsed++; // when moving on to next byte, clear it nativeChunk->area[nativeChunk->amountUsed] = 0; // if area is full, double the area size if(nativeChunk->amountUsed == nativeChunk->allocatedSize) { nativeChunk->allocatedSize *= 2; nativeChunk->area = (uint8*)realloc(nativeChunk->area, nativeChunk->allocatedSize); } } } } // end of variable argument list va_end(ptr); if(bit) TRACE("Instruction that didn't fit into byte output! \n"); } void CGenerator::setCodeCache(CCodeCache *aCodeCache) { // codeCache = aCodeCache; } ////////////////////////////////////////////////////////////////////// // Actual code generation functions ////////////////////////////////////////////////////////////////////// // // updates the N and Z flags according to the constant value // inline void CGenerator::updateNZFlags(uint32 value) { /* clearConditionFlags( N_FLAG | Z_FLAG ); // check N and Z flags if(value == 0) { setConditionFlags(Z_FLAG); } else { if( getNegative(value) ) { setConditionFlags(N_FLAG); } } */ } // // updates NZCV flags for subtractions according to constant values // inline void CGenerator::updateSubFlags(uint32 result, uint32 operand1, uint32 operand2) { /* // all flags need adjusting clearConditionFlags( N_FLAG | Z_FLAG | C_FLAG | V_FLAG ); // carry flag - calculate if there was NOT a borrow from the subtraction // logic borrowed from ArcEm, incidentally the same as ARMphetamine and RedSquirrel if( ( getNegative(operand1) && getPositive(operand2) ) || ( getNegative(operand1) && getPositive(result) ) || ( getPositive(operand2) && getPositive(result) ) ) { setConditionFlags(C_FLAG); } // if need to set V flag if( ( getNegative(operand1) && getPositive(operand2) && getPositive(result) ) || ( getPositive(operand1) && getNegative(operand2) && getNegative(result) ) ) { setConditionFlags(V_FLAG); } // check N and Z flags if(result == 0) { setConditionFlags(Z_FLAG); } else { if( getNegative(result) ) { setConditionFlags(N_FLAG); } } */ } // // updates NZCV flags for additions according to constant values // inline void CGenerator::updateAddFlags(uint32 result, uint32 operand1, uint32 operand2) { /* // all flags need adjusting clearConditionFlags( N_FLAG | Z_FLAG | C_FLAG | V_FLAG ); // check if C or V can possibly need updating if( (operand1 | operand2) >> 30) { // logic borrowed from ArcEm, incidentally the same as ARMphetamine and RedSquirrel // update carry if( ( getNegative(operand1) && getNegative(operand2) ) || ( getNegative(operand1) && getPositive(result) ) || ( getNegative(operand2) && getPositive(result) ) ) { setConditionFlags( C_FLAG ); } // update overflow if( ( getNegative(operand1) && getNegative(operand2) && getPositive(result) ) || ( getPositive(operand1) && getPositive(operand2) && getNegative(result) ) ) { setConditionFlags( V_FLAG ); } } // check N and Z flags if(result == 0) { setConditionFlags(Z_FLAG); } else { if( getNegative(result) ) { setConditionFlags(N_FLAG); } } */ } // // takes an armlet variable and returns an x86 register that's allocated to it // the x86 reg does not (necessarily) contain the value for that variable // this is used for getting a register for instruction destinations. // spill contents of x86 reg before hand if necessary // uint8 CGenerator::getVariableInRegister(uint8 variable) { // ??? debugging TRACE("getVariableInRegister variable=%d regAl->varLocation[variable].location=%d \n", variable, regAl->varLocation[variable].location); // if variable is in a register already if( regAl->varLocation[variable].location == locNATIVEREGISTER) { // return that register return regAl->varLocation[variable].reg; } else { // search through native regs to find one that's unused for(int nativeReg = 0; nativeRegregisterUse[nativeReg] == vUNUSED ) { // found one that's unused so set up details regAl->registerUse[nativeReg] = variable; regAl->varLocation[variable].location = locNATIVEREGISTER; regAl->varLocation[variable].reg = nativeReg; // return value return nativeReg; } } // all native regs are used, so we must spill one // define array detailing which x86 register allocations are used locally int usedLocally[EDI]; for(int localReg = 0; localRegvarLocation[ armletChunk[armletCounter]->rd ].location == locNATIVEREGISTER ) usedLocally[ armletChunk[armletCounter]->rd ]+=3; if( regAl->varLocation[ armletChunk[armletCounter]->rx ].location == locNATIVEREGISTER ) usedLocally[ armletChunk[armletCounter]->rx ]+=2; if( regAl->varLocation[ armletChunk[armletCounter]->ry ].location == locNATIVEREGISTER ) usedLocally[ armletChunk[armletCounter]->ry ]+=2; // do the same for the following armlet (if there is one) if(armletCounter + 1 < totalArmlets) { if( regAl->varLocation[ armletChunk[armletCounter+1]->rd ].location == locNATIVEREGISTER ) usedLocally[ armletChunk[armletCounter+1]->rd ]+=2; if( regAl->varLocation[ armletChunk[armletCounter+1]->rx ].location == locNATIVEREGISTER ) usedLocally[ armletChunk[armletCounter+1]->rx ]++; if( regAl->varLocation[ armletChunk[armletCounter+1]->ry ].location == locNATIVEREGISTER ) usedLocally[ armletChunk[armletCounter+1]->ry ]++; } // find first reg that's not used in this armlet nor next one // then find first reg not used in this armlet // then just find first reg int position = 0; while(position < 3) { for(int selectedReg = 0; selectedReg < EDI; selectedReg++) { if(usedLocally[selectedReg] == position) { // find out what armlet var was in that x86 reg uint8 displacedVariable = regAl->registerUse[selectedReg]; // spill to memory int mod = mod_DISP32; int disp = getVariableOffset(displacedVariable); int rm = EBP; int reg = selectedReg; #define MODSIBDISP MODDISP32 if(displacedVariable>= vNFLAG && displacedVariable <= vFFLAG) assemble(0, MOVbmr, 0); else assemble(0, MOVlmr, 0); #undef MODSIBDISP regAl->varLocation[ displacedVariable ].location = locMEMORY; // found suitable reg to spill regAl->registerUse[selectedReg] = variable; regAl->varLocation[variable].location = locNATIVEREGISTER; regAl->varLocation[variable].reg = selectedReg; // return value return selectedReg; } } // look for registers closer to current armlet position++; } // it must have found a register by now TRACE("ERROR - no x86 reg found to spill to! \n"); return 0; } } // // Same as getVariableInRegister except fetches value from memory into // native register if it's not in one already // uint8 CGenerator::putVariableInRegister(uint8 variable) { // this is the same as getVariableInRegister except that // when a register has to be allocated (i.e. the variable wasn't // already in a register) the variable value is loaded into that register // if variable is in a register already if( regAl->varLocation[variable].location == locNATIVEREGISTER) { // return that register return regAl->varLocation[variable].reg; } else { // search through native regs to find one that's unused for(int nativeReg = 0; nativeRegregisterUse[nativeReg] == vUNUSED ) { // found one that's unused so set up details regAl->registerUse[nativeReg] = variable; regAl->varLocation[variable].location = locNATIVEREGISTER; regAl->varLocation[variable].reg = nativeReg; // fetch variable from memory int mod = mod_DISP32; int disp = getVariableOffset(variable); int rm = EBP; int reg = nativeReg; #define MODSIBDISP MODDISP32 if(variable>= vNFLAG && variable <= vFFLAG) assemble(0, MOVZXlrbm, 0); else assemble(0, MOVlrm, 0); #undef MODSIBDISP // return register used return nativeReg; } } // all native regs are used, so we must spill one // define array detailing which x86 register allocations are used locally int usedLocally[EDI]; for(int localReg = 0; localRegvarLocation[ armletChunk[armletCounter]->rd ].location == locNATIVEREGISTER ) usedLocally[ armletChunk[armletCounter]->rd ]+=3; if( regAl->varLocation[ armletChunk[armletCounter]->rx ].location == locNATIVEREGISTER ) usedLocally[ armletChunk[armletCounter]->rx ]+=2; if( regAl->varLocation[ armletChunk[armletCounter]->ry ].location == locNATIVEREGISTER ) usedLocally[ armletChunk[armletCounter]->ry ]+=2; // do the same for the following armlet (if there is one) if(armletCounter + 1 < totalArmlets) { if( regAl->varLocation[ armletChunk[armletCounter+1]->rd ].location == locNATIVEREGISTER ) usedLocally[ armletChunk[armletCounter+1]->rd ]+=2; if( regAl->varLocation[ armletChunk[armletCounter+1]->rx ].location == locNATIVEREGISTER ) usedLocally[ armletChunk[armletCounter+1]->rx ]++; if( regAl->varLocation[ armletChunk[armletCounter+1]->ry ].location == locNATIVEREGISTER ) usedLocally[ armletChunk[armletCounter+1]->ry ]++; } // find first reg that's not used in this armlet nor next one // then find first reg not used in this armlet // then just find first reg int position = 0; while(position < 3) { for(int selectedReg = 0; selectedReg < EDI; selectedReg++) { if(usedLocally[selectedReg] == position) { // find out what armlet var was in that x86 reg uint8 displacedVariable = regAl->registerUse[selectedReg]; // spill to memory int mod = mod_DISP8; int disp = getVariableOffset(displacedVariable); int rm = EBP; int reg = selectedReg; #define MODSIBDISP MODDISP8 if(displacedVariable>= vNFLAG && displacedVariable <= vFFLAG) assemble(0, MOVbmr, 0); else assemble(0, MOVlmr, 0); #undef MODSIBDISP regAl->varLocation[ displacedVariable ].location = locMEMORY; // found suitable reg to spill regAl->registerUse[selectedReg] = variable; regAl->varLocation[variable].location = locNATIVEREGISTER; regAl->varLocation[variable].reg = selectedReg; // fetch variable from memory mod = mod_DISP8; disp = getVariableOffset(variable); rm = EBP; reg = selectedReg; #define MODSIBDISP MODDISP8 if(displacedVariable>= vNFLAG && displacedVariable <= vFFLAG) assemble(0, MOVZXlrbm, 0); else assemble(0, MOVlrm, 0); #undef MODSIBDISP // return value return selectedReg; } } // look for registers closer to current armlet position++; } // it must have found a register by now TRACE("ERROR - no x86 reg found to spill to! \n"); return 0; } } // // return offset (in bytes) from base of context to the armlet variable specified // uint8 CGenerator::getVariableOffset(uint8 variable) { // has to deal with fact that flags are stored in bytes // (for easy access from native flags) if(variable < vNFLAG) { return 4*variable; } else { if(variable < vUNUSED) { return (4*vPC) + (variable - vPC); } else { return (4*vPC) + 7 + (4* (variable - vT0) ); } } } // // ARM flags are kept in x86 flags most of the time except if an // instruction is going to change them differently to the way the // ARM instruction would in which case they are spilled to memory // in advance. // void CGenerator::preFlags(uint8 outflags, uint8 nativeChanged) { // find out which flags the x86 instruction changes but // the ARM one doesn't (hence which ones need spilling) uint32 spillFlags = nativeChanged & ~outflags; // for each flag, check if it's to be spilt and check if it's in // the native flag. It could be in native reg or memory already // in which case we don't need to spill it int mod = mod_DISP8; // N flag if( (spillFlags & fN) && (regAl->varLocation[vNFLAG].location = locNATIVEFLAG) ) { uint8 rm = EBP; uint8 disp = getVariableOffset(vNFLAG); #define MODSIBDISP MODDISP8 assemble(0, SETSm, 0); #undef MODSIBDISP regAl->varLocation[vNFLAG].location = locMEMORY; } // Z flag if( (spillFlags & fZ) && (regAl->varLocation[vZFLAG].location = locNATIVEFLAG) ) { uint8 rm = EBP; uint8 disp = getVariableOffset(vZFLAG); #define MODSIBDISP MODDISP8 assemble(0, SETZm, 0); #undef MODSIBDISP regAl->varLocation[vZFLAG].location = locMEMORY; } // C flag if( (spillFlags & fC) && (regAl->varLocation[vCFLAG].location = locNATIVEFLAG) ) { uint8 rm = EBP; uint8 disp = getVariableOffset(vCFLAG); #define MODSIBDISP MODDISP8 assemble(0, SETCm, 0); #undef MODSIBDISP regAl->varLocation[vCFLAG].location = locMEMORY; } // V flag if( (spillFlags & fV) && (regAl->varLocation[vVFLAG].location = locNATIVEFLAG) ) { uint8 rm = EBP; uint8 disp = getVariableOffset(vVFLAG); #define MODSIBDISP MODDISP8 assemble(0, SETOm, 0); #undef MODSIBDISP regAl->varLocation[vVFLAG].location = locMEMORY; } } // // flags that the x86 instruction changed but the ARM one doesn't are // restored to ARM values from memory. Any flags that the ARM one changes // but the x86 one doesn't are also updated. // void CGenerator::postFlags(uint8 outflags, uint8 nativeChanged, uint8 destReg) { // if cmp, sbb or sub then inverts C flag // if N and Z weren't updated by the x86 command (e.g. MOV) and they // should be because the ARM instruction was MOVS then use TEST to do work if( (outflags & (fN|fZ)) && !(nativeChanged & fN) && !(nativeChanged | fZ) ) { uint32 imm = 0; uint8 rm = destReg; preFlags(TESTdef, TESTdef|TESTundef); assemble(0, TESTlri, 0); nativeChanged |= (fN|fZ); } // update info on where flags are stored according to which ones have changed nativeChanged &= outflags; if(nativeChanged & fN) regAl->varLocation[vNFLAG].location = locNATIVEFLAG; if(nativeChanged & fZ) regAl->varLocation[vZFLAG].location = locNATIVEFLAG; if(nativeChanged & fC) regAl->varLocation[vCFLAG].location = locNATIVEFLAG; if(nativeChanged & fV) regAl->varLocation[vVFLAG].location = locNATIVEFLAG; } // add ra ra # void CGenerator::genAdd1(Armlet* armlet, uint32 value) { // add value to armlet->rd preFlags(armlet->outflags, ADDdef|ADDundef); // put ra in register and add immediate to it uint8 rm = getVariableInRegister(armlet->rd); uint32 imm = value; assemble(0, ADDlri, 0); postFlags(armlet->outflags, ADDundef, rm); constantValid[armlet->rd] = FALSE; } // add ra rb # // add ra # rb void CGenerator::genAdd2(Armlet* armlet, uint8 rb, uint32 value) { uint8 rm = getVariableInRegister(armlet->rd); uint8 reg = putVariableInRegister(rb); uint32 imm = value; // mov rb to armlet->rd // add constant to rd preFlags(armlet->outflags, MOVdef | MOVundef | ADDdef | ADDundef); assemble(0, MOVlrr, ADDlri, 0); postFlags(armlet->opcode, MOVundef | ADDundef, rm); constantValid[armlet->rd] = FALSE; } // add ra ra ra // add ra ra rb // add ra rb ra (same as add ra ra rb) void CGenerator::genAdd3(Armlet *armlet, uint8 ra, uint8 rb) { // add rb to ra preFlags(armlet->outflags, ADDdef | ADDundef); TRACE("genAdd3 armlet->rd=%d ra=%d rb=%d \n", armlet->rd, ra, rb); if( regAl->varLocation[rb].location == locNATIVEREGISTER ) { uint8 rm = putVariableInRegister(ra); uint8 reg = getVariableInRegister(rb); assemble(0, ADDlrr, 0); postFlags(armlet->opcode, ADDundef, rm); } else { uint8 reg = putVariableInRegister(ra); uint8 rm = EBP; uint8 disp = getVariableOffset(rb); int mod = mod_DISP8; #define MODSIBDISP MODDISP8 assemble(0, ADDlrm, 0); #undef MODSIBDISP postFlags(armlet->opcode, ADDundef, reg); } constantValid[armlet->rd] = FALSE; } // add ra rb rb // add ra rb rc void CGenerator::genAdd4(Armlet *armlet, uint8 rb, uint8 rc) { // mov rb to rd // add rc to rd preFlags(armlet->outflags, MOVdef | MOVundef | ADDdef | ADDundef); uint8 dest; if( regAl->varLocation[rb].location == locNATIVEREGISTER) { // if rb in reg already then move rb into rd uint8 rm = getVariableInRegister(armlet->rd); uint8 reg = getVariableInRegister(rb); assemble(0, MOVlrr, 0); dest = rm; } else { // otherwise fetch rb from memory into rd uint8 reg = getVariableInRegister(rb); uint8 rm = EBP; uint8 disp = getVariableOffset(rb); int mod = mod_DISP8; #define MODSIBDISP MODDISP8 assemble(0, MOVlrm, 0); #undef MODSIBDISP dest = reg; } if( regAl->varLocation[rc].location == locNATIVEREGISTER) { // if second operand in reg already then add rc to dest uint8 rm = dest; uint8 reg = getVariableInRegister(rc); assemble(0, ADDlrr, 0); } else { // else add rc from memory into dest uint8 reg = dest; uint8 rm = EBP; uint8 disp = getVariableOffset(rc); int mod = mod_DISP8; #define MODSIBDISP MODDISP8 assemble(0, ADDlrm, 0); #undef MODSIBDISP } postFlags(armlet->opcode, MOVundef | ADDundef, dest); constantValid[armlet->rd] = FALSE; } // mov ra, ra // mov rb, rb void CGenerator::genMov1(Armlet *armlet) { preFlags(armlet->outflags, MOVdef | MOVundef); int dest; if( regAl->varLocation[armlet->rx].location == locNATIVEREGISTER) { // if rx in reg already then move rb into rd uint8 rm = getVariableInRegister(armlet->rd); uint8 reg = putVariableInRegister(armlet->rx); assemble(0, MOVlrr, 0); dest = rm; } else { // otherwise fetch rb from memory into rd uint8 reg = getVariableInRegister(armlet->rd); uint8 rm = EBP; uint8 disp = getVariableOffset(armlet->rx); int mod = mod_DISP8; #define MODSIBDISP MODDISP8 assemble(0, MOVlrm, 0); #undef MODSIBDISP dest = reg; } postFlags(armlet->opcode, MOVundef, dest); constantValid[armlet->rd] = FALSE; } // mov ra, # (used for spilling constant pool etc.) void CGenerator::genMov2(uint8 rd, uint32 value) { preFlags(fNONE, MOVdef | MOVundef); uint8 rm = getVariableInRegister(rd); uint32 imm = value; assemble(0, MOVlri, 0); postFlags(fNONE, MOVundef, rm); } // cmp ra,rb void CGenerator::genCmp1(Armlet *armlet) { preFlags(armlet->outflags, CMPdef | CMPundef ); if( regAl->varLocation[armlet->ry].location == locNATIVEREGISTER) { // if ry in reg already then do comparison reg to reg uint8 rm = putVariableInRegister(armlet->rx); uint8 reg = putVariableInRegister(armlet->ry); assemble(0, CMPlrr, 0); if(armlet->outflags & fC) assemble(0, CMC, 0); } else { // otherwise fetch rx from memory into rd and to // comparison with ry still in memory uint8 reg = putVariableInRegister(armlet->rx); uint8 rm = EBP; uint8 disp = getVariableOffset(armlet->ry); int mod = mod_DISP8; #define MODSIBDISP MODDISP8 assemble(0, CMPlrm, 0); #undef MODSIBDISP if(armlet->outflags & fC) assemble(0, CMC, 0); } // note, don't worry about dest reg, it doesn't affect cmp as it sets all flags postFlags(armlet->opcode, CMPundef, 0); } // // add constant to pool // void CGenerator::addConstant(uint8 variable, uint32 value) { constantPool[variable] = value; constantValid[variable] = TRUE; } // sub ra ra ra // sub ra ra rb void CGenerator::genSub1(Armlet *armlet) { preFlags(armlet->outflags, SUBdef | SUBundef ); uint8 dest; if( regAl->varLocation[armlet->ry].location == locNATIVEREGISTER ) { // if ry in register uint8 rm = putVariableInRegister(armlet->rd); uint8 reg = putVariableInRegister(armlet->ry); assemble(0, SUBlrr, 0); dest = rm; } else { // if ry in memory uint8 reg = putVariableInRegister(armlet->rd); uint8 rm = EBP; uint8 disp = getVariableOffset(armlet->ry); int mod = mod_DISP8; #define MODSIBDISP MODDISP8 assemble(0, SUBlrm, 0); #undef MODSIBDISP dest = reg; } // deal with inverted carry flag if(armlet->outflags & fC) assemble(0, CMC, 0); postFlags(armlet->opcode, SUBundef, dest); } // sub ra rb rb // sub ra rb ra // sub ra rb rc void CGenerator::genSub6(Armlet *armlet) { preFlags(armlet->outflags, SUBdef | SUBundef | MOVdef | MOVundef ); uint8 dest; // move rx into rd if( regAl->varLocation[armlet->rx].location == locNATIVEREGISTER) { // if rx in register uint8 rm = getVariableInRegister(armlet->rd); uint8 reg = putVariableInRegister(armlet->rx); assemble(0, MOVlrr, 0); dest = rm; } else { // if rx in memory uint8 reg = getVariableInRegister(armlet->rd); uint8 rm = EBP; uint8 disp = getVariableOffset(armlet->rx); int mod = mod_DISP8; #define MODSIBDISP MODDISP8 assemble(0, MOVlrm, 0); #undef MODSIBDISP dest = reg; } // subtract ry from rd if( regAl->varLocation[armlet->ry].location == locNATIVEREGISTER) { // if ry in register uint8 rm = dest; uint8 reg = putVariableInRegister(armlet->ry); assemble(0, SUBlrr, 0); } else { // if ry in memory uint8 reg = dest; uint8 rm = EBP; uint8 disp = getVariableOffset(armlet->ry); int mod = mod_DISP8; #define MODSIBDISP MODDISP8 assemble(0, SUBlrm, 0); #undef MODSIBDISP } // deal with inverted carry flag if(armlet->outflags & fC) assemble(0, CMC, 0); postFlags(armlet->opcode, SUBundef, dest); } // cmp ra, # void CGenerator::genCmp2(Armlet *armlet) { preFlags(armlet->outflags, CMPdef | CMPundef ); if( regAl->varLocation[armlet->rx].location == locNATIVEREGISTER) { // if rx in reg already then do comparison reg to imm uint8 rm = putVariableInRegister(armlet->rx); uint32 imm = constantPool[armlet->ry]; assemble(0, CMPlri, 0); if(armlet->outflags & fC) assemble(0, CMC, 0); } else { // otherwise do cmp with rx still in memory uint32 imm = constantPool[armlet->ry]; uint8 rm = EBP; uint8 disp = getVariableOffset(armlet->rx); int mod = mod_DISP8; #define MODSIBDISP MODDISP8 assemble(0, CMPlmi, 0); #undef MODSIBDISP if(armlet->outflags & fC) assemble(0, CMC, 0); } // note, don't worry about dest reg, it doesn't affect cmp as it sets all flags postFlags(armlet->opcode, CMPundef, 0); } // // shed all constants in constant pool to code so that they're allocated // void CGenerator::shedConstantPool() { // ??? don't spill temps since they are never used across // basic block boundaries, need to confirm I never misused them int currentVar; for(currentVar=0; currentVarvalue] != NULL) { // so spill our regs and allocate theres as necessary // in order to match their reg allocation allocateRegAllocation(regAl, basicBlockRA[armlet->value]); } else { // else it doesn't have a reg allocation already so // copy my reg allocation to it basicBlockRA[armlet->value] = copyRegisterAllocation(regAl); } // note, armlet following this goto will deal with allocating regs for itself // so don't need to worry about this } // // called before a leave is generated so that everything can be spilled to memory // void CGenerator::spillAll() { } // // make copy of a register allocation // return a ptr to a new RA that is identical to the one passed to this // RegisterAllocation* CGenerator::copyRegisterAllocation(RegisterAllocation *sourceRA) { // ??? is there an easier way to copy structs built into C++? please? // should be able to cast both to struct-object and copy that way // using '=', then returning just pointer NB suggests: // memcpy((void *) X, (void *) &OldStruct, sizeof(*X)); RegisterAllocation* newRegAl = new RegisterAllocation; // copy all armlet variable to location details for(int varCounter = 0; varCountervarLocation[varCounter].location = sourceRA->varLocation[varCounter].location; newRegAl->varLocation[varCounter].reg = sourceRA->varLocation[varCounter].reg; } // copy all x86 reg to armlet variable details for(int nativeReg=0; nativeRegregisterUse[nativeReg] = sourceRA->registerUse[nativeReg]; } return newRegAl; } // // spill old register allocation and allocate new register allocation // making sure there's no overlap creating unnecessary spills // called when leader found with predefined reg allocation // void CGenerator::allocateRegAllocation(RegisterAllocation *oldRegAl, RegisterAllocation *newRegAl) { TRACE(" calling allocateRegAllocation() armletCounter=%d \n", armletCounter ); // go through native regs for(int nativeReg=0; nativeRegregisterUse[nativeReg] = %d \n" ,oldRegAl->registerUse[nativeReg]); TRACE("newRegAl->registerUse[nativeReg] = %d \n" ,newRegAl->registerUse[nativeReg]); // if allocation to this reg is different if( oldRegAl->registerUse[nativeReg] != newRegAl->registerUse[nativeReg] ) { // can't see if the oldreg is allocated somewhere in the new reg // as it gets too complex trying to put the value previously // stored in this variable's new register somewhere while // we do the swap, this is not a compiler, just spill to memory // if reg wasn't unused if(oldRegAl->registerUse[nativeReg] != vUNUSED) { uint8 displacedVariable = oldRegAl->registerUse[nativeReg]; // spill old var to memory int mod = mod_DISP32; int disp = getVariableOffset(displacedVariable); int rm = EBP; int reg = nativeReg; #define MODSIBDISP MODDISP32 if(displacedVariable>= vNFLAG && displacedVariable <= vFFLAG) assemble(0, MOVbmr, 0); else assemble(0, MOVlmr, 0); #undef MODSIBDISP } // load new var from memory if new reg isn't unused if(newRegAl->registerUse[nativeReg] != vUNUSED) { // fetch new variable from memory uint8 newVariable = newRegAl->registerUse[nativeReg]; int mod = mod_DISP32; int disp = getVariableOffset(newVariable); int rm = EBP; int reg = nativeReg; #define MODSIBDISP MODDISP32 if(newVariable>= vNFLAG && newVariable <= vFFLAG) assemble(0, MOVZXlrbm, 0); else assemble(0, MOVlrm, 0); #undef MODSIBDISP } // ??? also need to check through varLocation for where to put // things like flags etc. } } }