mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-28 19:31:58 +00:00
GVN does simple propagation of conditions: when it sees a conditional
branch "br i1 %x, label %if_true, label %if_false" then it replaces "%x" with "true" in places only reachable via the %if_true arm, and with "false" in places only reachable via the %if_false arm. Except that actually it doesn't: if value numbering shows that %y is equal to %x then, yes, %y will be turned into true/false in this way, but any occurrences of %x itself are not transformed. Fix this. What's more, it's often the case that %x is an equality comparison such as "%x = icmp eq %A, 0", in which case every occurrence of %A that is only reachable via the %if_true arm can be replaced with 0. Implement this and a few other variations on this theme. This reduces the number of lines of LLVM IR in "GCC as one big file" by 0.2%. It has a bigger impact on Ada code, typically reducing the number of lines of bitcode by around 0.4% by removing repeated compiler generated checks. Passes the LLVM nightly testsuite and the Ada ACATS testsuite. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@141177 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
452c58f4c4
commit
02b5e72ac6
@ -41,12 +41,16 @@
|
|||||||
#include "llvm/Support/CommandLine.h"
|
#include "llvm/Support/CommandLine.h"
|
||||||
#include "llvm/Support/Debug.h"
|
#include "llvm/Support/Debug.h"
|
||||||
#include "llvm/Support/IRBuilder.h"
|
#include "llvm/Support/IRBuilder.h"
|
||||||
|
#include "llvm/Support/PatternMatch.h"
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
using namespace PatternMatch;
|
||||||
|
|
||||||
STATISTIC(NumGVNInstr, "Number of instructions deleted");
|
STATISTIC(NumGVNInstr, "Number of instructions deleted");
|
||||||
STATISTIC(NumGVNLoad, "Number of loads deleted");
|
STATISTIC(NumGVNLoad, "Number of loads deleted");
|
||||||
STATISTIC(NumGVNPRE, "Number of instructions PRE'd");
|
STATISTIC(NumGVNPRE, "Number of instructions PRE'd");
|
||||||
STATISTIC(NumGVNBlocks, "Number of blocks merged");
|
STATISTIC(NumGVNBlocks, "Number of blocks merged");
|
||||||
|
STATISTIC(NumGVNSimpl, "Number of instructions simplified");
|
||||||
|
STATISTIC(NumGVNEqProp, "Number of equalities propagated");
|
||||||
STATISTIC(NumPRELoad, "Number of loads PRE'd");
|
STATISTIC(NumPRELoad, "Number of loads PRE'd");
|
||||||
|
|
||||||
static cl::opt<bool> EnablePRE("enable-pre",
|
static cl::opt<bool> EnablePRE("enable-pre",
|
||||||
@ -548,6 +552,9 @@ namespace {
|
|||||||
void cleanupGlobalSets();
|
void cleanupGlobalSets();
|
||||||
void verifyRemoved(const Instruction *I) const;
|
void verifyRemoved(const Instruction *I) const;
|
||||||
bool splitCriticalEdges();
|
bool splitCriticalEdges();
|
||||||
|
unsigned replaceAllDominatedUsesWith(Value *From, Value *To,
|
||||||
|
BasicBlock *Root);
|
||||||
|
bool propagateEquality(Value *LHS, Value *RHS, BasicBlock *Root);
|
||||||
};
|
};
|
||||||
|
|
||||||
char GVN::ID = 0;
|
char GVN::ID = 0;
|
||||||
@ -1881,6 +1888,97 @@ Value *GVN::findLeader(BasicBlock *BB, uint32_t num) {
|
|||||||
return Val;
|
return Val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// replaceAllDominatedUsesWith - Replace all uses of 'From' with 'To' if the
|
||||||
|
/// use is dominated by the given basic block. Returns the number of uses that
|
||||||
|
/// were replaced.
|
||||||
|
unsigned GVN::replaceAllDominatedUsesWith(Value *From, Value *To,
|
||||||
|
BasicBlock *Root) {
|
||||||
|
unsigned Count = 0;
|
||||||
|
for (Value::use_iterator UI = From->use_begin(), UE = From->use_end();
|
||||||
|
UI != UE; ) {
|
||||||
|
Instruction *User = cast<Instruction>(*UI);
|
||||||
|
unsigned OpNum = UI.getOperandNo();
|
||||||
|
++UI;
|
||||||
|
|
||||||
|
if (DT->dominates(Root, User->getParent())) {
|
||||||
|
User->setOperand(OpNum, To);
|
||||||
|
++Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// propagateEquality - The given values are known to be equal in every block
|
||||||
|
/// dominated by 'Root'. Exploit this, for example by replacing 'LHS' with
|
||||||
|
/// 'RHS' everywhere in the scope. Returns whether a change was made.
|
||||||
|
bool GVN::propagateEquality(Value *LHS, Value *RHS, BasicBlock *Root) {
|
||||||
|
if (LHS == RHS) return false;
|
||||||
|
assert(LHS->getType() == RHS->getType() && "Equal but types differ!");
|
||||||
|
|
||||||
|
// Don't try to propagate equalities between constants.
|
||||||
|
if (isa<Constant>(LHS) && isa<Constant>(RHS))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Make sure that any constants are on the right-hand side. In general the
|
||||||
|
// best results are obtained by placing the longest lived value on the RHS.
|
||||||
|
if (isa<Constant>(LHS))
|
||||||
|
std::swap(LHS, RHS);
|
||||||
|
|
||||||
|
// If neither term is constant then bail out. This is not for correctness,
|
||||||
|
// it's just that the non-constant case is much less useful: it occurs just
|
||||||
|
// as often as the constant case but handling it hardly ever results in an
|
||||||
|
// improvement.
|
||||||
|
if (!isa<Constant>(RHS))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If value numbering later deduces that an instruction in the scope is equal
|
||||||
|
// to 'LHS' then ensure it will be turned into 'RHS'.
|
||||||
|
addToLeaderTable(VN.lookup_or_add(LHS), RHS, Root);
|
||||||
|
|
||||||
|
// Replace all occurrences of 'LHS' with 'RHS' everywhere in the scope.
|
||||||
|
unsigned NumReplacements = replaceAllDominatedUsesWith(LHS, RHS, Root);
|
||||||
|
bool Changed = NumReplacements > 0;
|
||||||
|
NumGVNEqProp += NumReplacements;
|
||||||
|
|
||||||
|
// Now try to deduce additional equalities from this one. For example, if the
|
||||||
|
// known equality was "(A != B)" == "false" then it follows that A and B are
|
||||||
|
// equal in the scope. Only boolean equalities with an explicit true or false
|
||||||
|
// RHS are currently supported.
|
||||||
|
if (!RHS->getType()->isIntegerTy(1))
|
||||||
|
// Not a boolean equality - bail out.
|
||||||
|
return Changed;
|
||||||
|
ConstantInt *CI = dyn_cast<ConstantInt>(RHS);
|
||||||
|
if (!CI)
|
||||||
|
// RHS neither 'true' nor 'false' - bail out.
|
||||||
|
return Changed;
|
||||||
|
// Whether RHS equals 'true'. Otherwise it equals 'false'.
|
||||||
|
bool isKnownTrue = CI->isAllOnesValue();
|
||||||
|
bool isKnownFalse = !isKnownTrue;
|
||||||
|
|
||||||
|
// If "A && B" is known true then both A and B are known true. If "A || B"
|
||||||
|
// is known false then both A and B are known false.
|
||||||
|
Value *A, *B;
|
||||||
|
if ((isKnownTrue && match(LHS, m_And(m_Value(A), m_Value(B)))) ||
|
||||||
|
(isKnownFalse && match(LHS, m_Or(m_Value(A), m_Value(B))))) {
|
||||||
|
Changed |= propagateEquality(A, RHS, Root);
|
||||||
|
Changed |= propagateEquality(B, RHS, Root);
|
||||||
|
return Changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are propagating an equality like "(A == B)" == "true" then also
|
||||||
|
// propagate the equality A == B.
|
||||||
|
if (ICmpInst *Cmp = dyn_cast<ICmpInst>(LHS)) {
|
||||||
|
// Only equality comparisons are supported.
|
||||||
|
if ((isKnownTrue && Cmp->getPredicate() == CmpInst::ICMP_EQ) ||
|
||||||
|
(isKnownFalse && Cmp->getPredicate() == CmpInst::ICMP_NE)) {
|
||||||
|
Value *Op0 = Cmp->getOperand(0), *Op1 = Cmp->getOperand(1);
|
||||||
|
Changed |= propagateEquality(Op0, Op1, Root);
|
||||||
|
}
|
||||||
|
return Changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Changed;
|
||||||
|
}
|
||||||
|
|
||||||
/// processInstruction - When calculating availability, handle an instruction
|
/// processInstruction - When calculating availability, handle an instruction
|
||||||
/// by inserting it into the appropriate sets
|
/// by inserting it into the appropriate sets
|
||||||
@ -1898,6 +1996,7 @@ bool GVN::processInstruction(Instruction *I) {
|
|||||||
if (MD && V->getType()->isPointerTy())
|
if (MD && V->getType()->isPointerTy())
|
||||||
MD->invalidateCachedPointerInfo(V);
|
MD->invalidateCachedPointerInfo(V);
|
||||||
markInstructionForDeletion(I);
|
markInstructionForDeletion(I);
|
||||||
|
++NumGVNSimpl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1910,15 +2009,15 @@ bool GVN::processInstruction(Instruction *I) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For conditions branches, we can perform simple conditional propagation on
|
// For conditional branches, we can perform simple conditional propagation on
|
||||||
// the condition value itself.
|
// the condition value itself.
|
||||||
|
// TODO: Add conditional propagation of switch cases.
|
||||||
if (BranchInst *BI = dyn_cast<BranchInst>(I)) {
|
if (BranchInst *BI = dyn_cast<BranchInst>(I)) {
|
||||||
if (!BI->isConditional() || isa<Constant>(BI->getCondition()))
|
if (!BI->isConditional() || isa<Constant>(BI->getCondition()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Value *BranchCond = BI->getCondition();
|
Value *BranchCond = BI->getCondition();
|
||||||
uint32_t CondVN = VN.lookup_or_add(BranchCond);
|
|
||||||
|
|
||||||
BasicBlock *TrueSucc = BI->getSuccessor(0);
|
BasicBlock *TrueSucc = BI->getSuccessor(0);
|
||||||
BasicBlock *FalseSucc = BI->getSuccessor(1);
|
BasicBlock *FalseSucc = BI->getSuccessor(1);
|
||||||
BasicBlock *Parent = BI->getParent();
|
BasicBlock *Parent = BI->getParent();
|
||||||
@ -1947,16 +2046,14 @@ bool GVN::processInstruction(Instruction *I) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TrueSucc)
|
// Replace the condition with true/false in basic blocks that can only be
|
||||||
addToLeaderTable(CondVN,
|
// reached via the true/false arm of the branch.
|
||||||
ConstantInt::getTrue(TrueSucc->getContext()),
|
return (TrueSucc && propagateEquality(BranchCond,
|
||||||
TrueSucc);
|
ConstantInt::getTrue(TrueSucc->getContext()),
|
||||||
if (FalseSucc)
|
TrueSucc))
|
||||||
addToLeaderTable(CondVN,
|
|| (FalseSucc && propagateEquality(BranchCond,
|
||||||
ConstantInt::getFalse(FalseSucc->getContext()),
|
ConstantInt::getFalse(FalseSucc->getContext()),
|
||||||
FalseSucc);
|
FalseSucc));
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instructions with void type don't return a value, so there's
|
// Instructions with void type don't return a value, so there's
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
@a = external global i32 ; <i32*> [#uses=7]
|
@a = external global i32 ; <i32*> [#uses=7]
|
||||||
|
|
||||||
; CHECK: @foo
|
; CHECK: @test1
|
||||||
define i32 @foo() nounwind {
|
define i32 @test1() nounwind {
|
||||||
entry:
|
entry:
|
||||||
%0 = load i32* @a, align 4
|
%0 = load i32* @a, align 4
|
||||||
%1 = icmp eq i32 %0, 4
|
%1 = icmp eq i32 %0, 4
|
||||||
@ -54,22 +54,46 @@ return: ; preds = %bb8
|
|||||||
ret i32 %.0
|
ret i32 %.0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare void @ext(i1)
|
declare void @foo(i1)
|
||||||
|
|
||||||
; CHECK: @bar
|
; CHECK: @test2
|
||||||
define void @bar(i1 %x, i1 %y) {
|
define void @test2(i1 %x, i1 %y) {
|
||||||
%z = or i1 %x, %y
|
%z = or i1 %x, %y
|
||||||
br i1 %z, label %true, label %false
|
br i1 %z, label %true, label %false
|
||||||
true:
|
true:
|
||||||
; CHECK: true:
|
; CHECK: true:
|
||||||
%z2 = or i1 %x, %y
|
%z2 = or i1 %x, %y
|
||||||
call void @ext(i1 %z2)
|
call void @foo(i1 %z2)
|
||||||
; CHECK: call void @ext(i1 true)
|
; CHECK: call void @foo(i1 true)
|
||||||
br label %true
|
br label %true
|
||||||
false:
|
false:
|
||||||
; CHECK: false:
|
; CHECK: false:
|
||||||
%z3 = or i1 %x, %y
|
%z3 = or i1 %x, %y
|
||||||
call void @ext(i1 %z3)
|
call void @foo(i1 %z3)
|
||||||
; CHECK: call void @ext(i1 false)
|
; CHECK: call void @foo(i1 false)
|
||||||
br label %false
|
br label %false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare void @bar(i32)
|
||||||
|
|
||||||
|
; CHECK: @test3
|
||||||
|
define void @test3(i32 %x, i32 %y) {
|
||||||
|
%xz = icmp eq i32 %x, 0
|
||||||
|
%yz = icmp eq i32 %y, 0
|
||||||
|
%z = and i1 %xz, %yz
|
||||||
|
br i1 %z, label %both_zero, label %nope
|
||||||
|
both_zero:
|
||||||
|
call void @foo(i1 %xz)
|
||||||
|
; CHECK: call void @foo(i1 true)
|
||||||
|
call void @foo(i1 %yz)
|
||||||
|
; CHECK: call void @foo(i1 true)
|
||||||
|
call void @bar(i32 %x)
|
||||||
|
; CHECK: call void @bar(i32 0)
|
||||||
|
call void @bar(i32 %y)
|
||||||
|
; CHECK: call void @bar(i32 0)
|
||||||
|
ret void
|
||||||
|
nope:
|
||||||
|
call void @foo(i1 %z)
|
||||||
|
; CHECK: call void @foo(i1 false)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
@ -14,7 +14,7 @@ target datalayout = "e-p:64:64:64"
|
|||||||
@G = external global [100 x i32]
|
@G = external global [100 x i32]
|
||||||
define i32 @foo(i32 %x, i32 %z) {
|
define i32 @foo(i32 %x, i32 %z) {
|
||||||
entry:
|
entry:
|
||||||
%tobool = icmp eq i32 %x, 0
|
%tobool = icmp eq i32 %z, 0
|
||||||
br i1 %tobool, label %end, label %then
|
br i1 %tobool, label %end, label %then
|
||||||
|
|
||||||
then:
|
then:
|
||||||
|
Loading…
Reference in New Issue
Block a user