Index: vm/jitrino/src/codegenerator/ia32/Ia32DCE.cpp =================================================================== --- vm/jitrino/src/codegenerator/ia32/Ia32DCE.cpp (revision 476034) +++ vm/jitrino/src/codegenerator/ia32/Ia32DCE.cpp (working copy) @@ -62,7 +62,9 @@ irManager->getLiveAtExit(node, ls); for (Inst * inst=(Inst*)node->getLastInst(), * prevInst=NULL; inst!=NULL; inst=prevInst){ prevInst=inst->getPrevInst(); - bool deadInst=!inst->hasSideEffect(); + // Prevent debug traps or instructions with side effects + // like (MOVS) from being removed. + bool deadInst=!inst->hasSideEffect() && (inst->getMnemonic() != Mnemonic_INT3); if (deadInst){ if (inst->hasKind(Inst::Kind_CopyPseudoInst)){ Opnd * opnd=inst->getOpnd(1); Index: vm/jitrino/src/codegenerator/ia32/Ia32IRManager.cpp =================================================================== --- vm/jitrino/src/codegenerator/ia32/Ia32IRManager.cpp (revision 476034) +++ vm/jitrino/src/codegenerator/ia32/Ia32IRManager.cpp (working copy) @@ -25,6 +25,7 @@ #include "Log.h" #include "Ia32Printer.h" #include "Ia32CodeGenerator.h" +#include "Dominator.h" #include "float.h" #include @@ -422,7 +423,7 @@ if (opnd0!=NULL){ opnds[i] = opnd0; i++; if (opnd1!=NULL){ opnds[i] = opnd1; i++; if (opnd2!=NULL){ opnds[i] = opnd2; i++; - if (opnd3!=NULL){ opnds[i] = opnd3; i++; assert(opnd3->getSize()==OpndSize_64); + if (opnd3!=NULL){ opnds[i] = opnd3; i++; }}}}; inst->defOpndCount=defCount; inst->opndCount = i; @@ -2206,7 +2207,22 @@ } } +void SessionAction::computeDominators(void) +{ + ControlFlowGraph* cfg = irManager->getFlowGraph(); + DominatorTree* dominatorTree = cfg->getDominatorTree(); + if(dominatorTree != NULL && dominatorTree->isValid()) { + // Already valid. + return; + } + static CountTime computeDominatorsTimer("ia32::helper::computeDominators"); + AutoTimer tm(computeDominatorsTimer); + DominatorBuilder db; + dominatorTree = db.computeDominators(irManager->getMemoryManager(), cfg,false,true); + cfg->setDominatorTree(dominatorTree); +} + } //namespace Ia32 } //namespace Jitrino Index: vm/jitrino/src/codegenerator/ia32/Ia32InstCodeSelector.cpp =================================================================== --- vm/jitrino/src/codegenerator/ia32/Ia32InstCodeSelector.cpp (revision 476034) +++ vm/jitrino/src/codegenerator/ia32/Ia32InstCodeSelector.cpp (working copy) @@ -804,13 +804,22 @@ srcOpnd2=(Opnd*)convert(src2, dstType); #ifndef _EM64T_ + // + // NOTE: as we don't have IREM mnemonic, then generate I8Inst + // with IDIV mnemonic. The 4th fake non-zero argument means + // what we really need REM, not DIV. + // This is handled specially in I8Lowerer. + // The scheme with the fake arg looks ugly, might need to + // reconsider. + // + Opnd* fakeReminderFlag = NULL; if (rem) { - Opnd * args[]={ srcOpnd1, srcOpnd2 }; - CallInst * callInst=irManager.newRuntimeHelperCallInst(CompilationInterface::Helper_RemI64, 2, args, dst); - appendInsts(callInst); - } else { - appendInsts(irManager.newI8PseudoInst(Mnemonic_IDIV, 1, dst,srcOpnd1,srcOpnd2)); + Type* int32type = irManager.getTypeFromTag(Type::Int32); + fakeReminderFlag = irManager.newImmOpnd(int32type, 12345678); } + Inst* ii = irManager.newI8PseudoInst(Mnemonic_IDIV, 1, dst, srcOpnd1, srcOpnd2, fakeReminderFlag); + appendInsts(ii); + #else Opnd * dstOpnd0=irManager.newOpnd(dstType); Opnd * dstOpnd1=irManager.newOpnd(dstType); Index: vm/jitrino/src/codegenerator/ia32/Ia32CgUtils.h =================================================================== --- vm/jitrino/src/codegenerator/ia32/Ia32CgUtils.h (revision 0) +++ vm/jitrino/src/codegenerator/ia32/Ia32CgUtils.h (revision 0) @@ -0,0 +1,292 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @author Alexander Astapchuk + * @version $Revision$ + */ + +#if !defined(__IA32_CGUTILS_INCLUDED__) +#define __IA32_CGUTILS_INCLUDED__ + +#include "Ia32IRManager.h" + +namespace Jitrino { +namespace Ia32 { + +class IRManagerHolder { +public: + IRManagerHolder() + { + m_irManager = NULL; + } + + void setIRManager(IRManager* irm) + { + m_irManager = irm; + } + IRManager* getIRManager(void) + { + return m_irManager; + } +protected: + IRManager* m_irManager; +}; + +/** + * A mix-in that provides various Opnd manipulations IRManager. + */ +class OpndUtils : virtual protected IRManagerHolder { +public: + OpndUtils() + { + m_opndIntZero = NULL; + m_opndDoubleZero = NULL; + m_opndFloatZero = NULL; + } + + /** + * Tests whether the Opnd has only one def. + */ + static bool isSingleDef(const Opnd* opnd); + + /** + * Tests whether the given operand is placed on (any) register and + * optionally test against the specified RegName (\c what). + */ + static bool isReg(const Opnd* op, RegName what = RegName_Null); + + /** + * Tests whether the given operand is placed on XMM register and + * optionally test against the specified RegName (\c what). + */ + static bool isXmmReg(const Opnd* op, RegName what = RegName_Null); + + /** + * Tests whether the given operand is placed on memory. + */ + static bool isMem(const Opnd* op); + + /** + * Tests whether the operand is immediate. + * + * @note The method only returns \n true for immediate-s without + * RuntimeInfo. This is because the real value of immediate with + * RuntimeInfo is unknown until very last - they get resolved only + * in CodeEmitter. + */ + static bool isImm(const Opnd* op); + + /** + * Tests whether the Opnd is immediate and has the provided value. + * + * @note See note at isImm(const Opnd* op). + */ + static bool isImm(const Opnd* op, int iVal); + + /** + * Tests whether the Opnd is immediate of the zero value. + * + * No more than a named shortcut for isImm(op, 0). + */ + static bool isZeroImm(const Opnd* op); + + /** + * Tests whether the Opnd is Type::Int8 immediate. + * + * @note See note at isImm(const Opnd* op). + */ + static bool isImm8(const Opnd* op); + + /** + * Tests whether the Opnd is Type::Int32 immediate. + * + * @note See note at isImm(const Opnd* op). + */ + static bool isImm32(const Opnd* op); + + /** + * Tests whether the Opnd is an immediate (note: of \b any type) + * and whether it is equal to the provided value. + * + * @note See note at isImm(const Opnd* op). + */ + static bool isFPConst(const Opnd* op, double d); + + /** + * Tests whether the Opnd is an immediate (note: of \b any type) + * and whether it is equal to the provided value. + * + * @note See note at isImm(const Opnd* op). + */ + static bool isFPConst(const Opnd* op, float f); + + /** + * Tests whether the Opnd is a constant area item. + */ + static bool isConstAreaItem(const Opnd* op); + + /** + * Extracts address of constant area item. + * + * @return NULL if \c op is not a constant area item. + */ + static const void* extractAddrOfConst(const Opnd* op); + + /** + * Extracts integer constant from constant area item. + * + * @note The \c op must be the constant item. + */ + static int extractIntConst(const Opnd* op); + + /** + * Extracts double constant from constant area item. + * + * @note The \c op must be the constant item. + */ + static double extractDoubleConst(const Opnd* op); + + /** + * Extracts float constant from constant area item. + * + * @note The \c op must be the constant item. + */ + static float extractFloatConst(const Opnd* op); + + /** + * Tests whether the provided Opnd is immediate and its value may be + * placed in a single byte. + */ + static bool fitsImm8(const Opnd* op); + + /** + * Tests 2 operands for equality. + */ + static bool equals(const Opnd* a, const Opnd* b); + // + // The following are not static and require IRManager + // + Opnd* convertImmToImm8(Opnd* imm); + Opnd* convertToXmmReg64(Opnd* xmmReg); + + Opnd* getIntZeroConst(void); + Opnd* getDoubleZeroConst(void); + Opnd* getFloatZeroConst(void); + Opnd* getZeroConst(Type* type); + +private: + Opnd* m_opndIntZero; + Opnd* m_opndDoubleZero; + Opnd* m_opndFloatZero; +}; + +class InstUtils : virtual protected IRManagerHolder { +public: + static bool isPseudoInst(const Inst*); + static void removeInst(Inst* toBeRemoved); + static void replaceInst(Inst* old, Inst* brandNewInst); +}; + +class SubCfgBuilderUtils : virtual protected IRManagerHolder { +public: + SubCfgBuilderUtils() + { + m_subCFG = NULL; + m_currNode = NULL; + } + /** + * Creates new sub CFG and makes it current. + */ + ControlFlowGraph* newSubGFG(bool withReturn=true, bool withUnwind=false); + /** + * Creates new basic block and makes it current. + */ + BasicBlock* newBB(void); + /** + * Sets current node to operate on. + */ + Node* setCurrentNode(Node* node); + /** + * Returns current node. + */ + Node* getCurrentNode(void) const; + /** + * Returns entry node of current subCFG. + */ + Node* getSubCfgEntryNode(void); + /** + * Returns return node of current subCFG. + */ + Node* getSubCfgReturnNode(void); + /** + * Sets current subCFG to operate on. + */ + ControlFlowGraph* setSubCFG(ControlFlowGraph* subCFG); + /** + * Returns current subCFG. + */ + ControlFlowGraph* getSubCFG(void); + /** + * Replaces the given Inst with the subCFG just built, and clears + * current subCFG. + * + * Also invokes minor cleanup on CFG when \c purgeEmptyNodes == \c true. + */ + void propagateSubCFG(Inst* inst, bool purgeEmptyNodes = true); + /** + * Creates an instruction with the given mnemonic and arguments and + * adds it to the end of current node. + * + * Special handling for Mnemonic_MOV: CopyPseudoInstruction + * is generated. + */ + Inst* newInst(Mnemonic mn, unsigned defsCount, + Opnd* op0 = NULL, Opnd* op1 = NULL, Opnd* op2 = NULL, + Opnd* op3 = NULL, Opnd* op4 = NULL, Opnd* op5 = NULL); + /** + * Creates an instruction with the given mnemonic and arguments and + * adds it to the end of current node. + * + * Special handling for Mnemonic_MOV: CopyPseudoInstruction + * is generated. + */ + Inst* newInst(Mnemonic mn, + Opnd* op0 = NULL, Opnd* op1 = NULL, Opnd*op2 = NULL); + /** + * Generates branch instruction with the given mnemonic and given target + * nodes, adds it to the end of current node, and also adds edges + * from current to provided nodes. + */ + Inst* newBranch(Mnemonic mn, Node* trueTarget, Node* falseTarget, + double trueProbability=0.5, double falseProbability=0.5); + /** + * Creates an edge from the current node to the specified node. + */ + void connectNodeTo(Node* to); + /** + * Creates an edge from \c from node, to the \c to node. + */ + void connectNodes(Node* from, Node* to); +private: + ControlFlowGraph * m_subCFG; + Node* m_currNode; +}; + + +}}; // ~namespace Jitrino::Ia32 + +#endif // ~ifndef __IA32_CGUTILS_INCLUDED__ Index: vm/jitrino/src/codegenerator/ia32/Ia32I8Lowerer.cpp =================================================================== --- vm/jitrino/src/codegenerator/ia32/Ia32I8Lowerer.cpp (revision 476034) +++ vm/jitrino/src/codegenerator/ia32/Ia32I8Lowerer.cpp (working copy) @@ -16,35 +16,174 @@ */ /** * @author Nikolay A. Sidelnikov - * @version $Revision: 1.12.6.2.4.3 $ + * @version $Revision$ */ #include "Ia32IRManager.h" #include "Ia32Inst.h" +#include "Dominator.h" +#include "Ia32CgUtils.h" -namespace Jitrino -{ +namespace Jitrino { namespace Ia32 { -class I8Lowerer : public SessionAction { - void runImpl(); +/** + * Replaces operations with 64 bits integer values with a set of operations + * supported by IA-32 ISA. + * + * I8PseudoInstruction-s, normally generated by InstCodeSelector. + */ +class I8Lowerer : + public SessionAction, + protected OpndUtils, + protected SubCfgBuilderUtils +{ + /** + * Main entry point of this Action. + */ + void runImpl(void); +private: + typedef StlMap OPND_PAIRS_MAP; + typedef StlVector INST_ARRAY; + typedef StlMap INST_TO_NODE_MAP; + // + // virtuals + // + uint32 getNeedInfo(void) const + { + return NeedInfo_LoopInfo; + } + uint32 getSideEffects(void) const + { + // Simplest presumption - if we found at least one I8PseudoInst + // we might affect everything, including liveness, loop info + // and dominator tree + return foundI8Opnds; + } protected: - void processOpnds(Inst * inst, StlMap& pairs); - void prepareNewOpnds(Opnd * longOpnd, StlMap& pairs, Opnd*& newOp1, Opnd*& newOp2); - bool isI8Type(Type * t){ return t->tag==Type::Int64 || t->tag==Type::UInt64; } - uint32 getNeedInfo () const {return 0;} - virtual uint32 getSideEffects()const {return foundI8Opnds;} + /** + * Dispatches instruction processing, basing on its Mnemonic. + */ + void processOpnds(Inst * inst); + /** + * Splits the long operand up to the 2 32bits operands. + */ + void prepareNewOpnds(Opnd * longOpnd, Opnd*& lowPartOpnd, Opnd*& hiPartOpnd); void buildShiftSubGraph(Inst * inst, Opnd * src1_1, Opnd * src1_2, Opnd * src2, Opnd * dst_1, Opnd * dst_2, Mnemonic mnem, Mnemonic opMnem); void buildComplexSubGraph(Inst * inst, Opnd * src1_1,Opnd * src1_2,Opnd * src2_1,Opnd * src2_2, Inst * condInst = NULL); void buildSetSubGraph(Inst * inst, Opnd * src1_1,Opnd * src1_2,Opnd * src2_1,Opnd * src2_2, Inst * condInst = NULL); void buildJumpSubGraph(Inst * inst, Opnd * src1_1,Opnd * src1_2,Opnd * src2_1,Opnd * src2_2, Inst * condInst = NULL); + /** + * Processes I8 IMUL instruction. + * + * Depending on a flag, generates either call to internal helper, + * or generates and inserts sub-graph. + * + * @see inlineMul64 + */ + void lowerMul64(Inst* inst); + /** + * Processes I8 IDIV instruction. + * + * Depending on a flag, generates either call to internal helper, + * or generates and inserts sub-graph. + */ + void lowerDiv64(Inst* inst); + /** + * Processes I8 remainder instruction. + * + * Depending on a flag, generates either call to internal helper, + * or generates and inserts sub-graph. + */ + void lowerRem64(Inst* inst); + /** + * Generates sub-graph for multiplication of 2 64-bits long values. + */ + void inlineMul64(Inst* inst); + /** + * Generates sub-graph for calculating quotient and remainder of 2 + * 64 bits integer values, using integer instructions. + */ + void inlineDivRem64(bool wantReminder, Inst* inst); + /** + * Tries to propagate and reuse results of inlined Div64 or Rem64 + * instruction, to reduce code size and complex operations in the + * resulting CFG. + */ + void propagateDivRemResults(Inst* originalInst, Node* originalInstNode, + Opnd* quot_lo, Opnd* quot_hi, Opnd* rem_lo, Opnd* rem_hi); uint32 foundI8Opnds; +private: + /** + * Tests whether the type is subject for lowering. + */ + static bool isI8Type(Type * t) + { + return t->tag==Type::Int64 || t->tag==Type::UInt64; + } + /** + * Tests whether the given Inst represents I8PseudoInstruction of + * remainder calculation. + */ + static bool isI8RemInst(const Inst* inst) + { + if (!inst->hasKind(Inst::Kind_I8PseudoInst)) { + return false; + } + if (inst->getMnemonic() != Mnemonic_IDIV) { + return false; + } + if (inst->getOpndCount()<=3) { + return false; + } + // The additional fake parameter shows that in fact + // we need REMinder, not DIV. See InstCodeSelector +#ifdef _DEBUG + Opnd* fakeFlag = inst->getOpnd(3); + // Hard - coded values as they're hard coded in CodeSelector + // Just to check no one started to use the I8PseudoInst with + // IDIV mnemonic for anything else. + assert(isImm(fakeFlag) && fakeFlag->getImmValue()==12345678); +#endif + return true; + } + /** + * Points to the list of instructions to process. + * + * The pointer points to the local variable in runImpl(); + */ + INST_ARRAY* m_pI8Insts; + /** + * Points to map of pairs 64 bits operand => two 32 bit operands. + * + * The pointer points to the local variable in runImpl(); + */ + OPND_PAIRS_MAP* m_pairs; + /** + * Points to a map of Inst => header of the loop it belongs to. + * + * The pointer points to the local variable in runImpl(); + */ + INST_TO_NODE_MAP* m_pLoopInfos; }; -static ActionFactory _i8l("i8l"); +static const char* help = +"inline_mul64=true/false\n" +" default=true. Inlines multiplication of 64 bits longs, instead of generating call to helper.\n" +"inline_mul64_checks=true/false\n" +" default=false. Adds additional checks whether multipliers are indeed int32 for simpler operation.\n" +"inline_div64=true/false\n" +" default=true. Inlines division of 64 bits longs, instead of generating call to helper.\n" +"inline_rem64=true/false\n" +" default=true. Inlines calculation of remainder of 64 bits longs, instead of generating call to helper.\n" +"propagate_div_rem=true/false\n" +" default=true. Tries to match pairs 'A/B, A%B' and inline only single code sequence.\n" +""; +static ActionFactory _i8l("i8l", help); + //_______________________________________________________________________________________________________________ // I8 operation internal helpers int64 __stdcall imul64(const int64 src1, const int64 src2) stdcall__; @@ -55,90 +194,143 @@ int64 __stdcall idiv64(const int64 src1, const int64 src2) { return src1/src2; } -//_______________________________________________________________________________________________________________ +int64 __stdcall irem64(const int64 src1, const int64 src2) stdcall__; +int64 __stdcall irem64(const int64 src1, const int64 src2) +{ return src1%src2; } + + void I8Lowerer::runImpl() { - -// I8 operation internal helpers + // I8 operation internal helpers irManager->registerInternalHelperInfo("imul64", IRManager::InternalHelperInfo((void*)&imul64,&CallingConvention_STDCALL)); irManager->registerInternalHelperInfo("idiv64", IRManager::InternalHelperInfo((void*)&idiv64,&CallingConvention_STDCALL)); + irManager->registerInternalHelperInfo("irem64", IRManager::InternalHelperInfo((void*)&irem64,&CallingConvention_STDCALL)); + + // Initialize various xxUtils + setIRManager(irManager); - StlMap pairs(irManager->getMemoryManager()); - StlVector i8Insts(irManager->getMemoryManager()); + ControlFlowGraph* fg = irManager->getFlowGraph(); - ControlFlowGraph* fg = irManager->getFlowGraph(); + const unsigned totalI8InstCountEstimate = fg->getNodeCount()*5; + unsigned memSizeEstimate = + // for i8Insts + sizeof(Inst*)*totalI8InstCountEstimate + + // for loopInfo + sizeof(unsigned)*totalI8InstCountEstimate + + // for pairs + (sizeof(Opnd*)+sizeof(Opnd**)*2)*totalI8InstCountEstimate; + + memSizeEstimate = (unsigned)(memSizeEstimate*0.1); + MemoryManager memMgr(memSizeEstimate, "i8lowerer"); + + StlMap pairs(memMgr); + m_pairs = &pairs; + + INST_ARRAY i8Insts(memMgr); + m_pI8Insts = &i8Insts; + + INST_TO_NODE_MAP loopInfos(memMgr); + m_pLoopInfos = &loopInfos; + + irManager->calculateOpndStatistics(); + const Nodes* postOrder = &fg->getNodesPostOrder(); + const LoopTree* loopTree = fg->getLoopTree(); + + // + // 1. Walk the CFG, collect Inst-s to be processed. + // Also, count the defs [for I8] operands + // for (Nodes::const_reverse_iterator it = postOrder->rbegin(), end = postOrder->rend(); it!=end; ++it) { Node* node = *it; - if (node->isBlockNode()) { - for (Inst* inst = (Inst*)node->getFirstInst(); inst!=NULL; inst = inst->getNextInst()) { - if (inst->hasKind(Inst::Kind_I8PseudoInst) || (inst->getMnemonic()==Mnemonic_CALL - || inst->getMnemonic()==Mnemonic_RET ||inst->hasKind(Inst::Kind_EntryPointPseudoInst)) - || inst->hasKind(Inst::Kind_AliasPseudoInst)){ - i8Insts.push_back(inst); - foundI8Opnds = ~(uint32)0; - } + if (!node->isBlockNode()) { + continue; + } + for (Inst* inst = (Inst*)node->getFirstInst(); inst!=NULL; inst = inst->getNextInst()) { + bool doProcess = + inst->hasKind(Inst::Kind_I8PseudoInst) || + inst->getMnemonic()==Mnemonic_CALL || + inst->getMnemonic()==Mnemonic_RET || + inst->hasKind(Inst::Kind_EntryPointPseudoInst) || + inst->hasKind(Inst::Kind_AliasPseudoInst); + if (doProcess) { + i8Insts.push_back(inst); + Node* loopHeader = loopTree->getLoopHeader(node, false); + loopInfos[inst] = loopHeader; } } } + // Will be used in propagateDivRem() + computeDominators(); + for(StlVector::iterator it = i8Insts.begin(); it != i8Insts.end(); it++) { - processOpnds(*it, pairs); + Inst* inst = *it; + // Processes instructions get replaced with NULL + if (inst != NULL) { + processOpnds(inst); + *it = NULL; + } } - + fg->purgeEmptyNodes(); + fg->purgeUnreachableNodes(); postOrder = &fg->getNodesPostOrder(); for (Nodes::const_reverse_iterator it = postOrder->rbegin(), end = postOrder->rend(); it!=end; ++it) { Node * node= *it; - if (node->isBlockNode()) { - Inst * cdq = NULL; - for (Inst* inst = (Inst*)node->getFirstInst(),*nextInst=NULL; inst!=NULL; inst = nextInst) { - nextInst = inst->getNextInst(); - uint32 defCount = inst->getOpndCount(Inst::OpndRole_InstLevel|Inst::OpndRole_Def); - if(inst->getMnemonic() == Mnemonic_CDQ) { - if (inst->getNextInst()!=NULL && inst->getNextInst()->getMnemonic() == Mnemonic_IDIV) { - continue; - } - cdq = inst; - } else if ( cdq && inst->getMnemonic() == Mnemonic_AND && - inst->getOpnd(defCount+1)->isPlacedIn(OpndKind_Imm) && - inst->getOpnd(defCount+1)->getImmValue() == 0 && - cdq->getOpnd(0)==inst->getOpnd(defCount)) { - Inst * tmpInst = irManager->newCopyPseudoInst(Mnemonic_MOV,inst->getOpnd(0), irManager->newImmOpnd(inst->getOpnd(0)->getType(),0)); - tmpInst->insertAfter(inst); - inst->unlink(); - inst = tmpInst; - cdq->unlink(); - cdq = NULL; - } else if ( inst->getMnemonic() == Mnemonic_AND && - inst->getOpnd(defCount+1)->isPlacedIn(OpndKind_Imm) && - inst->getOpnd(defCount+1)->getImmValue() == 0xFFFFFFFF) { - Inst * tmpInst = irManager->newCopyPseudoInst(Mnemonic_MOV,inst->getOpnd(0), inst->getOpnd(defCount)); + if (!node->isBlockNode()) { + continue; + } + Inst * cdq = NULL; + for (Inst* inst = (Inst*)node->getFirstInst(),*nextInst=NULL; inst!=NULL; inst = nextInst) { + nextInst = inst->getNextInst(); + uint32 defCount = inst->getOpndCount(Inst::OpndRole_InstLevel|Inst::OpndRole_Def); + if(inst->getMnemonic() == Mnemonic_CDQ) { + if (inst->getNextInst()!=NULL && inst->getNextInst()->getMnemonic() == Mnemonic_IDIV) { + continue; + } + cdq = inst; + } else if ( cdq && inst->getMnemonic() == Mnemonic_AND && + isZeroImm(inst->getOpnd(defCount+1)) && + cdq->getOpnd(0)==inst->getOpnd(defCount)) { + Inst * tmpInst = irManager->newCopyPseudoInst(Mnemonic_MOV,inst->getOpnd(0), irManager->newImmOpnd(inst->getOpnd(0)->getType(),0)); tmpInst->insertAfter(inst); inst->unlink(); inst = tmpInst; - } + cdq->unlink(); + cdq = NULL; + } else if ( inst->getMnemonic() == Mnemonic_AND && + isImm(inst->getOpnd(defCount+1)) && + inst->getOpnd(defCount+1)->getImmValue() == 0xFFFFFFFF) { + Inst * tmpInst = irManager->newCopyPseudoInst(Mnemonic_MOV,inst->getOpnd(0), inst->getOpnd(defCount)); + tmpInst->insertAfter(inst); + inst->unlink(); + inst = tmpInst; } } } } -void I8Lowerer::processOpnds(Inst * inst, StlMap& pairs) +void I8Lowerer::processOpnds(Inst * inst) { Opnd * newOp1 = NULL, *newOp2 = NULL; Opnd * dst_1 = NULL, * dst_2 = NULL, * src1_1 = NULL, * src1_2 = NULL, * src2_1 = NULL, * src2_2 = NULL; Mnemonic mn = inst->getMnemonic(); - if (mn==Mnemonic_CALL || mn==Mnemonic_RET ||inst->hasKind(Inst::Kind_EntryPointPseudoInst) - || inst->hasKind(Inst::Kind_AliasPseudoInst)) { + if (mn==Mnemonic_CALL || + mn==Mnemonic_RET || + inst->hasKind(Inst::Kind_EntryPointPseudoInst) || + inst->hasKind(Inst::Kind_AliasPseudoInst)) { for(uint32 i = 0; i < inst->getOpndCount(); i++) { Opnd * opnd = inst->getOpnd(i); - if (!isI8Type(opnd->getType())) + if (!isI8Type(opnd->getType())) { continue; + } + foundI8Opnds = ~(uint32)0; uint32 roles = inst->getOpndRoles(i); if (inst->hasKind(Inst::Kind_AliasPseudoInst)) { if (roles & Inst::OpndRole_Use) { - prepareNewOpnds(opnd,pairs,newOp1,newOp2); + prepareNewOpnds(opnd,newOp1,newOp2); inst->setOpnd(i, newOp1); inst->insertOpnd(i+1, newOp2, roles); inst->setConstraint(i, Constraint(OpndKind_Mem, OpndSize_32)); @@ -146,11 +338,11 @@ i++; } } else { - prepareNewOpnds(opnd,pairs,newOp1,newOp2); - inst->setOpnd(i++, newOp1); - inst->insertOpnd(i, newOp2, roles); + prepareNewOpnds(opnd,newOp1,newOp2); + inst->setOpnd(i++, newOp1); + inst->insertOpnd(i, newOp2, roles); + } } - } } else if (inst->hasKind(Inst::Kind_I8PseudoInst)){ uint32 defCount = inst->getOpndCount(Inst::OpndRole_InstLevel|Inst::OpndRole_Def), useCount = inst->getOpndCount(Inst::OpndRole_InstLevel|Inst::OpndRole_Use); @@ -160,11 +352,11 @@ if (mn!=Mnemonic_IDIV && mn!=Mnemonic_IMUL) { if (dst) - prepareNewOpnds(dst,pairs,dst_1,dst_2); + prepareNewOpnds(dst, dst_1,dst_2); if (src1) - prepareNewOpnds(src1,pairs,src1_1,src1_2); + prepareNewOpnds(src1, src1_1,src1_2); if (src2) - prepareNewOpnds(src2,pairs,src2_1,src2_2); + prepareNewOpnds(src2, src2_1,src2_2); } switch(mn) { @@ -213,12 +405,17 @@ irManager->newInstEx(Mnemonic_MOV, 1, dst_1, src1_1)->insertBefore(inst); } if (mn==Mnemonic_MOVSX){ + // It's possible to substitute complex CDQ with a tight + // constraints to the set of simpler instructions + // with a wider constraints to let more freedom + // to regalloc and constraint resolver. + // However, this seems does not change anything currently, + // so leaving as-is. + //test low, low + //setns hi ; if lo is positive, then load 1 into hi + //sub hi, 1 ; if lo is positive, then hi is now '0'. otherwise, it's -1 irManager->newInstEx(Mnemonic_CDQ, 1, dst_2, dst_1)->insertBefore(inst); inst->unlink(); - } else { - Opnd* zero = irManager->newImmOpnd(irManager->getTypeManager().getInt32Type(), 0); - irManager->newInstEx(Mnemonic_MOV, 1, dst_2, zero)->insertBefore(inst); - inst->unlink(); } break; case Mnemonic_PUSH : @@ -271,26 +468,17 @@ inst->unlink(); break; } - case Mnemonic_IMUL : - { - assert(dst && src1 && src2); - Opnd * args[2]={ src1, src2 }; - CallInst * callInst = irManager->newInternalRuntimeHelperCallInst("imul64", 2, args, dst); - callInst->insertBefore(inst); - processOpnds(callInst, pairs); - inst->unlink(); + case Mnemonic_IMUL: + lowerMul64(inst); break; - } - case Mnemonic_IDIV : - { - assert(dst && src1 && src2); - Opnd * args[2]={ src1, src2 }; - CallInst * callInst = irManager->newInternalRuntimeHelperCallInst("idiv64", 2, args, dst); - callInst->insertBefore(inst); - processOpnds(callInst, pairs); - inst->unlink(); + case Mnemonic_IDIV: + if (isI8RemInst(inst)) { + lowerRem64(inst); + } + else { + lowerDiv64(inst); + } break; - } default : assert(0); }//end switch by mnemonics @@ -378,16 +566,14 @@ ControlFlowGraph* subCFG = irManager->createSubCFG(true, false); Node* bbHMain = subCFG->getEntryNode(); - Node* bbHCmp = subCFG->createBlockNode(); Node* bbLMain = subCFG->createBlockNode(); Node* sinkNode = subCFG->getReturnNode(); bbHMain->appendInst(irManager->newInst(Mnemonic_CMP, src1_2, src2_2)); - bbHMain->appendInst(irManager->newBranchInst(Mnemonic_JNZ, bbHCmp, bbLMain)); + bbHMain->appendInst(irManager->newBranchInst(Mnemonic_JNZ, sinkNode, bbLMain)); bbLMain->appendInst(irManager->newInst(Mnemonic_CMP, src1_1, src2_1)); - bbHCmp->appendInst(irManager->newInst(Mnemonic_CMP, src1_2, src2_2)); Node* bbFT = bb->getFalseEdgeTarget(); Node* bbDB = bb->getTrueEdgeTarget(); @@ -402,8 +588,7 @@ } bbLMain->appendInst(irManager->newBranchInst(mnem, bbDB, bbFT)); - subCFG->addEdge(bbHMain, bbHCmp, 0.1); - subCFG->addEdge(bbHCmp, sinkNode, 1); + subCFG->addEdge(bbHMain, sinkNode, 0.1); subCFG->addEdge(bbHMain, bbLMain, 0.9); subCFG->addEdge(bbLMain, bbFT, 0.1); subCFG->addEdge(bbLMain, bbDB, 0.9); @@ -507,7 +692,7 @@ irManager->getFlowGraph()->spliceFlowGraphInline(inst, *subCFG); } -void I8Lowerer::prepareNewOpnds(Opnd * longOpnd, StlMap& pairs, Opnd*& newOp1, Opnd*& newOp2) +void I8Lowerer::prepareNewOpnds(Opnd * longOpnd, Opnd*& newOp1, Opnd*& newOp2) { if (!isI8Type(longOpnd->getType())){ newOp1=longOpnd; @@ -515,9 +700,9 @@ return; } - if(pairs.find(longOpnd)!=pairs.end()) { - newOp1 = pairs[longOpnd][0]; - newOp2 = pairs[longOpnd][1]; + if(m_pairs->find(longOpnd)!=m_pairs->end()) { + newOp1 = (*m_pairs)[longOpnd][0]; + newOp2 = (*m_pairs)[longOpnd][1]; } else { if(longOpnd->isPlacedIn(OpndKind_Memory)) { newOp1 = irManager->newOpnd(irManager->getTypeManager().getUInt32Type()); @@ -531,10 +716,639 @@ newOp1 = irManager->newOpnd(irManager->getTypeManager().getUInt32Type()); newOp2 = irManager->newOpnd(irManager->getTypeManager().getInt32Type()); } - pairs[longOpnd] = new(irManager->getMemoryManager()) Opnd*[2]; - pairs[longOpnd][0]=newOp1; - pairs[longOpnd][1]=newOp2; + (*m_pairs)[longOpnd] = new(irManager->getMemoryManager()) Opnd*[2]; + (*m_pairs)[longOpnd][0]=newOp1; + (*m_pairs)[longOpnd][1]=newOp2; } } +void I8Lowerer::lowerMul64(Inst* inst) +{ + assert(inst->getOpndRoles(0) & Inst::OpndRole_Def); + assert(inst->getOpndRoles(1) & Inst::OpndRole_Use); + assert(inst->getOpndRoles(2) & Inst::OpndRole_Use); + Opnd* dst = inst->getOpnd(0); + Opnd* src1 = inst->getOpnd(1); + Opnd* src2 = inst->getOpnd(2); + assert(dst && src1 && src2); + + if ( isZeroImm(src1) || isZeroImm(src2)) { + // Multiplue by zero - noop. + inst->unlink(); + return; + } + + bool inline_mul64 = true; + getArg("inline_mul64", inline_mul64); + + if (!inline_mul64) { + Opnd * args[2]={ src1, src2 }; + CallInst * callInst = irManager->newInternalRuntimeHelperCallInst("imul64", 2, args, dst); + callInst->insertBefore(inst); + processOpnds(callInst); + inst->unlink(); + return; + } + + inlineMul64(inst); +} + +void I8Lowerer::inlineMul64(Inst* inst) +{ + assert(inst->getOpndRoles(0) & Inst::OpndRole_Def); + assert(inst->getOpndRoles(1) & Inst::OpndRole_Use); + assert(inst->getOpndRoles(2) & Inst::OpndRole_Use); + Opnd* dst = inst->getOpnd(0); + Opnd* src1 = inst->getOpnd(1); + Opnd* src2 = inst->getOpnd(2); + + bool inline_mul64_checks = false; + getArg("inline_mul64_checks", inline_mul64_checks); + + Opnd * dst_1 = NULL, * dst_2 = NULL; + Opnd * src1_1 = NULL, * src1_2 = NULL; + Opnd * src2_1 = NULL, * src2_2 = NULL; + + prepareNewOpnds(dst, dst_1, dst_2); + prepareNewOpnds(src1, src1_1, src1_2); + prepareNewOpnds(src2, src2_1, src2_2); + + TypeManager& tm = irManager->getTypeManager(); + Type* int32type = tm.getInt32Type(); + // Name them eax, ecx, edx here, in code only, to refer same regs as in + // the comments, but let the register allocator to decide the proper + // ones. + Opnd*eax; + Opnd*edx; + Opnd*ecx; + + eax = irManager->newOpnd(int32type); + edx = irManager->newOpnd(int32type); + ecx = irManager->newOpnd(int32type); + + Opnd* zero = irManager->newImmOpnd(int32type, 0); + + // + Opnd* a_lo = src1_1; + Opnd* a_hi = src1_2; + Opnd* b_lo = src2_1; + Opnd* b_hi = src2_2; + Opnd* res_lo = dst_1; + Opnd* res_hi = dst_2; + + /* + Strategy: + result = a*b ; + a = a_lo + a_hi*(1<<32) + b = b_lo + b_hi*(1<<32) + - a*b = (a_lo + a_hi*(1<<32))*(b_lo + b_hi*(1<<32)) = + a_lo*b_lo + a_hi*(1<<32)*b_lo + a_lo*b_hi*(1<<32) + a_hi*(1<<32)*b_hi*(1<<32) + + - a_hi*(1<<32)*b_hi*(1<<32) - can be dropped off, it's overflow + - with any of `a_hi*b_lo*(1<<32)` + `a_lo*b_hi*(1<<32)` we can ignore high 32 bits + of result (EDX) - these are overflow again + So, we're getting: + + a_lo*b_lo + a_hi*b_lo*(1<<32) + a_lo*b_hi*(1<<32) + (1) (2) (3) + */ + // + newSubGFG(); + Node* entryNode = getSubCfgEntryNode(); + + Node* longMulNode = newBB(); + Node* storeResultNode = newBB(); + setCurrentNode(NULL); + if (!inline_mul64_checks) { + connectNodes(entryNode, longMulNode); + } + else { + Node* test_A_Node = newBB(); + Node* test_B_Node = newBB(); + Node* simpleMulNode = newBB(); + setCurrentNode(NULL); + + connectNodes(entryNode, test_A_Node); + setCurrentNode(test_A_Node); + // + // test_A_Node: + // + /* cmp a_hi, 0*/ newInst(Mnemonic_CMP, a_hi, zero); + /* jnz longMul */ newBranch(Mnemonic_JNZ, longMulNode, test_B_Node); + setCurrentNode(NULL); + test_A_Node = (Node*)0xDEADBEEF; + + // + // test_B_Node: + // + setCurrentNode(test_B_Node); + /* cmp b_hi, 0*/ newInst(Mnemonic_CMP, b_hi, zero); + /* jnz longMul */ newBranch(Mnemonic_JNZ, longMulNode, simpleMulNode); + setCurrentNode(NULL); + test_B_Node = (Node*)0xDEADBEEF; + + // + // simpleMulNode: + // + setCurrentNode(simpleMulNode); + /* mov eax, a_lo */ newInst(Mnemonic_MOV, eax, a_lo); + /* imul b_lo */ newInst(Mnemonic_MUL, 2, edx, eax, eax, b_lo); + connectNodeTo(storeResultNode); + setCurrentNode(NULL); + simpleMulNode = (Node*)0xDEADBEEF; + } + entryNode = (Node*)0xDEADBEEF; + + setCurrentNode(longMulNode); + /* mov eax, a_lo*/ newInst(Mnemonic_MOV, eax, a_lo); + /* mul b_hi */ newInst(Mnemonic_MUL, 2, edx, eax, eax, b_hi); + // + // Now, EAX=a_lo*b_hi, EDX content is dropped + // + // save a_lo*b_hi(EAX) + /* mov ecx, eax */ newInst(Mnemonic_MOV, ecx, eax); + /* mov eax, a_hi*/ newInst(Mnemonic_MOV, eax, a_hi); + /* mul b_lo */ newInst(Mnemonic_MUL, 2, edx, eax, eax, b_lo); + /* add ecx, eax */ newInst(Mnemonic_ADD, 1, ecx, ecx, eax); + + /* mov eax, a_lo */ newInst(Mnemonic_MOV, eax, a_lo); + /* mul b_lo */ newInst(Mnemonic_MUL, 2, edx, eax, eax, b_lo); + /* add edx, ecx */ newInst(Mnemonic_ADD, 1, edx, edx, ecx); + connectNodes(longMulNode, storeResultNode); + setCurrentNode(NULL); + longMulNode = (Node*)0xDEADBEEF; + + // + // storeResultNode: + // + setCurrentNode(storeResultNode); + /* mov res_lo, eax*/ newInst(Mnemonic_MOV, res_lo, eax); + /* mov res_hi, edx*/ newInst(Mnemonic_MOV, res_hi, edx); + setCurrentNode(NULL); + connectNodes(storeResultNode, getSubCfgReturnNode()); + + propagateSubCFG(inst); +} + +void I8Lowerer::lowerDiv64(Inst* inst) +{ + assert(inst->getOpndRoles(0) & Inst::OpndRole_Def); + assert(inst->getOpndRoles(1) & Inst::OpndRole_Use); + assert(inst->getOpndRoles(2) & Inst::OpndRole_Use); + Opnd* dst = inst->getOpnd(0); + Opnd* src1 = inst->getOpnd(1); + Opnd* src2 = inst->getOpnd(2); + assert(dst && src1 && src2); + + bool inline_div64 = true; + getArg("inline_div64", inline_div64); + + if (!inline_div64) { + Opnd * args[2]={ src1, src2 }; + CallInst * callInst = irManager->newInternalRuntimeHelperCallInst("idiv64", 2, args, dst); + callInst->insertBefore(inst); + processOpnds(callInst); + inst->unlink(); + return; + } + inlineDivRem64(false, inst); +} + +void I8Lowerer::lowerRem64(Inst* inst) +{ + assert(inst->getOpndRoles(0) & Inst::OpndRole_Def); + assert(inst->getOpndRoles(1) & Inst::OpndRole_Use); + assert(inst->getOpndRoles(2) & Inst::OpndRole_Use); + Opnd* dst = inst->getOpnd(0); + Opnd* src1 = inst->getOpnd(1); + Opnd* src2 = inst->getOpnd(2); + assert(dst && src1 && src2); + + bool inline_rem64 = true; + getArg("inline_rem64", inline_rem64); + + if (!inline_rem64) { + Opnd * args[2]={ src1, src2 }; + CallInst * callInst = irManager->newInternalRuntimeHelperCallInst("irem64", 2, args, dst); + callInst->insertBefore(inst); + processOpnds(callInst); + inst->unlink(); + return; + } + inlineDivRem64(true, inst); +} + +void I8Lowerer::inlineDivRem64(bool wantReminder, Inst* inst) +{ + assert(inst->getOpndRoles(0) & Inst::OpndRole_Def); + assert(inst->getOpndRoles(1) & Inst::OpndRole_Use); + assert(inst->getOpndRoles(2) & Inst::OpndRole_Use); + Opnd* dst = inst->getOpnd(0); + Opnd* src1 = inst->getOpnd(1); + Opnd* src2 = inst->getOpnd(2); + + Opnd * res_lo = NULL, * res_hi = NULL; + Opnd * a_lo = NULL, * a_hi = NULL; + Opnd * b_lo = NULL, * b_hi = NULL; + + prepareNewOpnds(dst, res_lo, res_hi); + prepareNewOpnds(src1, a_lo, a_hi); + prepareNewOpnds(src2, b_lo, b_hi); + + // + TypeManager& tm = irManager->getTypeManager(); + Type* int32type = tm.getInt32Type(); + + Opnd* edx = irManager->newOpnd(int32type); + Opnd* eax = irManager->newOpnd(int32type); + Opnd* ecx = irManager->newOpnd(int32type); + Opnd* ebx = irManager->newOpnd(int32type); + Opnd* edi = irManager->newOpnd(int32type); + Opnd* esi = irManager->newOpnd(int32type); + + Opnd* temp_lo = irManager->newOpnd(int32type); + Opnd* temp_hi = irManager->newOpnd(int32type); + Opnd* zero = irManager->newImmOpnd(int32type, 0); + Opnd* one = irManager->newImmOpnd(int32type, 1); + // + + newSubGFG(); + + Node* entryNode = getSubCfgEntryNode(); + Node* check_A_SignNode = newBB(); + Node* neg_A_Node = newBB(); + Node* check_B_SignNode = newBB(); + Node* neg_B_Node = newBB(); + + Node* testBigDvsrNode = newBB(); + Node* testOneDivNode = newBB(); + Node* simpleDivNode = newBB(); + Node* oneDivNode = newBB(); + Node* bigDivisorNode = newBB(); + Node* storeResultNode = newBB(); + + // + // Here we go. + // The algorithm is based on unsigned div from assembly gems page + // and modified for signed arithmetics and for the Jitrino CG specifics. + // + + // + // entryNode: + // + // Preparation - load all into 'registers' + // + + Opnd* fixQuotSign = irManager->newOpnd(int32type); + Opnd* fixRemSign = irManager->newOpnd(int32type); + // Sign presumed positive + setCurrentNode(entryNode); + /* mov fixQuotSign, 0*/ newInst(Mnemonic_MOV, fixQuotSign, zero); + /* mov fixRemSign, 0*/ newInst(Mnemonic_MOV, fixRemSign, zero); + + /* mov edx, a_hi*/ newInst(Mnemonic_MOV, edx, a_hi); + /* mov eax, a_lo*/ newInst(Mnemonic_MOV, eax, a_lo); + /* mov ecx, b_hi*/ newInst(Mnemonic_MOV, ecx, b_hi); + /* mov ebx, b_lo*/ newInst(Mnemonic_MOV, ebx, b_lo); + connectNodeTo(check_A_SignNode); + setCurrentNode(NULL); + entryNode = (Node*)0xDEADBEEF; + + // + // check_A_SignNode: + // + // Test for signs and convert to unsigned when needed + // + + setCurrentNode(check_A_SignNode); + /* test edx, edx*/ newInst(Mnemonic_TEST, edx, edx); + /* jns check_B_Sign*/ newBranch(Mnemonic_JNS, check_B_SignNode, neg_A_Node); + setCurrentNode(NULL); + check_A_SignNode = (Node*)0xDEADBEEF; + + // + // neg_A: + // + // + + setCurrentNode(neg_A_Node); + /* mov fixQuotSign, 1*/ newInst(Mnemonic_MOV, fixQuotSign, one); + /* mov fixRemSign, 1*/ newInst(Mnemonic_MOV, fixRemSign, one); + /* neg eax */ newInst(Mnemonic_NEG, 1, eax, eax); + /* adc edx, 0 */ newInst(Mnemonic_ADC, 1, edx, edx, zero); + /* neg edx */ newInst(Mnemonic_NEG, 1, edx, edx); + connectNodeTo(check_B_SignNode); + setCurrentNode(NULL); + neg_A_Node = (Node*)0xDEADBEEF; + + // + // check_B_Sign_Node: + // + setCurrentNode(check_B_SignNode); + /* test ecx, ecx*/ newInst(Mnemonic_TEST, ecx, ecx); + /* jns testBigDvsrNode*/ newBranch(Mnemonic_JNS, testBigDvsrNode, neg_B_Node); + setCurrentNode(NULL); + check_B_SignNode = (Node*)0xDEADBEEF; + + // + // neg_B_Node: + // + // When doing REM, the result's sing only depends on + // dividend's sign no need to change fixRemSign + + setCurrentNode(neg_B_Node); + /* XOR fixQuotSign, 1*/ newInst(Mnemonic_XOR, 1, fixQuotSign, fixQuotSign, one); + /* neg ebx */ newInst(Mnemonic_NEG, 1, ebx, ebx); + /* adc ecx, 0 */ newInst(Mnemonic_ADC, 1, ecx, ecx, zero); + /* neg ecx */ newInst(Mnemonic_NEG, 1, ecx, ecx); + connectNodeTo(testBigDvsrNode); + setCurrentNode(NULL); + neg_B_Node = (Node*)0xDEADBEEF; + + // + // testBigDvsr: + // + // divisor > 2^32-1 ? + setCurrentNode(testBigDvsrNode); + /* cmp ecx, 0 */ newInst(Mnemonic_CMP, ecx, zero); + // YES - proceed to the hard way. + // NO - fall to the simpler path + /* jnz BIG_DVSOR*/ newBranch(Mnemonic_JNZ, bigDivisorNode, testOneDivNode); + setCurrentNode(NULL); + testBigDvsrNode = (Node*)0xDEADBEEF; + + // + // testOneDivNode: + // + // + //only one division needed ? (ecx = 0) + setCurrentNode(testOneDivNode); + /* cmp edx, ebx */ newInst(Mnemonic_CMP, edx, ebx); + // YES - one division sufficient + /* jb ONE_DIV */ newBranch(Mnemonic_JB, oneDivNode, simpleDivNode); + setCurrentNode(NULL); + testOneDivNode = (Node*)0xDEADBEEF; + + // + // simpleDivNode: + // + + setCurrentNode(simpleDivNode); + /* mov ecx, eax */ newInst(Mnemonic_MOV, ecx, eax); // save dividend-lo in ecx + /* mov eax, edx */ newInst(Mnemonic_MOV, eax, edx); // get dividend-hi + /* xor edx, edx */ newInst(Mnemonic_MOV, edx, zero); // zero extend it into edx:eax + /* div ebx */ newInst(Mnemonic_DIV, 2, edx, eax, edx, eax, ebx); // quotient-hi in eax + /* xchg eax, ecx*/ newInst(Mnemonic_XCHG, eax, ecx); //ecx = quotient-hi, eax =dividend-lo + connectNodeTo(oneDivNode); + setCurrentNode(NULL); + simpleDivNode = (Node*)0xDEADBEEF; + + // + // oneDivNode: + // + + setCurrentNode(oneDivNode); + /* div ebx */ newInst(Mnemonic_DIV, 2, edx, eax, edx, eax, ebx); // eax = quotient-lo + /* mov ebx, edx */ newInst(Mnemonic_MOV, ebx, edx); // ebx = remainder-lo + /* mov edx, ecx */ newInst(Mnemonic_MOV, edx, ecx); // edx = quotient-hi(quotient in edx:eax) + /* xor ecx, ecx */ newInst(Mnemonic_MOV, ecx, zero); // ecx = remainder-hi (rem. in ecx:ebx) + /* jmp saveResult*/ + connectNodeTo(storeResultNode); + setCurrentNode(NULL); + oneDivNode = (Node*)0xDEADBEEF; + + // + // bigDivisorNode: + // + setCurrentNode(bigDivisorNode); + /* mov temp_hi, edx */ newInst(Mnemonic_MOV, temp_hi, edx); + /* mov temp_lo, eax */ newInst(Mnemonic_MOV, temp_lo, eax); // save dividend + /* mov esi, ebx */ newInst(Mnemonic_MOV, esi, ebx); + /* mov edi, ecx */ newInst(Mnemonic_MOV, edi, ecx); + // ^^^divisor now in edi:ebx and ecx:esi + + // shift both divisor and dividend right on 1 bit + /* shr edx, 1 */ newInst(Mnemonic_SHR, edx, one); + /* rcr eax, 1 */ newInst(Mnemonic_RCR, eax, one); + /* ror edi, 1 */ newInst(Mnemonic_ROR, edi, one); + /* rcr ebx, 1 */ newInst(Mnemonic_RCR, ebx, one); + + // + // FIXME: workarounding WebMaker problem, which does not like + // both DEF and USE of the same arg in the same instruction. + // Replace, 'reg = BSR reg, 0' with + // temp = reg ; reg = BSR temp, 0 + // Funny thing is that this only happens with BSR. + // +#if 0 // _FIXME_ + /* bsr ecx, ecx */ newInst(Mnemonic_BSR, 1, ecx, ecx); +#else + Opnd* tmp = irManager->newOpnd(ecx->getType()); + /* mov tmp, ecx */ newInst(Mnemonic_MOV, 1, tmp, ecx); + /* bsr ecx, tmp */ newInst(Mnemonic_BSR, 1, ecx, tmp); +#endif + // ecx = number of remaining shifts + + // scale divisor and dividend down, so divisor fits + // into EBX (<2^32) + // FIXME: Currently, constraint resolver fails to assign 'ecx' operand to the + // real ECX, and this causes SpillGen failure. + // Forcing the ECX right here: +#if 0 //_FIXME_ + /* shrd ebx, edi, CL*/ newInst(Mnemonic_SHRD, ebx, edi, ecx); + /* shrd eax, edx, CL*/ newInst(Mnemonic_SHRD, eax, edx, ecx); + /* shr edx, CL */ newInst(Mnemonic_SHR, edx, ecx); +#else + Opnd* trueECX = irManager->getRegOpnd(RegName_ECX); + newInst(Mnemonic_MOV, trueECX, ecx); + //~FIXME + /* shrd ebx, edi, CL*/ newInst(Mnemonic_SHRD, ebx, edi, trueECX); + /* shrd eax, edx, CL*/ newInst(Mnemonic_SHRD, eax, edx, trueECX); + /* shr edx, CL */ newInst(Mnemonic_SHR, edx, trueECX); +#endif + /* rol edi, 1 */ newInst(Mnemonic_ROL, edi, one); + // get quotient + /* div ebx */ newInst(Mnemonic_DIV, 2, edx, eax, edx, eax, ebx, NULL); + /* mov ebx, temp_lo*/ newInst(Mnemonic_MOV, ebx, temp_lo); + /* mov ecx, eax */ newInst(Mnemonic_MOV, ecx, eax); + /* imul edi, eax */ newInst(Mnemonic_IMUL, edi, eax); + /* mul esi */ newInst(Mnemonic_MUL, 2, edx, eax, eax, esi, NULL); + /* add edx, edi */ newInst(Mnemonic_ADD, 1, edx, edx, edi); // edx:eax = quotient * divisor + /* sub ebx, eax */ newInst(Mnemonic_SUB, 1, ebx, ebx, eax); // dividend-lo - (quot.*divisor)-lo + /* mov eax, ecx */ newInst(Mnemonic_MOV, eax, ecx); + /* mov ecx, temp_hi*/ newInst(Mnemonic_MOV, ecx, temp_hi); // restore dividend hi-word + /* sbb ecx, edx */ newInst(Mnemonic_SBB, 1, ecx, ecx, edx); // subtract divisor * quot. from dividend + /* sbb edx, edx */ newInst(Mnemonic_SBB, 1, edx, edx, edx); // 0 if remainder > 0, else FFFFFFFFh + /* and esi, edx */ newInst(Mnemonic_AND, 1, esi, esi, edx); // nothing to add + /* and edi, edx */ newInst(Mnemonic_AND, 1, edi, edi, edx); // back if remainder positive + /* add ebx, esi */ newInst(Mnemonic_ADD, 1, ebx, ebx, esi); // correct remainder + /* adc ecx, edi */ newInst(Mnemonic_ADC, 1, ecx, ecx, edi); // and quotient if + /* add eax, edx */ newInst(Mnemonic_ADD, 1, eax, eax, edx); // necessary + /* xor edx, edx */ newInst(Mnemonic_MOV, edx, zero); // clear hi-word of quot (eax<=FFFFFFFFh) + + connectNodeTo(storeResultNode); + setCurrentNode(NULL); + bigDivisorNode = (Node*)0xDEADBEEF; + + Node* testFixQuotSignNode = newBB(); + Node* fixQuotSignNode = newBB(); + Node* testFixRemSignNode = newBB(); + Node* fixRemSignNode = newBB(); + Node* finitaNode = newBB(); + + // + // storeResultNode: + // + Opnd* quot_lo = irManager->newOpnd(int32type); + Opnd* quot_hi = irManager->newOpnd(int32type); + Opnd* rem_lo = irManager->newOpnd(int32type); + Opnd* rem_hi = irManager->newOpnd(int32type); + + setCurrentNode(storeResultNode); + /* mov rem_lo, ebx*/ newInst(Mnemonic_MOV, rem_lo, ebx); + /* mov rem_hi, ecx*/ newInst(Mnemonic_MOV, rem_hi, ecx); + /* mov quot_lo, eax*/ newInst(Mnemonic_MOV, quot_lo, eax); + /* mov quot_hi, edx*/ newInst(Mnemonic_MOV, quot_hi, edx); + connectNodeTo(testFixQuotSignNode); + setCurrentNode(NULL); + storeResultNode = (Node*)0xDEADBEEF; + + // + // testFixQuotSignNode: + // + // + setCurrentNode(testFixQuotSignNode); + /* cmp fixQuotSign, 0 */ newInst(Mnemonic_CMP, fixQuotSign, zero); + /* jz testFixRemSignNode */ newBranch(Mnemonic_JZ, testFixRemSignNode, fixQuotSignNode); + setCurrentNode(NULL); + testFixQuotSignNode = (Node*)0xDEADBEEF; + + // + // fixQuotSignNode: + // + setCurrentNode(fixQuotSignNode); + /* neg res_lo */ newInst(Mnemonic_NEG, 1, quot_lo, quot_lo); + /* adc res_hi, 0 */ newInst(Mnemonic_ADC, 1, quot_hi, quot_hi, zero); + /* neg res_lo */ newInst(Mnemonic_NEG, 1, quot_hi, quot_hi); + connectNodeTo(testFixRemSignNode); + setCurrentNode(NULL); + fixQuotSignNode = (Node*)0xDEADBEEF; + + // + // testFixRemSignNode: + // + // + setCurrentNode(testFixRemSignNode); + /* cmp fixRemSign, 0 */ newInst(Mnemonic_CMP, fixRemSign, zero); + /* jz finitaNode */ newBranch(Mnemonic_JZ, finitaNode, fixRemSignNode); + setCurrentNode(NULL); + testFixQuotSignNode = (Node*)0xDEADBEEF; + + // + // fixRemSignNode: + // + setCurrentNode(fixRemSignNode); + /* neg res_lo */ newInst(Mnemonic_NEG, 1, rem_lo, rem_lo); + /* adc res_hi, 0 */ newInst(Mnemonic_ADC, 1, rem_hi, rem_hi, zero); + /* neg res_lo */ newInst(Mnemonic_NEG, 1, rem_hi, rem_hi); + connectNodeTo(finitaNode); + setCurrentNode(NULL); + fixQuotSignNode = (Node*)0xDEADBEEF; + + // + // finitaNode: + // + setCurrentNode(finitaNode); + if (wantReminder) { + /* mov res_lo, rem_lo*/ newInst(Mnemonic_MOV, res_lo, rem_lo); + /* mov res_hi, rem_hi*/ newInst(Mnemonic_MOV, res_hi, rem_hi); + } + else { + /* mov res_lo, quot_lo*/ newInst(Mnemonic_MOV, res_lo, quot_lo); + /* mov res_hi, quot_hi*/ newInst(Mnemonic_MOV, res_hi, quot_hi); + } + connectNodes(finitaNode, getSubCfgReturnNode()); + setCurrentNode(NULL); + finitaNode = (Node*)0xDEADBEEF; + + Node* originalInstNode = inst->getNode(); + propagateSubCFG(inst); + propagateDivRemResults(inst, originalInstNode, quot_lo, quot_hi, rem_lo, rem_hi); +} + +void I8Lowerer::propagateDivRemResults( + Inst* origInst, Node* originalInstNode, + Opnd* quot_lo, Opnd* quot_hi, + Opnd* rem_lo, Opnd* rem_hi) +{ + + bool propagate_div_rem = false; //FIXME: somehow crashes on Linux/IA-32 in dominators - need to fix. + getArg("propagate_div_rem", propagate_div_rem); + if (!propagate_div_rem) { + return; + } + + assert(origInst->getOpndRoles(0) & Inst::OpndRole_Def); + assert(origInst->getOpndRoles(1) & Inst::OpndRole_Use); + assert(origInst->getOpndRoles(2) & Inst::OpndRole_Use); + Opnd* src1 = origInst->getOpnd(1); + Opnd* src2 = origInst->getOpnd(2); + if (!isSingleDef(src1) || !isSingleDef(src2)) { + // Not a single def operands - can't propagate with current + // trivial implementation, need more sophisticated routine. + return; + } + + ControlFlowGraph* cfg = irManager->getFlowGraph(); + DominatorTree* dt = cfg->getDominatorTree(); + + INST_ARRAY& i8insts = *m_pI8Insts; + for (INST_ARRAY::iterator i=i8insts.begin(); i != i8insts.end(); i++) { + Inst* inst = *i; + if (inst == origInst || inst == NULL) { + continue; + } + if (!inst->hasKind(Inst::Kind_I8PseudoInst) || + inst->getMnemonic() != Mnemonic_IDIV) { + continue; + } + assert(inst->getOpndRoles(0) & Inst::OpndRole_Def); + assert(inst->getOpndRoles(1) & Inst::OpndRole_Use); + assert(inst->getOpndRoles(2) & Inst::OpndRole_Use); + Opnd* otherSrc1 = origInst->getOpnd(1); + Opnd* otherSrc2 = origInst->getOpnd(2); + if (otherSrc1 != src1 || otherSrc2 != src2) { + continue; + } + // + // Test whether both instructions belong to the same loop + // + Node* origNodeLoopHdr = (*m_pLoopInfos)[origInst]; + Node* instLoopHdr = (*m_pLoopInfos)[inst]; + if (origNodeLoopHdr != instLoopHdr) { + continue; + } + // + // Test whether one instruction dominates another + // + Node* instNode = inst->getNode(); + if (!dt->dominates(originalInstNode, instNode)) { + continue; + } + Log::out() << "Propagating: I" << origInst->getId() << " => I" << inst->getId() << std::endl; + Opnd* otherDst = inst->getOpnd(0); + Opnd* otherDst_hi, *otherDst_lo; + prepareNewOpnds(otherDst, otherDst_lo, otherDst_hi); + const bool isRem = isI8RemInst(inst); + Inst* ii; + ii = irManager->newCopyPseudoInst(Mnemonic_MOV, otherDst_lo, isRem ? rem_lo : quot_lo); + ii->insertBefore(inst); + ii = irManager->newCopyPseudoInst(Mnemonic_MOV, otherDst_hi, isRem ? rem_hi : quot_hi); + ii->insertBefore(inst); + inst->unlink(); + *i = NULL; + } +} + }} + Index: vm/jitrino/src/codegenerator/ia32/Ia32Printer.cpp =================================================================== --- vm/jitrino/src/codegenerator/ia32/Ia32Printer.cpp (revision 476034) +++ vm/jitrino/src/codegenerator/ia32/Ia32Printer.cpp (working copy) @@ -324,6 +324,9 @@ if (inst->hasKind(Inst::Kind_PseudoInst)){ os<getKind()); + if (inst->getMnemonic() != Mnemonic_Null) { + os<< "/" << Encoder::getMnemonicString(inst->getMnemonic()); + } }else{ if( inst->getMnemonic() != Mnemonic_Null ) os<< Encoder::getMnemonicString( inst->getMnemonic() ); Index: vm/jitrino/src/codegenerator/ia32/Ia32IRManager.h =================================================================== --- vm/jitrino/src/codegenerator/ia32/Ia32IRManager.h (revision 476034) +++ vm/jitrino/src/codegenerator/ia32/Ia32IRManager.h (working copy) @@ -617,7 +617,10 @@ Optional, defaults to all possible side effects */ virtual uint32 getSideEffects()const; - + /** + * Forces dominator tree to be valid + */ + void computeDominators(void); virtual bool verify(bool force=false); virtual void debugOutput(const char * subKind); Index: vm/jitrino/src/codegenerator/ia32/Ia32CgUtils.cpp =================================================================== --- vm/jitrino/src/codegenerator/ia32/Ia32CgUtils.cpp (revision 0) +++ vm/jitrino/src/codegenerator/ia32/Ia32CgUtils.cpp (revision 0) @@ -0,0 +1,362 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @author Alexander Astapchuk + * @version $Revision$ + */ + +#include "Ia32CgUtils.h" + +namespace Jitrino { +namespace Ia32 { + +bool OpndUtils::isReg(const Opnd* op, RegName what) +{ + if (!op->isPlacedIn(OpndKind_Reg)) { + return false; + } + if (what == RegName_Null) { + return true; + } + return op->getRegName() == what; +} + +bool OpndUtils::isXmmReg(const Opnd* op, RegName what) +{ + if (!isReg(op)) { + return false; + } + const RegName regName = op->getRegName(); + if (getRegKind(regName) != OpndKind_XMMReg) { + return false; + } + if (what == RegName_Null) { + return true; + } + return regName == what; +} + +bool OpndUtils::isImm(const Opnd* op) +{ + return op->isPlacedIn(OpndKind_Imm) && (op->getRuntimeInfo() == NULL); +} + +bool OpndUtils::isImm(const Opnd* op, int iVal) +{ + return isImm(op) && (op->getImmValue() == iVal); +} + +bool OpndUtils::isImm8(const Opnd* op) +{ + return isImm(op) && op->getSize() == OpndSize_8; +} + +bool OpndUtils::isImm32(const Opnd* op) +{ + return isImm(op) && op->getSize() == OpndSize_32; +} + +bool OpndUtils::fitsImm8(const Opnd* op) +{ + return isImm(op) && + (CHAR_MIN <= op->getImmValue() && op->getImmValue() <= CHAR_MAX); +} + +bool OpndUtils::isMem(const Opnd* op) +{ + return op->isPlacedIn(OpndKind_Mem); +} + +bool OpndUtils::isZeroImm(const Opnd* op) +{ + return isImm(op, 0); +} + +bool OpndUtils::isSingleDef(const Opnd* opnd) +{ + return isImm(opnd) || (opnd->getDefiningInst() != NULL); +} + + +const void* OpndUtils::extractAddrOfConst(const Opnd* op) +{ + if (op->getMemOpndKind() != MemOpndKind_ConstantArea) { + return NULL; + } + // Actually, it's currently only works for IA-32 - I expect + // the address of constant completely in the displacement. + // On Intel64, the address already get loaded into a register, + // so more complicated analysis needed to find the proper constant + Opnd* disp = op->getMemOpndSubOpnd(MemOpndSubOpndKind_Displacement); + if (disp == NULL) { + // Perhaps, it's IA-32? + return NULL; + } + + Opnd::RuntimeInfo* rtInfo = disp->getRuntimeInfo(); + assert(rtInfo != NULL); + assert(rtInfo->getKind() == Opnd::RuntimeInfo::Kind_ConstantAreaItem); + ConstantAreaItem* item = (ConstantAreaItem*)rtInfo->getValue(0); + // At this point we must have the address... + assert(item->getValue()!= NULL); + return item->getValue(); +} + +bool OpndUtils::isConstAreaItem(const Opnd* op) +{ + return extractAddrOfConst(op) != NULL; +} + +bool OpndUtils::isFPConst(const Opnd* op, double dVal) +{ + const void* addr = extractAddrOfConst(op); + return (addr == NULL) ? false : (dVal == *(const float*)addr); +} + +bool OpndUtils::isFPConst(const Opnd* op, float fVal) +{ + const void* addr = extractAddrOfConst(op); + return (addr == NULL) ? false : (fVal == *(const float*)addr); +} + +int OpndUtils::extractIntConst(const Opnd* op) +{ + assert(isConstAreaItem(op)); + const void* addr = extractAddrOfConst(op); + return *(const int*)addr; +} + +double OpndUtils::extractDoubleConst(const Opnd* op) +{ + assert(isConstAreaItem(op)); + const void* addr = extractAddrOfConst(op); + return *(const double*)addr; +} + +float OpndUtils::extractFloatConst(const Opnd* op) +{ + assert(isConstAreaItem(op)); + const void* addr = extractAddrOfConst(op); + return *(const float*)addr; +} + +bool OpndUtils::equals(const Opnd* a, const Opnd* b) +{ + //TODO: + return a == b; +} + +Opnd* OpndUtils::convertImmToImm8(Opnd* imm) +{ + if (isImm8(imm)) { + return imm; + } + assert(fitsImm8(imm)); + TypeManager& typeMan = m_irManager->getTypeManager(); + Type* int8type = typeMan.getInt8Type(); + Opnd* imm8 = m_irManager->newImmOpnd(int8type, imm->getImmValue()); + return imm8; +} + +Opnd* OpndUtils::convertToXmmReg64(Opnd* xmmReg) +{ + assert(isXmmReg(xmmReg)); + RegName regName = xmmReg->getRegName(); + OpndSize size = getRegSize(regName); + if (size == OpndSize_64) { + return xmmReg; + } + TypeManager& typeMan = m_irManager->getTypeManager(); + Type* doubleType = typeMan.getDoubleType(); + unsigned regIndex = getRegIndex(regName); + RegName regName64 = getRegName(OpndKind_XMMReg, OpndSize_64, regIndex); + Opnd* xmm64 = m_irManager->newRegOpnd(doubleType, regName64); + return xmm64; +} + +Opnd* OpndUtils::getZeroConst(Type* type) +{ + if (type->isDouble()) { + return getDoubleZeroConst(); + } + if (type->isSingle()) { + return getFloatZeroConst(); + } + if (type->isInt4()) { + return getIntZeroConst(); + } + return m_irManager->newImmOpnd(type, 0); +} + +Opnd* OpndUtils::getIntZeroConst(void) +{ + if (m_opndIntZero == NULL) { + Type* type = m_irManager->getTypeFromTag(Type::Int32); + m_opndIntZero = m_irManager->newImmOpnd(type, 0); + } + return m_opndIntZero; +} +Opnd* OpndUtils::getDoubleZeroConst(void) +{ + if (m_opndDoubleZero == NULL) { + m_opndDoubleZero = m_irManager->newFPConstantMemOpnd((double)0); + } + return m_opndDoubleZero; +} + +Opnd* OpndUtils::getFloatZeroConst(void) +{ + if (m_opndFloatZero == NULL) { + m_opndFloatZero = m_irManager->newFPConstantMemOpnd((float)0); + } + return m_opndFloatZero; +} + + + +bool InstUtils::isPseudoInst(const Inst* inst) +{ + return inst->hasKind(Inst::Kind_PseudoInst); +} + +void InstUtils::removeInst(Inst* toBeRemoved) +{ + toBeRemoved->unlink(); +} + +void InstUtils::replaceInst(Inst* toBeReplaced, Inst* brandNewInst) +{ + BasicBlock* bb = toBeReplaced->getBasicBlock(); + bb->appendInst(brandNewInst, toBeReplaced); + removeInst(toBeReplaced); +} + + +ControlFlowGraph* SubCfgBuilderUtils::newSubGFG(bool withReturn, bool withUnwind) +{ + m_subCFG = m_irManager->createSubCFG(withReturn, withUnwind); + return m_subCFG; +} + +Node* SubCfgBuilderUtils::getSubCfgEntryNode(void) +{ + return m_subCFG->getEntryNode(); +} + +Node* SubCfgBuilderUtils::getSubCfgReturnNode(void) +{ + return m_subCFG->getReturnNode(); +} + +BasicBlock* SubCfgBuilderUtils::newBB(void) +{ + BasicBlock* bb = (BasicBlock*)m_subCFG->createBlockNode(); + setCurrentNode(bb); + return bb; +} + +Node* SubCfgBuilderUtils::setCurrentNode(Node* node) +{ + Node* old = m_currNode; + m_currNode = node; + return old; +} + +Node* SubCfgBuilderUtils::getCurrentNode(void) const +{ + return m_currNode; +} + +Inst* SubCfgBuilderUtils::newInst( + Mnemonic mn, + unsigned defsCount, + Opnd* op0, Opnd* op1, Opnd* op2, + Opnd* op3, Opnd* op4, Opnd* op5) +{ + Inst* inst = NULL; + if (mn == Mnemonic_MOV) { + assert(op0 != NULL && op1 != NULL); + assert(op2==NULL && op3 == NULL); + assert(op4 == NULL && op5 == NULL); + inst = m_irManager->newCopyPseudoInst(mn, op0, op1); + } + else { + inst = m_irManager->newInstEx(mn, defsCount, op0, op1, op2, op3, op4, op5); + } + m_currNode->appendInst(inst); + return inst; +} + +Inst* SubCfgBuilderUtils::newInst( + Mnemonic mn, Opnd* op0, Opnd* op1, Opnd*op2) +{ + if (mn == Mnemonic_MOV) { + // special handling in another newInst() + return newInst(mn, 0, op0, op1, op2); + + } + Inst* inst = m_irManager->newInst(mn, op0, op1, op2); + m_currNode->appendInst(inst); + return inst; +} + +Inst* SubCfgBuilderUtils::newBranch( + Mnemonic mn, + Node* trueTarget, Node* falseTarget, + double trueProbability, double falseProbability) +{ + Inst* branch = m_irManager->newBranchInst(mn, trueTarget, falseTarget); + m_currNode->appendInst(branch); + m_subCFG->addEdge(m_currNode, trueTarget, trueProbability); + m_subCFG->addEdge(m_currNode, falseTarget, falseProbability); + return branch; +} + +void SubCfgBuilderUtils::connectNodes(Node* from, Node* to) +{ + m_subCFG->addEdge(from, to); +} + +void SubCfgBuilderUtils::connectNodeTo(Node* to) +{ + connectNodes(m_currNode, to); +} + +ControlFlowGraph* SubCfgBuilderUtils::getSubCFG(void) +{ + return m_subCFG; +} + +ControlFlowGraph* SubCfgBuilderUtils::setSubCFG(ControlFlowGraph* subCFG) +{ + ControlFlowGraph* old = m_subCFG; + m_subCFG = subCFG; + return old; +} + +void SubCfgBuilderUtils::propagateSubCFG(Inst* inst, bool purgeEmptyNodes) +{ + ControlFlowGraph* mainCFG = m_irManager->getFlowGraph(); + mainCFG->spliceFlowGraphInline(inst, *m_subCFG); + InstUtils::removeInst(inst); + if (purgeEmptyNodes) { + mainCFG->purgeEmptyNodes(true, false); + } + m_subCFG = NULL; +} + +}}; // ~namespace Jitrino::Ia32