52#include <llvm/Config/llvm-config.h>
54#include "llvm/ADT/APInt.h"
55#include "llvm/ADT/DenseMap.h"
56#include "llvm/ADT/MapVector.h"
57#include "llvm/ADT/SmallPtrSet.h"
58#include "llvm/ADT/SmallVector.h"
60#include "llvm/IR/BasicBlock.h"
61#include "llvm/IR/Constants.h"
62#include "llvm/IR/DataLayout.h"
63#include "llvm/IR/Dominators.h"
65#include "llvm/IR/CFG.h"
66#include "llvm/IR/Function.h"
67#include "llvm/IR/GetElementPtrTypeIterator.h"
68#include "llvm/IR/IRBuilder.h"
69#include "llvm/IR/InstrTypes.h"
70#include "llvm/IR/Instruction.h"
71#include "llvm/IR/Instructions.h"
72#include "llvm/IR/Value.h"
74#include "llvm/Analysis/AliasAnalysis.h"
75#include "llvm/Analysis/MemoryLocation.h"
76#include "llvm/Analysis/TargetLibraryInfo.h"
77#include "llvm/Analysis/ValueTracking.h"
79#include "llvm/IR/LegacyPassManager.h"
81#include "llvm/Support/Debug.h"
82#include "llvm/Support/raw_ostream.h"
84#include "llvm/Transforms/Utils/Local.h"
94#define DEBUG_TYPE "simple-gvn"
99Value *extractValue(IRBuilder<> &Builder, Value *StoredVal, Type *LoadType,
100 const DataLayout &DL, APInt LoadOffset, APInt StoreOffset,
102 Type *StoreType = StoredVal->getType();
103 uint64_t StoreSize = DL.getTypeStoreSize(StoreType);
106 int64_t RelativeOffset = (LoadOffset - StoreOffset).getSExtValue();
109 if (RelativeOffset < 0 || (uint64_t)RelativeOffset + LoadSize > StoreSize) {
114 if (RelativeOffset == 0 && LoadType == StoreType) {
118 if (RelativeOffset == 0 && isa<PointerType>(LoadType) &&
119 isa<PointerType>(StoreType)) {
120 return Builder.CreatePointerCast(StoredVal, LoadType);
123 if (RelativeOffset == 0 && StoreSize >= LoadSize &&
124 StoreType->isAggregateType()) {
125 auto first = Builder.CreateExtractValue(StoredVal, 0);
126 auto res = extractValue(Builder, first, LoadType, DL, LoadOffset,
127 StoreOffset, LoadSize);
131 if (
auto I = dyn_cast<Instruction>(first))
132 I->eraseFromParent();
138 if (!StoreType->isIntegerTy()) {
139 IntegerType *IntTy = Builder.getIntNTy(StoreSize * 8);
140 if (!CastInst::castIsValid(Instruction::BitCast, StoredVal->getType(),
144 StoredVal = Builder.CreateBitCast(StoredVal, IntTy);
148 if (RelativeOffset > 0) {
149 uint64_t ShiftBits = RelativeOffset * 8;
150 StoredVal = Builder.CreateLShr(StoredVal, ShiftBits);
154 IntegerType *LoadIntTy = Builder.getIntNTy(LoadSize * 8);
155 if (StoredVal->getType() != LoadIntTy) {
156 StoredVal = Builder.CreateTrunc(StoredVal, LoadIntTy);
160 if (LoadIntTy != LoadType) {
161 if (LoadType->isPointerTy()) {
162 StoredVal = Builder.CreateIntToPtr(StoredVal, LoadType);
164 if (!CastInst::castIsValid(Instruction::BitCast, StoredVal->getType(),
168 StoredVal = Builder.CreateBitCast(StoredVal, LoadType);
179static bool dominatesAndCovers(Instruction *Source, Instruction *Target,
180 const APInt &SourceOffset,
181 const APInt &TargetOffset, uint64_t TargetSize,
182 const DataLayout &DL, DominatorTree &DT) {
183 if (!DT.dominates(Source, Target))
188 if (
auto *SI = dyn_cast<StoreInst>(Source)) {
189 SourceSize = DL.getTypeStoreSize(SI->getValueOperand()->getType());
190 }
else if (
auto *LI = dyn_cast<LoadInst>(Source)) {
191 SourceSize = DL.getTypeStoreSize(LI->getType());
196 int64_t RelOffset = (TargetOffset - SourceOffset).getSExtValue();
197 return RelOffset >= 0 && (uint64_t)RelOffset + TargetSize <= SourceSize;
203static bool memoryRangesAlias(
const APInt &Offset1, uint64_t Size1,
204 const APInt &Offset2, uint64_t Size2) {
206 if ((Offset2 + Size2).sle(Offset1))
210 if ((Offset1 + Size1).sle(Offset2))
222collectMemoryOps(Value *Arg,
const DataLayout &DL,
223 SmallVectorImpl<std::pair<StoreInst *, APInt>> &Stores,
224 SmallVectorImpl<std::pair<LoadInst *, APInt>> &Loads,
225 SmallVectorImpl<std::pair<CallInst *, APInt>> &Calls) {
227 SmallVector<std::pair<Value *, APInt>, 16> ToProcess;
228 SmallPtrSet<Value *, 16> Visited;
230 APInt ZeroOffset(DL.getIndexTypeSizeInBits(Arg->getType()), 0);
231 ToProcess.push_back({Arg, ZeroOffset});
233 while (!ToProcess.empty()) {
234 auto [V, CurrentOffset] = ToProcess.pop_back_val();
237 if (!Visited.insert(V).second)
240 for (Use &U : V->uses()) {
241 User *Usr = U.getUser();
242 if (
auto *LI = dyn_cast<LoadInst>(Usr)) {
243 Loads.push_back({LI, CurrentOffset});
244 }
else if (
auto *SI = dyn_cast<StoreInst>(Usr)) {
247 if (SI->getPointerOperand() == V) {
248 Stores.push_back({SI, CurrentOffset});
253 }
else if (
auto *GEP = dyn_cast<GetElementPtrInst>(Usr)) {
255 APInt GEPOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0);
256 if (!GEP->accumulateConstantOffset(DL, GEPOffset)) {
261 APInt NewOffset = CurrentOffset + GEPOffset;
262 ToProcess.push_back({GEP, NewOffset});
263 }
else if (
auto *CI = dyn_cast<CastInst>(Usr)) {
265 ToProcess.push_back({CI, CurrentOffset});
266 }
else if (
auto *
Call = dyn_cast<CallInst>(Usr)) {
268 unsigned ArgIdx = U.getOperandNo();
270 Calls.push_back({
Call, CurrentOffset});
286bool simplifyGVN(Function &F, DominatorTree &DT,
const DataLayout &DL) {
287 bool Changed =
false;
290 SmallVector<Value *, 4> CandidateArgs;
291 for (Argument &Arg : F.args()) {
292 if (Arg.getType()->isPointerTy() && Arg.hasNoAliasAttr()) {
293 CandidateArgs.push_back(&Arg);
297 for (BasicBlock &BB : F) {
298 for (Instruction &I : BB) {
299 if (isa<AllocaInst>(&I)) {
300 CandidateArgs.push_back(&I);
305 if (CandidateArgs.empty())
309 for (Value *Arg : CandidateArgs) {
311 SmallVector<std::pair<StoreInst *, APInt>, 8> Stores;
312 SmallVector<std::pair<LoadInst *, APInt>, 8> Loads;
313 SmallVector<std::pair<CallInst *, APInt>, 8> Calls;
317 if (!collectMemoryOps(Arg, DL, Stores, Loads, Calls)) {
322 APInt ZeroOffset(DL.getIndexTypeSizeInBits(Arg->getType()), 0);
326 for (
auto &[LI, LoadOffset] : Loads) {
327 uint64_t LoadSize = DL.getTypeStoreSize(LI->getType());
330 SmallVector<std::tuple<Instruction *, APInt, uint64_t>, 8> AliasingStores;
331 for (
auto &[SI, StoreOffset] : Stores) {
333 DL.getTypeStoreSize(SI->getValueOperand()->getType());
334 if (memoryRangesAlias(LoadOffset, LoadSize, StoreOffset, StoreSize)) {
335 AliasingStores.push_back({SI, StoreOffset, StoreSize});
341 for (
auto &[CI, CallOffset] : Calls) {
342 AliasingStores.push_back({CI, LoadOffset, LoadSize});
348 SmallVector<std::tuple<Instruction *, APInt, Value *>, 8>
349 DominatingCoveringStores;
350 for (
auto &[I, StoreOffset, StoreSize] : AliasingStores) {
351 if (
auto SI = dyn_cast<StoreInst>(I))
352 if (dominatesAndCovers(SI, LI, StoreOffset, LoadOffset, LoadSize, DL,
354 DominatingCoveringStores.push_back(
355 {SI, StoreOffset, SI->getValueOperand()});
361 if (AliasingStores.size() == 1 && DominatingCoveringStores.size() == 1) {
362 Instruction *SI = std::get<0>(DominatingCoveringStores[0]);
363 APInt StoreOffset = std::get<1>(DominatingCoveringStores[0]);
365 IRBuilder<> Builder(LI);
366 Value *StoredVal = std::get<2>(DominatingCoveringStores[0]);
367 Value *ExtractedVal =
368 extractValue(Builder, StoredVal, LI->getType(), DL, LoadOffset,
369 StoreOffset, LoadSize);
372 LLVM_DEBUG(dbgs() <<
"SimpleGVN: Forwarding (single alias)\n"
373 <<
" Store: " << *SI <<
"\n"
374 <<
" Load: " << *LI <<
"\n");
375 LI->replaceAllUsesWith(ExtractedVal);
376 LI->eraseFromParent();
383 for (
auto &[LI2, LoadOffset2] : Loads) {
384 if (!LI2 || LI2 == LI)
386 if (dominatesAndCovers(LI2, LI, LoadOffset2, LoadOffset, LoadSize, DL,
388 DominatingCoveringStores.emplace_back(LI2, LoadOffset2, LI2);
393 if (DominatingCoveringStores.empty()) {
398 DenseMap<BasicBlock *, std::tuple<Instruction *, APInt, uint64_t>>
399 LastStoreInBlockBeforeLI;
400 for (
auto &[SI, StoreOffset, Size] : AliasingStores) {
401 BasicBlock *BB = SI->getParent();
402 if (BB == LI->getParent()) {
404 if (SI->comesBefore(LI)) {
405 auto &Entry = LastStoreInBlockBeforeLI[BB];
406 if (!std::get<0>(Entry) || std::get<0>(Entry)->comesBefore(SI)) {
407 Entry = {SI, StoreOffset, Size};
412 auto &Entry = LastStoreInBlockBeforeLI[BB];
413 if (!std::get<0>(Entry) || std::get<0>(Entry)->comesBefore(SI)) {
414 Entry = {SI, StoreOffset, Size};
420 BasicBlock *LIBlock = LI->getParent();
421 auto It = LastStoreInBlockBeforeLI.find(LIBlock);
422 if (It != LastStoreInBlockBeforeLI.end()) {
423 Instruction *SI = std::get<0>(It->second);
425 for (
auto &&[DCS, StoreOffset, StoredVal] : DominatingCoveringStores) {
427 (DCS->getParent() == LI->getParent() && SI->comesBefore(DCS))) {
429 IRBuilder<> Builder(LI);
430 Value *ExtractedVal =
431 extractValue(Builder, StoredVal, LI->getType(), DL, LoadOffset,
432 StoreOffset, LoadSize);
435 LLVM_DEBUG(dbgs() <<
"SimpleGVN: Forwarding (same block)\n"
436 <<
" Store: " << *DCS <<
"\n"
437 <<
" Load: " << *LI <<
"\n");
438 LI->replaceAllUsesWith(ExtractedVal);
439 LI->eraseFromParent();
448 for (
auto &&[DCS, StoreOffset, StoredVal] : DominatingCoveringStores) {
449 if (DCS->getParent() == LI->getParent()) {
451 IRBuilder<> Builder(LI);
452 Value *ExtractedVal =
453 extractValue(Builder, StoredVal, LI->getType(), DL, LoadOffset,
454 StoreOffset, LoadSize);
457 LLVM_DEBUG(dbgs() <<
"SimpleGVN: Forwarding (same block)\n"
458 <<
" Store: " << *DCS <<
"\n"
459 <<
" Load: " << *LI <<
"\n");
460 LI->replaceAllUsesWith(ExtractedVal);
461 LI->eraseFromParent();
474 SmallPtrSet<BasicBlock *, 32> Visited;
475 SmallVector<BasicBlock *, 16> Worklist;
476 StoreInst *Candidate =
nullptr;
477 APInt CandidateOffset = ZeroOffset;
480 for (BasicBlock *Pred : predecessors(LIBlock)) {
481 if (Visited.insert(Pred).second)
482 Worklist.push_back(Pred);
485 while (!Worklist.empty()) {
486 BasicBlock *BB = Worklist.pop_back_val();
488 auto It = LastStoreInBlockBeforeLI.find(BB);
489 if (It != LastStoreInBlockBeforeLI.end()) {
490 StoreInst *SI = dyn_cast<StoreInst>(std::get<0>(It->second));
491 APInt StoreOffset = std::get<1>(It->second);
493 if (!SI || !dominatesAndCovers(SI, LI, StoreOffset, LoadOffset,
503 CandidateOffset = StoreOffset;
504 }
else if (Candidate != SI) {
512 for (BasicBlock *Pred : predecessors(BB)) {
513 if (Visited.insert(Pred).second)
514 Worklist.push_back(Pred);
520 IRBuilder<> Builder(LI);
521 Value *StoredVal = Candidate->getValueOperand();
522 Value *ExtractedVal =
523 extractValue(Builder, StoredVal, LI->getType(), DL, LoadOffset,
524 CandidateOffset, LoadSize);
527 LLVM_DEBUG(dbgs() <<
"SimpleGVN: Forwarding (BFS candidate)\n"
528 <<
" Store: " << *Candidate <<
"\n"
529 <<
" Load: " << *LI <<
"\n");
530 LI->replaceAllUsesWith(ExtractedVal);
531 LI->eraseFromParent();
541class SimpleGVN final :
public FunctionPass {
544 SimpleGVN() : FunctionPass(ID) {}
546 void getAnalysisUsage(AnalysisUsage &AU)
const override {
547 AU.addRequired<DominatorTreeWrapperPass>();
550 bool runOnFunction(Function &F)
override {
551 auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
552 const DataLayout &DL = F.getParent()->getDataLayout();
553 return simplifyGVN(F, DT, DL);
565char SimpleGVN::ID = 0;
567static RegisterPass<SimpleGVN>
X(
"simple-gvn",
568 "GVN-like load forwarding optimization");
571 FunctionAnalysisManager &FAM) {
572 bool Changed =
false;
573 const DataLayout &DL = F.getParent()->getDataLayout();
574 Changed = simplifyGVN(F, FAM.getResult<DominatorTreeAnalysis>(F), DL);
575 return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
578llvm::AnalysisKey SimpleGVNNewPM::Key;
static RegisterPass< SimpleGVN > X("simple-gvn", "GVN-like load forwarding optimization")
FunctionPass * createSimpleGVNPass()
void LLVMAddSimpleGVNPass(LLVMPassManagerRef PM)
static bool isNoCapture(const llvm::CallBase *call, size_t idx)
llvm::PreservedAnalyses Result
Result run(llvm::Function &F, llvm::FunctionAnalysisManager &FAM)