mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-28 19:31:58 +00:00
[InstCombine] Re-commit of r218721 (Optimize icmp-select-icmp sequence)
Fixes the self-host fail. Note that this commit activates dominator analysis in the combiner by default (like the original commit did). git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@222590 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
0b1407b5cf
commit
5182ad54b2
@ -258,6 +258,13 @@ public:
|
||||
/// guaranteed to be empty.
|
||||
void replaceAllUsesWith(Value *V);
|
||||
|
||||
/// replaceUsesOutsideBlock - Go through the uses list for this definition and
|
||||
/// make each use point to "V" instead of "this" when the use is outside the
|
||||
/// block. 'This's use list is expected to have at least one element.
|
||||
/// Unlike replaceAllUsesWith this function does not support basic block
|
||||
/// values or constant users.
|
||||
void replaceUsesOutsideBlock(Value *V, BasicBlock *BB);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Methods for handling the chain of uses of this Value.
|
||||
//
|
||||
|
@ -353,6 +353,28 @@ void Value::replaceAllUsesWith(Value *New) {
|
||||
BB->replaceSuccessorsPhiUsesWith(cast<BasicBlock>(New));
|
||||
}
|
||||
|
||||
// Like replaceAllUsesWith except it does not handle constants or basic blocks.
|
||||
// This routine leaves uses within BB.
|
||||
void Value::replaceUsesOutsideBlock(Value *New, BasicBlock *BB) {
|
||||
assert(New && "Value::replaceUsesOutsideBlock(<null>, BB) is invalid!");
|
||||
assert(!contains(New, this) &&
|
||||
"this->replaceUsesOutsideBlock(expr(this), BB) is NOT valid!");
|
||||
assert(New->getType() == getType() &&
|
||||
"replaceUses of value with new value of different type!");
|
||||
assert(BB && "Basic block that may contain a use of 'New' must be defined\n");
|
||||
|
||||
use_iterator UI = use_begin(), E = use_end();
|
||||
for (; UI != E;) {
|
||||
Use &U = *UI;
|
||||
++UI;
|
||||
auto *Usr = dyn_cast<Instruction>(U.getUser());
|
||||
if (Usr && Usr->getParent() == BB)
|
||||
continue;
|
||||
U.set(New);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Various metrics for how much to strip off of pointers.
|
||||
enum PointerStripKind {
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "llvm/Analysis/AssumptionTracker.h"
|
||||
#include "llvm/Analysis/TargetFolder.h"
|
||||
#include "llvm/Analysis/ValueTracking.h"
|
||||
#include "llvm/IR/Dominators.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/InstVisitor.h"
|
||||
#include "llvm/IR/IntrinsicInst.h"
|
||||
@ -98,7 +99,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombiner
|
||||
AssumptionTracker *AT;
|
||||
const DataLayout *DL;
|
||||
TargetLibraryInfo *TLI;
|
||||
DominatorTree *DT; // not required
|
||||
DominatorTree *DT;
|
||||
bool MadeIRChange;
|
||||
LibCallSimplifier *Simplifier;
|
||||
bool MinimizeSize;
|
||||
@ -113,7 +114,8 @@ public:
|
||||
BuilderTy *Builder;
|
||||
|
||||
static char ID; // Pass identification, replacement for typeid
|
||||
InstCombiner() : FunctionPass(ID), DL(nullptr), Builder(nullptr) {
|
||||
InstCombiner()
|
||||
: FunctionPass(ID), DL(nullptr), DT(nullptr), Builder(nullptr) {
|
||||
MinimizeSize = false;
|
||||
initializeInstCombinerPass(*PassRegistry::getPassRegistry());
|
||||
}
|
||||
@ -245,6 +247,16 @@ public:
|
||||
// visitInstruction - Specify what to return for unhandled instructions...
|
||||
Instruction *visitInstruction(Instruction &I) { return nullptr; }
|
||||
|
||||
// True when DB dominates all uses of DI execpt UI.
|
||||
// UI must be in the same block as DI.
|
||||
// The routine checks that the DI parent and DB are different.
|
||||
bool dominatesAllUses(const Instruction *DI, const Instruction *UI,
|
||||
const BasicBlock *DB) const;
|
||||
|
||||
// Replace select with select operand SIOpd in SI-ICmp sequence when possible
|
||||
bool replacedSelectWithOperand(SelectInst *SI, const ICmpInst *Icmp,
|
||||
const unsigned SIOpd);
|
||||
|
||||
private:
|
||||
bool ShouldChangeType(Type *From, Type *To) const;
|
||||
Value *dyn_castNegVal(Value *V) const;
|
||||
|
@ -12,6 +12,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "InstCombine.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/Analysis/ConstantFolding.h"
|
||||
#include "llvm/Analysis/InstructionSimplify.h"
|
||||
#include "llvm/Analysis/MemoryBuiltins.h"
|
||||
@ -20,12 +21,20 @@
|
||||
#include "llvm/IR/GetElementPtrTypeIterator.h"
|
||||
#include "llvm/IR/IntrinsicInst.h"
|
||||
#include "llvm/IR/PatternMatch.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Target/TargetLibraryInfo.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace PatternMatch;
|
||||
|
||||
#define DEBUG_TYPE "instcombine"
|
||||
|
||||
// How many times is a select replaced by one of its operands?
|
||||
STATISTIC(NumSel, "Number of select opts");
|
||||
|
||||
// Initialization Routines
|
||||
|
||||
static ConstantInt *getOne(Constant *C) {
|
||||
return ConstantInt::get(cast<IntegerType>(C->getType()), 1);
|
||||
}
|
||||
@ -2446,6 +2455,122 @@ static bool swapMayExposeCSEOpportunities(const Value * Op0,
|
||||
return GlobalSwapBenefits > 0;
|
||||
}
|
||||
|
||||
/// \brief Check that one use is in the same block as the definition and all
|
||||
/// other uses are in blocks dominated by a given block
|
||||
///
|
||||
/// \param DI Definition
|
||||
/// \param UI Use
|
||||
/// \param DB Block that must dominate all uses of \p DI outside
|
||||
/// the parent block
|
||||
/// \return true when \p UI is the only use of \p DI in the parent block
|
||||
/// and all other uses of \p DI are in blocks dominated by \p DB.
|
||||
///
|
||||
bool InstCombiner::dominatesAllUses(const Instruction *DI,
|
||||
const Instruction *UI,
|
||||
const BasicBlock *DB) const {
|
||||
assert(DI && UI && "Instruction not defined\n");
|
||||
// ignore incomplete definitions
|
||||
if (!DI->getParent())
|
||||
return false;
|
||||
// DI and UI must be in the same block
|
||||
if (DI->getParent() != UI->getParent())
|
||||
return false;
|
||||
// Protect from self-referencing blocks
|
||||
if (DI->getParent() == DB)
|
||||
return false;
|
||||
// DominatorTree available?
|
||||
if (!DT)
|
||||
return false;
|
||||
for (const User *U : DI->users()) {
|
||||
auto *Usr = cast<Instruction>(U);
|
||||
if (Usr != UI && !DT->dominates(DB, Usr->getParent()))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
///
|
||||
/// true when the instruction sequence within a block is select-cmp-br.
|
||||
///
|
||||
static bool isChainSelectCmpBranch(const SelectInst *SI) {
|
||||
const BasicBlock *BB = SI->getParent();
|
||||
if (!BB)
|
||||
return false;
|
||||
auto *BI = dyn_cast_or_null<BranchInst>(BB->getTerminator());
|
||||
if (!BI || BI->getNumSuccessors() != 2)
|
||||
return false;
|
||||
auto *IC = dyn_cast<ICmpInst>(BI->getCondition());
|
||||
if (!IC || (IC->getOperand(0) != SI && IC->getOperand(1) != SI))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief True when a select result is replaced by one of its operands
|
||||
/// in select-icmp sequence. This will eventually result in the elimination
|
||||
/// of the select.
|
||||
///
|
||||
/// \param SI Select instruction
|
||||
/// \param Icmp Compare instruction
|
||||
/// \param SIOpd Operand that replaces the select
|
||||
///
|
||||
/// Notes:
|
||||
/// - The replacement is global and requires dominator information
|
||||
/// - The caller is responsible for the actual replacement
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// entry:
|
||||
/// %4 = select i1 %3, %C* %0, %C* null
|
||||
/// %5 = icmp eq %C* %4, null
|
||||
/// br i1 %5, label %9, label %7
|
||||
/// ...
|
||||
/// ; <label>:7 ; preds = %entry
|
||||
/// %8 = getelementptr inbounds %C* %4, i64 0, i32 0
|
||||
/// ...
|
||||
///
|
||||
/// can be transformed to
|
||||
///
|
||||
/// %5 = icmp eq %C* %0, null
|
||||
/// %6 = select i1 %3, i1 %5, i1 true
|
||||
/// br i1 %6, label %9, label %7
|
||||
/// ...
|
||||
/// ; <label>:7 ; preds = %entry
|
||||
/// %8 = getelementptr inbounds %C* %0, i64 0, i32 0 // replace by %0!
|
||||
///
|
||||
/// Similar when the first operand of the select is a constant or/and
|
||||
/// the compare is for not equal rather than equal.
|
||||
///
|
||||
/// NOTE: The function is only called when the select and compare constants
|
||||
/// are equal, the optimization can work only for EQ predicates. This is not a
|
||||
/// major restriction since a NE compare should be 'normalized' to an equal
|
||||
/// compare, which usually happens in the combiner and test case
|
||||
/// select-cmp-br.ll
|
||||
/// checks for it.
|
||||
bool InstCombiner::replacedSelectWithOperand(SelectInst *SI,
|
||||
const ICmpInst *Icmp,
|
||||
const unsigned SIOpd) {
|
||||
assert(SIOpd == 1 || SIOpd == 2 && "Invalid select operand!\n");
|
||||
if (isChainSelectCmpBranch(SI) && Icmp->getPredicate() == ICmpInst::ICMP_EQ) {
|
||||
BasicBlock *Succ = SI->getParent()->getTerminator()->getSuccessor(1);
|
||||
// The check for the unique predecessor is not the best that can be
|
||||
// done. But it protects efficiently against cases like when SI's
|
||||
// home block has two successors, Succ and Succ1, and Succ1 predecessor
|
||||
// of Succ. Then SI can't be replaced by SIOpd because the use that gets
|
||||
// replaced can be reached on either path. So the uniqueness check
|
||||
// guarantees that the path all uses of SI (outside SI's parent) are on
|
||||
// is disjoint from all other paths out of SI. But that information
|
||||
// is more expensive to compute, and the trade-off here is in favor
|
||||
// of compile-time.
|
||||
if (Succ->getUniquePredecessor() && dominatesAllUses(SI, Icmp, Succ)) {
|
||||
NumSel++;
|
||||
SI->replaceUsesOutsideBlock(SI->getOperand(SIOpd), SI->getParent());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Instruction *InstCombiner::visitICmpInst(ICmpInst &I) {
|
||||
bool Changed = false;
|
||||
Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1);
|
||||
@ -2898,18 +3023,39 @@ Instruction *InstCombiner::visitICmpInst(ICmpInst &I) {
|
||||
// comparison into the select arms, which will cause one to be
|
||||
// constant folded and the select turned into a bitwise or.
|
||||
Value *Op1 = nullptr, *Op2 = nullptr;
|
||||
if (Constant *C = dyn_cast<Constant>(LHSI->getOperand(1)))
|
||||
ConstantInt *CI = 0;
|
||||
if (Constant *C = dyn_cast<Constant>(LHSI->getOperand(1))) {
|
||||
Op1 = ConstantExpr::getICmp(I.getPredicate(), C, RHSC);
|
||||
if (Constant *C = dyn_cast<Constant>(LHSI->getOperand(2)))
|
||||
CI = dyn_cast<ConstantInt>(Op1);
|
||||
}
|
||||
if (Constant *C = dyn_cast<Constant>(LHSI->getOperand(2))) {
|
||||
Op2 = ConstantExpr::getICmp(I.getPredicate(), C, RHSC);
|
||||
CI = dyn_cast<ConstantInt>(Op2);
|
||||
}
|
||||
|
||||
// We only want to perform this transformation if it will not lead to
|
||||
// additional code. This is true if either both sides of the select
|
||||
// fold to a constant (in which case the icmp is replaced with a select
|
||||
// which will usually simplify) or this is the only user of the
|
||||
// select (in which case we are trading a select+icmp for a simpler
|
||||
// select+icmp).
|
||||
if ((Op1 && Op2) || (LHSI->hasOneUse() && (Op1 || Op2))) {
|
||||
// select+icmp) or all uses of the select can be replaced based on
|
||||
// dominance information ("Global cases").
|
||||
bool Transform = false;
|
||||
if (Op1 && Op2)
|
||||
Transform = true;
|
||||
else if (Op1 || Op2) {
|
||||
// Local case
|
||||
if (LHSI->hasOneUse())
|
||||
Transform = true;
|
||||
// Global cases
|
||||
else if (CI && !CI->isZero())
|
||||
// When Op1 is constant try replacing select with second operand.
|
||||
// Otherwise Op2 is constant and try replacing select with first
|
||||
// operand.
|
||||
Transform = replacedSelectWithOperand(cast<SelectInst>(LHSI), &I,
|
||||
Op1 ? 2 : 1);
|
||||
}
|
||||
if (Transform) {
|
||||
if (!Op1)
|
||||
Op1 = Builder->CreateICmp(I.getPredicate(), LHSI->getOperand(1),
|
||||
RHSC, I.getName());
|
||||
|
@ -86,6 +86,7 @@ INITIALIZE_PASS_BEGIN(InstCombiner, "instcombine",
|
||||
"Combine redundant instructions", false, false)
|
||||
INITIALIZE_PASS_DEPENDENCY(AssumptionTracker)
|
||||
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfo)
|
||||
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
|
||||
INITIALIZE_PASS_END(InstCombiner, "instcombine",
|
||||
"Combine redundant instructions", false, false)
|
||||
|
||||
@ -93,6 +94,8 @@ void InstCombiner::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AU.setPreservesCFG();
|
||||
AU.addRequired<AssumptionTracker>();
|
||||
AU.addRequired<TargetLibraryInfo>();
|
||||
AU.addRequired<DominatorTreeWrapperPass>();
|
||||
AU.addPreserved<DominatorTreeWrapperPass>();
|
||||
}
|
||||
|
||||
|
||||
@ -2962,12 +2965,9 @@ bool InstCombiner::runOnFunction(Function &F) {
|
||||
AT = &getAnalysis<AssumptionTracker>();
|
||||
DataLayoutPass *DLP = getAnalysisIfAvailable<DataLayoutPass>();
|
||||
DL = DLP ? &DLP->getDataLayout() : nullptr;
|
||||
DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
|
||||
TLI = &getAnalysis<TargetLibraryInfo>();
|
||||
|
||||
DominatorTreeWrapperPass *DTWP =
|
||||
getAnalysisIfAvailable<DominatorTreeWrapperPass>();
|
||||
DT = DTWP ? &DTWP->getDomTree() : nullptr;
|
||||
|
||||
// Minimizing size?
|
||||
MinimizeSize = F.getAttributes().hasAttribute(AttributeSet::FunctionIndex,
|
||||
Attribute::MinSize);
|
||||
|
@ -4,6 +4,7 @@ define void @entry() nounwind {
|
||||
entry:
|
||||
br label %for.cond
|
||||
|
||||
; CHECK: br label %for.cond
|
||||
for.cond:
|
||||
%local = phi <1 x i32> [ <i32 0>, %entry ], [ %phi2, %cond.end47 ]
|
||||
%phi3 = sub <1 x i32> zeroinitializer, %local
|
||||
@ -18,7 +19,6 @@ cond.end:
|
||||
|
||||
cond.end47:
|
||||
%sum = add <1 x i32> %cond, <i32 92>
|
||||
; CHECK: sub <1 x i32> <i32 -92>, %cond
|
||||
%phi2 = sub <1 x i32> zeroinitializer, %sum
|
||||
br label %for.cond
|
||||
}
|
||||
|
25
test/Transforms/InstCombine/pr21199.ll
Normal file
25
test/Transforms/InstCombine/pr21199.ll
Normal file
@ -0,0 +1,25 @@
|
||||
; do not replace a 'select' with 'or' in 'select - cmp - br' sequence
|
||||
; RUN: opt -instcombine -S < %s | FileCheck %s
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
declare void @f(i32)
|
||||
|
||||
define void @test(i32 %len) {
|
||||
entry:
|
||||
%cmp = icmp ult i32 %len, 8
|
||||
%cond = select i1 %cmp, i32 %len, i32 8
|
||||
%cmp11 = icmp ult i32 0, %cond
|
||||
br i1 %cmp11, label %for.body, label %for.end
|
||||
|
||||
for.body: ; preds = %entry, %for.body
|
||||
%i.02 = phi i32 [ %inc, %for.body ], [ 0, %entry ]
|
||||
tail call void @f(i32 %cond)
|
||||
%inc = add i32 %i.02, 1
|
||||
%cmp1 = icmp ult i32 %inc, %cond
|
||||
br i1 %cmp1, label %for.body, label %for.end
|
||||
|
||||
for.end: ; preds = %for.body, %entry
|
||||
ret void
|
||||
; CHECK: select
|
||||
}
|
50
test/Transforms/InstCombine/pr21210.ll
Normal file
50
test/Transforms/InstCombine/pr21210.ll
Normal file
@ -0,0 +1,50 @@
|
||||
; RUN: opt < %s -mtriple=x86_64-unknown-linux-gnu -instcombine -S | FileCheck %s
|
||||
; Checks that the select-icmp optimization is safe in two cases
|
||||
declare void @foo(i32)
|
||||
declare i32 @bar(i32)
|
||||
|
||||
; don't replace 'cond' by 'len' in the home block ('bb') that
|
||||
; contains the select
|
||||
define void @test1(i32 %len) {
|
||||
entry:
|
||||
br label %bb
|
||||
|
||||
bb:
|
||||
%cmp = icmp ult i32 %len, 8
|
||||
%cond = select i1 %cmp, i32 %len, i32 8
|
||||
call void @foo(i32 %cond)
|
||||
%cmp11 = icmp eq i32 %cond, 8
|
||||
br i1 %cmp11, label %for.end, label %bb
|
||||
|
||||
for.end:
|
||||
ret void
|
||||
; CHECK: select
|
||||
; CHECK: icmp eq i32 %cond, 8
|
||||
}
|
||||
|
||||
; don't replace 'cond' by 'len' in a block ('b1') that dominates all uses
|
||||
; of the select outside the home block ('bb'), but can be reached from the home
|
||||
; block on another path ('bb -> b0 -> b1')
|
||||
define void @test2(i32 %len) {
|
||||
entry:
|
||||
%0 = call i32 @bar(i32 %len);
|
||||
%cmp = icmp ult i32 %len, 4
|
||||
br i1 %cmp, label %bb, label %b1
|
||||
bb:
|
||||
%cond = select i1 %cmp, i32 %len, i32 8
|
||||
%cmp11 = icmp eq i32 %cond, 8
|
||||
br i1 %cmp11, label %b0, label %b1
|
||||
|
||||
b0:
|
||||
call void @foo(i32 %len)
|
||||
br label %b1
|
||||
|
||||
b1:
|
||||
; CHECK: phi i32 [ %cond, %bb ], [ undef, %b0 ], [ %0, %entry ]
|
||||
%1 = phi i32 [ %cond, %bb ], [ undef, %b0 ], [ %0, %entry ]
|
||||
br label %ret
|
||||
|
||||
ret:
|
||||
call void @foo(i32 %1)
|
||||
ret void
|
||||
}
|
155
test/Transforms/InstCombine/select-cmp-br.ll
Normal file
155
test/Transforms/InstCombine/select-cmp-br.ll
Normal file
@ -0,0 +1,155 @@
|
||||
; Replace a 'select' with 'or' in 'select - cmp [eq|ne] - br' sequence
|
||||
; RUN: opt -instcombine -S < %s | FileCheck %s
|
||||
|
||||
%C = type <{ %struct.S }>
|
||||
%struct.S = type { i64*, i32, i32 }
|
||||
|
||||
declare void @bar(%struct.S *) #1
|
||||
declare void @foobar()
|
||||
|
||||
define void @test1(%C*) {
|
||||
entry:
|
||||
%1 = getelementptr inbounds %C* %0, i64 0, i32 0, i32 0
|
||||
%m = load i64** %1, align 8
|
||||
%2 = getelementptr inbounds %C* %0, i64 1, i32 0, i32 0
|
||||
%n = load i64** %2, align 8
|
||||
%3 = getelementptr inbounds i64* %m, i64 9
|
||||
%4 = bitcast i64* %3 to i64 (%C*)**
|
||||
%5 = load i64 (%C*)** %4, align 8
|
||||
%6 = icmp eq i64* %m, %n
|
||||
%7 = select i1 %6, %C* %0, %C* null
|
||||
%8 = icmp eq %C* %7, null
|
||||
br i1 %8, label %12, label %10
|
||||
|
||||
; <label>:9 ; preds = %10, %12
|
||||
ret void
|
||||
|
||||
; <label>:10 ; preds = %entry
|
||||
%11 = getelementptr inbounds %C* %7, i64 0, i32 0
|
||||
tail call void @bar(%struct.S* %11)
|
||||
br label %9
|
||||
|
||||
; <label>:12 ; preds = %entry
|
||||
%13 = tail call i64 %5(%C* %0)
|
||||
br label %9
|
||||
; CHECK-LABEL: @test1(
|
||||
; CHECK-NOT: select
|
||||
; CHECK: or
|
||||
; CHECK-NOT: select
|
||||
}
|
||||
|
||||
define void @test2(%C*) {
|
||||
entry:
|
||||
%1 = getelementptr inbounds %C* %0, i64 0, i32 0, i32 0
|
||||
%m = load i64** %1, align 8
|
||||
%2 = getelementptr inbounds %C* %0, i64 1, i32 0, i32 0
|
||||
%n = load i64** %2, align 8
|
||||
%3 = getelementptr inbounds i64* %m, i64 9
|
||||
%4 = bitcast i64* %3 to i64 (%C*)**
|
||||
%5 = load i64 (%C*)** %4, align 8
|
||||
%6 = icmp eq i64* %m, %n
|
||||
%7 = select i1 %6, %C* null, %C* %0
|
||||
%8 = icmp eq %C* %7, null
|
||||
br i1 %8, label %12, label %10
|
||||
|
||||
; <label>:9 ; preds = %10, %12
|
||||
ret void
|
||||
|
||||
; <label>:10 ; preds = %entry
|
||||
%11 = getelementptr inbounds %C* %7, i64 0, i32 0
|
||||
tail call void @bar(%struct.S* %11)
|
||||
br label %9
|
||||
|
||||
; <label>:12 ; preds = %entry
|
||||
%13 = tail call i64 %5(%C* %0)
|
||||
br label %9
|
||||
; CHECK-LABEL: @test2(
|
||||
; CHECK-NOT: select
|
||||
; CHECK: or
|
||||
; CHECK-NOT: select
|
||||
}
|
||||
|
||||
define void @test3(%C*) {
|
||||
entry:
|
||||
%1 = getelementptr inbounds %C* %0, i64 0, i32 0, i32 0
|
||||
%m = load i64** %1, align 8
|
||||
%2 = getelementptr inbounds %C* %0, i64 1, i32 0, i32 0
|
||||
%n = load i64** %2, align 8
|
||||
%3 = getelementptr inbounds i64* %m, i64 9
|
||||
%4 = bitcast i64* %3 to i64 (%C*)**
|
||||
%5 = load i64 (%C*)** %4, align 8
|
||||
%6 = icmp eq i64* %m, %n
|
||||
%7 = select i1 %6, %C* %0, %C* null
|
||||
%8 = icmp ne %C* %7, null
|
||||
br i1 %8, label %10, label %12
|
||||
|
||||
; <label>:9 ; preds = %10, %12
|
||||
ret void
|
||||
|
||||
; <label>:10 ; preds = %entry
|
||||
%11 = getelementptr inbounds %C* %7, i64 0, i32 0
|
||||
tail call void @bar(%struct.S* %11)
|
||||
br label %9
|
||||
|
||||
; <label>:12 ; preds = %entry
|
||||
%13 = tail call i64 %5(%C* %0)
|
||||
br label %9
|
||||
; CHECK-LABEL: @test3(
|
||||
; CHECK-NOT: select
|
||||
; CHECK: or
|
||||
; CHECK-NOT: select
|
||||
}
|
||||
|
||||
define void @test4(%C*) {
|
||||
entry:
|
||||
%1 = getelementptr inbounds %C* %0, i64 0, i32 0, i32 0
|
||||
%m = load i64** %1, align 8
|
||||
%2 = getelementptr inbounds %C* %0, i64 1, i32 0, i32 0
|
||||
%n = load i64** %2, align 8
|
||||
%3 = getelementptr inbounds i64* %m, i64 9
|
||||
%4 = bitcast i64* %3 to i64 (%C*)**
|
||||
%5 = load i64 (%C*)** %4, align 8
|
||||
%6 = icmp eq i64* %m, %n
|
||||
%7 = select i1 %6, %C* null, %C* %0
|
||||
%8 = icmp ne %C* %7, null
|
||||
br i1 %8, label %10, label %12
|
||||
|
||||
; <label>:9 ; preds = %10, %12
|
||||
ret void
|
||||
|
||||
; <label>:10 ; preds = %entry
|
||||
%11 = getelementptr inbounds %C* %7, i64 0, i32 0
|
||||
tail call void @bar(%struct.S* %11)
|
||||
br label %9
|
||||
|
||||
; <label>:12 ; preds = %entry
|
||||
%13 = tail call i64 %5(%C* %0)
|
||||
br label %9
|
||||
; CHECK-LABEL: @test4(
|
||||
; CHECK-NOT: select
|
||||
; CHECK: or
|
||||
; CHECK-NOT: select
|
||||
}
|
||||
|
||||
define void @test5(%C*, i1) {
|
||||
entry:
|
||||
%2 = select i1 %1, %C* null, %C* %0
|
||||
%3 = icmp ne %C* %2, null
|
||||
br i1 %3, label %5, label %7
|
||||
|
||||
; <label>:4 ; preds = %10, %12
|
||||
ret void
|
||||
|
||||
; <label>:5 ; preds = %entry
|
||||
%6 = getelementptr inbounds %C* %2, i64 0, i32 0
|
||||
tail call void @bar(%struct.S* %6)
|
||||
br label %4
|
||||
|
||||
; <label>:7 ; preds = %entry
|
||||
tail call void @foobar()
|
||||
br label %4
|
||||
; CHECK-LABEL: @test5(
|
||||
; CHECK-NOT: select
|
||||
; CHECK: or
|
||||
; CHECK-NOT: select
|
||||
}
|
Loading…
Reference in New Issue
Block a user