blob: 84dcdb332ed388928929131a0ef733354002b4cd [file] [log] [blame]
/*
* 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 Vyacheslav P. Shakin
*/
#include "Ia32ConstraintsResolver.h"
#include "Ia32Printer.h"
namespace Jitrino
{
namespace Ia32{
//========================================================================================
// class Ia32ConstraintsResolver
//========================================================================================
/**
* class Ia32ConstraintsResolver performs resolution of operand constraints
* and assigns calculated constraints (Opnd::ConstraintKind_Calculated) to operands.
* The resulting calculated constraints of operands determine allowable physical location
* for the operand.
*
* This transformer allows to insert operands into instructions before it
* regardless instruction constraints except that Initial constraints of explicit
* instruction operands must have non-null intersections with corresponding constraints
* of at least one opcode group of the instruction.
*
* ConstraintResolver analyzes instruction constraints and splits operands when necessary.
*
* This transformer ensures that
* 1) All instruction constraints for EntryPoints, CALLs and RETs
* are set appropriately (IRManager::applyCallingConventions())
* 2) All operands has non-null calculated constraints
* 3) All operands fits into instructions they are used in (in terms of instruction constraints)
* For example:
* Original code piece:
* I38: (AD:s65:double) =CopyPseudoInst (AU:t1:double)
* I32: MULSD .s65.:double,.t2:double
* I33: RET t66(0):int16 (AU:s65:double)
*
* RET imposes constraint on s65 requiring to place it into FP0 register
* (its FP0D alias in this particular case)
* MULSD imposes constraint on s65 requiring to place it into XMM register
*
* After the pass:
* I38: (AD:s65:double) =CopyPseudoInst (AU:t1:double)
* I32: MULSD .s65.:double,.t2:double
* I46: (AD:t75:double) =CopyPseudoInst (AU:s65:double)
* I33: RET t66(20):int16 (AU:t75:double)
*
* Thus, ConstraintResolver inserted I46 splitting s65 to s65 and t75
* s65 is assigned with Mem|XMM calculated constraint and t75
* is assigned with FP0D calculated calculated constraint
*
* 4) If the live range of an operand crosses a call site and the operand is not redefined
* in the call site, the calculated constraint of the operand is narrowed the callee-save regs
* or memory (stack)
*
* 5) If the operand (referred as original operand here) is live at entry of a catch handler
* then necessary operand splitting is performed as close as possible to the instruction
* which caused the splitting and original operand is used before and after the instruction.
*
* The main principle of the algorithm is anding of instruction constraints into
* operand calculated constraints and splitting operands to ensure that the calculated constraint
* is not null
*
* This transformer must be inserted before register allocator which relies on
* calculated operand constraints.
*
* The implementation of this transformer is located in the ConstraintResolverImpl class.
*
*/
static const char* help =
" The 'constraints' action accepts 3 sets of 3 parameters which \n"
" define profile-guided operand splitting for operand\n"
" uses, defs, and crossing call sites if these factors make\n"
" operand constraints worse (permitting less registers).\n"
" Each parameter defines a threshold enabling the splitting.\n"
" The threshold is applied to basic block execution count divided \n"
" by method entry execution count.\n"
" Thus, the threshold being 0 means never, and being 1000000000 means always.\n"
" Parameters:\n"
" callSplitThresholdForNoRegs=<integer>\n"
" Applied if crossing a call site narrows an operand constraint to NO regs.\n"
" Default value is 'always'.\n"
" callSplitThresholdFor1Reg=<integer>\n"
" Applied if crossing a call site narrows an operand constraint to <=1 reg.\n"
" Default value is 1 (very cold code).\n"
" callSplitThresholdFor4Regs=<integer>\n"
" Applied if crossing a call site narrows an operand constraint to <=4 regs.\n"
" Default value is 4 (very cold code).\n"
" defSplitThresholdForNoRegs=<integer>\n"
" Default value is 0 ('never').\n"
" defSplitThresholdFor1Reg=<integer>\n"
" Default value is 0 ('never').\n"
" defSplitThresholdFor4Regs=<integer>\n"
" Default value is 0 ('never').\n"
" useSplitThresholdForNoRegs=<integer>\n"
" Default value is 0 ('never').\n"
" useSplitThresholdFor1Reg=<integer>\n"
" Default value is 0 ('never').\n"
" useSplitThresholdFor4Regs=<integer>\n"
" Default value is 0 ('never').\n"
;
class ConstraintsResolver : public SessionAction {
/** runImpl is required override, calls ConstraintsResolverImpl.runImpl */
void runImpl();
/** This transformer requires up-to-date liveness info */
U_32 getNeedInfo()const{ return NeedInfo_LivenessInfo; }
U_32 getSideEffects()const{ return 0; }
};
static ActionFactory<ConstraintsResolver> _constraints("constraints", help);
//========================================================================================
// class ConstraintsResolverImpl
//========================================================================================
/**
* class Ia32ConstraintsResolverImpl is an implementation of simple constraint resolution algorithm
* The algorithm takes one-pass over CFG.
*
* The algorithm works as follows:
*
* 1) Creates an array of basic blocks and orders by bb->getExecCount()
* in createBasicBlockArray().
* Thus, the algorithm handles hottest basic blocks first and constraints are assigned to operands first
* from the most frequently used instructions
*
* 2) Collects a bit vector of all operands live at entries of all dispatch node entries
* in calculateLiveAtDispatchBlockEntries()
*
* 3) For all operands:
* - If an operand has already been assigned to some location
* (its location constraint is not null) the calculated constraint is set to
* the location constraint
*
* - If an operand is live at entry of a dispatch node
* the calculated constraint is set to the constraint
* preserving operand values during exception throwing
* This constraint is returned by getDispatchEntryConstraint
* In fact this is the constraint for the DRL calling convention
*
* This is done in calculateStartupOpndConstraints()
* Originally all calculated constraints are equal to Initial constraints
*
* 4) Walks through all basic blocks collected and arranged at step 1
* in resolveConstraints()
*
* The opndReplaceWorkset array of operand replacements is maintained
* (indexed by from-operand id).
*
* This is the array of current replacement for operands
* and is reset for each basic block (local within basic blocks)
*
* This array is filled as a result of operand splitting and indicates
* which operand must be used instead of original ones for all the instructions
* above the one caused splitting
*
* 4.1) Walks throw all instruction of a basic block in backward order
* in resolveConstraints(BasicBlock * bb)
* 4.1.1) resolves constraints for each instruction
* in resolveConstraints(Inst * inst);
*
* To do this already collected calculated constraint of
* either original operand or its current replacement is anded
* with instruction constraint for this operand occurrence and
* if the result is null, new operand is created and substituted instead
*
* 4.1.1.1) All def operands of the instruction are traversed
* and operand splitting is performed after the instruction (when necessary)
* def&use cases are also handled during this step
* 4.1.1.2) If the instruction is CALL, all hovering operands of
* the instruction are traversed.
*
* Hovering operands are operands which are live across a call site and are not
* redefined in the call site
* This step ensures operands are saved in callee-save regs or memory
* and takes into account whether an operand is live at dispatch node entries
*
* Operand splitting is performed before the instruction (when necessary)
* 4.1.1.3) All use operands of the instruction are traversed
* and operand splitting is performed before the instruction (when necessary)
*
* The current implementation doesn't deal properly with conditional memory constraints.
* I.e. it doesn't resolve properly things like ADD m, m when both operands are already
* assigned.
*
* For more details please refer to ConstraintsResolverImpl source code
*/
//_________________________________________________________________________________________________
Constraint ConstraintsResolverImpl::getCalleeSaveConstraint(Inst * inst, Opnd * opnd)
{
// This implementation don't take into account operand types
// and provides only GP call-safe regs (thus only memory for non-integer and non-pointer types)
assert(inst->getKind()==Inst::Kind_CallInst);
Constraint c=(Constraint(OpndKind_Memory)|STACK_REG|((CallInst*)inst)->getCalleeSaveRegs()) & opnd->getConstraint(Opnd::ConstraintKind_Initial);
return c.isNull()?Constraint(OpndKind_Memory, opnd->getSize()):c;
}
//_________________________________________________________________________________________________
Constraint ConstraintsResolverImpl::getDispatchEntryConstraint(Opnd * opnd)
{
// Currently the same result as from getCalleeSaveConstraint
Constraint c=(Constraint(OpndKind_Memory)|STACK_REG|Constraint(irManager.getEntryPointInst()->getCallingConventionClient().getCallingConvention()->getCalleeSavedRegs(OpndKind_GPReg))) & opnd->getConstraint(Opnd::ConstraintKind_Initial);
return c.isNull()?Constraint(OpndKind_Memory, opnd->getSize()):c;
}
//_________________________________________________________________________________________________
void ConstraintsResolverImpl::run()
{
// Set all instruction constraints for EntryPoints, CALLs and RETs
if (!second)
irManager.applyCallingConventions();
// Initialization
originalOpndCount=irManager.getOpndCount();
liveOpnds.resizeClear(originalOpndCount);
needsOriginalOpnd.resizeClear(originalOpndCount);
liveAtDispatchBlockEntry.resizeClear(originalOpndCount);
opndReplaceWorkset.resize(originalOpndCount);
for (U_32 i=0; i<originalOpndCount; i++)
opndReplaceWorkset[i]=NULL;
opndUsage.resize(originalOpndCount);
for (U_32 i=0; i<originalOpndCount; i++)
opndUsage[i]=0;
hoveringOpnds.resize(originalOpndCount);
// Fill array of basic blocks and order it
createBasicBlockArray();
// Collect operands live at dispatch blocks
calculateLiveAtDispatchBlockEntries();
// Pre-set calculated constraints for operands
calculateStartupOpndConstraints();
// Resolve constraints
resolveConstraints();
// This is a local transformation, resize liveness vectors
irManager.fixLivenessInfo();
}
//_________________________________________________________________________________________________
double ConstraintsResolverImpl::getBasicBlockPriority(Node * node)
{
// Use simple heuristics to handle prologs and epilogs after all other nodes.
// This improves performance as prologs and epilogs usually set bad constraints
// to operands (entry points, rets)
return
irManager.getFlowGraph()->getEntryNode() == node ? (double)0 :
irManager.isEpilog(node) ? (double)1 :
10 + node->getExecCount();
}
//_________________________________________________________________________________________________
void ConstraintsResolverImpl::createBasicBlockArray()
{
// Filling of basicBlock, simple insertion-based ordering of basic blocks
const Nodes& nodes = irManager.getFlowGraph()->getNodes();
for (Nodes::const_iterator it = nodes.begin(), end = nodes.end(); it!=end; ++it) {
Node* node = *it;
if (node->isBlockNode()){
double bbecv = getBasicBlockPriority(node);
U_32 ibb=0;
for (U_32 nbb=(U_32)basicBlocks.size(); ibb<nbb; ++ibb){
if (bbecv > getBasicBlockPriority(basicBlocks[ibb]))
break;
}
basicBlocks.insert(basicBlocks.begin()+ibb, node);
}
}
}
//_________________________________________________________________________________________________
void ConstraintsResolverImpl::calculateLiveAtDispatchBlockEntries()
{
const Nodes& nodes = irManager.getFlowGraph()->getNodes();
for (Nodes::const_iterator it = nodes.begin(), end = nodes.end(); it!=end; ++it) {
Node *node = *it;
if (node->isDispatchNode()) {
liveAtDispatchBlockEntry.unionWith(*irManager.getLiveAtEntry(node));
}
}
}
//_________________________________________________________________________________________________
void ConstraintsResolverImpl::calculateStartupOpndConstraints()
{
// Reset calculated constraints to null constraints
irManager.resetOpndConstraints();
// For all operands in the CFG
for (U_32 i=0; i<originalOpndCount; i++){
Opnd * opnd=irManager.getOpnd(i);
Constraint c=opnd->getConstraint(Opnd::ConstraintKind_Initial);
Constraint cl=opnd->getConstraint(Opnd::ConstraintKind_Location);
if (!cl.isNull()) // Set Calculated to Location
c=cl;
if (liveAtDispatchBlockEntry.getBit(i)){ // Set calculated to the constraint for dispatch entries
if (cl.isNull()){ // if location is set it must satisfy dispatch entry constraints.
c=c & getDispatchEntryConstraint(opnd);
}
needsOriginalOpnd.setBit(i);
}
// result must not be null as well as opnd initial constrains
assert(!c.isNull() && !opnd->getConstraint(Opnd::ConstraintKind_Initial).isNull());
// set the results
opnd->setCalculatedConstraint(c);
}
}
//_________________________________________________________________________________________________
bool ConstraintsResolverImpl::constraintIsWorse(Constraint cnew, Constraint cold, unsigned normedBBExecCount,
unsigned splitThresholdForNoRegs, unsigned splitThresholdFor1Reg, unsigned splitThresholdFor4Regs
)
{
if (cnew.isNull())
return true;
U_32 newMask = cnew.getMask(), oldMask = cold.getMask();
if ((newMask & oldMask) != oldMask){
U_32 newMaskCount = countOnes(newMask);
return
(newMaskCount == 0 && normedBBExecCount < splitThresholdForNoRegs) ||
(newMaskCount <= 1 && normedBBExecCount < splitThresholdFor1Reg) ||
(newMaskCount <= 4 && normedBBExecCount < splitThresholdFor4Regs);
}
return false;
}
//_________________________________________________________________________________________________
void ConstraintsResolverImpl::resolveConstraintsWithOG(Inst * inst)
{
// Initialize hoveringOpnds with operands live after the call if the inst is CALL
if (inst->getMnemonic()==Mnemonic_CALL)
hoveringOpnds.copyFrom(liveOpnds);
double dblExecCount = 1000. * inst->getBasicBlock()->getExecCount() / irManager.getFlowGraph()->getEntryNode()->getExecCount();
if (dblExecCount > 100000000.)
dblExecCount = 100000000.;
unsigned execCount = (unsigned)dblExecCount;
// first handle all defs
{Inst::Opnds opnds(inst, Inst::OpndRole_AllDefs);
for (Inst::Opnds::iterator it = opnds.begin(); it != opnds.end(); it = opnds.next(it)){
Opnd * originalOpnd=inst->getOpnd(it);
// We haven't changed def-operands yet for this instruction
assert(originalOpnd->getId()<originalOpndCount);
assert(!needsOriginalOpnd.getBit(originalOpnd->getId())||opndReplaceWorkset[originalOpnd->getId()]==NULL);
// currentOpnd is either the current replacement or the original operand
Opnd * currentOpnd=opndReplaceWorkset[originalOpnd->getId()];
if (currentOpnd==NULL){
currentOpnd=originalOpnd;
opndUsage[originalOpnd->getId()]+=execCount;
}
// get what is already collected
Constraint cc=currentOpnd->getConstraint(Opnd::ConstraintKind_Calculated);
assert(!cc.isNull());
// get the weak instruction constraint for this occurrence
Constraint ci=inst->getConstraint(it, (1 << it) , originalOpnd->getSize());
assert(!ci.isNull());
// & the result
Constraint cr=cc & ci;
Opnd * opndToSet=currentOpnd;
if (!constraintIsWorse(cr, cc, execCount, defSplitThresholdForNoRegs, defSplitThresholdFor1Reg, defSplitThresholdFor4Regs)){
// can substitute currentReplacementOpnd into this position
currentOpnd->setCalculatedConstraint(cr);
}else{
// cannot substitute currentReplacementOpnd into this position, needs splitting
opndToSet=irManager.newOpnd( originalOpnd->getType(), ci | Constraint(OpndKind_Mem, ci.getSize()) );
Inst * copySequence=irManager.newCopyPseudoInst(Mnemonic_MOV, currentOpnd, opndToSet);
// split after the defining instruction
copySequence->insertAfter(inst);
if (inst->getOpndRoles(it)&Inst::OpndRole_Use){
// This is def&use case (like add t0, t1 for t0)
if (!needsOriginalOpnd.getBit(originalOpnd->getId())){
// use the new operand for all the instructions above
opndReplaceWorkset[originalOpnd->getId()]=opndToSet;
}else{
// use the original operand for all the instructions above
assert(currentOpnd==originalOpnd);
Inst * copySequence=irManager.newCopyPseudoInst(Mnemonic_MOV, opndToSet, originalOpnd);
// split above the instruction
copySequence->insertBefore(inst);
opndUsage[originalOpnd->getId()]+=execCount;
}
}
}
// Update liveness
if (inst->isLiveRangeEnd(it)){ // if pure def, not def&use, terminate live range
liveOpnds.setBit(originalOpnd->getId(), false);
// also terminate replacement chain
opndReplaceWorkset[originalOpnd->getId()] = NULL;
}
// need to set the new operand into this place of the instruction
if (opndToSet!=originalOpnd)
inst->setOpnd(it, opndToSet);
}}
// now handle operands hovering over call insts
if (inst->getMnemonic()==Mnemonic_CALL){
// for all operands
BitSet::IterB ib(hoveringOpnds);
for (int i = ib.getNext(); i != -1; i = ib.getNext()){
Opnd * originalOpnd=irManager.getOpnd(i);
assert(originalOpnd->getId()<originalOpndCount);
assert(!needsOriginalOpnd.getBit(originalOpnd->getId())||opndReplaceWorkset[originalOpnd->getId()]==NULL);
// currentOpnd is either the current replacement or the original operand
Opnd * currentOpnd=opndReplaceWorkset[originalOpnd->getId()];
if (currentOpnd==NULL){
currentOpnd=originalOpnd;
opndUsage[originalOpnd->getId()]+=execCount;
}
Opnd * opndToSet=NULL;
// was live and is not redefined by this inst
if (liveOpnds.getBit(originalOpnd->getId())){
// Instruction-level constraints are constraints describing locations safe across CALLs
Constraint ci=getCalleeSaveConstraint(inst, currentOpnd);
// we have at least memory
assert(!ci.isNull());
Constraint cc=currentOpnd->getConstraint(Opnd::ConstraintKind_Calculated);
assert(!cc.isNull());
// & the result
Constraint cr=cc & ci;
opndToSet=currentOpnd;
if (!constraintIsWorse(cr, cc, execCount, callSplitThresholdForNoRegs, callSplitThresholdFor1Reg, callSplitThresholdFor4Regs)){
// can substitute currentReplacementOpnd into this position
opndToSet->setCalculatedConstraint(cr);
}else{
// cannot substitute currentReplacementOpnd into this position, needs splitting
// Try to use originalOpnd over this instruction and for the instructions above
Constraint co=originalOpnd->getConstraint(Opnd::ConstraintKind_Calculated);
Constraint cr=co & ci;
if (!constraintIsWorse(cr, cc, execCount, callSplitThresholdForNoRegs, callSplitThresholdFor1Reg, callSplitThresholdFor4Regs)){
opndToSet=originalOpnd;
opndToSet->setCalculatedConstraint(cr);
}else{
// cannot use original, create a new one
opndToSet=irManager.newOpnd(originalOpnd->getType(), ci | Constraint(OpndKind_Mem, ci.getSize()));
}
}
}
if (opndToSet!=NULL){
if (opndToSet!=currentOpnd){
// an operand different to the current replacement
// is required to be over this call site, append splitting below the call site
// this is like restoring from a call-safe location under a call
Inst * copySequence=irManager.newCopyPseudoInst(Mnemonic_MOV, currentOpnd, opndToSet);
copySequence->insertAfter(inst);
}
if (!needsOriginalOpnd.getBit(originalOpnd->getId()))
opndReplaceWorkset[originalOpnd->getId()]=opndToSet; // can use the replacement operand above
else if (opndToSet!=originalOpnd){
// add splitting above
// this is like saving into a call-safe location above a call
assert(currentOpnd==originalOpnd);
Inst * copySequence=irManager.newCopyPseudoInst(Mnemonic_MOV, opndToSet, originalOpnd);
copySequence->insertBefore(inst);
opndUsage[originalOpnd->getId()]+=execCount;
}
}
}
}
// now handle all uses
{Inst::Opnds opnds(inst, Inst::OpndRole_AllUses);
for (Inst::Opnds::iterator it = opnds.begin(); it != opnds.end(); it = opnds.next(it)){
Opnd * originalOpnd=inst->getOpnd(it);
if ((inst->getOpndRoles(it)&Inst::OpndRole_Def)==0){ // the use&def case was handled above
assert(originalOpnd->getId()<originalOpndCount);
assert(!needsOriginalOpnd.getBit(originalOpnd->getId())||opndReplaceWorkset[originalOpnd->getId()]==NULL);
// currentOpnd is either the current replacement or the original operand
Opnd * currentOpnd=opndReplaceWorkset[originalOpnd->getId()];
if (currentOpnd==NULL){
currentOpnd=originalOpnd;
opndUsage[originalOpnd->getId()]+=execCount;
}
// get what is already collected
Constraint cc=currentOpnd->getConstraint(Opnd::ConstraintKind_Calculated);
assert(!cc.isNull());
Constraint ci=inst->getConstraint(it, (1 << it), originalOpnd->getSize());
assert(!ci.isNull());
Constraint cr=cc & ci;
Opnd * opndToSet=currentOpnd;
if (!constraintIsWorse(cr, cc, execCount, useSplitThresholdForNoRegs, useSplitThresholdFor1Reg, useSplitThresholdFor4Regs)){
// can substitute currentReplacementOpnd into this position
currentOpnd->setCalculatedConstraint(cr);
}else{
// cannot substitute currentReplacementOpnd into this position, needs splitting
// split above the inst, force to insert the new operand into the inst, and use
// currentOpnd above
opndToSet=irManager.newOpnd(originalOpnd->getType(), ci | Constraint(OpndKind_Mem, ci.getSize()));
Inst * copySequence=irManager.newCopyPseudoInst(Mnemonic_MOV, opndToSet, currentOpnd);
copySequence->insertBefore(inst);
}
// update liveness (for def/use case
if (inst->isLiveRangeStart(it))
liveOpnds.setBit(originalOpnd->getId(), true);
// need to set the new operand into this place of the instruction
if (opndToSet!=originalOpnd)
inst->setOpnd(it, opndToSet);
}
}}
}
//_________________________________________________________________________________________________
void ConstraintsResolverImpl::resolveConstraints(Node * bb)
{
assert(bb->isBlockNode());
// scan all insts of bb in reverse order
irManager.getLiveAtExit(bb, liveOpnds);
for (Inst * inst=(Inst*)bb->getLastInst(), * prevInst=NULL; inst!=NULL; inst=prevInst){
prevInst=inst->getPrevInst();
resolveConstraintsWithOG(inst);
}
// if we come to bb entry with some replacement for an operand and the operand is live at the entry
// insert copying from the original operand to the replacement operand
U_32 execCount = (U_32)bb->getExecCount();
BitSet * ls = irManager.getLiveAtEntry(bb);
BitSet::IterB ib(*ls);
for (int i = ib.getNext(); i != -1; i = ib.getNext()){
Opnd * originalOpnd = irManager.getOpnd(i);
assert(originalOpnd->getId()<originalOpndCount);
Opnd * currentOpnd=opndReplaceWorkset[originalOpnd->getId()];
if (currentOpnd!=NULL){
if (currentOpnd!=originalOpnd){
// assert(irManager.getLiveAtEntry(bb)->isLive(originalOpnd));
Inst * copySequence=irManager.newCopyPseudoInst(Mnemonic_MOV, currentOpnd, originalOpnd);
bb->prependInst(copySequence);
opndUsage[originalOpnd->getId()]+=execCount;
}
opndReplaceWorkset[originalOpnd->getId()]=NULL;
}
}
}
//_________________________________________________________________________________________________
void ConstraintsResolverImpl::resolveConstraints()
{
// for all basic blocks in the array
for (U_32 ibb=0, nbb=(U_32)basicBlocks.size(); ibb<nbb; ++ibb){
resolveConstraints(basicBlocks[ibb]);
}
}
//_________________________________________________________________________________________________
void ConstraintsResolver::runImpl()
{
// call the private implementation of the algorithm
ConstraintsResolverImpl impl(*irManager);
getArg("callSplitThresholdForNoRegs", impl.callSplitThresholdForNoRegs);
getArg("callSplitThresholdFor1Reg", impl.callSplitThresholdFor1Reg);
getArg("callSplitThresholdFor4Regs", impl.callSplitThresholdFor4Regs);
getArg("defSplitThresholdForNoRegs", impl.defSplitThresholdForNoRegs);
getArg("defSplitThresholdFor1Reg", impl.defSplitThresholdFor1Reg);
getArg("defSplitThresholdFor4Regs", impl.defSplitThresholdFor4Regs);
getArg("useSplitThresholdForNoRegs", impl.useSplitThresholdForNoRegs);
getArg("useSplitThresholdFor1Reg", impl.useSplitThresholdFor1Reg);
getArg("useSplitThresholdFor4Regs", impl.useSplitThresholdFor4Regs);
impl.run();
}
//_________________________________________________________________________________________________
}}; //namespace Ia32