|
| 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 | +namespace { |
| 53 | + |
| 54 | +/// A pass that tries to sink const offset in GEP chain to tail. |
| 55 | +/// It is a FunctionPass because searching for the constant offset may inspect |
| 56 | +/// other basic blocks. |
| 57 | +class SinkGEPConstOffsetLegacyPass : public FunctionPass { |
| 58 | +public: |
| 59 | + static char ID; |
| 60 | + |
| 61 | + SinkGEPConstOffsetLegacyPass() : FunctionPass(ID) { |
| 62 | + initializeSinkGEPConstOffsetLegacyPassPass( |
| 63 | + *PassRegistry::getPassRegistry()); |
| 64 | + } |
| 65 | + |
| 66 | + void getAnalysisUsage(AnalysisUsage &AU) const override { |
| 67 | + AU.setPreservesCFG(); |
| 68 | + } |
| 69 | + |
| 70 | + bool runOnFunction(Function &F) override; |
| 71 | +}; |
| 72 | + |
| 73 | +} // end anonymous namespace |
| 74 | + |
| 75 | +char SinkGEPConstOffsetLegacyPass::ID = 0; |
| 76 | + |
| 77 | +INITIALIZE_PASS_BEGIN( |
| 78 | + SinkGEPConstOffsetLegacyPass, "sink-gep-const-offset", |
| 79 | + "Sink const offsets down the GEP chain to the tail for reduction of " |
| 80 | + "register usage", false, false) |
| 81 | +INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass) |
| 82 | +INITIALIZE_PASS_END( |
| 83 | + SinkGEPConstOffsetLegacyPass, "sink-gep-const-offset", |
| 84 | + "Sink const offsets down the GEP chain to the tail for reduction of " |
| 85 | + "register usage", false, false) |
| 86 | + |
| 87 | +FunctionPass *llvm::createSinkGEPConstOffsetPass() { |
| 88 | + return new SinkGEPConstOffsetLegacyPass(); |
| 89 | +} |
| 90 | + |
| 91 | +/// The purpose of this function is to sink the constant offsets in the base |
| 92 | +/// GEP to current GEP. |
| 93 | +/// |
| 94 | +/// A simple example is given: |
| 95 | +/// |
| 96 | +/// %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 512 |
| 97 | +/// %gep1 = getelementptr half, ptr addrspace(3) %gep0, i32 %ofst0 |
| 98 | +/// %data = load half, ptr addrspace(3) %gep1, align 2 |
| 99 | +/// ==> |
| 100 | +/// %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 %ofst0 |
| 101 | +/// %gep1 = getelementptr half, ptr addrspace(3) %gep0, i32 512 |
| 102 | +/// %data = load half, ptr addrspace(3) %gep1, align 2 |
| 103 | +static bool sinkGEPConstantOffset(Value *Ptr, const DataLayout *DL) { |
| 104 | + GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Ptr); |
| 105 | + if (!GEP) |
| 106 | + return false; |
| 107 | + |
| 108 | + if (GEP->getNumIndices() != 1) |
| 109 | + return false; |
| 110 | + |
| 111 | + GetElementPtrInst *BaseGEP = |
| 112 | + dyn_cast<GetElementPtrInst>(GEP->getPointerOperand()); |
| 113 | + if (!BaseGEP) |
| 114 | + return false; |
| 115 | + |
| 116 | + if (BaseGEP->getNumIndices() != 1) |
| 117 | + return false; |
| 118 | + |
| 119 | + Value *Idx = GEP->getOperand(1); |
| 120 | + Value *BaseIdx = BaseGEP->getOperand(1); |
| 121 | + |
| 122 | + ConstantInt *BaseIdxAsCI = dyn_cast<ConstantInt>(BaseIdx); |
| 123 | + if (!BaseIdxAsCI) |
| 124 | + return false; |
| 125 | + |
| 126 | + Type *ResTy = GEP->getResultElementType(); |
| 127 | + Type *BaseResTy = BaseGEP->getResultElementType(); |
| 128 | + |
| 129 | + ConstantInt *IdxAsCI = dyn_cast<ConstantInt>(Idx); |
| 130 | + if (IdxAsCI) { |
| 131 | + // %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 8 |
| 132 | + // %gep1 = getelementptr half, ptr addrspace(3) %gep0, i32 4 |
| 133 | + // as: |
| 134 | + // %gep1 = getelementptr half, ptr addrspace(3) %ptr, i32 12 |
| 135 | + Type *NewResTy = nullptr; |
| 136 | + int64_t NewIdxValue = 0; |
| 137 | + if (ResTy == BaseResTy) { |
| 138 | + NewResTy = ResTy; |
| 139 | + NewIdxValue = BaseIdxAsCI->getSExtValue() + IdxAsCI->getSExtValue(); |
| 140 | + } else { |
| 141 | + NewResTy = Type::getInt8Ty(GEP->getContext()); |
| 142 | + NewIdxValue = (BaseIdxAsCI->getSExtValue() * |
| 143 | + DL->getTypeAllocSize(BaseResTy)) + |
| 144 | + (IdxAsCI->getSExtValue() * |
| 145 | + DL->getTypeAllocSize(ResTy)); |
| 146 | + } |
| 147 | + assert(NewResTy); |
| 148 | + Type *NewIdxType = (Idx->getType()->getPrimitiveSizeInBits() > |
| 149 | + BaseIdx->getType()->getPrimitiveSizeInBits()) |
| 150 | + ? Idx->getType() : BaseIdx->getType(); |
| 151 | + Constant *NewIdx = ConstantInt::get(NewIdxType, NewIdxValue); |
| 152 | + auto *NewGEP = GetElementPtrInst::Create( |
| 153 | + NewResTy, BaseGEP->getPointerOperand(), NewIdx); |
| 154 | + NewGEP->setIsInBounds(GEP->isInBounds()); |
| 155 | + NewGEP->insertBefore(GEP->getIterator()); |
| 156 | + NewGEP->takeName(GEP); |
| 157 | + |
| 158 | + GEP->replaceAllUsesWith(NewGEP); |
| 159 | + RecursivelyDeleteTriviallyDeadInstructions(GEP); |
| 160 | + |
| 161 | + return true; |
| 162 | + } |
| 163 | + |
| 164 | + // %gep0 = getelementptr half, ptr addrspace(3) %ptr, i32 8 |
| 165 | + // %gep1 = getelementptr half, ptr addrspace(3) %gep0, i32 %idx |
| 166 | + // as: |
| 167 | + // %gepx0 = getelementptr half, ptr addrspace(3) %ptr, i32 %idx |
| 168 | + // %gepx1 = getelementptr half, ptr addrspace(3) %gepx0, i32 8 |
| 169 | + auto *GEPX0 = |
| 170 | + GetElementPtrInst::Create(ResTy, BaseGEP->getPointerOperand(), Idx); |
| 171 | + GEPX0->insertBefore(GEP->getIterator()); |
| 172 | + auto *GEPX1 = GetElementPtrInst::Create(BaseResTy, GEPX0, BaseIdx); |
| 173 | + GEPX1->setIsInBounds(GEP->isInBounds()); |
| 174 | + GEPX1->insertBefore(GEP->getIterator()); |
| 175 | + GEPX1->takeName(GEP); |
| 176 | + |
| 177 | + GEP->replaceAllUsesWith(GEPX1); |
| 178 | + RecursivelyDeleteTriviallyDeadInstructions(GEP); |
| 179 | + |
| 180 | + return true; |
| 181 | +} |
| 182 | + |
| 183 | +static bool sinkGEPConstantOffset(Function &F) { |
| 184 | + const DataLayout *DL = &F.getDataLayout(); |
| 185 | + ReversePostOrderTraversal<Function*> RPOT(&F); |
| 186 | + bool Changed = false; |
| 187 | + for (auto *BB : RPOT) |
| 188 | + for (Instruction &I : llvm::make_early_inc_range(*BB)) |
| 189 | + if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(&I)) |
| 190 | + Changed |= sinkGEPConstantOffset(GEP, DL); |
| 191 | + |
| 192 | + return Changed; |
| 193 | +} |
| 194 | + |
| 195 | +bool SinkGEPConstOffsetLegacyPass::runOnFunction(Function &F) { |
| 196 | + if (skipFunction(F)) |
| 197 | + return false; |
| 198 | + |
| 199 | + return sinkGEPConstantOffset(F); |
| 200 | +} |
| 201 | + |
| 202 | +void SinkGEPConstOffsetPass::printPipeline( |
| 203 | + raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) { |
| 204 | + static_cast<PassInfoMixin<SinkGEPConstOffsetPass> *>(this) |
| 205 | + ->printPipeline(OS, MapClassName2PassName); |
| 206 | +} |
| 207 | + |
| 208 | +PreservedAnalyses |
| 209 | +SinkGEPConstOffsetPass::run(Function &F, FunctionAnalysisManager &AM) { |
| 210 | + if (!sinkGEPConstantOffset(F)) |
| 211 | + return PreservedAnalyses::all(); |
| 212 | + |
| 213 | + PreservedAnalyses PA; |
| 214 | + PA.preserveSet<CFGAnalyses>(); |
| 215 | + return PA; |
| 216 | +} |
0 commit comments