Skip to content

Commit 8abd24e

Browse files
committed
[SinkGEPConstOffset] FEAT: Sink constant offsets down a GEP chain to tail for reduction of register usage.
Summary: Sink constant offsets down the GEP chain to the tail helps reduce register usage. For example: %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 512 %gep1 = getelementptr half, ptr addrspace(3) %gep0, i32 %ofst0 %gep2 = getelementptr half, ptr addrspace(3) %gep1, i32 %ofst1 %data = load half, ptr addrspace(3) %gep2, align 2 ==> %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 %ofst0 %gep1 = getelementptr half, ptr addrspace(3) %gep0, i32 %ofst1 %gep2 = getelementptr half, ptr addrspace(3) %gep1, i32 512 %data = load half, ptr addrspace(3) %gep2, align 2
1 parent ec7a509 commit 8abd24e

File tree

18 files changed

+920
-503
lines changed

18 files changed

+920
-503
lines changed

llvm/include/llvm/InitializePasses.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ void initializeScalarizerLegacyPassPass(PassRegistry &);
286286
void initializeScavengerTestPass(PassRegistry &);
287287
void initializeScopedNoAliasAAWrapperPassPass(PassRegistry &);
288288
void initializeSeparateConstOffsetFromGEPLegacyPassPass(PassRegistry &);
289+
void initializeSinkGEPConstOffsetLegacyPassPass(PassRegistry &);
289290
void initializeShadowStackGCLoweringPass(PassRegistry &);
290291
void initializeShrinkWrapLegacyPass(PassRegistry &);
291292
void initializeSingleLoopExtractorPass(PassRegistry &);

