1//===- AssumeBundleBuilder.cpp - tools to preserve informations -*- C++ -*-===//
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/Utils/AssumeBundleBuilder.h"
10#include "llvm/ADT/DepthFirstIterator.h"
11#include "llvm/ADT/MapVector.h"
12#include "llvm/ADT/Statistic.h"
13#include "llvm/Analysis/AssumeBundleQueries.h"
14#include "llvm/Analysis/AssumptionCache.h"
15#include "llvm/Analysis/ValueTracking.h"
16#include "llvm/IR/Dominators.h"
17#include "llvm/IR/Function.h"
18#include "llvm/IR/InstIterator.h"
19#include "llvm/IR/IntrinsicInst.h"
20#include "llvm/IR/Module.h"
21#include "llvm/IR/Operator.h"
22#include "llvm/Support/CommandLine.h"
23#include "llvm/Support/DebugCounter.h"
24#include "llvm/Transforms/Utils/Local.h"
25
26using namespace llvm;
27
28namespace llvm {
29cl::opt<bool> EnableKnowledgeRetention(
30 "enable-knowledge-retention", cl::init(Val: false), cl::Hidden,
31 cl::desc(
32 "enable preservation of attributes throughout code transformation"));
33} // namespace llvm
34
35#define DEBUG_TYPE "assume-builder"
36
37STATISTIC(NumAssumeBuilt, "Number of assume built by the assume builder");
38STATISTIC(NumBundlesInAssumes, "Total number of Bundles in the assume built");
39STATISTIC(NumAssumesMerged,
40 "Number of assume merged by the assume simplify pass");
41STATISTIC(NumAssumesRemoved,
42 "Number of assume removed by the assume simplify pass");
43
44DEBUG_COUNTER(BuildAssumeCounter, "assume-builder-counter",
45 "Controls which assumes gets created");
46
47namespace {
48
49bool isUsefullToPreserve(Attribute::AttrKind Kind) {
50 switch (Kind) {
51 case Attribute::NonNull:
52 case Attribute::NoUndef:
53 case Attribute::Alignment:
54 case Attribute::Dereferenceable:
55 case Attribute::DereferenceableOrNull:
56 case Attribute::Cold:
57 return true;
58 default:
59 return false;
60 }
61}
62
63/// This function will try to transform the given knowledge into a more
64/// canonical one. the canonical knowledge maybe the given one.
65RetainedKnowledge canonicalizedKnowledge(RetainedKnowledge RK,
66 const DataLayout &DL) {
67 switch (RK.AttrKind) {
68 default:
69 return RK;
70 case Attribute::NonNull:
71 RK.WasOn = RK.WasOn->stripInBoundsOffsets();
72 return RK;
73 case Attribute::Alignment: {
74 Value *V = RK.WasOn->stripInBoundsOffsets(Func: [&](const Value *Strip) {
75 if (auto *GEP = dyn_cast<GEPOperator>(Val: Strip))
76 RK.ArgValue =
77 MinAlign(A: RK.ArgValue, B: GEP->getMaxPreservedAlignment(DL).value());
78 });
79 RK.WasOn = V;
80 return RK;
81 }
82 case Attribute::Dereferenceable:
83 case Attribute::DereferenceableOrNull: {
84 int64_t Offset = 0;
85 Value *V = GetPointerBaseWithConstantOffset(Ptr: RK.WasOn, Offset, DL,
86 /*AllowNonInBounds*/ AllowNonInbounds: false);
87 if (Offset < 0)
88 return RK;
89 RK.ArgValue = RK.ArgValue + Offset;
90 RK.WasOn = V;
91 }
92 }
93 return RK;
94}
95
96/// This class contain all knowledge that have been gather while building an
97/// llvm.assume and the function to manipulate it.
98struct AssumeBuilderState {
99 Module *M;
100
101 using MapKey = std::pair<Value *, Attribute::AttrKind>;
102 SmallMapVector<MapKey, uint64_t, 8> AssumedKnowledgeMap;
103 Instruction *InstBeingModified = nullptr;
104 AssumptionCache* AC = nullptr;
105 DominatorTree* DT = nullptr;
106
107 AssumeBuilderState(Module *M, Instruction *I = nullptr,
108 AssumptionCache *AC = nullptr, DominatorTree *DT = nullptr)
109 : M(M), InstBeingModified(I), AC(AC), DT(DT) {}
110
111 bool tryToPreserveWithoutAddingAssume(RetainedKnowledge RK) {
112 if (!InstBeingModified || !RK.WasOn || !AC)
113 return false;
114 bool HasBeenPreserved = false;
115 Use* ToUpdate = nullptr;
116 getKnowledgeForValue(
117 V: RK.WasOn, AttrKinds: {RK.AttrKind}, AC&: *AC,
118 Filter: [&](RetainedKnowledge RKOther, Instruction *Assume,
119 const CallInst::BundleOpInfo *Bundle) {
120 if (!isValidAssumeForContext(I: Assume, CxtI: InstBeingModified, DT))
121 return false;
122 if (RKOther.ArgValue >= RK.ArgValue) {
123 HasBeenPreserved = true;
124 return true;
125 } else if (isValidAssumeForContext(I: InstBeingModified, CxtI: Assume, DT)) {
126 HasBeenPreserved = true;
127 IntrinsicInst *Intr = cast<IntrinsicInst>(Val: Assume);
128 ToUpdate = &Intr->op_begin()[Bundle->Begin + ABA_Argument];
129 return true;
130 }
131 return false;
132 });
133 if (ToUpdate)
134 ToUpdate->set(
135 ConstantInt::get(Ty: Type::getInt64Ty(C&: M->getContext()), V: RK.ArgValue));
136 return HasBeenPreserved;
137 }
138
139 bool isKnowledgeWorthPreserving(RetainedKnowledge RK) {
140 if (!RK)
141 return false;
142 if (!RK.WasOn)
143 return true;
144 if (RK.WasOn->getType()->isPointerTy()) {
145 Value *UnderlyingPtr = getUnderlyingObject(V: RK.WasOn);
146 if (isa<AllocaInst>(Val: UnderlyingPtr) || isa<GlobalValue>(Val: UnderlyingPtr))
147 return false;
148 }
149 if (auto *Arg = dyn_cast<Argument>(Val: RK.WasOn)) {
150 if (Arg->hasAttribute(Kind: RK.AttrKind) &&
151 (!Attribute::isIntAttrKind(Kind: RK.AttrKind) ||
152 Arg->getAttribute(Kind: RK.AttrKind).getValueAsInt() >= RK.ArgValue))
153 return false;
154 return true;
155 }
156 if (auto *Inst = dyn_cast<Instruction>(Val: RK.WasOn))
157 if (wouldInstructionBeTriviallyDead(I: Inst)) {
158 if (RK.WasOn->use_empty())
159 return false;
160 Use *SingleUse = RK.WasOn->getSingleUndroppableUse();
161 if (SingleUse && SingleUse->getUser() == InstBeingModified)
162 return false;
163 }
164 return true;
165 }
166
167 void addKnowledge(RetainedKnowledge RK) {
168 RK = canonicalizedKnowledge(RK, DL: M->getDataLayout());
169
170 if (!isKnowledgeWorthPreserving(RK))
171 return;
172
173 if (tryToPreserveWithoutAddingAssume(RK))
174 return;
175 MapKey Key{RK.WasOn, RK.AttrKind};
176 auto [Lookup, Inserted] = AssumedKnowledgeMap.try_emplace(Key, Args&: RK.ArgValue);
177 if (Inserted)
178 return;
179 assert(((Lookup->second == 0 && RK.ArgValue == 0) ||
180 (Lookup->second != 0 && RK.ArgValue != 0)) &&
181 "inconsistent argument value");
182
183 /// This is only desirable because for all attributes taking an argument
184 /// higher is better.
185 Lookup->second = std::max(a: Lookup->second, b: RK.ArgValue);
186 }
187
188 void addAttribute(Attribute Attr, Value *WasOn) {
189 if (Attr.isTypeAttribute() || Attr.isStringAttribute() ||
190 !isUsefullToPreserve(Kind: Attr.getKindAsEnum()))
191 return;
192 uint64_t AttrArg = 0;
193 if (Attr.isIntAttribute())
194 AttrArg = Attr.getValueAsInt();
195 addKnowledge(RK: {Attr.getKindAsEnum(), AttrArg, WasOn});
196 }
197
198 void addCall(const CallBase *Call) {
199 auto addAttrList = [&](AttributeList AttrList, unsigned NumArgs) {
200 for (unsigned Idx = 0; Idx < NumArgs; Idx++)
201 for (Attribute Attr : AttrList.getParamAttrs(ArgNo: Idx)) {
202 bool IsPoisonAttr = Attr.hasAttribute(Val: Attribute::NonNull) ||
203 Attr.hasAttribute(Val: Attribute::Alignment);
204 if (!IsPoisonAttr || Call->isPassingUndefUB(ArgNo: Idx))
205 addAttribute(Attr, WasOn: Call->getArgOperand(i: Idx));
206 }
207 for (Attribute Attr : AttrList.getFnAttrs())
208 addAttribute(Attr, WasOn: nullptr);
209 };
210 addAttrList(Call->getAttributes(), Call->arg_size());
211 if (Function *Fn = Call->getCalledFunction())
212 addAttrList(Fn->getAttributes(), Fn->arg_size());
213 }
214
215 AssumeInst *build() {
216 if (AssumedKnowledgeMap.empty())
217 return nullptr;
218 if (!DebugCounter::shouldExecute(Counter&: BuildAssumeCounter))
219 return nullptr;
220 Function *FnAssume =
221 Intrinsic::getOrInsertDeclaration(M, id: Intrinsic::assume);
222 LLVMContext &C = M->getContext();
223 SmallVector<OperandBundleDef, 8> OpBundle;
224 for (auto &MapElem : AssumedKnowledgeMap) {
225 SmallVector<Value *, 2> Args;
226 if (MapElem.first.first)
227 Args.push_back(Elt: MapElem.first.first);
228
229 /// This is only valid because for all attribute that currently exist a
230 /// value of 0 is useless. and should not be preserved.
231 if (MapElem.second)
232 Args.push_back(Elt: ConstantInt::get(Ty: Type::getInt64Ty(C&: M->getContext()),
233 V: MapElem.second));
234 OpBundle.push_back(Elt: OperandBundleDefT<Value *>(
235 std::string(Attribute::getNameFromAttrKind(AttrKind: MapElem.first.second)),
236 Args));
237 NumBundlesInAssumes++;
238 }
239 NumAssumeBuilt++;
240 return cast<AssumeInst>(Val: CallInst::Create(
241 Func: FnAssume, Args: ArrayRef<Value *>({ConstantInt::getTrue(Context&: C)}), Bundles: OpBundle));
242 }
243
244 void addAccessedPtr(Instruction *MemInst, Value *Pointer, Type *AccType,
245 MaybeAlign MA) {
246 unsigned DerefSize = MemInst->getModule()
247 ->getDataLayout()
248 .getTypeStoreSize(Ty: AccType)
249 .getKnownMinValue();
250 if (DerefSize != 0) {
251 addKnowledge(RK: {Attribute::Dereferenceable, DerefSize, Pointer});
252 if (!NullPointerIsDefined(F: MemInst->getFunction(),
253 AS: Pointer->getType()->getPointerAddressSpace()))
254 addKnowledge(RK: {Attribute::NonNull, 0u, Pointer});
255 }
256 if (MA.valueOrOne() > 1)
257 addKnowledge(RK: {Attribute::Alignment, MA.valueOrOne().value(), Pointer});
258 }
259
260 void addInstruction(Instruction *I) {
261 if (auto *Call = dyn_cast<CallBase>(Val: I))
262 return addCall(Call);
263 if (auto *Load = dyn_cast<LoadInst>(Val: I))
264 return addAccessedPtr(MemInst: I, Pointer: Load->getPointerOperand(), AccType: Load->getType(),
265 MA: Load->getAlign());
266 if (auto *Store = dyn_cast<StoreInst>(Val: I))
267 return addAccessedPtr(MemInst: I, Pointer: Store->getPointerOperand(),
268 AccType: Store->getValueOperand()->getType(),
269 MA: Store->getAlign());
270 if (auto *RMW = dyn_cast<AtomicRMWInst>(Val: I))
271 return addAccessedPtr(MemInst: I, Pointer: RMW->getPointerOperand(),
272 AccType: RMW->getValOperand()->getType(), MA: RMW->getAlign());
273 if (auto *CmpXchg = dyn_cast<AtomicCmpXchgInst>(Val: I))
274 return addAccessedPtr(MemInst: I, Pointer: CmpXchg->getPointerOperand(),
275 AccType: CmpXchg->getCompareOperand()->getType(),
276 MA: CmpXchg->getAlign());
277 // TODO: Maybe we should look around and merge with other llvm.assume.
278 }
279};
280
281} // namespace
282
283AssumeInst *llvm::buildAssumeFromInst(Instruction *I) {
284 if (!EnableKnowledgeRetention)
285 return nullptr;
286 AssumeBuilderState Builder(I->getModule());
287 Builder.addInstruction(I);
288 return Builder.build();
289}
290
291bool llvm::salvageKnowledge(Instruction *I, AssumptionCache *AC,
292 DominatorTree *DT) {
293 if (!EnableKnowledgeRetention || I->isTerminator())
294 return false;
295 bool Changed = false;
296 AssumeBuilderState Builder(I->getModule(), I, AC, DT);
297 Builder.addInstruction(I);
298 if (auto *Intr = Builder.build()) {
299 Intr->insertBefore(InsertPos: I->getIterator());
300 Changed = true;
301 if (AC)
302 AC->registerAssumption(CI: Intr);
303 }
304 return Changed;
305}
306
307namespace {
308
309struct AssumeSimplify {
310 Function &F;
311 AssumptionCache &AC;
312 DominatorTree *DT;
313 LLVMContext &C;
314 SmallDenseSet<IntrinsicInst *> CleanupToDo;
315 StringMapEntry<uint32_t> *IgnoreTag;
316 SmallDenseMap<BasicBlock *, SmallVector<IntrinsicInst *, 4>, 8> BBToAssume;
317 bool MadeChange = false;
318
319 AssumeSimplify(Function &F, AssumptionCache &AC, DominatorTree *DT,
320 LLVMContext &C)
321 : F(F), AC(AC), DT(DT), C(C),
322 IgnoreTag(C.getOrInsertBundleTag(TagName: IgnoreBundleTag)) {}
323
324 void buildMapping(bool FilterBooleanArgument) {
325 BBToAssume.clear();
326 for (Value *V : AC.assumptions()) {
327 if (!V)
328 continue;
329 IntrinsicInst *Assume = cast<IntrinsicInst>(Val: V);
330 if (FilterBooleanArgument) {
331 auto *Arg = dyn_cast<ConstantInt>(Val: Assume->getOperand(i_nocapture: 0));
332 if (!Arg || Arg->isZero())
333 continue;
334 }
335 BBToAssume[Assume->getParent()].push_back(Elt: Assume);
336 }
337
338 for (auto &Elem : BBToAssume) {
339 llvm::sort(C&: Elem.second,
340 Comp: [](const IntrinsicInst *LHS, const IntrinsicInst *RHS) {
341 return LHS->comesBefore(Other: RHS);
342 });
343 }
344 }
345
346 /// Remove all asumes in CleanupToDo if there boolean argument is true and
347 /// ForceCleanup is set or the assume doesn't hold valuable knowledge.
348 void RunCleanup(bool ForceCleanup) {
349 for (IntrinsicInst *Assume : CleanupToDo) {
350 auto *Arg = dyn_cast<ConstantInt>(Val: Assume->getOperand(i_nocapture: 0));
351 if (!Arg || Arg->isZero() ||
352 (!ForceCleanup &&
353 !isAssumeWithEmptyBundle(Assume: cast<AssumeInst>(Val&: *Assume))))
354 continue;
355 MadeChange = true;
356 if (ForceCleanup)
357 NumAssumesMerged++;
358 else
359 NumAssumesRemoved++;
360 Assume->eraseFromParent();
361 }
362 CleanupToDo.clear();
363 }
364
365 /// Remove knowledge stored in assume when it is already know by an attribute
366 /// or an other assume. This can when valid update an existing knowledge in an
367 /// attribute or an other assume.
368 void dropRedundantKnowledge() {
369 struct MapValue {
370 IntrinsicInst *Assume;
371 uint64_t ArgValue;
372 CallInst::BundleOpInfo *BOI;
373 };
374 buildMapping(FilterBooleanArgument: false);
375 SmallDenseMap<std::pair<Value *, Attribute::AttrKind>,
376 SmallVector<MapValue, 2>, 16>
377 Knowledge;
378 for (BasicBlock *BB : depth_first(G: &F))
379 for (Value *V : BBToAssume[BB]) {
380 if (!V)
381 continue;
382 IntrinsicInst *Assume = cast<IntrinsicInst>(Val: V);
383 for (CallInst::BundleOpInfo &BOI : Assume->bundle_op_infos()) {
384 auto RemoveFromAssume = [&]() {
385 CleanupToDo.insert(V: Assume);
386 if (BOI.Begin != BOI.End) {
387 Use *U = &Assume->op_begin()[BOI.Begin + ABA_WasOn];
388 U->set(PoisonValue::get(T: U->get()->getType()));
389 }
390 BOI.Tag = IgnoreTag;
391 };
392 if (BOI.Tag == IgnoreTag) {
393 CleanupToDo.insert(V: Assume);
394 continue;
395 }
396 RetainedKnowledge RK =
397 getKnowledgeFromBundle(Assume&: cast<AssumeInst>(Val&: *Assume), BOI);
398 if (auto *Arg = dyn_cast_or_null<Argument>(Val: RK.WasOn)) {
399 bool HasSameKindAttr = Arg->hasAttribute(Kind: RK.AttrKind);
400 if (HasSameKindAttr)
401 if (!Attribute::isIntAttrKind(Kind: RK.AttrKind) ||
402 Arg->getAttribute(Kind: RK.AttrKind).getValueAsInt() >=
403 RK.ArgValue) {
404 RemoveFromAssume();
405 continue;
406 }
407 if (isValidAssumeForContext(
408 I: Assume, CxtI: &*F.getEntryBlock().getFirstInsertionPt()) ||
409 Assume == &*F.getEntryBlock().getFirstInsertionPt()) {
410 if (HasSameKindAttr)
411 Arg->removeAttr(Kind: RK.AttrKind);
412 Arg->addAttr(Attr: Attribute::get(Context&: C, Kind: RK.AttrKind, Val: RK.ArgValue));
413 MadeChange = true;
414 RemoveFromAssume();
415 continue;
416 }
417 }
418 auto &Lookup = Knowledge[{RK.WasOn, RK.AttrKind}];
419 for (MapValue &Elem : Lookup) {
420 if (!isValidAssumeForContext(I: Elem.Assume, CxtI: Assume, DT))
421 continue;
422 if (Elem.ArgValue >= RK.ArgValue) {
423 RemoveFromAssume();
424 continue;
425 } else if (isValidAssumeForContext(I: Assume, CxtI: Elem.Assume, DT)) {
426 Elem.Assume->op_begin()[Elem.BOI->Begin + ABA_Argument].set(
427 ConstantInt::get(Ty: Type::getInt64Ty(C), V: RK.ArgValue));
428 MadeChange = true;
429 RemoveFromAssume();
430 continue;
431 }
432 }
433 Lookup.push_back(Elt: {.Assume: Assume, .ArgValue: RK.ArgValue, .BOI: &BOI});
434 }
435 }
436 }
437
438 using MergeIterator = SmallVectorImpl<IntrinsicInst *>::iterator;
439
440 /// Merge all Assumes from Begin to End in and insert the resulting assume as
441 /// high as possible in the basicblock.
442 void mergeRange(BasicBlock *BB, MergeIterator Begin, MergeIterator End) {
443 if (Begin == End || std::next(x: Begin) == End)
444 return;
445 /// Provide no additional information so that AssumeBuilderState doesn't
446 /// try to do any punning since it already has been done better.
447 AssumeBuilderState Builder(F.getParent());
448
449 /// For now it is initialized to the best value it could have
450 BasicBlock::iterator InsertPt = BB->getFirstNonPHIIt();
451 if (isa<LandingPadInst>(Val: InsertPt))
452 InsertPt = std::next(x: InsertPt);
453 for (IntrinsicInst *I : make_range(x: Begin, y: End)) {
454 CleanupToDo.insert(V: I);
455 for (CallInst::BundleOpInfo &BOI : I->bundle_op_infos()) {
456 RetainedKnowledge RK =
457 getKnowledgeFromBundle(Assume&: cast<AssumeInst>(Val&: *I), BOI);
458 if (!RK)
459 continue;
460 Builder.addKnowledge(RK);
461 if (auto *I = dyn_cast_or_null<Instruction>(Val: RK.WasOn))
462 if (I->getParent() == InsertPt->getParent() &&
463 (InsertPt->comesBefore(Other: I) || &*InsertPt == I))
464 InsertPt = I->getNextNode()->getIterator();
465 }
466 }
467
468 /// Adjust InsertPt if it is before Begin, since mergeAssumes only
469 /// guarantees we can place the resulting assume between Begin and End.
470 if (InsertPt->comesBefore(Other: *Begin))
471 for (auto It = (*Begin)->getIterator(), E = InsertPt->getIterator();
472 It != E; --It)
473 if (!isGuaranteedToTransferExecutionToSuccessor(I: &*It)) {
474 InsertPt = std::next(x: It);
475 break;
476 }
477 auto *MergedAssume = Builder.build();
478 if (!MergedAssume)
479 return;
480 MadeChange = true;
481 MergedAssume->insertBefore(InsertPos: InsertPt);
482 AC.registerAssumption(CI: MergedAssume);
483 }
484
485 /// Merge assume when they are in the same BasicBlock and for all instruction
486 /// between them isGuaranteedToTransferExecutionToSuccessor returns true.
487 void mergeAssumes() {
488 buildMapping(FilterBooleanArgument: true);
489
490 SmallVector<MergeIterator, 4> SplitPoints;
491 for (auto &Elem : BBToAssume) {
492 SmallVectorImpl<IntrinsicInst *> &AssumesInBB = Elem.second;
493 if (AssumesInBB.size() < 2)
494 continue;
495 /// AssumesInBB is already sorted by order in the block.
496
497 BasicBlock::iterator It = AssumesInBB.front()->getIterator();
498 BasicBlock::iterator E = AssumesInBB.back()->getIterator();
499 SplitPoints.push_back(Elt: AssumesInBB.begin());
500 MergeIterator LastSplit = AssumesInBB.begin();
501 for (; It != E; ++It)
502 if (!isGuaranteedToTransferExecutionToSuccessor(I: &*It)) {
503 for (; (*LastSplit)->comesBefore(Other: &*It); ++LastSplit)
504 ;
505 if (SplitPoints.back() != LastSplit)
506 SplitPoints.push_back(Elt: LastSplit);
507 }
508 SplitPoints.push_back(Elt: AssumesInBB.end());
509 for (auto SplitIt = SplitPoints.begin();
510 SplitIt != std::prev(x: SplitPoints.end()); SplitIt++) {
511 mergeRange(BB: Elem.first, Begin: *SplitIt, End: *(SplitIt + 1));
512 }
513 SplitPoints.clear();
514 }
515 }
516};
517
518bool simplifyAssumes(Function &F, AssumptionCache *AC, DominatorTree *DT) {
519 AssumeSimplify AS(F, *AC, DT, F.getContext());
520
521 /// Remove knowledge that is already known by a dominating other assume or an
522 /// attribute.
523 AS.dropRedundantKnowledge();
524
525 /// Remove assume that are empty.
526 AS.RunCleanup(ForceCleanup: false);
527
528 /// Merge assume in the same basicblock when possible.
529 AS.mergeAssumes();
530
531 /// Remove assume that were merged.
532 AS.RunCleanup(ForceCleanup: true);
533 return AS.MadeChange;
534}
535
536} // namespace
537
538PreservedAnalyses AssumeSimplifyPass::run(Function &F,
539 FunctionAnalysisManager &AM) {
540 if (!EnableKnowledgeRetention)
541 return PreservedAnalyses::all();
542 if (!simplifyAssumes(F, AC: &AM.getResult<AssumptionAnalysis>(IR&: F),
543 DT: AM.getCachedResult<DominatorTreeAnalysis>(IR&: F)))
544 return PreservedAnalyses::all();
545 PreservedAnalyses PA;
546 PA.preserveSet<CFGAnalyses>();
547 return PA;
548}
549
550PreservedAnalyses AssumeBuilderPass::run(Function &F,
551 FunctionAnalysisManager &AM) {
552 AssumptionCache *AC = &AM.getResult<AssumptionAnalysis>(IR&: F);
553 DominatorTree* DT = AM.getCachedResult<DominatorTreeAnalysis>(IR&: F);
554 bool Changed = false;
555 for (Instruction &I : instructions(F))
556 Changed |= salvageKnowledge(I: &I, AC, DT);
557 if (!Changed)
558 PreservedAnalyses::all();
559 PreservedAnalyses PA;
560 PA.preserveSet<CFGAnalyses>();
561 return PA;
562}
563