Enzyme main
Loading...
Searching...
No Matches
SimpleGVN.cpp
Go to the documentation of this file.
1//=- SimpleGVN.cpp - GVN-like load forwarding optimization ============//
2//
3// Enzyme Project
4//
5// Part of the Enzyme Project, under the Apache License v2.0 with LLVM
6// Exceptions. See https://llvm.org/LICENSE.txt for license information.
7// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8//
9// If using this code in an academic setting, please cite the following:
10// @incollection{enzymeNeurips,
11// title = {Instead of Rewriting Foreign Code for Machine Learning,
12// Automatically Synthesize Fast Gradients},
13// author = {Moses, William S. and Churavy, Valentin},
14// booktitle = {Advances in Neural Information Processing Systems 33},
15// year = {2020},
16// note = {To appear in},
17// }
18//
19//===----------------------------------------------------------------------===//
20//
21// This file contains a GVN-like optimization pass that forwards loads from
22// noalias/nocapture arguments to their corresponding stores, with support
23// for offsets and type conversions.
24//
25// This pass addresses the limitation of LLVM's built-in GVN pass which has
26// a small limit on the number of instructions/memory offsets it analyzes
27// via its use of the memdep analysis.
28//
29// Algorithm:
30// 1. Identify function arguments with noalias and nocapture attributes
31// 2. Verify all uses are exclusively loads, stores, or GEP instructions
32// 3. For each load from such an argument:
33// a. Find all stores to the argument with constant offsets
34// b. Find a dominating store that covers the load's memory range
35// c. Check that no aliasing store exists between the store and load
36// d. If safe, replace the load with the stored value, performing
37// type conversion or extraction as needed
38//
39// Example transformation:
40// define i32 @foo(i32* noalias nocapture %ptr) {
41// store i32 42, i32* %ptr
42// %v = load i32, i32* %ptr
43// ret i32 %v
44// }
45// becomes:
46// define i32 @foo(i32* noalias nocapture %ptr) {
47// store i32 42, i32* %ptr
48// ret i32 42
49// }
50//
51//===----------------------------------------------------------------------===//
52#include <llvm/Config/llvm-config.h>
53
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"
59
60#include "llvm/IR/BasicBlock.h"
61#include "llvm/IR/Constants.h"
62#include "llvm/IR/DataLayout.h"
63#include "llvm/IR/Dominators.h"
64
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"
73
74#include "llvm/Analysis/AliasAnalysis.h"
75#include "llvm/Analysis/MemoryLocation.h"
76#include "llvm/Analysis/TargetLibraryInfo.h"
77#include "llvm/Analysis/ValueTracking.h"
78
79#include "llvm/IR/LegacyPassManager.h"
80
81#include "llvm/Support/Debug.h"
82#include "llvm/Support/raw_ostream.h"
83
84#include "llvm/Transforms/Utils/Local.h"
85
86#include "SimpleGVN.h"
87#include "Utils.h"
88
89using namespace llvm;
90
91#ifdef DEBUG_TYPE
92#undef DEBUG_TYPE
93#endif
94#define DEBUG_TYPE "simple-gvn"
95
96namespace {
97
98// Extract a value with potential type conversion
99Value *extractValue(IRBuilder<> &Builder, Value *StoredVal, Type *LoadType,
100 const DataLayout &DL, APInt LoadOffset, APInt StoreOffset,
101 uint64_t LoadSize) {
102 Type *StoreType = StoredVal->getType();
103 uint64_t StoreSize = DL.getTypeStoreSize(StoreType);
104
105 // Calculate relative offset
106 int64_t RelativeOffset = (LoadOffset - StoreOffset).getSExtValue();
107
108 // Check if the load is completely within the stored value
109 if (RelativeOffset < 0 || (uint64_t)RelativeOffset + LoadSize > StoreSize) {
110 return nullptr;
111 }
112
113 // If types match and offsets are the same, return directly
114 if (RelativeOffset == 0 && LoadType == StoreType) {
115 return StoredVal;
116 }
117
118 if (RelativeOffset == 0 && isa<PointerType>(LoadType) &&
119 isa<PointerType>(StoreType)) {
120 auto FromAS = cast<PointerType>(StoreType)->getAddressSpace();
121 auto ToAS = cast<PointerType>(LoadType)->getAddressSpace();
122 if (FromAS == 10 && ToAS == 0) {
123 Module *mod = Builder.GetInsertBlock()->getModule();
124 Function *F = mod->getFunction("julia.pointer_from_objref");
125 if (!F) {
126 Type *T_jlvalue = StructType::get(mod->getContext());
127 Type *T_prjlvalue = getPointerType(T_jlvalue, 11); // Derived is 11
128 Type *T_pjlvalue = getPointerType(T_jlvalue, 0);
129 FunctionType *FTy = FunctionType::get(T_pjlvalue, {T_prjlvalue}, false);
130 F = Function::Create(FTy, Function::ExternalLinkage,
131 "julia.pointer_from_objref", mod);
132 }
133
134 Type *T_jlvalue = StructType::get(mod->getContext());
135 Value *TrackedVal =
136 Builder.CreateAddrSpaceCast(StoredVal, getPointerType(T_jlvalue, 11));
137 Value *RawPtr = Builder.CreateCall(F, {TrackedVal});
138 return Builder.CreatePointerCast(RawPtr, LoadType);
139 }
140 return Builder.CreatePointerCast(StoredVal, LoadType);
141 }
142
143 if (RelativeOffset == 0 && StoreSize >= LoadSize &&
144 StoreType->isAggregateType()) {
145 auto first = Builder.CreateExtractValue(StoredVal, 0);
146 auto res = extractValue(Builder, first, LoadType, DL, LoadOffset,
147 StoreOffset, LoadSize);
148 if (res) {
149 return res;
150 } else {
151 if (auto I = dyn_cast<Instruction>(first))
152 I->eraseFromParent();
153 }
154 }
155
156 // Handle extraction with offset or type mismatch
157 // First, bitcast to an integer type if needed
158 if (!StoreType->isIntegerTy()) {
159 IntegerType *IntTy = Builder.getIntNTy(StoreSize * 8);
160 if (!CastInst::castIsValid(Instruction::BitCast, StoredVal->getType(),
161 IntTy)) {
162 return nullptr;
163 }
164 StoredVal = Builder.CreateBitCast(StoredVal, IntTy);
165 }
166
167 // Extract the relevant bits if there's an offset
168 if (RelativeOffset > 0) {
169 uint64_t ShiftBits = RelativeOffset * 8;
170 StoredVal = Builder.CreateLShr(StoredVal, ShiftBits);
171 }
172
173 // Truncate or extend to the load size if needed
174 IntegerType *LoadIntTy = Builder.getIntNTy(LoadSize * 8);
175 if (StoredVal->getType() != LoadIntTy) {
176 unsigned StoredWidth = StoredVal->getType()->getIntegerBitWidth();
177 unsigned LoadWidth = LoadIntTy->getIntegerBitWidth();
178 if (StoredWidth > LoadWidth) {
179 StoredVal = Builder.CreateTrunc(StoredVal, LoadIntTy);
180 } else if (StoredWidth < LoadWidth) {
181 StoredVal = Builder.CreateZExt(StoredVal, LoadIntTy);
182 }
183 }
184
185 // Bitcast to the final type if needed
186 if (LoadIntTy != LoadType) {
187 if (LoadType->isPointerTy()) {
188 if (cast<PointerType>(LoadType)->getAddressSpace() == 10) {
189 Type *EmptyStructTy = StructType::get(LoadType->getContext());
190 Type *PtrAddrSpace0 = getPointerType(EmptyStructTy, 0);
191 Value *Ptr0 = Builder.CreateIntToPtr(StoredVal, PtrAddrSpace0);
192 StoredVal = Builder.CreateAddrSpaceCast(Ptr0, LoadType);
193 } else {
194 StoredVal = Builder.CreateIntToPtr(StoredVal, LoadType);
195 }
196 } else {
197 if (!CastInst::castIsValid(Instruction::BitCast, StoredVal->getType(),
198 LoadType)) {
199 return nullptr;
200 }
201 StoredVal = Builder.CreateBitCast(StoredVal, LoadType);
202 }
203 }
204
205 return StoredVal;
206}
207
208// Helper to check if a source instruction dominates and completely covers a
209// target instruction's memory access
210// For stores: checks if store covers a load
211// For loads: checks if load covers another load
212static bool dominatesAndCovers(Instruction *Source, Instruction *Target,
213 const APInt &SourceOffset,
214 const APInt &TargetOffset, uint64_t TargetSize,
215 const DataLayout &DL, DominatorTree &DT) {
216 if (!DT.dominates(Source, Target))
217 return false;
218
219 // Get the size of the source memory access
220 uint64_t SourceSize;
221 if (auto *SI = dyn_cast<StoreInst>(Source)) {
222 SourceSize = DL.getTypeStoreSize(SI->getValueOperand()->getType());
223 } else if (auto *LI = dyn_cast<LoadInst>(Source)) {
224 SourceSize = DL.getTypeStoreSize(LI->getType());
225 } else {
226 return false;
227 }
228
229 int64_t RelOffset = (TargetOffset - SourceOffset).getSExtValue();
230 return RelOffset >= 0 && (uint64_t)RelOffset + TargetSize <= SourceSize;
231}
232
233// Helper to check if two memory ranges alias
234// Range1: [Offset1, Offset1 + Size1)
235// Range2: [Offset2, Offset2 + Size2)
236static bool memoryRangesAlias(const APInt &Offset1, uint64_t Size1,
237 const APInt &Offset2, uint64_t Size2) {
238 // Check if range2 ends before range1 begins
239 if ((Offset2 + Size2).sle(Offset1))
240 return false;
241
242 // Check if range1 ends before range2 begins
243 if ((Offset1 + Size1).sle(Offset2))
244 return false;
245
246 // Otherwise, they may alias
247 return true;
248}
249
250// Collect memory operations (loads, stores) and calls for a given pointer value
251// Returns false if the value has uses that prevent optimization
252// Nocapture calls are only rejected (causing failure) if Calls is empty on
253// entry If Calls is non-empty on entry, nocapture calls are collected
254static bool
255collectMemoryOps(Value *Arg, const DataLayout &DL,
256 SmallVectorImpl<std::pair<StoreInst *, APInt>> &Stores,
257 SmallVectorImpl<std::pair<LoadInst *, APInt>> &Loads,
258 SmallVectorImpl<std::pair<CallInst *, APInt>> &Calls) {
259 // WorkList tracks (Value*, Offset from Arg)
260 SmallVector<std::pair<Value *, APInt>, 16> ToProcess;
261 SmallPtrSet<Value *, 16> Visited;
262
263 APInt ZeroOffset(DL.getIndexTypeSizeInBits(Arg->getType()), 0);
264 ToProcess.push_back({Arg, ZeroOffset});
265
266 while (!ToProcess.empty()) {
267 auto [V, CurrentOffset] = ToProcess.pop_back_val();
268
269 // Skip if already visited
270 if (!Visited.insert(V).second)
271 continue;
272
273 for (Use &U : V->uses()) {
274 User *Usr = U.getUser();
275 if (auto *LI = dyn_cast<LoadInst>(Usr)) {
276 Loads.push_back({LI, CurrentOffset});
277 } else if (auto *SI = dyn_cast<StoreInst>(Usr)) {
278 // Check if this is a store TO the pointer (not storing the pointer
279 // value)
280 if (SI->getPointerOperand() == V) {
281 Stores.push_back({SI, CurrentOffset});
282 } else {
283 // Pointer value is being stored somewhere - reject this argument
284 return false;
285 }
286 } else if (auto *GEP = dyn_cast<GetElementPtrInst>(Usr)) {
287 // Compute the offset for this GEP
288 APInt GEPOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0);
289 if (!GEP->accumulateConstantOffset(DL, GEPOffset)) {
290 // Cannot compute constant offset - reject this argument
291 return false;
292 }
293
294 APInt NewOffset = CurrentOffset + GEPOffset;
295 ToProcess.push_back({GEP, NewOffset});
296 } else if (auto *CI = dyn_cast<CastInst>(Usr)) {
297 // Casts don't change offset
298 ToProcess.push_back({CI, CurrentOffset});
299 } else if (auto *Call = dyn_cast<CallInst>(Usr)) {
300 // Get the argument index from the Use
301 unsigned ArgIdx = U.getOperandNo();
302 if (isNoCapture(Call, ArgIdx)) {
303 Calls.push_back({Call, CurrentOffset});
304 } else {
305 // Call that may capture - reject this argument
306 return false;
307 }
308 } else {
309 // Unknown use - reject this argument
310 return false;
311 }
312 }
313 }
314
315 return true;
316}
317
318static bool hasInterveningMod(Instruction *Start, Instruction *End,
319 LoadInst *LI, AAResults &AA,
320 TargetLibraryInfo &TLI) {
321 assert(Start->getParent() == End->getParent());
322 for (Instruction *I = Start->getNextNode(); I != End; I = I->getNextNode()) {
323 if (I->mayWriteToMemory()) {
324 if (writesToMemoryReadBy(nullptr, AA, TLI, LI, I)) {
325 return true;
326 }
327 }
328 }
329 return false;
330}
331
332// Main optimization function
333bool simplifyGVN(Function &F, DominatorTree &DT, const DataLayout &DL,
334 AAResults &AA, TargetLibraryInfo &TLI) {
335 bool Changed = false;
336
337 // Find candidates (noalias arguments, allocas, and call returns)
338 SmallVector<std::pair<Value *, bool>, 4> CandidateArgs;
339 for (Argument &Arg : F.args()) {
340 if (Arg.getType()->isPointerTy()) {
341 CandidateArgs.push_back({&Arg, Arg.hasNoAliasAttr()});
342 }
343 }
344
345 for (BasicBlock &BB : F) {
346 for (Instruction &I : BB) {
347 if (isa<AllocaInst>(&I)) {
348 CandidateArgs.push_back({&I, true});
349 } else if (auto *CB = dyn_cast<CallBase>(&I)) {
350 if (CB->getType()->isPointerTy()) {
351 CandidateArgs.push_back({CB, CB->hasRetAttr(Attribute::NoAlias)});
352 }
353 }
354 }
355 }
356
357 if (CandidateArgs.empty())
358 return false;
359
360 // For each candidate argument, collect stores and loads with their offsets
361 for (auto &pair : CandidateArgs) {
362 Value *Arg = pair.first;
363 bool isNoAlias = pair.second;
364
365 // Collect all stores and loads to this argument with offsets
366 SmallVector<std::pair<StoreInst *, APInt>, 8> Stores;
367 SmallVector<std::pair<LoadInst *, APInt>, 8> Loads;
368 SmallVector<std::pair<CallInst *, APInt>, 8> Calls;
369
370 // First pass: strict collection (no nocapture calls) for store-load
371 // forwarding (pass empty Calls to reject nocapture calls)
372 if (!collectMemoryOps(Arg, DL, Stores, Loads, Calls)) {
373 // Argument has uses that prevent optimization
374 continue;
375 }
376
377 APInt ZeroOffset(DL.getIndexTypeSizeInBits(Arg->getType()), 0);
378
379 // Try to forward {stores, previous loads} to loads using simplified
380 // algorithm
381 for (auto &[LI, LoadOffset] : Loads) {
382 uint64_t LoadSize = DL.getTypeStoreSize(LI->getType());
383
384 // Step 1: Find all stores that may alias with this load
385 SmallVector<std::tuple<Instruction *, APInt, uint64_t>, 8> AliasingStores;
386 for (auto &[SI, StoreOffset] : Stores) {
387 uint64_t StoreSize =
388 DL.getTypeStoreSize(SI->getValueOperand()->getType());
389 if (memoryRangesAlias(LoadOffset, LoadSize, StoreOffset, StoreSize)) {
390 AliasingStores.push_back({SI, StoreOffset, StoreSize});
391 }
392 }
393
394 // Assume the call can touch any memory, so just set it to directly
395 // overlap.
396 for (auto &[CI, CallOffset] : Calls) {
397 AliasingStores.push_back({CI, LoadOffset, LoadSize});
398 }
399
400 // Step 2: Filter to dominating + covering stores
401 // Tuple of instruction storing, offset in the instruction, and the
402 // equivalent value.
403 SmallVector<std::tuple<Instruction *, APInt, Value *>, 8>
404 DominatingCoveringStores;
405 for (auto &[I, StoreOffset, StoreSize] : AliasingStores) {
406 if (auto SI = dyn_cast<StoreInst>(I))
407 if (dominatesAndCovers(SI, LI, StoreOffset, LoadOffset, LoadSize, DL,
408 DT)) {
409 DominatingCoveringStores.push_back(
410 {SI, StoreOffset, SI->getValueOperand()});
411 }
412 }
413
414 // Step 3: If only one aliasing store and it's dominating+covering,
415 // forward
416 if (AliasingStores.size() == 1 && DominatingCoveringStores.size() == 1) {
417 Instruction *SI = std::get<0>(DominatingCoveringStores[0]);
418 APInt StoreOffset = std::get<1>(DominatingCoveringStores[0]);
419
420 IRBuilder<> Builder(LI);
421 Value *StoredVal = std::get<2>(DominatingCoveringStores[0]);
422 Value *ExtractedVal =
423 extractValue(Builder, StoredVal, LI->getType(), DL, LoadOffset,
424 StoreOffset, LoadSize);
425
426 if (ExtractedVal) {
427 bool Safe = true;
428 if (!isNoAlias) {
429 if (SI->getParent() != LI->getParent()) {
430 Safe = false;
431 } else {
432 Safe = !hasInterveningMod(SI, LI, LI, AA, TLI);
433 }
434 }
435 if (Safe) {
436 LLVM_DEBUG(dbgs() << "SimpleGVN: Forwarding (single alias)\n"
437 << " Store: " << *SI << "\n"
438 << " Load: " << *LI << "\n");
439 LI->replaceAllUsesWith(ExtractedVal);
440 LI->eraseFromParent();
441 LI = nullptr;
442 Changed = true;
443 }
444 }
445 if (LI == nullptr)
446 continue;
447 }
448
449 for (auto &[LI2, LoadOffset2] : Loads) {
450 if (!LI2 || LI2 == LI)
451 continue;
452 if (dominatesAndCovers(LI2, LI, LoadOffset2, LoadOffset, LoadSize, DL,
453 DT)) {
454 DominatingCoveringStores.emplace_back(LI2, LoadOffset2, LI2);
455 }
456 }
457
458 // Step 4: If no dominating+covering stores, bail
459 if (DominatingCoveringStores.empty()) {
460 continue;
461 }
462
463 // Step 5: Build map of last store in each block before LI
464 DenseMap<BasicBlock *, std::tuple<Instruction *, APInt, uint64_t>>
465 LastStoreInBlockBeforeLI;
466 for (auto &[SI, StoreOffset, Size] : AliasingStores) {
467 BasicBlock *BB = SI->getParent();
468 if (BB == LI->getParent()) {
469 // Only consider stores before LI in the same block
470 if (SI->comesBefore(LI)) {
471 auto &Entry = LastStoreInBlockBeforeLI[BB];
472 if (!std::get<0>(Entry) || std::get<0>(Entry)->comesBefore(SI)) {
473 Entry = {SI, StoreOffset, Size};
474 }
475 }
476 } else {
477 // For other blocks, take the last store in the block
478 auto &Entry = LastStoreInBlockBeforeLI[BB];
479 if (!std::get<0>(Entry) || std::get<0>(Entry)->comesBefore(SI)) {
480 Entry = {SI, StoreOffset, Size};
481 }
482 }
483 }
484
485 // Step 6: Check if LI's parent block has a dominating+covering store
486 BasicBlock *LIBlock = LI->getParent();
487 auto It = LastStoreInBlockBeforeLI.find(LIBlock);
488 if (It != LastStoreInBlockBeforeLI.end()) {
489 Instruction *SI = std::get<0>(It->second);
490
491 for (auto &&[DCS, StoreOffset, StoredVal] : DominatingCoveringStores) {
492 if (SI == DCS ||
493 (DCS->getParent() == LI->getParent() && SI->comesBefore(DCS))) {
494
495 IRBuilder<> Builder(LI);
496 Value *ExtractedVal =
497 extractValue(Builder, StoredVal, LI->getType(), DL, LoadOffset,
498 StoreOffset, LoadSize);
499
500 if (ExtractedVal) {
501 bool Safe = true;
502 if (!isNoAlias) {
503 Safe = !hasInterveningMod(DCS, LI, LI, AA, TLI);
504 }
505 if (Safe) {
506 LLVM_DEBUG(dbgs() << "SimpleGVN: Forwarding (same block)\n"
507 << " Store: " << *DCS << "\n"
508 << " Load: " << *LI << "\n");
509 LI->replaceAllUsesWith(ExtractedVal);
510 LI->eraseFromParent();
511 LI = nullptr;
512 Changed = true;
513 break;
514 }
515 }
516 }
517 }
518 continue;
519 } else {
520 for (auto &&[DCS, StoreOffset, StoredVal] : DominatingCoveringStores) {
521 if (DCS->getParent() == LI->getParent()) {
522
523 IRBuilder<> Builder(LI);
524 Value *ExtractedVal =
525 extractValue(Builder, StoredVal, LI->getType(), DL, LoadOffset,
526 StoreOffset, LoadSize);
527
528 if (ExtractedVal) {
529 bool Safe = true;
530 if (!isNoAlias) {
531 Safe = !hasInterveningMod(DCS, LI, LI, AA, TLI);
532 }
533 if (Safe) {
534 LLVM_DEBUG(dbgs() << "SimpleGVN: Forwarding (same block)\n"
535 << " Store: " << *DCS << "\n"
536 << " Load: " << *LI << "\n");
537 LI->replaceAllUsesWith(ExtractedVal);
538 LI->eraseFromParent();
539 LI = nullptr;
540 Changed = true;
541 break;
542 }
543 }
544 }
545 }
546 if (LI == nullptr) {
547 continue;
548 }
549 }
550
551 if (isNoAlias) {
552 // Step 7: BFS backwards from LI's parent block
553 SmallPtrSet<BasicBlock *, 32> Visited;
554 SmallVector<BasicBlock *, 16> Worklist;
555 StoreInst *Candidate = nullptr;
556 APInt CandidateOffset = ZeroOffset;
557
558 // Start with predecessors of LI's block
559 for (BasicBlock *Pred : predecessors(LIBlock)) {
560 if (Visited.insert(Pred).second)
561 Worklist.push_back(Pred);
562 }
563
564 while (!Worklist.empty()) {
565 BasicBlock *BB = Worklist.pop_back_val();
566
567 auto It = LastStoreInBlockBeforeLI.find(BB);
568 if (It != LastStoreInBlockBeforeLI.end()) {
569 StoreInst *SI = dyn_cast<StoreInst>(std::get<0>(It->second));
570 APInt StoreOffset = std::get<1>(It->second);
571
572 if (!SI || !dominatesAndCovers(SI, LI, StoreOffset, LoadOffset,
573 LoadSize, DL, DT)) {
574 // Non-dominating+covering store on path, bail
575 Candidate = nullptr;
576 break;
577 }
578
579 // Found dominating+covering store
580 if (!Candidate) {
581 Candidate = SI;
582 CandidateOffset = StoreOffset;
583 } else if (Candidate != SI) {
584 // Multiple different candidates, bail
585 Candidate = nullptr;
586 break;
587 }
588 }
589
590 // Continue BFS
591 for (BasicBlock *Pred : predecessors(BB)) {
592 if (Visited.insert(Pred).second)
593 Worklist.push_back(Pred);
594 }
595 }
596
597 // Step 8: If unique candidate found, forward
598 if (Candidate) {
599 IRBuilder<> Builder(LI);
600 Value *StoredVal = Candidate->getValueOperand();
601 Value *ExtractedVal =
602 extractValue(Builder, StoredVal, LI->getType(), DL, LoadOffset,
603 CandidateOffset, LoadSize);
604
605 if (ExtractedVal) {
606 LLVM_DEBUG(dbgs() << "SimpleGVN: Forwarding (BFS candidate)\n"
607 << " Store: " << *Candidate << "\n"
608 << " Load: " << *LI << "\n");
609 LI->replaceAllUsesWith(ExtractedVal);
610 LI->eraseFromParent();
611 LI = nullptr;
612 Changed = true;
613 }
614 }
615 }
616 }
617 }
618 return Changed;
619}
620
621class SimpleGVN final : public FunctionPass {
622public:
623 static char ID;
624 SimpleGVN() : FunctionPass(ID) {}
625
626 void getAnalysisUsage(AnalysisUsage &AU) const override {
627 AU.addRequired<DominatorTreeWrapperPass>();
628 AU.addRequired<AAResultsWrapperPass>();
629 AU.addRequired<TargetLibraryInfoWrapperPass>();
630 }
631
632 bool runOnFunction(Function &F) override {
633 auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
634 auto &AA = getAnalysis<AAResultsWrapperPass>().getAAResults();
635 auto &TLI = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F);
636 const DataLayout &DL = F.getParent()->getDataLayout();
637 return simplifyGVN(F, DT, DL, AA, TLI);
638 }
639};
640
641} // namespace
642
643FunctionPass *createSimpleGVNPass() { return new SimpleGVN(); }
644
645extern "C" void LLVMAddSimpleGVNPass(LLVMPassManagerRef PM) {
646 unwrap(PM)->add(createSimpleGVNPass());
647}
648
649char SimpleGVN::ID = 0;
650
651static RegisterPass<SimpleGVN> X("simple-gvn",
652 "GVN-like load forwarding optimization");
653
655 FunctionAnalysisManager &FAM) {
656 bool Changed = false;
657 const DataLayout &DL = F.getParent()->getDataLayout();
658 Changed = simplifyGVN(F, FAM.getResult<DominatorTreeAnalysis>(F), DL,
659 FAM.getResult<AAManager>(F),
660 FAM.getResult<TargetLibraryAnalysis>(F));
661 return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
662}
663
664llvm::AnalysisKey SimpleGVNNewPM::Key;
static RegisterPass< SimpleGVN > X("simple-gvn", "GVN-like load forwarding optimization")
FunctionPass * createSimpleGVNPass()
void LLVMAddSimpleGVNPass(LLVMPassManagerRef PM)
bool writesToMemoryReadBy(const TypeResults *TR, llvm::AAResults &AA, llvm::TargetLibraryInfo &TLI, llvm::Instruction *maybeReader, llvm::Instruction *maybeWriter)
Return whether maybeReader can read from memory written to by maybeWriter.
Definition Utils.cpp:3155
static llvm::PointerType * getPointerType(llvm::Type *T, unsigned AddressSpace=0)
Pointer into the given address space.
Definition Utils.h:1313
static bool isNoAlias(const llvm::CallBase *call)
Definition Utils.h:2127
static bool isNoCapture(const llvm::CallBase *call, size_t idx)
Definition Utils.h:2109
llvm::PreservedAnalyses Result
Definition SimpleGVN.h:49
Result run(llvm::Function &F, llvm::FunctionAnalysisManager &FAM)