llvm/include/llvm/LinkAllPasses.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ struct ForcePassLinking {
134134
(void)llvm::createPartiallyInlineLibCallsPass();
135135
(void)llvm::createScalarizerPass();
136136
(void)llvm::createSeparateConstOffsetFromGEPPass();
137+
(void)llvm::createSinkGEPConstOffsetPass();
137138
(void)llvm::createSpeculativeExecutionPass();
138139
(void)llvm::createSpeculativeExecutionIfHasBranchDivergencePass();
139140
(void)llvm::createStraightLineStrengthReducePass();

llvm/include/llvm/Transforms/Scalar.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,13 @@ FunctionPass *createPartiallyInlineLibCallsPass();
164164
//
165165
FunctionPass *createSeparateConstOffsetFromGEPPass(bool LowerGEP = false);
166166

167+
//===----------------------------------------------------------------------===//
168+
//
169+
// SinkGEPConstOffset - Sink constant offsets down the GEP chain to the tail for
170+
// reduction of register usage.
171+
//
172+
FunctionPass *createSinkGEPConstOffsetPass();
173+
167174
//===----------------------------------------------------------------------===//
168175
//
169176
// SpeculativeExecution - Aggressively hoist instructions to enable
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//===- SinkGEPConstOffset.h -----------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_TRANSFORMS_SCALAR_SINKGEPCONSTOFFSET_H
10+
#define LLVM_TRANSFORMS_SCALAR_SINKGEPCONSTOFFSET_H
11+
12+
#include "llvm/IR/PassManager.h"
13+
14+
namespace llvm {
15+
16+
class SinkGEPConstOffsetPass
17+
: public PassInfoMixin<SinkGEPConstOffsetPass> {
18+
public:
19+
SinkGEPConstOffsetPass() {}
20+
void printPipeline(raw_ostream &OS,
21+
function_ref<StringRef(StringRef)> MapClassName2PassName);
22+
PreservedAnalyses run(Function &F, FunctionAnalysisManager &);
23+
};
24+
25+
} // end namespace llvm
26+
27+
#endif // LLVM_TRANSFORMS_SCALAR_SINKGEPCONSTOFFSET_H

llvm/lib/Passes/PassBuilder.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@
329329
#include "llvm/Transforms/Scalar/ScalarizeMaskedMemIntrin.h"
330330
#include "llvm/Transforms/Scalar/Scalarizer.h"
331331
#include "llvm/Transforms/Scalar/SeparateConstOffsetFromGEP.h"
332+
#include "llvm/Transforms/Scalar/SinkGEPConstOffset.h"
332333
#include "llvm/Transforms/Scalar/SimpleLoopUnswitch.h"
333334
#include "llvm/Transforms/Scalar/SimplifyCFG.h"
334335
#include "llvm/Transforms/Scalar/Sink.h"

llvm/lib/Passes/PassRegistry.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,8 @@ FUNCTION_PASS("sccp", SCCPPass())
474474
FUNCTION_PASS("select-optimize", SelectOptimizePass(TM))
475475
FUNCTION_PASS("separate-const-offset-from-gep",
476476
SeparateConstOffsetFromGEPPass())
477+
FUNCTION_PASS("sink-gep-const-offset",
478+
SinkGEPConstOffsetPass())
477479
FUNCTION_PASS("sink", SinkingPass())
478480
FUNCTION_PASS("sjlj-eh-prepare", SjLjEHPreparePass(TM))
479481
FUNCTION_PASS("slp-vectorizer", SLPVectorizerPass())

llvm/lib/Target/AMDGPU/AMDGPUTargetMachine.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
#include "llvm/Transforms/Scalar/LoopDataPrefetch.h"
105105
#include "llvm/Transforms/Scalar/NaryReassociate.h"
106106
#include "llvm/Transforms/Scalar/SeparateConstOffsetFromGEP.h"
107+
#include "llvm/Transforms/Scalar/SinkGEPConstOffset.h"
107108
#include "llvm/Transforms/Scalar/Sink.h"
108109
#include "llvm/Transforms/Scalar/StraightLineStrengthReduce.h"
109110
#include "llvm/Transforms/Scalar/StructurizeCFG.h"
@@ -1210,6 +1211,7 @@ void AMDGPUPassConfig::addStraightLineScalarOptimizationPasses() {
12101211
if (isPassEnabled(EnableLoopPrefetch, CodeGenOptLevel::Aggressive))
12111212
addPass(createLoopDataPrefetchPass());
12121213
addPass(createSeparateConstOffsetFromGEPPass());
1214+
addPass(createSinkGEPConstOffsetPass());
12131215
// ReassociateGEPs exposes more opportunities for SLSR. See
12141216
// the example in reassociate-geps-and-slsr.ll.
12151217
addPass(createStraightLineStrengthReducePass());
@@ -2288,6 +2290,8 @@ void AMDGPUCodeGenPassBuilder::addStraightLineScalarOptimizationPasses(
22882290

22892291
addPass(SeparateConstOffsetFromGEPPass());
22902292

2293+
addPass(SinkGEPConstOffsetPass());
2294+
22912295
// ReassociateGEPs exposes more opportunities for SLSR. See
22922296
// the example in reassociate-geps-and-slsr.ll.
22932297
addPass(StraightLineStrengthReducePass());

llvm/lib/Transforms/Scalar/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ add_llvm_component_library(LLVMScalarOpts
7171
Scalarizer.cpp
7272
ScalarizeMaskedMemIntrin.cpp
7373
SeparateConstOffsetFromGEP.cpp
74+
SinkGEPConstOffset.cpp
7475
SimpleLoopUnswitch.cpp
7576
SimplifyCFGPass.cpp
7677
Sink.cpp

llvm/lib/Transforms/Scalar/Scalar.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ void llvm::initializeScalarOpts(PassRegistry &Registry) {
4545
initializeSinkingLegacyPassPass(Registry);
4646
initializeTailCallElimPass(Registry);
4747
initializeSeparateConstOffsetFromGEPLegacyPassPass(Registry);
48+
initializeSinkGEPConstOffsetLegacyPassPass(Registry);
4849
initializeSpeculativeExecutionLegacyPassPass(Registry);
4950
initializeStraightLineStrengthReduceLegacyPassPass(Registry);
5051
initializePlaceBackedgeSafepointsLegacyPassPass(Registry);
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
//===- SinkGEPConstOffset.cpp -------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/Transforms/Scalar/SinkGEPConstOffset.h"
10+
#include "llvm/ADT/APInt.h"
11+
#include "llvm/ADT/DenseMap.h"
12+
#include "llvm/ADT/DepthFirstIterator.h"
13+
#include "llvm/ADT/SmallVector.h"
14+
#include "llvm/Analysis/LoopInfo.h"
15+
#include "llvm/Analysis/MemoryBuiltins.h"
16+
#include "llvm/Analysis/TargetLibraryInfo.h"
17+
#include "llvm/Analysis/TargetTransformInfo.h"
18+
#include "llvm/Analysis/ValueTracking.h"
19+
#include "llvm/IR/BasicBlock.h"
20+
#include "llvm/IR/Constant.h"
21+
#include "llvm/IR/Constants.h"
22+
#include "llvm/IR/DataLayout.h"
23+
#include "llvm/IR/DerivedTypes.h"
24+
#include "llvm/IR/Dominators.h"
25+
#include "llvm/IR/Function.h"
26+
#include "llvm/IR/GetElementPtrTypeIterator.h"
27+
#include "llvm/IR/IRBuilder.h"
28+
#include "llvm/IR/InstrTypes.h"
29+
#include "llvm/IR/Instruction.h"
30+
#include "llvm/IR/Instructions.h"
31+
#include "llvm/IR/Module.h"
32+
#include "llvm/IR/PassManager.h"
33+
#include "llvm/IR/PatternMatch.h"
34+
#include "llvm/IR/Type.h"
35+
#include "llvm/IR/User.h"
36+
#include "llvm/IR/Value.h"
37+
#include "llvm/InitializePasses.h"
38+
#include "llvm/Pass.h"
39+
#include "llvm/Support/Casting.h"
40+
#include "llvm/Support/CommandLine.h"
41+
#include "llvm/Support/ErrorHandling.h"
42+
#include "llvm/Support/raw_ostream.h"
43+
#include "llvm/Transforms/Scalar.h"
44+
#include "llvm/Transforms/Utils/Local.h"
45+
#include <cassert>
46+
#include <cstdint>
47+
#include <string>
48+
49+
using namespace llvm;
50+
using namespace llvm::PatternMatch;
51+
52+
static cl::opt<bool> DisableSinkGEPConstOffset(
53+
"disable-sink-gep-const-offset", cl::init(false),
54+
cl::desc("Do not sink the constant offset from a GEP instruction"),
55+
cl::Hidden);
56+
57+
namespace {
58+
59+
/// A pass that tries to sink const offset in GEP chain to tail.
60+
/// It is a FunctionPass because searching for the constant offset may inspect
61+
/// other basic blocks.
62+
class SinkGEPConstOffsetLegacyPass : public FunctionPass {
63+
public:
64+
static char ID;
65+
66+
SinkGEPConstOffsetLegacyPass() : FunctionPass(ID) {
67+
initializeSinkGEPConstOffsetLegacyPassPass(
68+
*PassRegistry::getPassRegistry());
69+
}
70+
71+
void getAnalysisUsage(AnalysisUsage &AU) const override {
72+
AU.setPreservesCFG();
73+
}
74+
75+
bool runOnFunction(Function &F) override;
76+
};
77+
78+
/// A pass that tries to sink const offset in GEP chain to tail.
79+
/// It is a FunctionPass because searching for the constant offset may inspect
80+
/// other basic blocks.
81+
class SinkGEPConstOffset {
82+
public:
83+
SinkGEPConstOffset() {}
84+
85+
bool run(Function &F);
86+
87+
private:
88+
/// Sink constant offset in a GEP chain to tail. For example,
89+
/// %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 512
90+
/// %gep1 = getelementptr half, ptr addrspace(3) %gep0, i32 %ofst0
91+
/// %gep2 = getelementptr half, ptr addrspace(3) %gep1, i32 %ofst1
92+
/// %data = load half, ptr addrspace(3) %gep2, align 2
93+
/// ==>
94+
/// %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 %ofst0
95+
/// %gep1 = getelementptr half, ptr addrspace(3) %gep0, i32 %ofst1
96+
/// %gep2 = getelementptr half, ptr addrspace(3) %gep1, i32 512
97+
/// %data = load half, ptr addrspace(3) %gep2, align 2
98+
///
99+
/// Return true if Ptr is a candidate for upper GEP in recursive calling.
100+
bool sinkGEPConstantOffset(Value *Ptr, bool &Changed);
101+
102+
const DataLayout *DL = nullptr;
103+
};
104+
105+
} // end anonymous namespace
106+
107+
char SinkGEPConstOffsetLegacyPass::ID = 0;
108+
109+
INITIALIZE_PASS_BEGIN(
110+
SinkGEPConstOffsetLegacyPass, "sink-gep-const-offset",
111+
"Sink const offsets down the GEP chain to the tail for reduction of "
112+
"register usage", false, false)
113+
INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass)
114+
INITIALIZE_PASS_END(
115+
SinkGEPConstOffsetLegacyPass, "sink-gep-const-offset",
116+
"Sink const offsets down the GEP chain to the tail for reduction of "
117+
"register usage", false, false)
118+
119+
FunctionPass *llvm::createSinkGEPConstOffsetPass() {
120+
return new SinkGEPConstOffsetLegacyPass();
121+
}
122+
123+
bool SinkGEPConstOffsetLegacyPass::runOnFunction(Function &F) {
124+
if (skipFunction(F))
125+
return false;
126+
127+
SinkGEPConstOffset Impl;
128+
return Impl.run(F);
129+
}
130+
131+
bool SinkGEPConstOffset::run(Function &F) {
132+
if (DisableSinkGEPConstOffset)
133+
return false;
134+
135+
DL = &F.getDataLayout();
136+
137+
bool Changed = false;
138+
for (BasicBlock &B : F)
139+
for (Instruction &I : llvm::make_early_inc_range(B))
140+
if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(&I))
141+
sinkGEPConstantOffset(GEP, Changed);
142+
143+
return Changed;
144+
}
145+
146+
bool SinkGEPConstOffset::sinkGEPConstantOffset(Value *Ptr, bool &Changed) {
147+
// The purpose of this function is to sink the constant offsets in the GEP
148+
// chain to the tail of the chain.
149+
// This algorithm is implemented recursively, the algorithm starts from the
150+
// tail of the chain through the DFS method and shifts the constant offset
151+
// of the GEP step by step upwards by bottom-up DFS method, i.e. step by step
152+
// down to the tail.
153+
// A simple example is given:
154+
/// %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 512
155+
/// %gep1 = getelementptr half, ptr addrspace(3) %gep0, i32 %ofst0
156+
/// %gep2 = getelementptr half, ptr addrspace(3) %gep1, i32 %ofst1
157+
/// %data = load half, ptr addrspace(3) %gep2, align 2
158+
/// ==>
159+
/// %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 %ofst0
160+
/// %gep1 = getelementptr half, ptr addrspace(3) %gep0, i32 %ofst1
161+
/// %gep2 = getelementptr half, ptr addrspace(3) %gep1, i32 512
162+
/// %data = load half, ptr addrspace(3) %gep2, align 2
163+
GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Ptr);
164+
if (!GEP)
165+
return false;
166+
167+
if (!GEP->getParent())
168+
return false;
169+
170+
bool BaseResult = sinkGEPConstantOffset(GEP->getPointerOperand(), Changed);
171+
172+
if (GEP->getNumIndices() != 1)
173+
return false;
174+
175+
ConstantInt *C = nullptr;
176+
Value *Idx = GEP->getOperand(1);
177+
bool MatchConstant = match(Idx, m_ConstantInt(C));
178+
179+
if (!BaseResult)
180+
return MatchConstant;
181+
182+
Type *ResTy = GEP->getResultElementType();
183+
GetElementPtrInst *BaseGEP =
184+
cast<GetElementPtrInst>(GEP->getPointerOperand());
185+
Value *BaseIdx = BaseGEP->getOperand(1);
186+
Type *BaseResTy = BaseGEP->getResultElementType();
187+
188+
if (MatchConstant) {
189+
// %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 8
190+
// %gep1 = getelementptr half, ptr addrspace(3) %gep0, i32 4
191+
// as:
192+
// %gep1 = getelementptr half, ptr addrspace(3) %ptr, i32 12
193+
Type *NewResTy = nullptr;
194+
int64_t NewIdxValue = 0;
195+
if (ResTy == BaseResTy) {
196+
NewResTy = ResTy;
197+
NewIdxValue = cast<ConstantInt>(BaseIdx)->getSExtValue() +
198+
cast<ConstantInt>(Idx)->getSExtValue();
199+
} else {
200+
NewResTy = Type::getInt8Ty(GEP->getContext());
201+
NewIdxValue = (cast<ConstantInt>(BaseIdx)->getSExtValue() *
202+
DL->getTypeAllocSize(BaseResTy)) +
203+
(cast<ConstantInt>(Idx)->getSExtValue() *
204+
DL->getTypeAllocSize(ResTy));
205+
}
206+
assert(NewResTy);
207+
Type *NewIdxType = (Idx->getType()->getPrimitiveSizeInBits() >
208+
BaseIdx->getType()->getPrimitiveSizeInBits())
209+
? Idx->getType() : BaseIdx->getType();
210+
Constant *NewIdx = ConstantInt::get(NewIdxType, NewIdxValue);
211+
auto *NewGEP = GetElementPtrInst::Create(
212+
NewResTy, BaseGEP->getPointerOperand(), NewIdx);
213+
NewGEP->setIsInBounds(GEP->isInBounds());
214+
NewGEP->insertBefore(GEP->getIterator());
215+
NewGEP->takeName(GEP);
216+
217+
GEP->replaceAllUsesWith(NewGEP);
218+
RecursivelyDeleteTriviallyDeadInstructions(GEP);
219+
220+
Changed = true;
221+
return true;
222+
}
223+
224+
// %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 8
225+
// %gep1 = getelementptr half, ptr addrspace(3) %gep0, i32 %idx
226+
// as:
227+
// %gepx0 = getelementptr half, ptr addrspace(3) %ptr, i32 %idx
228+
// %gepx1 = getelementptr half, ptr addrspace(3) %gepx0, i32 8
229+
auto *GEPX0 =
230+
GetElementPtrInst::Create(ResTy, BaseGEP->getPointerOperand(), Idx);
231+
GEPX0->setIsInBounds(BaseGEP->isInBounds());
232+
GEPX0->insertBefore(GEP->getIterator());
233+
auto *GEPX1 = GetElementPtrInst::Create(BaseResTy, GEPX0, BaseIdx);
234+
GEPX1->setIsInBounds(GEP->isInBounds());
235+
GEPX1->insertBefore(GEP->getIterator());
236+
GEPX1->takeName(GEP);
237+
238+
GEP->replaceAllUsesWith(GEPX1);
239+
RecursivelyDeleteTriviallyDeadInstructions(GEP);
240+
241+
Changed = true;
242+
return true;
243+
}
244+
245+
void SinkGEPConstOffsetPass::printPipeline(
246+
raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {
247+
static_cast<PassInfoMixin<SinkGEPConstOffsetPass> *>(this)
248+
->printPipeline(OS, MapClassName2PassName);
249+
}
250+
251+
PreservedAnalyses
252+
SinkGEPConstOffsetPass::run(Function &F, FunctionAnalysisManager &AM) {
253+
SinkGEPConstOffset Impl;
254+
if (!Impl.run(F))
255+
return PreservedAnalyses::all();
256+
257+
PreservedAnalyses PA;
258+
PA.preserveSet<CFGAnalyses>();
259+
return PA;
260+
}

0 commit comments

Comments
 (0)