blob: f9b04732d86e76356a2af8787a8db9301764dd15 [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 Intel, Pavel A. Ozhdikhin
* @version $Revision: 1.14.24.4 $
*
*/
#include <assert.h>
#include <ostream>
#include "Stl.h"
#include "Log.h"
#include "constantfolder.h"
#include "abcd.h"
#include "abcdbounds.h"
#include "abcdsolver.h"
#include "opndmap.h"
#include "optarithmetic.h"
namespace Jitrino {
AbcdSolver::AbcdSolver(Abcd *pass) :
thePass(pass), cache(pass->mm), active(pass->mm), indent("")
{
}
AbcdSolver::~AbcdSolver()
{
}
// try to prove (var2 - var1 <= c)
bool AbcdSolver::demandProve(bool prove_lower_bound,
const VarBound &var1,
const VarBound &var2,
int64 c,
AbcdReasons *why)
{
return (prove(prove_lower_bound, var1, var2, ConstBound(c), why)
!= ProofLattice::ProvenFalse);
}
// if prove_lower_bound,
// try to prove var2 - var1 >= c
// else
// try to prove var2 - var1 <= c
ProofLattice AbcdSolver::prove(bool prove_lower_bound,
const VarBound &var1, // a
const VarBound &var2, // v
ConstBound c,
AbcdReasons *why)
{
pushindent saveindent(this);
if (prove_lower_bound) {
ConstBound negC = -c;
if (Log::isEnabled()) {
Log::out() << indent.c_str() << "Trying to prove ";
var2.print(Log::out());
Log::out() << " - ";
var1.print(Log::out());
Log::out() << " >= ";
c.print(Log::out());
Log::out() << " : ";
Log::out() << indent.c_str() << "Rephrased as ";
var1.print(Log::out());
Log::out() << " - ";
var2.print(Log::out());
Log::out() << " <= ";
negC.print(Log::out());
Log::out() << " : ";
}
// v2 - v1 >= c
// same as
// v1 - v2 <= -c
return prove(false, var2, var1, negC, why);
} else {
MemoizedBounds &mb
= cache[::std::make_pair(var2, var1)]; // bounds on (var2-var1)
if (Log::isEnabled()) {
Log::out() << indent.c_str() << "Trying to prove ";
var2.print(Log::out());
Log::out() << " - ";
var1.print(Log::out());
Log::out() << " <= ";
c.print(Log::out());
Log::out() << " : ";
}
if (mb.leastTrueUB <= c) { // already proved a lower UB.
if (Log::isEnabled()) {
Log::out() << "Case 1: mb.leastTrueUB ==";
mb.leastTrueUB.print(Log::out());
Log::out() << " => TRUE" << ::std::endl;
}
if (mb.leastTrueUBwhy) {
if (thePass->flags.useReasons) {
assert(why);
why->addReasons(*mb.leastTrueUBwhy);
}
}
return ProofLattice::ProvenTrue;
} else if (mb.greatestFalseUB >= c) { // already disproved a higher UB
if (Log::isEnabled()) {
Log::out() << "Case 2: mb.greatestFalseUB ==";
mb.greatestFalseUB.print(Log::out());
Log::out() << " => FALSE" << ::std::endl;
}
return ProofLattice::ProvenFalse;
} else if (mb.leastReducedUB <= c) { // has cycle with lower UB
if (Log::isEnabled()) {
Log::out() << "Case 3: mb.leastReducedUB ==";
mb.leastReducedUB.print(Log::out());
Log::out() << " => REDUCED" << ::std::endl;
}
if (mb.leastReducedUBwhy) {
if (thePass->flags.useReasons) {
assert(why);
why->addReasons(*mb.leastReducedUBwhy);
}
}
return ProofLattice::ProvenReduced;
} else if ((var1 == var2) &&
(c >= ConstBound(I_32(0)))) { // reached source:
if (Log::isEnabled()) {
Log::out() << "Case 4: => TRUE" << ::std::endl;
}
// don't need a reason, it's self-evident
return ProofLattice::ProvenTrue; // (a-a <= c), success
};
// check for a loop in proof
ConstBound &active_var = active[::std::make_pair(var1,var2)];
if (!active_var.isNull()) { // a cycle
if (Log::isEnabled()) {
Log::out() << "Case 5" << ::std::endl;
}
if (c < active_var) {
if (Log::isEnabled()) {
Log::out() << indent.c_str() << "Case 5a : active[";
var1.print(Log::out());
Log::out() << ", ";
var2.print(Log::out());
Log::out() << "]==";
active_var.print(Log::out());
Log::out() << " => FALSE" << ::std::endl;
}
// skip memorization step below by returning immediately
return ProofLattice::ProvenFalse; // a reducing cycle for lb
} else {
if (Log::isEnabled()) {
Log::out() << indent.c_str() << "Case 5b : active[";
var1.print(Log::out());
Log::out() << ", ";
var2.print(Log::out());
Log::out() << "]==";
active_var.print(Log::out());
Log::out() << " => REDUCED" << ::std::endl;
}
// skip memorization step below by returning immediately
return ProofLattice::ProvenReduced;
}
} else {
if (Log::isEnabled()) {
Log::out() << "Case 5c: => ..." << ::std::endl;
}
}
if (Log::isEnabled()) {
Log::out() << indent.c_str() << " Setting active[";
var1.print(Log::out());
Log::out() << ", ";
var2.print(Log::out());
Log::out() << "] to ";
c.print(Log::out());
Log::out() << ::std::endl;
}
active_var = c;
ProofLattice result = ProofLattice::ProvenFalse;
bool var1Blocked = var1.isEmpty();
AbcdReasons *subWhy = (thePass->flags.useReasons
? new (thePass->mm) AbcdReasons(thePass->mm)
: 0);
if (!var1Blocked) {
result =
proveForPredecessors(var1, var2, c, true, var1Blocked, subWhy); //var1
bool noSpecialCases = false;
if (result == ProofLattice::ProvenFalse) {
result =
proveForSpecialCases(var1, var2, c, true,
noSpecialCases,
subWhy); //var1
}
var1Blocked = var1Blocked && noSpecialCases;
}
if (var1Blocked) {
//
// Unlike ABCD paper, we do not build an explicit constraint
// graph, but instead just use the use->def edges in the IR.
// However, since we don't have def->use edges, we can only
// go one way in the graph.
//
// Therefore, unlike them, we must get predecessors of var1
// here, to dereference def->use edges from the var1 side.
// This allows us to find more paths.
//
// Note that we are safe from infinite recursion since we
// don't move to var2 until var1 is blocked.
//
bool ignore;
result =
proveForPredecessors(var1, var2,
c, false, ignore, subWhy); //var2
if (result == ProofLattice::ProvenFalse) {
result = proveForSpecialCases(var1, var2, c, false,
ignore, subWhy); //var2
}
}
active_var.setNull(); // reset it to Null
if (Log::isEnabled()) {
Log::out() << indent.c_str() << "Finished trying to prove ";
var2.print(Log::out());
Log::out() << " - ";
var1.print(Log::out());
Log::out() << " <= ";
c.print(Log::out());
Log::out() << " : ";
}
switch (result.value) {
case ProofLattice::ProvenTrue:
if (Log::isEnabled()) {
Log::out() << " TRUE " << ::std::endl;
}
if (c < mb.leastTrueUB) {
if (thePass->flags.useReasons) {
if (!mb.leastTrueUBwhy)
mb.leastTrueUBwhy = new (thePass->mm) AbcdReasons(thePass->mm);
mb.leastTrueUBwhy->clear();
assert(subWhy);
mb.leastTrueUBwhy->addReasons(*subWhy);
} else {
mb.leastTrueUBwhy = 0;
}
mb.leastTrueUB = c; break;
}
case ProofLattice::ProvenFalse:
if (Log::isEnabled()) {
Log::out() << " FALSE " << ::std::endl;
}
if (c > mb.greatestFalseUB) {
mb.greatestFalseUB = c; break;
}
case ProofLattice::ProvenReduced:
if (Log::isEnabled()) {
Log::out() << " REDUCED " << ::std::endl;
}
if (c < mb.leastReducedUB) {
if (thePass->flags.useReasons) {
if (!mb.leastReducedUBwhy)
mb.leastReducedUBwhy = new (thePass->mm) AbcdReasons(thePass->mm);
mb.leastReducedUBwhy->clear();
assert(subWhy);
mb.leastReducedUBwhy->addReasons(*subWhy);
}
mb.leastReducedUB = c; break;
}
}
if (thePass->flags.useReasons) {
assert(why && subWhy);
why->addReasons(*subWhy);
}
return result;
}
}
void AbcdSolver::tryToEliminate(Inst *theInst)
{
if (theInst->getOpcode() == Op_TauCheckBounds) {
assert(theInst->getNumSrcOperands() == 2);
Opnd *arrayOp = theInst->getSrc(0);
Opnd *idxOp = theInst->getSrc(1);
if (Log::isEnabled()) {
Log::out() << "Checking checkbounds instruction ";
theInst->print(Log::out());
Log::out() << " for redundancy" << ::std::endl;
}
bool successUB = true;
bool successLB = true;
AbcdReasons *why = (thePass->flags.useReasons
? new (thePass->mm) AbcdReasons(thePass->mm)
: 0);
// idx - a.size <= -1
if (demandProve(false,
VarBound(arrayOp), // may want lower bound of range
VarBound(idxOp), // may want upper bound of range
-1,
why)) {
if (Log::isEnabled()) {
Log::out() << "We can eliminate UB check of ";
theInst->print(Log::out());
if (thePass->flags.useReasons) {
Log::out() << " because of ";
why->print(Log::out());
}
Log::out() << ::std::endl;
}
} else {
if (Log::isEnabled()) {
Log::out() << "We cannot eliminate UB check of ";
theInst->print(Log::out());
Log::out() << ::std::endl;
}
successUB = false;
}
// idx - 0 >= 0
// same as
// 0 - idx <= 0
if (demandProve(false,
VarBound(idxOp), // may want lower bound of range
VarBound(), // upper bound of 0
0,
why)) {
if (Log::isEnabled()) {
Log::out() << "We can eliminate LB check of ";
theInst->print(Log::out());
if (thePass->flags.useReasons) {
Log::out() << " because of ";
why->print(Log::out());
}
Log::out() << ::std::endl;
}
} else {
if (Log::isEnabled()) {
Log::out() << "We cannot eliminate LB check of ";
theInst->print(Log::out());
Log::out() << ::std::endl;
}
successLB = false;
}
if (successUB) {
if (successLB) {
if (Log::isEnabled()) {
Log::out() << "!!! We can eliminate boundscheck of ";
theInst->print(Log::out());
if (thePass->flags.useReasons) {
Log::out() << " because of ";
why->print(Log::out());
}
Log::out() << ::std::endl;
}
AbcdReasons *ignore;
if (!thePass->isMarkedToEliminate(theInst, ignore)) {
if (why)
thePass->markCheckToEliminateAndWhy(theInst, why);
else
thePass->markCheckToEliminate(theInst);
}
} else {
if (Log::isEnabled()) {
Log::out() << "!!! We can eliminate UB check of ";
theInst->print(Log::out());
if (thePass->flags.useReasons) {
Log::out() << " because of ";
why->print(Log::out());
}
Log::out() << ::std::endl;
}
AbcdReasons *ignore;
if (!thePass->isMarkedToEliminateUB(theInst, ignore)) {
if (why)
thePass->markCheckToEliminateUBAndWhy(theInst, why);
else
thePass->markCheckToEliminateUB(theInst);
}
}
} else {
if (successLB) {
if (Log::isEnabled()) {
Log::out() << "!!! We can eliminate LB check of ";
theInst->print(Log::out());
if (thePass->flags.useReasons) {
Log::out() << " because of ";
why->print(Log::out());
}
Log::out() << ::std::endl;
}
AbcdReasons *ignore;
if (!thePass->isMarkedToEliminateLB(theInst, ignore)) {
if (why)
thePass->markCheckToEliminateLBAndWhy(theInst, why);
else
thePass->markCheckToEliminateLB(theInst);
}
}
}
if (!(successLB && successUB)) {
// see if we can disprove overflow
// idx >= 0
if (demandProve(true, // lower obund
VarBound(),
VarBound(idxOp),
0,
why)) {
U_32 elemSize = 16;
uint64 overflowSize = ((uint64)1 << 31) / elemSize;
if (demandProve(false, // upper bound
VarBound(),
VarBound(idxOp),
overflowSize,
why)) {
// overflow can't happen
theInst->setOverflowModifier(Overflow_None);
}
}
}
} else if (thePass->flags.remConv &&
(theInst->getOpcode() == Op_Conv)) {
assert(theInst->getNumSrcOperands() == 1);
Opnd *srcOp = theInst->getSrc(0);
Opnd *dstOp = theInst->getDst();
AbcdReasons *ignore;
if (Abcd::hasCheckableType(srcOp) &&
Abcd::hasCheckableType(dstOp) &&
Abcd::isCheckableType(theInst->getType()) &&
(srcOp->getType()->tag == dstOp->getType()->tag) &&
!thePass->isMarkedToEliminate(theInst, ignore)) {
if (Log::isEnabled()) {
Log::out() << "Checking conv instruction ";
theInst->print(Log::out());
Log::out() << " for redundancy" << ::std::endl;
}
if (Abcd::convPassesSource(dstOp)) {
if (Log::isEnabled()) {
Log::out() << "We can trivially remove conv instruction ";
theInst->print(Log::out());
Log::out() << ::std::endl;
}
thePass->markInstToEliminate(theInst);
} else {
VarBound dstBound = VarBound(dstOp);
VarBound srcBound = VarBound(srcOp);
PiCondition convBounds = PiCondition::convBounds(dstBound);
PiBound lb = convBounds.getLb();
PiBound ub = convBounds.getUb();
ProofLattice res1;
AbcdReasons *why = (thePass->flags.useReasons
? new (thePass->mm) AbcdReasons(thePass->mm)
: 0);
if (lb.isConstant()) {
ConstBound clb = lb.getConst();
res1 = prove(true, // lower bound
VarBound(),
srcBound,
clb,
why);
} else if (lb.isUndefined()) {
res1 = ProofLattice::ProvenTrue;
} else {
res1 = ProofLattice::ProvenFalse;
}
if (res1 != ProofLattice::ProvenFalse) {
if (ub.isConstant()) {
ConstBound cub = ub.getConst();
ProofLattice res2 = prove(false, // upper bound
VarBound(),
srcBound,
cub,
why);
res1.meetwith(res2);
} else if (!ub.isUndefined()) {
res1 = ProofLattice::ProvenFalse;
}
}
if (res1 != ProofLattice::ProvenFalse) {
// note that though these are constant bounds,
// they can result in reduced proofs
if (Log::isEnabled()) {
Log::out() << "!!! We can eliminate conversion: ";
theInst->print(Log::out());
if (thePass->flags.useReasons) {
Log::out() << " because of ";
why->print(Log::out());
}
Log::out() << ::std::endl;
}
if (why)
thePass->markInstToEliminateAndWhy(theInst, why);
else
thePass->markInstToEliminate(theInst);
}
}
}
} else if (thePass->flags.unmaskShifts &&
((theInst->getOpcode() == Op_Shr) ||
(theInst->getOpcode() == Op_Shl)) &&
(theInst->getShiftMaskModifier() == ShiftMask_Masked)) {
assert(theInst->getNumSrcOperands() == 2);
Opnd *shiftByOp = theInst->getSrc(1);
AbcdReasons *ignore;
if (Abcd::hasCheckableType(shiftByOp) &&
!thePass->isMarkedToEliminate(theInst, ignore)) {
if (Log::isEnabled()) {
Log::out() << "Checking shift instruction ";
theInst->print(Log::out());
Log::out() << " to eliminate mask" << ::std::endl;
}
I_32 mask;
switch (theInst->getType()) {
case Type::Int32:
mask = 31; break;
case Type::Int64:
mask = 63; break;
default:
mask = -1; break;
}
AbcdReasons *why = (thePass->flags.useReasons
? new (thePass->mm) AbcdReasons(thePass->mm)
: 0);
if (mask > 0) {
VarBound shiftByVar = VarBound(shiftByOp);
ConstBound zerob(I_32(0));
ProofLattice res1 = prove(true, // lower bound
VarBound(),
shiftByVar,
zerob, why);
if (res1 != ProofLattice::ProvenFalse) {
ConstBound upperb(mask);
ProofLattice res2 = prove(false, // upper bound
VarBound(),
shiftByVar,
upperb, why);
if (res2 != ProofLattice::ProvenFalse) {
// note that though these are constant bounds,
// they can result in reduced proofs
if (Log::isEnabled()) {
Log::out() << "!!! We can eliminate shift mask: ";
theInst->print(Log::out());
if (thePass->flags.useReasons) {
Log::out() << " because of ";
why->print(Log::out());
}
Log::out() << ::std::endl;
}
if (why)
thePass->markInstToEliminateAndWhy(theInst, why);
else
thePass->markInstToEliminate(theInst);
}
}
}
}
} else if (thePass->flags.remBr &&
(theInst->getOpcode() == Op_Branch)) {
if (Abcd::hasCheckableType(theInst->getSrc(0)))
tryToFoldBranch(theInst);
} else if (thePass->flags.remCmp &&
(theInst->getOpcode() == Op_Cmp)) {
if (Abcd::hasCheckableType(theInst->getSrc(0)))
tryToFoldCompare(theInst);
} else if (thePass->flags.remOverflow &&
Abcd::isCheckableType(theInst->getType()) &&
((theInst->getOpcode() == Op_Add) ||
(theInst->getOpcode() == Op_Sub))) {
assert(theInst->getNumSrcOperands() == 2);
Modifier mod = theInst->getModifier();
AbcdReasons *why = (thePass->flags.useReasons
? new (thePass->mm) AbcdReasons(thePass->mm)
: 0);
if ((mod.getOverflowModifier() == Overflow_None) ||
(mod.getExceptionModifier() == Exception_Sometimes)) {
// check whether we can overflow
bool isSigned = mod.getOverflowModifier() != Overflow_Unsigned;
Opnd *opnd0 = theInst->getSrc(0);
Opnd *opnd1 = theInst->getSrc(1);
int64 lb, ub;
Type::Tag instType = theInst->getType();
if (Abcd::hasTypeBounds(instType, lb, ub) &&
(theInst->getType() != Type::Int64)) { // is somewhat unbounded if int64
Opnd *constOpnd = 0;
Opnd *varOpnd = 0;
bool negate = theInst->getOpcode() == Op_Sub;
bool negateConstant = false;
if (ConstantFolder::isConstant(opnd0)) {
constOpnd = opnd0;
varOpnd = opnd1;
} else if (ConstantFolder::isConstant(opnd1)) {
constOpnd = opnd1;
varOpnd = opnd0;
negateConstant = negate;
}
bool provenNoOverflow = false;
if (constOpnd) {
// we can be fairly precise
// for var+constOpnd, bounds on var are [lb-constOpnd, ub-constOpnd],
// for var-constOpnd, bounds on var are [lb+constOpnd, ub+constOpnd]
// for constOpnd-var, bounds on var are [constOpnd-ub, constOpnd-lb]
// (with values in I_32, but calculations performed in int64)
ConstInst *constInst = constOpnd->getInst()->asConstInst();
assert(constInst);
#ifndef NDEBUG
Type::Tag constInstType = constInst->getType();
#endif
assert(isSigned ?
((constInstType == Type::Int8)||(constInstType == Type::Int16)||
(constInstType == Type::Int32)) :
((constInstType == Type::UInt8)||
(constInstType == Type::UInt16)||(constInstType == Type::UInt32)));
ConstInst::ConstValue constValue = constInst->getValue();
I_32 constValue32 = constValue.i4;
int64 varlb, varub;
int64 constValue64 = isSigned ? (int64)constValue32 : (int64)(uint64)(U_32)constValue32;
if (negateConstant) {
varlb = lb + constValue64 + 1; // tweak by 1 in case I have a fencepost problem
varub = ub + constValue64 - 1;
} else if (negate) {
varlb = constValue64 - ub + 1;
varub = constValue64 - lb - 1;
} else {
varlb = lb - constValue64 + 1;
varub = ub - constValue64 - 1;
}
VarBound varBound = VarBound(varOpnd);
lb = ::std::max(varlb, lb);
ub = ::std::min(varub, ub);
ProofLattice res1 = prove(true, // lower bound
VarBound(),
varBound,
lb, why);
if (res1 != ProofLattice::ProvenFalse) {
res1 = prove(false, // upper bound
VarBound(),
varBound,
ub, why);
}
if (res1 != ProofLattice::ProvenFalse) {
// we have proved that overflow cannot happen.
provenNoOverflow = true;
}
} else {
// we have to be more conservative
// for Add, we must prove
// lb <= opnd1 + opnd2 <= ub
// but we can't represent that, so instead just try for
// lb/2 +1 <= opnd1,opnd2 <= ub/2 -1
// which is sufficient to ensure no overflow
if (isSigned) {
assert((instType == Type::Int8)||(instType == Type::Int16)||
(instType == Type::Int32));
lb = lb / 2 + 1;
ub = ub / 2 - 1;
} else {
assert((instType == Type::UInt8)||
(instType == Type::UInt16)||(instType == Type::UInt32));
assert(lb ==(int64) 0);
ub = ub / 2 - 1;
}
VarBound varBound0 = VarBound(opnd0);
VarBound varBound1 = VarBound(opnd1);
ProofLattice res1 = prove(true, // lower bound
VarBound(),
varBound0,
lb, why);
if (res1 != ProofLattice::ProvenFalse) {
res1 = prove(false, // upper bound
VarBound(),
varBound0,
ub, why);
}
if (res1 != ProofLattice::ProvenFalse) {
res1 = prove(true, // lower bound
VarBound(),
varBound1,
lb, why);
}
if (res1 != ProofLattice::ProvenFalse) {
res1 = prove(false, // upper bound
VarBound(),
varBound1,
ub, why);
}
provenNoOverflow = true;
}
if (provenNoOverflow) {
Modifier newmod = mod;
newmod.setExceptionModifier(Exception_Never);
if (newmod.getOverflowModifier() == Overflow_None) {
if (isSigned) {
newmod.setOverflowModifier(Overflow_Signed);
} else {
newmod.setOverflowModifier(Overflow_Unsigned);
}
if (Log::isEnabled()) {
Log::out() << "Disproved overflow of add/sub instruction ";
theInst->print(Log::out());
Log::out() << " so marking it overflow but exception-free" << ::std::endl;
}
} else {
if (Log::isEnabled()) {
Log::out() << "Disproved overflow of add/sub instruction ";
theInst->print(Log::out());
Log::out() << " so marking it exception-free" << ::std::endl;
}
}
theInst->setModifier(newmod);
}
}
}
} else if (thePass->flags.remOverflow &&
Abcd::isCheckableType(theInst->getType()) &&
(theInst->getOpcode() == Op_Mul)) {
assert(theInst->getNumSrcOperands() == 2);
Modifier mod = theInst->getModifier();
if ((mod.getOverflowModifier() == Overflow_None) ||
(mod.getExceptionModifier() == Exception_Sometimes)) {
// check whether we can overflow
bool isSigned = mod.getOverflowModifier() != Overflow_Unsigned;
Opnd *opnd0 = theInst->getSrc(0);
Opnd *opnd1 = theInst->getSrc(1);
int64 lb, ub;
Type::Tag instType = theInst->getType();
AbcdReasons *why
= (thePass->flags.useReasons
? new (thePass->mm) AbcdReasons(thePass->mm)
: 0);
if (Abcd::hasTypeBounds(instType, lb, ub) &&
(theInst->getType() != Type::Int64)) { // is somewhat unbounded if int64
Opnd *constOpnd = 0;
Opnd *varOpnd = 0;
if (ConstantFolder::isConstant(opnd0)) {
constOpnd = opnd0;
varOpnd = opnd1;
} else if (ConstantFolder::isConstant(opnd1)) {
constOpnd = opnd1;
varOpnd = opnd0;
}
bool provenNoOverflow = false;
if (constOpnd) {
// we can hope to do something
// if constOpnd > 0, bounds on var are [lb/constOpnd+1, ub/constOpnd-1],
// if constOpnd < 0, bounds on var are [ub/constOpnd+1, lb/constOpnd+1]
ConstInst *constInst = constOpnd->getInst()->asConstInst();
assert(constInst);
#ifndef NDEBUG
Type::Tag constInstType = constInst->getType();
assert(isSigned ?
((constInstType == Type::Int8)||(constInstType == Type::Int16)||
(constInstType == Type::Int32)) :
((constInstType == Type::UInt8)||
(constInstType == Type::UInt16)||(constInstType == Type::UInt32)));
#endif
ConstInst::ConstValue constValue = constInst->getValue();
I_32 constValue32 = constValue.i4;
int64 varlb, varub;
if (isSigned) {
if (constValue32 < 0) {
varlb = lb/constValue32 + 1;
varub = ub/constValue32 - 1;
} else {
assert(constValue32 > 0); // inst should have been simplified otherwise
varlb = ub/constValue32 + 1;
varub = lb/constValue32 - 1;
}
} else {
assert(constValue32 > 0); // inst should have been simplified otherwise
varlb = 0;
varub = ub/(U_32)constValue32;
}
VarBound varBound = VarBound(varOpnd);
lb = ::std::max(varlb, lb);
ub = ::std::min(varub, ub);
ProofLattice res1 = prove(true, // lower bound
VarBound(),
varBound,
lb, why);
if (res1 != ProofLattice::ProvenFalse) {
res1 = prove(false, // upper bound
VarBound(),
varBound,
ub, why);
}
if (res1 != ProofLattice::ProvenFalse) {
// we have proved that overflow cannot happen.
provenNoOverflow = true;
}
} else {
// we have to be much more conservative
// for Mul, we must prove
// lb <= opnd1 * opnd2 <= ub
// but it could work to use
// sqrt(-lb)+1 <= opnd1, opnd2 <= sqrt(ub)-1 if signed
// or
// 0 <= opnd1, opnd2 <= sqrt(ub)-1 if unsigned
if (isSigned) {
assert((instType == Type::Int8)||(instType == Type::Int16)||
(instType == Type::Int32));
lb = 1 - isqrt<uint64>(uint64(-(lb+1)));
ub = isqrt<uint64>(uint64(ub)) - 1;
} else {
lb = 0;
ub = isqrt<uint64>(uint64(ub)) - 1;
}
VarBound varBound0 = VarBound(opnd0);
VarBound varBound1 = VarBound(opnd1);
ProofLattice res1 = prove(true, // lower bound
VarBound(),
varBound0,
lb, why);
if (res1 != ProofLattice::ProvenFalse) {
res1 = prove(false, // upper bound
VarBound(),
varBound0,
ub, why);
}
if (res1 != ProofLattice::ProvenFalse) {
res1 = prove(true, // lower bound
VarBound(),
varBound1,
lb, why);
}
if (res1 != ProofLattice::ProvenFalse) {
res1 = prove(false, // upper bound
VarBound(),
varBound1,
ub, why);
}
provenNoOverflow = true;
}
if (provenNoOverflow) {
Modifier newmod = mod;
newmod.setExceptionModifier(Exception_Never);
if (newmod.getOverflowModifier() == Overflow_None) {
if (isSigned) {
newmod.setOverflowModifier(Overflow_Signed);
} else {
newmod.setOverflowModifier(Overflow_Unsigned);
}
if (Log::isEnabled()) {
Log::out() << "Disproved overflow of mul instruction ";
theInst->print(Log::out());
Log::out() << " so marking it overflow but exception-free" << ::std::endl;
}
} else {
if (Log::isEnabled()) {
Log::out() << "Disproved overflow of mul instruction ";
theInst->print(Log::out());
Log::out() << " so marking it exception-free" << ::std::endl;
}
}
theInst->setModifier(newmod);
}
}
}
}
}
void AbcdSolver::tryToFoldBranch(Inst *) {}
void AbcdSolver::tryToFoldCompare(Inst *) {}
// try to prove var2 - var1 <= c
// by considering either var1 or var2, depending on derefVar1
// if (!checkVar1), var2 should have been fully dereferenced and checked first.
ProofLattice AbcdSolver::proveForSpecialCases(const VarBound &var1,
const VarBound &var2,
ConstBound c,
bool checkVar1,
bool &noneApply,
AbcdReasons *why)
{
const VarBound &theVar = (checkVar1 ? var1 : var2);
const VarBound &otherVar = (checkVar1 ? var2 : var1);
ProofLattice result = ProofLattice::ProvenFalse;
if (Log::isEnabled()) {
Log::out() << indent.c_str() << "Checking special cases for: ";
theVar.print(Log::out());
Log::out() << ::std::endl;
}
AbcdReasons *subWhy =
(thePass->flags.useReasons
? new (thePass->mm) AbcdReasons(thePass->mm)
: 0);
if (thePass->flags.useShr) {
if (otherVar.isEmpty() && theVar.isConvexFunction()) {
assert(theVar.the_var != 0);
Inst *the_inst = theVar.the_var->getInst();
if (Log::isEnabled()) {
Log::out() << indent.c_str() << "ConvexFunction: ";
the_inst->print(Log::out());
Log::out() << ::std::endl;
}
if (checkVar1) {
// we have var2 = 0, so
// we're trying to prove
// -var1 <= c
// or
// var1 >= -c
// where
// var1 = f(var3)
// by constraining input by
// var3 >= inputC = finv(-c)
ConstBound negC = -c;
VarBound var3;
ConstBound inputC =
var1.getConvexInputBound(true, negC, var3);
if (!inputC.isNull()) {
result = prove(true, // lower bound on var3
VarBound(),
var3,
inputC,
subWhy);
if (result != ProofLattice::ProvenFalse) {
if (thePass->flags.useReasons) {
assert(why && subWhy);
why->addReasons(*subWhy);
}
return result;
}
}
} else {
// here we have var1=0, so
// we're trying to prove
// var2 <= c
// where
// var2 = f(var3)
// by constraining input by
// var3 <= inputC = finv(-c)
VarBound var3;
ConstBound inputC =
var2.getConvexInputBound(false, c, var3);
if (!inputC.isNull()) {
result = prove(false, // upper bound on var3
VarBound(),
var3,
inputC,
subWhy);
if (result != ProofLattice::ProvenFalse) {
if (thePass->flags.useReasons) {
assert(why && subWhy);
why->addReasons(*subWhy);
}
return result;
}
}
}
}
}
if (thePass->flags.useConv
&& (result == ProofLattice::ProvenFalse)
&& (theVar.isConvVar())) {
// for a Conv, predecessors just are type-based bounds
// let's see if we can do better looking at the predecessor
VarBound var3 = theVar.getConvSource();
assert(theVar.the_var != 0);
Inst *the_inst = theVar.the_var->getInst();
if (Log::isEnabled()) {
Log::out() << indent.c_str() << "Have conv : ";
the_inst->print(Log::out());
Log::out() << ::std::endl;
}
bool passesSource = theVar.convPassesSource();
AbcdReasons *markedWhy = 0;
bool alreadyMarked = thePass->isMarkedToEliminate(the_inst, markedWhy);
if (passesSource) {
if (Log::isEnabled()) {
Log::out() << indent.c_str() << "Passes Source: ";
the_inst->print(Log::out());
Log::out() << ::std::endl;
}
};
if (alreadyMarked) {
if (Log::isEnabled()) {
Log::out() << indent.c_str() << "Already marked : ";
the_inst->print(Log::out());
Log::out() << ::std::endl;
}
};
if (passesSource || alreadyMarked) {
if (checkVar1) {
// no need to check whether source is in range
result = prove(false,
var3,
var2,
c,
subWhy);
if ((result != ProofLattice::ProvenFalse) && alreadyMarked) {
// need to add reasons why it was marked;
if (thePass->flags.useReasons) {
assert(subWhy && why);
subWhy->addReasons(*why);
}
}
} else {
// no need to check whether source is in range
result = prove(false,
var1,
var3,
c,
subWhy);
if ((result != ProofLattice::ProvenFalse) && alreadyMarked) {
// need to add reasons why it was marked;
if (thePass->flags.useReasons) {
assert(subWhy && why);
subWhy->addReasons(*why);
}
}
}
} else {
// check whether source is in range
PiCondition convBounds = PiCondition::convBounds(theVar);
if (Log::isEnabled()) {
Log::out() << indent.c_str() << "Checking for source in range : ";
the_inst->print(Log::out());
Log::out() << ::std::endl;
}
PiBound lb = convBounds.getLb();
PiBound ub = convBounds.getUb();
ProofLattice res1;
if (lb.isConstant()) {
ConstBound clb = lb.getConst();
if (checkVar1)
res1 = prove(true, var3, VarBound(), clb, subWhy);
else
res1 = prove(true, VarBound(), var3, clb, subWhy);
} else if (lb.isUndefined()) {
// no need to add a reason, LB is unconstrained
res1 = ProofLattice::ProvenTrue;
} else {
res1 = ProofLattice::ProvenFalse;
}
if (res1 != ProofLattice::ProvenFalse) {
if (ub.isConstant()) {
ConstBound cub = ub.getConst();
ProofLattice res2;
if (checkVar1)
res2 = prove(false, var3, VarBound(), cub, subWhy);
else
res2 = prove(false, VarBound(), var3, cub, subWhy);
res1.meetwith(res2);
} else if (!ub.isUndefined()) {
res1 = ProofLattice::ProvenFalse;
}
}
if (res1 != ProofLattice::ProvenFalse) {
if (Log::isEnabled()) {
Log::out() << indent.c_str() << "Source is in range, passing constraint: ";
the_inst->print(Log::out());
if (thePass->flags.useReasons) {
Log::out() << " because of ";
subWhy->print(Log::out());
}
Log::out() << ::std::endl;
}
// note that though these are constant bounds,
// they can result in reduced proofs
if (why)
thePass->markInstToEliminateAndWhy(the_inst, subWhy);
else
thePass->markInstToEliminate(the_inst);
// source is in range, visit it
if (checkVar1)
result = prove(false,
var3,
var2,
c,
subWhy);
else
result = prove(false,
var1,
var3,
c,
subWhy);
if (Log::isEnabled()) {
Log::out() << indent.c_str() << "Special case conv result = ";
result.print(Log::out());
Log::out() << ::std::endl;
}
if (result != ProofLattice::ProvenFalse) {
if (thePass->flags.useReasons) {
assert(why && subWhy);
why->addReasons(*subWhy);
}
}
return result;
}
}
}
noneApply = true;
if (Log::isEnabled()) {
Log::out() << indent.c_str() << "NoneApply, special case conv result = ";
result.print(Log::out());
Log::out() << ::std::endl;
}
if (result != ProofLattice::ProvenFalse) {
if (thePass->flags.useReasons) {
assert(why && subWhy);
why->addReasons(*subWhy);
}
}
return result;
}
// try to prove var2 - var1 <= c
// by dereferencing either Var1 (if derefVar1==true) or Var2 (otherwise)
ProofLattice AbcdSolver::proveForPredecessors(const VarBound &var1,
const VarBound &var2,
ConstBound c,
bool derefVar1,
bool &predsAreEmpty,
AbcdReasons *why)
{
PiBoundIter preds = (derefVar1
? var1.getPredecessors(true, thePass->flags.useReasons, thePass->mm) // boundBelow
: var2.getPredecessors(false, thePass->flags.useReasons, thePass->mm)); // boundAbove
ProofLattice result = ProofLattice::ProvenFalse;
VarBound derefVar = (derefVar1 ? var1 : var2);
bool boundDirection = derefVar1; // preds are lower bounds, MIN acts like PHI
if (preds.isEmpty()) {
if (Log::isEnabled()) {
Log::out() << indent.c_str() << " Predecessors of ";
if (derefVar1) {
Log::out() << "var1=";
} else {
Log::out() << "var2=";
}
derefVar.print(Log::out());
Log::out() << " are EMPTY";
Log::out() << ::std::endl;
}
predsAreEmpty = true;
} else {
if (derefVar.isPhiVar()) {
if (Log::isEnabled()) {
Log::out() << indent.c_str()
<< " Case 5c : Phi function" << ::std::endl;
}
result = ProofLattice::ProvenTrue;
AbcdReasons *subWhy
= (thePass->flags.useReasons
? new (thePass->mm) AbcdReasons(thePass->mm)
: 0);
StlVector<AbcdReasons *> *subSubWhys
= (thePass->flags.useReasons
? new (thePass->mm) StlVector<AbcdReasons *>(thePass->mm)
: 0);
StlVector<Opnd *> *subSubVars
= (thePass->flags.useReasons
? new (thePass->mm) StlVector<Opnd *>(thePass->mm)
: 0);
for ( ; !preds.isEmpty(); ++preds) {
PiBound pred = preds.getBound(subWhy);
assert(pred.getType() == Type::Int32);
assert(pred.isVar());
VarBound var3 = pred.getVar();
AbcdReasons *subSubWhy
= (thePass->flags.useReasons
? new (thePass->mm) AbcdReasons(thePass->mm)
: 0);
if (derefVar1) {
// we want to prove
// var2 <= c + var1
// we know
// var1 >= var3
// so sufficient to prove
// var2 <= c + var3
ProofLattice res1 = prove(false,
var3,
var2,
c,
subSubWhy);
result.meetwith(res1);
} else {
// we want to prove
// var2 <= c + var1
// we know
// var2 <= var3
// so sufficient to prove
// var3 <= c + var1
ProofLattice res1 = prove(false,
var1,
var3,
c,
subSubWhy);
result.meetwith(res1);
}
if (result == ProofLattice::ProvenFalse) break;
if (thePass->flags.useReasons) {
assert(subSubWhys && subSubVars);
subSubWhys->push_back(subSubWhy);
subSubVars->push_back(var3.the_var);
}
}
if (result != ProofLattice::ProvenFalse) {
if (thePass->flags.useReasons) {
assert(subSubWhys && subSubVars);
SsaTmpOpnd *tauOpnd = thePass->makeReasonPhi(derefVar.the_var, *subSubWhys, *subSubVars);
assert(why && tauOpnd);
why->addReason(tauOpnd);
}
}
return result;
} else {
bool andConditions = derefVar.isMinMax(boundDirection);
result = (andConditions
? ProofLattice::ProvenTrue
: ProofLattice::ProvenFalse);
AbcdReasons *subWhy =
((andConditions && thePass->flags.useReasons)
? new (thePass->mm) AbcdReasons(thePass->mm)
: 0);
StlVector<AbcdReasons *> *subSubWhys
= ((andConditions && thePass->flags.useReasons)
? new (thePass->mm) StlVector<AbcdReasons *>(thePass->mm)
: 0);
if (Log::isEnabled()) {
Log::out() << indent.c_str()
<< " Case 5d : non-Phi" << ::std::endl;
}
for ( ; !preds.isEmpty(); ++preds) {
AbcdReasons *subSubWhy =
(thePass->flags.useReasons
? new (thePass->mm) AbcdReasons(thePass->mm)
: 0);
PiBound pred = preds.getBound(subSubWhy);
if ((pred.getType() == Type::Int32) &&
(pred.isVarPlusConst() || pred.isConst())) {
VarBound var3 = pred.getVar();
int64 d1 = pred.getConst();
I_32 d = (I_32)d1;
assert(d1 == int64(d)); // check for overflow
ConstBound db(d);
if (derefVar1) {
// trying to prove
// var2 <= c + var1
// we know that
// var1 >= pred
// pred = var3 + d
// var1 + c >= var3 + d + c
// sufficient to prove
// var2 <= var3 + d + c
// or
// var2 - var3 <= d + c
ProofLattice res1 = prove(false,
var3,
var2,
c + db,
subSubWhy);
if (andConditions)
result.meetwith(res1);
else
result.joinwith(res1);
} else {
// trying to prove
// var2 <= c + var1
// we know that
// var2 <= pred
// pred = var3 + d
// var2 - c <= var3 + d - c
// sufficient to prove
// var1 >= var3 + d - c
// or
// var3 - var1 <= c - d
ProofLattice res1 = prove(false,
var1,
var3,
c - db,
subSubWhy);
if (andConditions)
result.meetwith(res1);
else
result.joinwith(res1);
}
if (andConditions) {
if (result == ProofLattice::ProvenFalse) {
break;
}
if (thePass->flags.useReasons) {
assert(subSubWhys);
subSubWhys->push_back(subSubWhy);
}
} else {
if (result == ProofLattice::ProvenTrue) {
// and with
if (thePass->flags.useReasons){
assert(subWhy && subSubWhy);
subWhy->addReasons(*subSubWhy);
}
break;
}
}
}
}
if (Log::isEnabled()) {
Log::out() << indent.c_str()
<< " returning ";
result.print(Log::out());
Log::out() << ::std::endl;
}
if (andConditions) {
if (result != ProofLattice::ProvenFalse) {
if (thePass->flags.useReasons) {
assert(why && subSubWhys);
why->addReasons(subSubWhys);
}
}
} else {
if (result == ProofLattice::ProvenTrue)
if (thePass->flags.useReasons) {
assert(why && subWhy);
why->addReasons(*subWhy);
}
}
return result;
}
}
if (Log::isEnabled()) {
Log::out() << indent.c_str()
<< " returning FALSE";
Log::out() << ::std::endl;
}
return ProofLattice::ProvenFalse;
}
} //namespace Jitrino