blob: d92291e5b60a4a1d0091ae52ec28fae49c736f06 [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 Nikolay A. Sidelnikov
*/
#include "Ia32IRManager.h"
namespace Jitrino
{
namespace Ia32 {
//========================================================================================
// class RCE
//========================================================================================
/**
* class RCE performs removing comparisons following instructions which
* affected flags in the same way as CMP. In some cases instructions can be
* reordered for resolving comparison as available for removing
*
* The algorithm takes one-pass over CFG.
*
* This transformer ensures that
*
* 1) All conditional instructions get the same EFLAGS value as before
* transformation
*
* 2) All reordered instructions do the same effects as before transformation
*
* For example:
*
* Original code piece:
* I29: t50.:I_32 (ID:v15(EFLGS):U_32) =AND .t28:I_32,t49(1):I_32
* I30: (AD:v1:I_32) =CopyPseudoInst (AU:t48:I_32)
* I31: (AD:v2:I_32) =CopyPseudoInst (AU:t25:I_32)
* I32: (AD:v3:I_8[]) =CopyPseudoInst (AU:t38:I_8[])
* I33: (ID:v15(EFLGS):U_32) =CMP .t50:I_32,t51(0):I_32
* I34: JNZ BB_12 t52(0):intptr (IU:v15(EFLGS):U_32)
*
* After optimization:
* I29: t50:I_32 (ID:v15(EFLGS):U_32) =AND .t28:I_32,t49(1):I_32
* I30: (AD:v1:I_32) =CopyPseudoInst (AU:t48:I_32)
* I31: (AD:v2:I_32) =CopyPseudoInst (AU:t25:I_32)
* I32: (AD:v3:I_8[]) =CopyPseudoInst (AU:t38:I_8[])
* I34: JNZ BB_12 t52(0):intptr (IU:v15(EFLGS):U_32)
*
* The implementation of this transformer is located in Ia32RCE.cpp
*
*/
class RCE : public SessionAction {
void runImpl();
protected:
// check if flags used by the conditional instruction (=condInst)
// are affected by the instruction (=inst) in the same way as
// a hypothetical CMP follower (with appropriate arguments) does
bool instAffectsFlagsAsCmpInst(Inst * inst, Inst * condInst);
// check instruction inst for possibility of removing
bool isSuitableToRemove(Inst * inst, Inst * condInst, Inst * cmpInst, Opnd * cmpOp);
};
static ActionFactory<RCE> _rce("rce");
/**
* The algorithm finds conditional instruction (=condInst) first, then
* corresponding CMP instruction (=cmpInst) and arithmetic instruction (=inst)
* which affects flags in the same way as CMP. Combination is considered as
* available to be reduced if there are no instructions between CMP and
* arithmetic instruction which influence to flags or CMP operands.
*
* Also it transforms some conditional instruction to make them more suitable
* for optimizations
*/
void
RCE::runImpl()
{
Inst * inst, * cmpInst, *condInst;
Opnd * cmpOp = NULL;
cmpInst = condInst = NULL;
const Nodes& nodes = irManager->getFlowGraph()->getNodesPostOrder();
for (Nodes::const_iterator it = nodes.begin(), end = nodes.end(); it!=end; ++it) {
Node* node = *it;
if (node->isBlockNode()) {
if(node->isEmpty()) {
continue;
}
cmpInst = NULL;
Inst* prevInst = NULL;
for(inst = (Inst*)node->getLastInst(); inst != NULL; inst = prevInst) {
prevInst = inst->getPrevInst();
//find conditional instruction
Mnemonic baseMnem = getBaseConditionMnemonic(inst->getMnemonic());
if (baseMnem != Mnemonic_NULL) {
condInst = condInst ? NULL : inst;
cmpInst = NULL;
} else if (condInst) {
//find CMP instruction corresponds to conditional instruction
if(inst->getMnemonic() == Mnemonic_CMP || inst->getMnemonic() == Mnemonic_UCOMISD || inst->getMnemonic() == Mnemonic_UCOMISS) {
if (cmpInst) {
//this comparison is redundant because of overrided by cmpInst
inst->unlink();
continue;
}
cmpInst = inst;
U_32 defCount = inst->getOpndCount(Inst::OpndRole_InstLevel|Inst::OpndRole_Def);
if(inst->getOpnd(defCount+1)->isPlacedIn(OpndKind_Imm)) {
//try to change conditional instruction to make combination available to optimize
cmpOp = inst->getOpnd(defCount);
Inst * newCondInst = NULL;
Mnemonic mnem;
int64 val = inst->getOpnd(defCount+1)->getImmValue();
if (val == 0) {
continue;
} else if (val == 1 && ConditionMnemonic(condInst->getMnemonic()-getBaseConditionMnemonic(condInst->getMnemonic())) == ConditionMnemonic_L){
mnem = Mnemonic((condInst->getMnemonic() - Mnemonic(ConditionMnemonic_L)) + Mnemonic(ConditionMnemonic_LE));
} else if (val == -1 && ConditionMnemonic(condInst->getMnemonic()-getBaseConditionMnemonic(condInst->getMnemonic())) == ConditionMnemonic_G) {
mnem = Mnemonic((condInst->getMnemonic() - Mnemonic(ConditionMnemonic_G)) + Mnemonic(ConditionMnemonic_GE));
} else if (val == -1 && ConditionMnemonic(condInst->getMnemonic()-getBaseConditionMnemonic(condInst->getMnemonic())) == ConditionMnemonic_B) {
mnem = Mnemonic((condInst->getMnemonic() - Mnemonic(ConditionMnemonic_B)) + Mnemonic(ConditionMnemonic_BE));
} else {
continue;
}
//replace old conditional instruction
if (condInst->hasKind(Inst::Kind_BranchInst)) {
BranchInst* br = (BranchInst*)condInst;
newCondInst = irManager->newBranchInst(mnem,br->getTrueTarget(), br->getFalseTarget(), condInst->getOpnd(0));
} else {
Mnemonic condMnem = getBaseConditionMnemonic(condInst->getMnemonic());
Inst::Opnds defs(condInst,Inst::OpndRole_Def|Inst::OpndRole_Explicit);
if (condMnem == Mnemonic_CMOVcc) {
Inst::Opnds uses(condInst,Inst::OpndRole_Use|Inst::OpndRole_Explicit);
newCondInst = irManager->newInst(mnem, condInst->getOpnd(defs.begin()), inst->getOpnd(uses.begin()));
} else if (condMnem == Mnemonic_SETcc) {
newCondInst = irManager->newInst(mnem, condInst->getOpnd(defs.begin()));
} else {
assert(0);
continue;
}
}
newCondInst->insertAfter(condInst);
condInst->unlink();
condInst = newCondInst;
inst->setOpnd(defCount+1, irManager->newImmOpnd(inst->getOpnd(defCount+1)->getType(),0));
}
//find flags affected instruction precedes cmpInst
} else if (instAffectsFlagsAsCmpInst(inst, condInst)) {
if (cmpInst) {
if (isSuitableToRemove(inst, condInst, cmpInst, cmpOp))
{
cmpInst->unlink();
}
}
condInst = NULL; // do not optimize cmpInst any more in this block
} else {
if (inst->getOpndCount(Inst::OpndRole_Implicit|Inst::OpndRole_Def) || inst->getMnemonic() == Mnemonic_CALL) {
// instruction affects flags, skip optimizing cmpInst
condInst = NULL;
} else {
//check for moving cmpInst operands
if ((inst->getMnemonic() == Mnemonic_MOV) && (inst->getOpnd(0) == cmpOp)) {
cmpOp = inst->getOpnd(1);
}
}
}
}//end if/else by condInst
}//end for() by Insts
}//end if BasicBlock
}//end for() by Nodes
}
bool
RCE::instAffectsFlagsAsCmpInst(Inst * inst, Inst * condInst)
{
if (!inst->getOpndCount(Inst::OpndRole_Implicit|Inst::OpndRole_Def))
//instruction doesn't change flags
return false;
ConditionMnemonic mn = ConditionMnemonic(condInst->getMnemonic()-getBaseConditionMnemonic(condInst->getMnemonic()));
switch (inst->getMnemonic()) {
case Mnemonic_SUB:
// instruction changes all flags, but an overflow may affect the OF flag,
// in that case jumping by JL, JLE, etc. is incorrect
return (mn != ConditionMnemonic_L && mn != ConditionMnemonic_LE && mn != ConditionMnemonic_GE && mn != ConditionMnemonic_G);
case Mnemonic_IDIV:
case Mnemonic_CALL:
case Mnemonic_IMUL:
case Mnemonic_MUL:
case Mnemonic_SBB: //SBB does not distinguish between signed and unsigned operands
//instruction changes flags in the way doesn't correspond CMP
return false;
default:
//instruction changes particular flags
return ( mn == ConditionMnemonic_Z || mn == ConditionMnemonic_NZ) ? true : false;
}
}
bool RCE::isSuitableToRemove(Inst * inst, Inst * condInst, Inst * cmpInst, Opnd * cmpOp)
{
/* cmpInst can be removed if inst defines the same operand which will be
* compared with zero by cmpInst
* Required: Native form of insts
*/
U_32 cmpOpCount = cmpInst->getOpndCount(Inst::OpndRole_InstLevel|Inst::OpndRole_UseDef);
if ((cmpOp == inst->getOpnd(0)) && cmpInst->getOpnd(cmpOpCount -1)->isPlacedIn(OpndKind_Imm) && (cmpInst->getOpnd(cmpOpCount -1)->getImmValue() == 0)) {
return true;
}
return false;
}
}} //end namespace Ia32