forked from OSchip/llvm-project
[ARM] Don't convert switches to lookup tables of pointers with ROPI/RWPI
With the ROPI and RWPI relocation models we can't always have pointers to global data or functions in constant data, so don't try to convert switches into lookup tables if any value in the lookup table would require a relocation. We can still safely emit lookup tables of other values, such as simple constants. Differential Revision: https://reviews.llvm.org/D24462 llvm-svn: 283530
This commit is contained in:
parent
68c6c8cd78
commit
4df1cc0b00
|
@ -381,6 +381,10 @@ public:
|
|||
/// target.
|
||||
bool shouldBuildLookupTables() const;
|
||||
|
||||
/// \brief Return true if switches should be turned into lookup tables
|
||||
/// containing this constant value for the target.
|
||||
bool shouldBuildLookupTablesForConstant(Constant *C) const;
|
||||
|
||||
/// \brief Don't restrict interleaved unrolling to small loops.
|
||||
bool enableAggressiveInterleaving(bool LoopHasReductions) const;
|
||||
|
||||
|
@ -703,6 +707,7 @@ public:
|
|||
virtual unsigned getJumpBufAlignment() = 0;
|
||||
virtual unsigned getJumpBufSize() = 0;
|
||||
virtual bool shouldBuildLookupTables() = 0;
|
||||
virtual bool shouldBuildLookupTablesForConstant(Constant *C) = 0;
|
||||
virtual bool enableAggressiveInterleaving(bool LoopHasReductions) = 0;
|
||||
virtual bool enableInterleavedAccessVectorization() = 0;
|
||||
virtual bool isFPVectorizationPotentiallyUnsafe() = 0;
|
||||
|
@ -888,6 +893,9 @@ public:
|
|||
bool shouldBuildLookupTables() override {
|
||||
return Impl.shouldBuildLookupTables();
|
||||
}
|
||||
bool shouldBuildLookupTablesForConstant(Constant *C) override {
|
||||
return Impl.shouldBuildLookupTablesForConstant(C);
|
||||
}
|
||||
bool enableAggressiveInterleaving(bool LoopHasReductions) override {
|
||||
return Impl.enableAggressiveInterleaving(LoopHasReductions);
|
||||
}
|
||||
|
|
|
@ -248,6 +248,7 @@ public:
|
|||
unsigned getJumpBufSize() { return 0; }
|
||||
|
||||
bool shouldBuildLookupTables() { return true; }
|
||||
bool shouldBuildLookupTablesForConstant(Constant *C) { return true; }
|
||||
|
||||
bool enableAggressiveInterleaving(bool LoopHasReductions) { return false; }
|
||||
|
||||
|
|
|
@ -178,6 +178,9 @@ unsigned TargetTransformInfo::getJumpBufSize() const {
|
|||
bool TargetTransformInfo::shouldBuildLookupTables() const {
|
||||
return TTIImpl->shouldBuildLookupTables();
|
||||
}
|
||||
bool TargetTransformInfo::shouldBuildLookupTablesForConstant(Constant *C) const {
|
||||
return TTIImpl->shouldBuildLookupTablesForConstant(C);
|
||||
}
|
||||
|
||||
bool TargetTransformInfo::enableAggressiveInterleaving(bool LoopHasReductions) const {
|
||||
return TTIImpl->enableAggressiveInterleaving(LoopHasReductions);
|
||||
|
|
|
@ -128,6 +128,16 @@ public:
|
|||
int getInterleavedMemoryOpCost(unsigned Opcode, Type *VecTy, unsigned Factor,
|
||||
ArrayRef<unsigned> Indices, unsigned Alignment,
|
||||
unsigned AddressSpace);
|
||||
|
||||
bool shouldBuildLookupTablesForConstant(Constant *C) const {
|
||||
// In the ROPI and RWPI relocation models we can't have pointers to global
|
||||
// variables or functions in constant data, so don't convert switches to
|
||||
// lookup tables if any of the values would need relocation.
|
||||
if (ST->isROPI() || ST->isRWPI())
|
||||
return !C->needsRelocation();
|
||||
|
||||
return true;
|
||||
}
|
||||
/// @}
|
||||
};
|
||||
|
||||
|
|
|
@ -4425,18 +4425,25 @@ static bool ForwardSwitchConditionToPHI(SwitchInst *SI) {
|
|||
|
||||
/// Return true if the backend will be able to handle
|
||||
/// initializing an array of constants like C.
|
||||
static bool ValidLookupTableConstant(Constant *C) {
|
||||
static bool ValidLookupTableConstant(Constant *C, const TargetTransformInfo &TTI) {
|
||||
if (C->isThreadDependent())
|
||||
return false;
|
||||
if (C->isDLLImportDependent())
|
||||
return false;
|
||||
|
||||
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(C))
|
||||
return CE->isGEPWithNoNotionalOverIndexing();
|
||||
if (!isa<ConstantFP>(C) && !isa<ConstantInt>(C) &&
|
||||
!isa<ConstantPointerNull>(C) && !isa<GlobalValue>(C) &&
|
||||
!isa<UndefValue>(C) && !isa<ConstantExpr>(C))
|
||||
return false;
|
||||
|
||||
return isa<ConstantFP>(C) || isa<ConstantInt>(C) ||
|
||||
isa<ConstantPointerNull>(C) || isa<GlobalValue>(C) ||
|
||||
isa<UndefValue>(C);
|
||||
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(C))
|
||||
if (!CE->isGEPWithNoNotionalOverIndexing())
|
||||
return false;
|
||||
|
||||
if (!TTI.shouldBuildLookupTablesForConstant(C))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// If V is a Constant, return it. Otherwise, try to look up
|
||||
|
@ -4490,8 +4497,8 @@ ConstantFold(Instruction *I, const DataLayout &DL,
|
|||
static bool
|
||||
GetCaseResults(SwitchInst *SI, ConstantInt *CaseVal, BasicBlock *CaseDest,
|
||||
BasicBlock **CommonDest,
|
||||
SmallVectorImpl<std::pair<PHINode *, Constant *>> &Res,
|
||||
const DataLayout &DL) {
|
||||
SmallVectorImpl<std::pair<PHINode *, Constant *> > &Res,
|
||||
const DataLayout &DL, const TargetTransformInfo &TTI) {
|
||||
// The block from which we enter the common destination.
|
||||
BasicBlock *Pred = SI->getParent();
|
||||
|
||||
|
@ -4553,7 +4560,7 @@ GetCaseResults(SwitchInst *SI, ConstantInt *CaseVal, BasicBlock *CaseDest,
|
|||
return false;
|
||||
|
||||
// Be conservative about which kinds of constants we support.
|
||||
if (!ValidLookupTableConstant(ConstVal))
|
||||
if (!ValidLookupTableConstant(ConstVal, TTI))
|
||||
return false;
|
||||
|
||||
Res.push_back(std::make_pair(PHI, ConstVal));
|
||||
|
@ -4585,14 +4592,15 @@ static bool InitializeUniqueCases(SwitchInst *SI, PHINode *&PHI,
|
|||
BasicBlock *&CommonDest,
|
||||
SwitchCaseResultVectorTy &UniqueResults,
|
||||
Constant *&DefaultResult,
|
||||
const DataLayout &DL) {
|
||||
const DataLayout &DL,
|
||||
const TargetTransformInfo &TTI) {
|
||||
for (auto &I : SI->cases()) {
|
||||
ConstantInt *CaseVal = I.getCaseValue();
|
||||
|
||||
// Resulting value at phi nodes for this case value.
|
||||
SwitchCaseResultsTy Results;
|
||||
if (!GetCaseResults(SI, CaseVal, I.getCaseSuccessor(), &CommonDest, Results,
|
||||
DL))
|
||||
DL, TTI))
|
||||
return false;
|
||||
|
||||
// Only one value per case is permitted
|
||||
|
@ -4610,7 +4618,7 @@ static bool InitializeUniqueCases(SwitchInst *SI, PHINode *&PHI,
|
|||
SmallVector<std::pair<PHINode *, Constant *>, 1> DefaultResults;
|
||||
BasicBlock *DefaultDest = SI->getDefaultDest();
|
||||
GetCaseResults(SI, nullptr, SI->getDefaultDest(), &CommonDest, DefaultResults,
|
||||
DL);
|
||||
DL, TTI);
|
||||
// If the default value is not found abort unless the default destination
|
||||
// is unreachable.
|
||||
DefaultResult =
|
||||
|
@ -4689,7 +4697,8 @@ static void RemoveSwitchAfterSelectConversion(SwitchInst *SI, PHINode *PHI,
|
|||
/// phi nodes in a common successor block with only two different
|
||||
/// constant values, replace the switch with select.
|
||||
static bool SwitchToSelect(SwitchInst *SI, IRBuilder<> &Builder,
|
||||
AssumptionCache *AC, const DataLayout &DL) {
|
||||
AssumptionCache *AC, const DataLayout &DL,
|
||||
const TargetTransformInfo &TTI) {
|
||||
Value *const Cond = SI->getCondition();
|
||||
PHINode *PHI = nullptr;
|
||||
BasicBlock *CommonDest = nullptr;
|
||||
|
@ -4697,7 +4706,7 @@ static bool SwitchToSelect(SwitchInst *SI, IRBuilder<> &Builder,
|
|||
SwitchCaseResultVectorTy UniqueResults;
|
||||
// Collect all the cases that will deliver the same value from the switch.
|
||||
if (!InitializeUniqueCases(SI, PHI, CommonDest, UniqueResults, DefaultResult,
|
||||
DL))
|
||||
DL, TTI))
|
||||
return false;
|
||||
// Selects choose between maximum two values.
|
||||
if (UniqueResults.size() != 2)
|
||||
|
@ -5135,7 +5144,7 @@ static bool SwitchToLookupTable(SwitchInst *SI, IRBuilder<> &Builder,
|
|||
typedef SmallVector<std::pair<PHINode *, Constant *>, 4> ResultsTy;
|
||||
ResultsTy Results;
|
||||
if (!GetCaseResults(SI, CaseVal, CI.getCaseSuccessor(), &CommonDest,
|
||||
Results, DL))
|
||||
Results, DL, TTI))
|
||||
return false;
|
||||
|
||||
// Append the result from this case to the list for each phi.
|
||||
|
@ -5161,8 +5170,9 @@ static bool SwitchToLookupTable(SwitchInst *SI, IRBuilder<> &Builder,
|
|||
// If the table has holes, we need a constant result for the default case
|
||||
// or a bitmask that fits in a register.
|
||||
SmallVector<std::pair<PHINode *, Constant *>, 4> DefaultResultsList;
|
||||
bool HasDefaultResults = GetCaseResults(SI, nullptr, SI->getDefaultDest(),
|
||||
&CommonDest, DefaultResultsList, DL);
|
||||
bool HasDefaultResults =
|
||||
GetCaseResults(SI, nullptr, SI->getDefaultDest(), &CommonDest,
|
||||
DefaultResultsList, DL, TTI);
|
||||
|
||||
bool NeedMask = (TableHasHoles && !HasDefaultResults);
|
||||
if (NeedMask) {
|
||||
|
@ -5458,7 +5468,7 @@ bool SimplifyCFGOpt::SimplifySwitch(SwitchInst *SI, IRBuilder<> &Builder) {
|
|||
if (EliminateDeadSwitchCases(SI, AC, DL))
|
||||
return SimplifyCFG(BB, TTI, BonusInstThreshold, AC) | true;
|
||||
|
||||
if (SwitchToSelect(SI, Builder, AC, DL))
|
||||
if (SwitchToSelect(SI, Builder, AC, DL, TTI))
|
||||
return SimplifyCFG(BB, TTI, BonusInstThreshold, AC) | true;
|
||||
|
||||
if (ForwardSwitchConditionToPHI(SI))
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
; RUN: opt -S -simplifycfg -mtriple=arm -relocation-model=static < %s | FileCheck %s --check-prefix=CHECK --check-prefix=ENABLE
|
||||
; RUN: opt -S -simplifycfg -mtriple=arm -relocation-model=pic < %s | FileCheck %s --check-prefix=CHECK --check-prefix=ENABLE
|
||||
; RUN: opt -S -simplifycfg -mtriple=arm -relocation-model=ropi < %s | FileCheck %s --check-prefix=CHECK --check-prefix=DISABLE
|
||||
; RUN: opt -S -simplifycfg -mtriple=arm -relocation-model=rwpi < %s | FileCheck %s --check-prefix=CHECK --check-prefix=DISABLE
|
||||
; RUN: opt -S -simplifycfg -mtriple=arm -relocation-model=ropi-rwpi < %s | FileCheck %s --check-prefix=CHECK --check-prefix=DISABLE
|
||||
|
||||
; CHECK: @{{.*}} = private unnamed_addr constant [3 x i32] [i32 1234, i32 5678, i32 15532]
|
||||
; ENABLE: @{{.*}} = private unnamed_addr constant [3 x i32*] [i32* @c1, i32* @c2, i32* @c3]
|
||||
; DISABLE-NOT: @{{.*}} = private unnamed_addr constant [3 x i32*] [i32* @c1, i32* @c2, i32* @c3]
|
||||
; ENABLE: @{{.*}} = private unnamed_addr constant [3 x i32*] [i32* @g1, i32* @g2, i32* @g3]
|
||||
; DISABLE-NOT: @{{.*}} = private unnamed_addr constant [3 x i32*] [i32* @g1, i32* @g2, i32* @g3]
|
||||
; ENABLE: @{{.*}} = private unnamed_addr constant [3 x i32 (i32, i32)*] [i32 (i32, i32)* @f1, i32 (i32, i32)* @f2, i32 (i32, i32)* @f3]
|
||||
; DISABLE-NOT: @{{.*}} = private unnamed_addr constant [3 x i32 (i32, i32)*] [i32 (i32, i32)* @f1, i32 (i32, i32)* @f2, i32 (i32, i32)* @f3]
|
||||
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "armv7a--none-eabi"
|
||||
|
||||
define i32 @test1(i32 %n) {
|
||||
entry:
|
||||
switch i32 %n, label %sw.default [
|
||||
i32 0, label %sw.bb
|
||||
i32 1, label %sw.bb1
|
||||
i32 2, label %sw.bb2
|
||||
]
|
||||
|
||||
sw.bb:
|
||||
br label %return
|
||||
|
||||
sw.bb1:
|
||||
br label %return
|
||||
|
||||
sw.bb2:
|
||||
br label %return
|
||||
|
||||
sw.default:
|
||||
br label %return
|
||||
|
||||
return:
|
||||
%retval.0 = phi i32 [ 15498, %sw.default ], [ 15532, %sw.bb2 ], [ 5678, %sw.bb1 ], [ 1234, %sw.bb ]
|
||||
ret i32 %retval.0
|
||||
}
|
||||
|
||||
@c1 = external constant i32, align 4
|
||||
@c2 = external constant i32, align 4
|
||||
@c3 = external constant i32, align 4
|
||||
@c4 = external constant i32, align 4
|
||||
|
||||
|
||||
define i32* @test2(i32 %n) {
|
||||
entry:
|
||||
switch i32 %n, label %sw.default [
|
||||
i32 0, label %sw.bb
|
||||
i32 1, label %sw.bb1
|
||||
i32 2, label %sw.bb2
|
||||
]
|
||||
|
||||
sw.bb:
|
||||
br label %return
|
||||
|
||||
sw.bb1:
|
||||
br label %return
|
||||
|
||||
sw.bb2:
|
||||
br label %return
|
||||
|
||||
sw.default:
|
||||
br label %return
|
||||
|
||||
return:
|
||||
%retval.0 = phi i32* [ @c4, %sw.default ], [ @c3, %sw.bb2 ], [ @c2, %sw.bb1 ], [ @c1, %sw.bb ]
|
||||
ret i32* %retval.0
|
||||
}
|
||||
|
||||
@g1 = external global i32, align 4
|
||||
@g2 = external global i32, align 4
|
||||
@g3 = external global i32, align 4
|
||||
@g4 = external global i32, align 4
|
||||
|
||||
define i32* @test3(i32 %n) {
|
||||
entry:
|
||||
switch i32 %n, label %sw.default [
|
||||
i32 0, label %sw.bb
|
||||
i32 1, label %sw.bb1
|
||||
i32 2, label %sw.bb2
|
||||
]
|
||||
|
||||
sw.bb:
|
||||
br label %return
|
||||
|
||||
sw.bb1:
|
||||
br label %return
|
||||
|
||||
sw.bb2:
|
||||
br label %return
|
||||
|
||||
sw.default:
|
||||
br label %return
|
||||
|
||||
return:
|
||||
%retval.0 = phi i32* [ @g4, %sw.default ], [ @g3, %sw.bb2 ], [ @g2, %sw.bb1 ], [ @g1, %sw.bb ]
|
||||
ret i32* %retval.0
|
||||
}
|
||||
|
||||
declare i32 @f1(i32, i32)
|
||||
declare i32 @f2(i32, i32)
|
||||
declare i32 @f3(i32, i32)
|
||||
declare i32 @f4(i32, i32)
|
||||
declare i32 @f5(i32, i32)
|
||||
|
||||
define i32 @test4(i32 %a, i32 %b, i32 %c) {
|
||||
entry:
|
||||
%cmp = icmp eq i32 %a, 1
|
||||
br i1 %cmp, label %cond.end11, label %cond.false
|
||||
|
||||
cond.false:
|
||||
%cmp1 = icmp eq i32 %a, 2
|
||||
br i1 %cmp1, label %cond.end11, label %cond.false3
|
||||
|
||||
cond.false3:
|
||||
%cmp4 = icmp eq i32 %a, 3
|
||||
br i1 %cmp4, label %cond.end11, label %cond.false6
|
||||
|
||||
cond.false6:
|
||||
%cmp7 = icmp eq i32 %a, 4
|
||||
%cond = select i1 %cmp7, i32 (i32, i32)* @f4, i32 (i32, i32)* @f5
|
||||
br label %cond.end11
|
||||
|
||||
cond.end11:
|
||||
%cond12 = phi i32 (i32, i32)* [ @f1, %entry ], [ @f2, %cond.false ], [ %cond, %cond.false6 ], [ @f3, %cond.false3 ]
|
||||
%call = call i32 %cond12(i32 %b, i32 %c) #2
|
||||
ret i32 %call
|
||||
}
|
Loading…
Reference in New Issue