Enzyme main
Loading...
Searching...
No Matches
FunctionUtils.cpp
Go to the documentation of this file.
1//===- FunctionUtils.cpp - Implementation of function utilities -----------===//
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 defines utilities on LLVM Functions that are used as part of the AD
22// process.
23//
24//===----------------------------------------------------------------------===//
25#include "FunctionUtils.h"
26
27#include "DiffeGradientUtils.h"
28#include "EnzymeLogic.h"
29#include "GradientUtils.h"
30#include "LibraryFuncs.h"
31
32#include "llvm/IR/Attributes.h"
33#include "llvm/IR/BasicBlock.h"
34#include "llvm/IR/DebugInfoMetadata.h"
35#include "llvm/IR/DerivedTypes.h"
36#include "llvm/IR/Function.h"
37#include "llvm/IR/IRBuilder.h"
38#include "llvm/IR/Module.h"
39#include "llvm/IR/Type.h"
40#include "llvm/IR/Verifier.h"
41#include "llvm/Passes/PassBuilder.h"
42
43#include "llvm/ADT/APSInt.h"
44#include "llvm/ADT/DenseMapInfo.h"
45#include "llvm/ADT/SetOperations.h"
46#include "llvm/ADT/SetVector.h"
47#include "llvm/ADT/SmallSet.h"
48#include "llvm/Analysis/AliasAnalysis.h"
49#include "llvm/Analysis/AssumptionCache.h"
50#include "llvm/Analysis/BasicAliasAnalysis.h"
51#include "llvm/Analysis/CallGraph.h"
52#include "llvm/Analysis/GlobalsModRef.h"
53#include "llvm/Analysis/LazyValueInfo.h"
54#include "llvm/Analysis/LoopInfo.h"
55#include "llvm/Analysis/MemoryDependenceAnalysis.h"
56#include "llvm/Analysis/MemorySSA.h"
57#include "llvm/Analysis/OptimizationRemarkEmitter.h"
58#include <set>
59
60#if LLVM_VERSION_MAJOR < 16
61#include "llvm/Analysis/CFLSteensAliasAnalysis.h"
62#endif
63#include "llvm/Analysis/DependenceAnalysis.h"
64#include "llvm/Analysis/TypeBasedAliasAnalysis.h"
65#include "llvm/CodeGen/UnreachableBlockElim.h"
66
67#include "llvm/Analysis/PhiValues.h"
68#include "llvm/Analysis/ProfileSummaryInfo.h"
69#include "llvm/Analysis/ScalarEvolution.h"
70#include "llvm/Analysis/ScopedNoAliasAA.h"
71#include "llvm/Analysis/TargetTransformInfo.h"
72
73#include "llvm/Support/TimeProfiler.h"
74
75#include "llvm/Transforms/IPO/FunctionAttrs.h"
76#include "llvm/Transforms/Utils/Mem2Reg.h"
77
78#include "llvm/Transforms/Utils.h"
79
80#include "llvm/Transforms/InstCombine/InstCombine.h"
81#include "llvm/Transforms/Scalar/CorrelatedValuePropagation.h"
82#include "llvm/Transforms/Scalar/DCE.h"
83#include "llvm/Transforms/Scalar/DeadStoreElimination.h"
84#include "llvm/Transforms/Scalar/EarlyCSE.h"
85#include "llvm/Transforms/Scalar/GVN.h"
86#include "llvm/Transforms/Scalar/IndVarSimplify.h"
87#include "llvm/Transforms/Scalar/InstSimplifyPass.h"
88#include "llvm/Transforms/Scalar/LoopIdiomRecognize.h"
89#include "llvm/Transforms/Scalar/MemCpyOptimizer.h"
90#include "llvm/Transforms/Scalar/SROA.h"
91#include "llvm/Transforms/Scalar/SimplifyCFG.h"
92#include "llvm/Transforms/Utils/Cloning.h"
93#include "llvm/Transforms/Utils/LCSSA.h"
94#include "llvm/Transforms/Utils/LowerInvoke.h"
95
96#include "llvm/Transforms/IPO/FunctionAttrs.h"
97#include "llvm/Transforms/Scalar/DCE.h"
98#include "llvm/Transforms/Scalar/LoopDeletion.h"
99#include "llvm/Transforms/Scalar/LoopRotation.h"
100
101#include "llvm/Transforms/Utils/CodeExtractor.h"
102
103#include "llvm/Transforms/Utils/BasicBlockUtils.h"
104#include "llvm/Transforms/Utils/Local.h"
105
106#include "llvm/IR/LegacyPassManager.h"
107#if LLVM_VERSION_MAJOR <= 16
108#include "llvm/Transforms/IPO/PassManagerBuilder.h"
109#endif
110#include "llvm/Analysis/ScalarEvolutionAliasAnalysis.h"
111
112#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h"
113
114#include <map>
115#include <optional>
116
117#include "CacheUtility.h"
118#include "Utils.h"
119
120#define addAttribute addAttributeAtIndex
121#define removeAttribute removeAttributeAtIndex
122#define getAttribute getAttributeAtIndex
123#define hasAttribute hasAttributeAtIndex
124
125#define DEBUG_TYPE "enzyme"
126using namespace llvm;
127
128extern "C" {
129cl::opt<bool> EnzymePreopt("enzyme-preopt", cl::init(true), cl::Hidden,
130 cl::desc("Run enzyme preprocessing optimizations"));
131
132cl::opt<bool> EnzymeInline("enzyme-inline", cl::init(false), cl::Hidden,
133 cl::desc("Force inlining of autodiff"));
134
135cl::opt<int> EnzymePostInlineOpt("enzyme-post-inline-opt", cl::init(0),
136 cl::Hidden,
137 cl::desc("Force inlining of autodiff"));
138
139cl::opt<bool> EnzymeNoAlias("enzyme-noalias", cl::init(false), cl::Hidden,
140 cl::desc("Force noalias of autodiff"));
141#if LLVM_VERSION_MAJOR < 16
142cl::opt<bool>
143 EnzymeAggressiveAA("enzyme-aggressive-aa", cl::init(false), cl::Hidden,
144 cl::desc("Use more unstable but aggressive LLVM AA"));
145#endif
146cl::opt<bool> EnzymeLowerGlobals(
147 "enzyme-lower-globals", cl::init(false), cl::Hidden,
148 cl::desc("Lower globals to locals assuming the global values are not "
149 "needed outside of this gradient"));
150
151cl::opt<int>
152 EnzymeInlineCount("enzyme-inline-count", cl::init(10000), cl::Hidden,
153 cl::desc("Limit of number of functions to inline"));
154
155cl::opt<bool> EnzymeCoalese("enzyme-coalese", cl::init(false), cl::Hidden,
156 cl::desc("Whether to coalese memory allocations"));
157
158static cl::opt<bool> EnzymePHIRestructure(
159 "enzyme-phi-restructure", cl::init(false), cl::Hidden,
160 cl::desc("Whether to restructure phi's to have better unwrap behavior"));
161
162cl::opt<bool>
163 EnzymeNameInstructions("enzyme-name-instructions", cl::init(false),
164 cl::Hidden,
165 cl::desc("Have enzyme name all instructions"));
166
167cl::opt<bool> EnzymeSelectOpt("enzyme-select-opt", cl::init(true), cl::Hidden,
168 cl::desc("Run Enzyme select optimization"));
169
170cl::opt<bool> EnzymeAutoSparsity("enzyme-auto-sparsity", cl::init(false),
171 cl::Hidden,
172 cl::desc("Run Enzyme auto sparsity"));
173
175 "enzyme-post-opt-level", cl::init(0), cl::Hidden,
176 cl::desc("Post optimization level within Enzyme differentiated function"));
177
179 "enzyme-always-inline", cl::init(false), cl::Hidden,
180 cl::desc("Mark generated functions as always-inline"));
181}
182
183/// Is the use of value val as an argument of call CI potentially captured
184bool couldFunctionArgumentCapture(llvm::CallInst *CI, llvm::Value *val) {
185 Function *F = CI->getCalledFunction();
186
187 if (auto castinst = dyn_cast<ConstantExpr>(CI->getCalledOperand())) {
188 if (castinst->isCast())
189 if (auto fn = dyn_cast<Function>(castinst->getOperand(0))) {
190 F = fn;
191 }
192 }
193
194 if (F == nullptr)
195 return true;
196
197 if (F->getIntrinsicID() == Intrinsic::memset)
198 return false;
199 if (F->getIntrinsicID() == Intrinsic::memcpy)
200 return false;
201 if (F->getIntrinsicID() == Intrinsic::memmove)
202 return false;
203
204 auto arg = F->arg_begin();
205 for (size_t i = 0, size = CI->arg_size(); i < size; i++) {
206 if (val == CI->getArgOperand(i)) {
207 // This is a vararg, assume captured
208 if (arg == F->arg_end()) {
209 return true;
210 } else {
211 if (!arg->hasNoCaptureAttr()) {
212 return true;
213 }
214 }
215 }
216 if (arg != F->arg_end())
217 arg++;
218 }
219 // No argument captured
220 return false;
221}
222
228/// Return whether this function eventually calls itself
229static bool
231 std::map<const Function *, RecurType> &Results) {
232
233 // If we haven't seen this function before, look at all callers
234 // and mark this as potentially recursive. If we see this function
235 // still as marked as MaybeRecursive, we will definitionally have
236 // found an eventual caller of the original function. If not,
237 // the function does not eventually call itself (in a static way)
238 if (Results.find(F) == Results.end()) {
239 Results[F] = MaybeRecursive; // staging
240 for (auto &BB : *F) {
241 for (auto &I : BB) {
242 if (auto call = dyn_cast<CallInst>(&I)) {
243 if (call->getCalledFunction() == nullptr)
244 continue;
245 if (call->getCalledFunction()->empty())
246 continue;
247 IsFunctionRecursive(call->getCalledFunction(), Results);
248 }
249 if (auto call = dyn_cast<InvokeInst>(&I)) {
250 if (call->getCalledFunction() == nullptr)
251 continue;
252 if (call->getCalledFunction()->empty())
253 continue;
254 IsFunctionRecursive(call->getCalledFunction(), Results);
255 }
256 }
257 }
258 if (Results[F] == MaybeRecursive) {
259 Results[F] = NotRecursive; // not recursive
260 }
261 } else if (Results[F] == MaybeRecursive) {
262 Results[F] = DefinitelyRecursive; // definitely recursive
263 }
264 assert(Results[F] != MaybeRecursive);
265 return Results[F] == DefinitelyRecursive;
266}
267
268static inline bool OnlyUsedInOMP(AllocaInst *AI) {
269 bool ompUse = false;
270 for (auto U : AI->users()) {
271 if (auto SI = dyn_cast<StoreInst>(U))
272 if (SI->getPointerOperand() == AI)
273 continue;
274 if (auto CI = dyn_cast<CallInst>(U)) {
275 if (auto F = CI->getCalledFunction()) {
276 if (F->getName() == "__kmpc_for_static_init_4" ||
277 F->getName() == "__kmpc_for_static_init_4u" ||
278 F->getName() == "__kmpc_for_static_init_8" ||
279 F->getName() == "__kmpc_for_static_init_8u") {
280 ompUse = true;
281 }
282 }
283 }
284 }
285
286 if (!ompUse)
287 return false;
288 return true;
289}
290
292 SmallVector<std::tuple<Value *, Value *, Instruction *>, 1> &Todo,
293 SmallVector<Instruction *, 1> &toErase, bool legal) {
294 SmallVector<StoreInst *, 1> toPostCache;
295 // Since MTI may have both source and dest replaced, we keep a list of the
296 // ones being replaced to the current dst, src so we can still follow usage
297 // chain.
298 DenseMap<MemTransferInst *, std::pair<Value *, Value *>> MTIReplacements;
299 while (Todo.size()) {
300 auto cur = Todo.back();
301 Todo.pop_back();
302 Value *rep = std::get<0>(cur);
303 Value *prev = std::get<1>(cur);
304 Value *inst = std::get<2>(cur);
305 if (auto ASC = dyn_cast<AddrSpaceCastInst>(inst)) {
306 auto AS = cast<PointerType>(rep->getType())->getAddressSpace();
307 if (AS == ASC->getDestAddressSpace()) {
308 ASC->replaceAllUsesWith(rep);
309 assert(ASC);
310 toErase.push_back(ASC);
311 continue;
312 }
314 cast<PointerType>(rep->getType())->getAddressSpace() == 0 &&
315 ASC->getDestAddressSpace() == 11) {
316 for (auto U : ASC->users()) {
317 Todo.push_back(
318 std::make_tuple(rep, (Value *)ASC, cast<Instruction>(U)));
319 }
320 assert(ASC);
321 toErase.push_back(ASC);
322 continue;
323 }
324 ASC->setOperand(0, rep);
325 continue;
326 }
327 if (auto CI = dyn_cast<CastInst>(inst)) {
328 if (!CI->getType()->isPointerTy()) {
329 CI->setOperand(0, rep);
330 continue;
331 }
332 IRBuilder<> B(CI);
333 Type *resTy;
334#if LLVM_VERSION_MAJOR < 17
335 if (CI->getContext().supportsTypedPointers()) {
336 resTy = getPointerType(
337 CI->getType()->getPointerElementType(),
338 cast<PointerType>(rep->getType())->getAddressSpace());
339 } else {
340 resTy = rep->getType();
341 }
342#else
343 resTy = rep->getType();
344#endif
345
346 auto nCI0 = B.CreateCast(CI->getOpcode(), rep, resTy);
347 if (auto nCI = dyn_cast<CastInst>(nCI0))
348 nCI->takeName(CI);
349 for (auto U : CI->users()) {
350 Todo.push_back(
351 std::make_tuple((Value *)nCI0, (Value *)CI, cast<Instruction>(U)));
352 }
353 assert(CI);
354 toErase.push_back(CI);
355 continue;
356 }
357 if (auto GEP = dyn_cast<GetElementPtrInst>(inst)) {
358 IRBuilder<> B(GEP);
360 cast<PointerType>(rep->getType())->getAddressSpace() == 10) {
361
362 Type *resTy =
363 changePointerAddrSpace(cast<PointerType>(rep->getType()), 11);
364 rep = B.CreateAddrSpaceCast(rep, resTy);
365 }
366 SmallVector<Value *, 1> ind(GEP->indices());
367 auto nGEP = cast<GetElementPtrInst>(
368 B.CreateGEP(GEP->getSourceElementType(), rep, ind));
369 nGEP->takeName(GEP);
370 for (auto U : GEP->users()) {
371 Todo.push_back(
372 std::make_tuple((Value *)nGEP, (Value *)GEP, cast<Instruction>(U)));
373 }
374 assert(GEP);
375 toErase.push_back(GEP);
376 continue;
377 }
378 if (auto P = dyn_cast<PHINode>(inst)) {
379 auto NumOperands = P->getNumIncomingValues();
380 SmallVector<Value *, 1> replacedOperands(NumOperands, nullptr);
381 for (size_t i = 0; i < NumOperands; i++)
382 if (P->getOperand(i) == prev)
383 replacedOperands[i] = rep;
384
385 for (auto tval : Todo) {
386 if (std::get<2>(tval) != P)
387 continue;
388 for (size_t i = 0; i < NumOperands; i++)
389 if (P->getOperand(i) == std::get<1>(tval)) {
390 replacedOperands[i] = std::get<0>(tval);
391 }
392 }
393 bool allReplaced = true;
394 for (size_t i = 0; i < NumOperands; i++) {
395 if (!replacedOperands[i]) {
396 allReplaced = false;
397 }
398 }
399 if (!allReplaced) {
400 bool remainingArePHIs = true;
401 for (auto v : Todo) {
402 if (isa<PHINode>(std::get<2>(v))) {
403 } else {
404 remainingArePHIs = false;
405 }
406 }
407 if (!remainingArePHIs) {
408 Todo.insert(Todo.begin(), cur);
409 continue;
410 }
411 // Some incoming values are not derived from the object being moved,
412 // so the phi keeps its address space. If that is a derived (not
413 // tracked) one, the moved object can be cast back to it: the GC
414 // treats a cast from the default address space as an untracked base.
415 // A tracked phi would instead root the stack memory, so is an error.
416 unsigned PAS = cast<PointerType>(P->getType())->getAddressSpace();
417 if (PAS != 10 && PAS != 0) {
418 for (size_t i = 0; i < NumOperands; i++) {
419 if (!replacedOperands[i])
420 continue;
421 IRBuilder<> B(P->getIncomingBlock(i)->getTerminator());
422 P->setIncomingValue(i, B.CreatePointerBitCastOrAddrSpaceCast(
423 replacedOperands[i], P->getType()));
424 }
425 for (int i = Todo.size() - 1; i >= 0; i--) {
426 if (std::get<2>(Todo[i]) != P)
427 continue;
428 Todo.erase(Todo.begin() + i);
429 }
430 continue;
431 }
432 } else {
433 IRBuilder<> B(&(*P->getParent()->getFirstNonPHIOrDbgOrLifetime()));
434 auto nP = B.CreatePHI(rep->getType(), P->getNumOperands());
435 for (size_t i = 0; i < NumOperands; i++) {
436 nP->addIncoming(replacedOperands[i], P->getIncomingBlock(i));
437 }
438 nP->takeName(P);
439 for (auto U : P->users()) {
440 Todo.push_back(
441 std::make_tuple((Value *)nP, (Value *)P, cast<Instruction>(U)));
442 }
443 assert(P);
444 toErase.push_back(P);
445 for (int i = Todo.size() - 1; i >= 0; i--) {
446 if (std::get<2>(Todo[i]) != P)
447 continue;
448 Todo.erase(Todo.begin() + i);
449 }
450 continue;
451 }
452 }
453 if (auto II = dyn_cast<IntrinsicInst>(inst)) {
454 if (isIntelSubscriptIntrinsic(*II)) {
455
456 const std::array<size_t, 4> idxArgsIndices{{0, 1, 2, 4}};
457 const size_t ptrArgIndex = 3;
458
459 SmallVector<Value *, 5> args(5);
460 for (auto i : idxArgsIndices) {
461 Value *idx = II->getOperand(i);
462 args[i] = idx;
463 }
464 args[ptrArgIndex] = rep;
465
466 IRBuilder<> B(II);
467 auto nII = cast<CallInst>(B.CreateCall(II->getCalledFunction(), args));
468 // Must copy the elementtype attribute as it is needed by the intrinsic
469 nII->addParamAttr(
470 ptrArgIndex,
471 II->getParamAttr(ptrArgIndex, Attribute::AttrKind::ElementType));
472 nII->takeName(II);
473 for (auto U : II->users()) {
474 Todo.push_back(
475 std::make_tuple((Value *)nII, (Value *)II, cast<Instruction>(U)));
476 }
477 assert(II);
478 toErase.push_back(II);
479 continue;
480 }
481 }
482 if (auto LI = dyn_cast<LoadInst>(inst)) {
484 cast<PointerType>(rep->getType())->getAddressSpace() == 10) {
485 IRBuilder<> B(LI);
486 Type *resTy =
487 changePointerAddrSpace(cast<PointerType>(rep->getType()), 11);
488 rep = B.CreateAddrSpaceCast(rep, resTy);
489 }
490 LI->setOperand(0, rep);
491 continue;
492 }
493 if (auto SI = dyn_cast<StoreInst>(inst)) {
494 if (SI->getPointerOperand() == prev) {
496 cast<PointerType>(rep->getType())->getAddressSpace() == 10) {
497 IRBuilder<> B(SI);
498 Type *resTy =
499 changePointerAddrSpace(cast<PointerType>(rep->getType()), 11);
500 rep = B.CreateAddrSpaceCast(rep, resTy);
501 }
502 SI->setOperand(1, rep);
504 cast<PointerType>(rep->getType())->getAddressSpace() == 11 &&
505 cast<PointerType>(SI->getPointerOperand()->getType())
506 ->getAddressSpace() == 0) {
507 IRBuilder<> B(SI);
508 auto subvals = getJuliaObjects(SI->getValueOperand(), B);
509 if (subvals.size()) {
510 auto JLT =
511 getPointerType(StructType::get(SI->getContext(), {}), 10);
512 auto FT = FunctionType::get(Type::getVoidTy(rep->getContext()),
513 {JLT}, true);
514 auto wb = B.GetInsertBlock()
515 ->getParent()
516 ->getParent()
517 ->getOrInsertFunction("julia.write_barrier", FT);
518 auto obj = getBaseObject(rep);
519 assert(obj->getType() == JLT);
520 subvals.insert(subvals.begin(), obj);
521 B.CreateCall(wb, subvals);
522 }
523 }
524 toPostCache.push_back(SI);
525 continue;
526 }
527 }
528 if (auto MS = dyn_cast<MemSetInst>(inst)) {
529 IRBuilder<> B(MS);
530
531 Value *nargs[] = {MS->getArgOperand(0), MS->getArgOperand(1),
532 MS->getArgOperand(2), MS->getArgOperand(3)};
533
534 if (nargs[0] == prev)
535 nargs[0] = rep;
536
537 if (nargs[1] == prev)
538 nargs[1] = rep;
539
540 Type *tys[] = {nargs[0]->getType(), nargs[2]->getType()};
541 auto nMS = cast<CallInst>(B.CreateCall(
542 getIntrinsicDeclaration(MS->getParent()->getParent()->getParent(),
543 Intrinsic::memset, tys),
544 nargs));
545 nMS->copyMetadata(*MS);
546 nMS->setAttributes(MS->getAttributes());
547 assert(MS);
548 toErase.push_back(MS);
549 continue;
550 }
551 if (auto MTI = dyn_cast<MemTransferInst>(inst)) {
552 auto &dstsrc = MTIReplacements[MTI];
553 if (!dstsrc.first) {
554 dstsrc.first = MTI->getArgOperand(0);
555 }
556 if (!dstsrc.second) {
557 dstsrc.second = MTI->getArgOperand(1);
558 }
559
560 if (MTI->getArgOperand(0) == prev)
561 dstsrc.first = rep;
562
563 if (MTI->getArgOperand(1) == prev)
564 dstsrc.second = rep;
565 continue;
566 }
567 if (auto CI = dyn_cast<CallInst>(inst)) {
568 if (auto F = CI->getCalledFunction()) {
569 if (F->getName() == "julia.write_barrier" && legal) {
570 assert(CI);
571 toErase.push_back(CI);
572 continue;
573 }
574 if (F->getName() == "julia.write_barrier_binding" && legal) {
575 assert(CI);
576 toErase.push_back(CI);
577 continue;
578 }
579 }
580 IRBuilder<> B(CI);
581 auto Addr = B.CreateAddrSpaceCast(rep, prev->getType());
582 for (size_t i = 0; i < CI->arg_size(); i++) {
583 if (CI->getArgOperand(i) == prev) {
584 CI->setArgOperand(i, Addr);
585 }
586 }
587 continue;
588 }
589 if (auto IVI = dyn_cast<InsertValueInst>(inst)) {
590 if (IVI->getInsertedValueOperand() == prev && EnzymeJuliaAddrLoad &&
591 cast<PointerType>(rep->getType())->getAddressSpace() == 0 &&
592 cast<PointerType>(IVI->getInsertedValueOperand()->getType())
593 ->getAddressSpace() == 11) {
594 IRBuilder<> B(IVI);
595 auto Addr = B.CreateAddrSpaceCast(rep, prev->getType());
596 IVI->setOperand(1, Addr);
597 continue;
598 }
599 }
600 if (auto Sel = dyn_cast<SelectInst>(inst)) {
601 // As for a phi: collect which of the two operands derive from the
602 // object being moved, including those still pending in Todo.
603 Value *replacedOperands[2] = {nullptr, nullptr};
604 for (size_t i = 0; i < 2; i++)
605 if (Sel->getOperand(i + 1) == prev)
606 replacedOperands[i] = rep;
607 for (auto tval : Todo) {
608 if (std::get<2>(tval) != Sel)
609 continue;
610 for (size_t i = 0; i < 2; i++)
611 if (Sel->getOperand(i + 1) == std::get<1>(tval))
612 replacedOperands[i] = std::get<0>(tval);
613 }
614 if (replacedOperands[0] && replacedOperands[1]) {
615 IRBuilder<> B(Sel);
616 auto nSel = B.CreateSelect(Sel->getCondition(), replacedOperands[0],
617 replacedOperands[1]);
618 nSel->takeName(Sel);
619 for (auto U : Sel->users()) {
620 Todo.push_back(std::make_tuple((Value *)nSel, (Value *)Sel,
621 cast<Instruction>(U)));
622 }
623 toErase.push_back(Sel);
624 for (int i = Todo.size() - 1; i >= 0; i--) {
625 if (std::get<2>(Todo[i]) != Sel)
626 continue;
627 Todo.erase(Todo.begin() + i);
628 }
629 continue;
630 }
631 // The other operand may still be reached through another pending use.
632 bool remainingAreMerges = true;
633 for (auto v : Todo) {
634 if (!isa<PHINode>(std::get<2>(v)) && !isa<SelectInst>(std::get<2>(v))) {
635 remainingAreMerges = false;
636 break;
637 }
638 }
639 if (!remainingAreMerges) {
640 Todo.insert(Todo.begin(), cur);
641 continue;
642 }
643 }
644 if (auto cmp = dyn_cast<CmpInst>(inst)) {
645 IRBuilder<> B(cmp);
646 Value *op0 = cmp->getOperand(0);
647 Value *op1 = cmp->getOperand(1);
648 if (op0 == prev && isa<Constant>(op1)) {
649 cmp->setOperand(0, rep);
650 cmp->setOperand(1, B.CreateAddrSpaceCast(op1, rep->getType()));
651 } else if (op1 == prev && isa<Constant>(op0)) {
652 cmp->setOperand(0, B.CreateAddrSpaceCast(op0, rep->getType()));
653 cmp->setOperand(1, rep);
654 } else {
655 auto Addr = B.CreateAddrSpaceCast(rep, prev->getType());
656 for (size_t i = 0; i < cmp->getNumOperands(); i++) {
657 if (cmp->getOperand(i) == prev) {
658 cmp->setOperand(i, Addr);
659 }
660 }
661 }
662 continue;
663 }
664
665 std::string s;
666 llvm::raw_string_ostream ss(s);
667 ss << "Illegal address space propagation\n";
668 ss << " + rep: " << *rep << "\n";
669 ss << " + prev: " << *prev << "\n";
670 ss << " + inst: " << *inst << "\n";
671
672 if (CustomErrorHandler) {
673 CustomErrorHandler(s.c_str(), wrap(inst), ErrorType::InternalError,
674 nullptr, nullptr, nullptr);
675 } else {
676 auto instI = cast<Instruction>(inst);
677 ss << *instI->getParent()->getParent() << "\n";
678 EmitFailure("IllegalAddressSpacePropagation", instI->getDebugLoc(), instI,
679 ss.str());
680 }
681 llvm_unreachable("Illegal address space propagation");
682 }
683
684 for (auto MTIPair : MTIReplacements) {
685 auto MTI = MTIPair.first;
686 auto dst = MTIPair.second.first;
687 auto src = MTIPair.second.second;
688
689 Value *nargs[] = {dst, src, MTI->getArgOperand(2), MTI->getArgOperand(3)};
690
691 Type *tys[] = {dst->getType(), src->getType(), nargs[2]->getType()};
692
693 IRBuilder<> B(MTI);
694 auto nMTI = cast<CallInst>(B.CreateCall(
695 getIntrinsicDeclaration(MTI->getParent()->getParent()->getParent(),
696 MTI->getIntrinsicID(), tys),
697 nargs));
698 nMTI->copyMetadata(*MTI);
699 nMTI->setAttributes(MTI->getAttributes());
700 assert(MTI);
701 MTI->eraseFromParent();
702 }
703
704 for (auto I : llvm::reverse(toErase)) {
705 assert(I);
706 I->eraseFromParent();
707 }
708 for (auto SI : toPostCache) {
709 IRBuilder<> B(SI->getNextNode());
710 PostCacheStore(SI, B);
711 }
712}
713
714void RecursivelyReplaceAddressSpace(Value *AI, Value *rep, bool legal) {
715 SmallVector<std::tuple<Value *, Value *, Instruction *>, 1> Todo;
716 for (auto U : AI->users()) {
717 Todo.push_back(
718 std::make_tuple((Value *)rep, (Value *)AI, cast<Instruction>(U)));
719 }
720 SmallVector<Instruction *, 1> toErase;
721 if (auto I = dyn_cast<Instruction>(AI)) {
722 assert(I);
723 toErase.push_back(I);
724 }
725 RecursivelyReplaceAddressSpace(Todo, toErase, legal);
726}
727
728/// Convert necessary stack allocations into mallocs for use in the reverse
729/// pass. Specifically if we're not topLevel all allocations must be upgraded
730/// Even if topLevel any allocations that aren't in the entry block (and
731/// therefore may not be reachable in the reverse pass) must be upgraded.
732static inline void
734 SmallPtrSetImpl<llvm::BasicBlock *> &Unreachable) {
735 SmallVector<AllocaInst *, 4> ToConvert;
736
737 for (auto &BB : *NewF) {
738 if (Unreachable.count(&BB))
739 continue;
740 for (auto &I : BB) {
741 if (auto AI = dyn_cast<AllocaInst>(&I)) {
742 bool UsableEverywhere = AI->getParent() == &NewF->getEntryBlock();
743 // TODO use is_value_needed_in_reverse (requiring GradientUtils)
744 if (OnlyUsedInOMP(AI))
745 continue;
746 if (!UsableEverywhere || mode != DerivativeMode::ReverseModeCombined) {
747 ToConvert.push_back(AI);
748 }
749 }
750 }
751 }
752
753#if LLVM_VERSION_MAJOR >= 22
754 Function *start_lifetime = nullptr;
755 Function *end_lifetime = nullptr;
756#endif
757
758 SmallVector<std::tuple<Value *, Value *, Instruction *>, 1> Todo;
759 SmallVector<Instruction *, 1> toErase;
760 for (auto AI : ToConvert) {
761 std::string nam = AI->getName().str();
762 AI->setName("");
763
764#if LLVM_VERSION_MAJOR >= 22
765 for (auto U : llvm::make_early_inc_range(AI->users())) {
766 if (auto II = dyn_cast<IntrinsicInst>(U)) {
767 if (II->getIntrinsicID() == Intrinsic::lifetime_start) {
768 if (!start_lifetime) {
769 start_lifetime = cast<Function>(
770 NewF->getParent()
771 ->getOrInsertFunction(
772 "llvm.enzyme.lifetime_start",
773 FunctionType::get(Type::getVoidTy(NewF->getContext()),
774 {}, true))
775 .getCallee());
776 }
777 IRBuilder<> B(II);
778 SmallVector<Value *, 2> args(II->arg_size());
779 for (unsigned i = 0; i < II->arg_size(); ++i) {
780 args[i] = II->getArgOperand(i);
781 }
782 auto newI = B.CreateCall(start_lifetime, args);
783 newI->takeName(II);
784 newI->setDebugLoc(II->getDebugLoc());
785 II->eraseFromParent();
786 continue;
787 }
788 if (II->getIntrinsicID() == Intrinsic::lifetime_end) {
789 if (!end_lifetime) {
790 end_lifetime = cast<Function>(
791 NewF->getParent()
792 ->getOrInsertFunction(
793 "llvm.enzyme.lifetime_end",
794 FunctionType::get(Type::getVoidTy(NewF->getContext()),
795 {}, true))
796 .getCallee());
797 }
798 IRBuilder<> B(II);
799 SmallVector<Value *, 2> args(II->arg_size());
800 for (unsigned i = 0; i < II->arg_size(); ++i) {
801 args[i] = II->getArgOperand(i);
802 }
803 auto newI = B.CreateCall(end_lifetime, args);
804 newI->takeName(II);
805 newI->setDebugLoc(II->getDebugLoc());
806 II->eraseFromParent();
807 continue;
808 }
809 }
810 }
811#endif
812
813 // Ensure we insert the malloc after the allocas
814 Instruction *insertBefore = AI;
815 while (isa<AllocaInst>(insertBefore->getNextNode())) {
816 insertBefore = insertBefore->getNextNode();
817 assert(insertBefore);
818 }
819
820 auto i64 = Type::getInt64Ty(NewF->getContext());
821 IRBuilder<> B(insertBefore);
822 CallInst *CI = nullptr;
823 Instruction *ZeroInst = nullptr;
824 auto rep = CreateAllocation(
825 B, AI->getAllocatedType(), B.CreateZExtOrTrunc(AI->getArraySize(), i64),
826 nam, &CI, /*ZeroMem*/ EnzymeZeroCache ? &ZeroInst : nullptr);
827 auto align = AI->getAlign().value();
828 CI->setMetadata(
829 "enzyme_fromstack",
830 MDNode::get(CI->getContext(),
831 {
832 ConstantAsMetadata::get(ConstantInt::get(
833 IntegerType::get(AI->getContext(), 64), align)),
834 ConstantAsMetadata::get(ConstantInt::get(
835 IntegerType::get(AI->getContext(), 64),
836 (size_t)AI->getAllocatedType())),
837 }));
838
839 for (auto MD : {"enzyme_active", "enzyme_inactive", "enzyme_type",
840 "enzymejl_allocart", "enzymejl_allocart_name",
841 "enzymejl_gc_alloc_rt"})
842 if (auto M = AI->getMetadata(MD))
843 CI->setMetadata(MD, M);
844
845 if (rep != CI) {
846 cast<Instruction>(rep)->setMetadata("enzyme_caststack",
847 MDNode::get(CI->getContext(), {}));
848 }
849 if (ZeroInst) {
850 ZeroInst->setMetadata("enzyme_zerostack",
851 MDNode::get(CI->getContext(), {}));
852 }
853
854 auto PT0 = cast<PointerType>(rep->getType());
855 auto PT1 = cast<PointerType>(AI->getType());
856 if (PT0->getAddressSpace() != PT1->getAddressSpace()) {
857 for (auto U : AI->users()) {
858 Todo.push_back(
859 std::make_tuple((Value *)rep, (Value *)AI, cast<Instruction>(U)));
860 }
861 if (auto I = dyn_cast<Instruction>(AI)) {
862 assert(I);
863 toErase.push_back(I);
864 }
865 } else {
866 assert(rep->getType() == AI->getType());
867 AI->replaceAllUsesWith(rep);
868 AI->eraseFromParent();
869 }
870 }
871 RecursivelyReplaceAddressSpace(Todo, toErase, /*legal*/ false);
872}
873
874// Create a stack variable containing the size of the allocation
875// error if not possible (e.g. not local)
876static inline AllocaInst *
877OldAllocationSize(Value *Ptr, CallInst *Loc, Function *NewF, IntegerType *T,
878 const std::map<CallInst *, Value *> &reallocSizes) {
879 IRBuilder<> B(&*NewF->getEntryBlock().begin());
880 AllocaInst *AI = B.CreateAlloca(T);
881
882 std::set<std::pair<Value *, Instruction *>> seen;
883 std::deque<std::pair<Value *, Instruction *>> todo = {{Ptr, Loc}};
884
885 while (todo.size()) {
886 auto next = todo.front();
887 todo.pop_front();
888 if (seen.count(next))
889 continue;
890 seen.insert(next);
891
892 if (auto CI = dyn_cast<CastInst>(next.first)) {
893 todo.push_back({CI->getOperand(0), CI});
894 continue;
895 }
896
897 // Assume zero size if realloc of undef pointer
898 if (isa<UndefValue>(next.first)) {
899 B.SetInsertPoint(next.second);
900 B.CreateStore(ConstantInt::get(T, 0), AI);
901 continue;
902 }
903
904 if (auto CE = dyn_cast<ConstantExpr>(next.first)) {
905 if (CE->isCast()) {
906 todo.push_back({CE->getOperand(0), next.second});
907 continue;
908 }
909 }
910
911 if (auto C = dyn_cast<Constant>(next.first)) {
912 if (C->isNullValue()) {
913 B.SetInsertPoint(next.second);
914 B.CreateStore(ConstantInt::get(T, 0), AI);
915 continue;
916 }
917 }
918 if (auto CI = dyn_cast<ConstantInt>(next.first)) {
919 // if negative or below 0xFFF this cannot possibly represent
920 // a real pointer, so ignore this case by setting to 0
921 if (CI->isNegative() || CI->getLimitedValue() <= 0xFFF) {
922 B.SetInsertPoint(next.second);
923 B.CreateStore(ConstantInt::get(T, 0), AI);
924 continue;
925 }
926 }
927
928 // Todo consider more general method for selects
929 if (auto SI = dyn_cast<SelectInst>(next.first)) {
930 if (auto C1 = dyn_cast<ConstantInt>(SI->getTrueValue())) {
931 // if negative or below 0xFFF this cannot possibly represent
932 // a real pointer, so ignore this case by setting to 0
933 if (C1->isNegative() || C1->getLimitedValue() <= 0xFFF) {
934 if (auto C2 = dyn_cast<ConstantInt>(SI->getFalseValue())) {
935 if (C2->isNegative() || C2->getLimitedValue() <= 0xFFF) {
936 B.SetInsertPoint(next.second);
937 B.CreateStore(ConstantInt::get(T, 0), AI);
938 continue;
939 }
940 }
941 }
942 }
943 }
944
945 if (auto PN = dyn_cast<PHINode>(next.first)) {
946 for (size_t i = 0; i < PN->getNumIncomingValues(); i++) {
947 todo.push_back({PN->getIncomingValue(i),
948 PN->getIncomingBlock(i)->getTerminator()});
949 }
950 continue;
951 }
952
953 if (auto CI = dyn_cast<CallInst>(next.first)) {
954 if (auto F = CI->getCalledFunction()) {
955 if (F->getName() == "malloc") {
956 B.SetInsertPoint(next.second);
957 B.CreateStore(CI->getArgOperand(0), AI);
958 continue;
959 }
960 if (F->getName() == "calloc") {
961 B.SetInsertPoint(next.second);
962 B.CreateStore(B.CreateMul(CI->getArgOperand(0), CI->getArgOperand(1)),
963 AI);
964 continue;
965 }
966 if (F->getName() == "realloc") {
967 assert(reallocSizes.find(CI) != reallocSizes.end());
968 B.SetInsertPoint(next.second);
969 B.CreateStore(reallocSizes.find(CI)->second, AI);
970 continue;
971 }
972 }
973 }
974
975 if (auto LI = dyn_cast<LoadInst>(next.first)) {
976 bool success = false;
977 for (Instruction *prev = LI->getPrevNode(); prev != nullptr;
978 prev = prev->getPrevNode()) {
979 if (auto CI = dyn_cast<CallInst>(prev)) {
980 if (auto F = CI->getCalledFunction()) {
981 if (F->getName() == "posix_memalign" &&
982 CI->getArgOperand(0) == LI->getOperand(0)) {
983 B.SetInsertPoint(next.second);
984 B.CreateStore(CI->getArgOperand(2), AI);
985 success = true;
986 break;
987 }
988 }
989 }
990 if (prev->mayWriteToMemory()) {
991 break;
992 }
993 }
994 if (success)
995 continue;
996
997 auto v2 = simplifyLoad(LI);
998 if (v2) {
999 todo.push_back({v2, next.second});
1000 continue;
1001 }
1002 }
1003
1004 EmitFailure("DynamicReallocSize", Loc->getDebugLoc(), Loc,
1005 "could not statically determine size of realloc ", *Loc,
1006 " - because of - ", *next.first);
1007 return AI;
1008
1009 std::string allocName;
1010 switch (llvm::Triple(NewF->getParent()->getTargetTriple()).getOS()) {
1011 case llvm::Triple::Linux:
1012 case llvm::Triple::FreeBSD:
1013 case llvm::Triple::NetBSD:
1014 case llvm::Triple::OpenBSD:
1015 case llvm::Triple::Fuchsia:
1016 allocName = "malloc_usable_size";
1017 break;
1018
1019 case llvm::Triple::Darwin:
1020 case llvm::Triple::IOS:
1021 case llvm::Triple::MacOSX:
1022 case llvm::Triple::WatchOS:
1023 case llvm::Triple::TvOS:
1024 allocName = "malloc_size";
1025 break;
1026
1027 case llvm::Triple::Win32:
1028 allocName = "_msize";
1029 break;
1030
1031 default:
1032 llvm_unreachable("unknown reallocation for OS");
1033 }
1034
1035 AttributeList list;
1036 list = list.addFnAttribute(NewF->getContext(), Attribute::ReadOnly);
1037 list = list.addParamAttribute(NewF->getContext(), 0, Attribute::ReadNone);
1038 list = addFunctionNoCapture(NewF->getContext(), list, 0);
1039 auto allocSize = NewF->getParent()->getOrInsertFunction(
1040 allocName,
1041 FunctionType::get(
1042 IntegerType::get(NewF->getContext(), 8 * sizeof(size_t)),
1043 {getInt8PtrTy(NewF->getContext())}, /*isVarArg*/ false),
1044 list);
1045
1046 B.SetInsertPoint(Loc);
1047 Value *sz = B.CreateZExtOrTrunc(B.CreateCall(allocSize, {Ptr}), T);
1048 B.CreateStore(sz, AI);
1049 return AI;
1050
1051 llvm_unreachable("DynamicReallocSize");
1052 }
1053 return AI;
1054}
1055
1056void PreProcessCache::AlwaysInline(Function *NewF) {
1057
1058 PreservedAnalyses PA;
1059 PA.preserve<AssumptionAnalysis>();
1060 PA.preserve<TargetLibraryAnalysis>();
1061 FAM.invalidate(*NewF, PA);
1062 SmallVector<CallInst *, 2> ToInline;
1063 // TODO this logic should be combined with the dynamic loop emission
1064 // to minimize the number of branches if the realloc is used for multiple
1065 // values with the same bound.
1066 for (auto &BB : *NewF) {
1067 for (auto &I : make_early_inc_range(BB)) {
1068 if (hasMetadata(&I, "enzyme_zerostack")) {
1069 if (isa<AllocaInst>(getBaseObject(I.getOperand(0)))) {
1070 I.eraseFromParent();
1071 continue;
1072 }
1073 }
1074 if (auto CI = dyn_cast<CallInst>(&I)) {
1075 if (!CI->getCalledFunction())
1076 continue;
1077 if (CI->getCalledFunction()->hasFnAttribute(Attribute::AlwaysInline))
1078 ToInline.push_back(CI);
1079 }
1080 }
1081 }
1082
1083 for (auto CI : ToInline) {
1084 InlineFunctionInfo IFI;
1085#if LLVM_VERSION_MAJOR >= 18 && LLVM_VERSION_MAJOR < 21
1086 auto F = CI->getCalledFunction();
1087 if (CI->getParent()->IsNewDbgInfoFormat != F->IsNewDbgInfoFormat) {
1088 if (CI->getParent()->IsNewDbgInfoFormat) {
1089 F->convertToNewDbgValues();
1090 } else {
1091 F->convertFromNewDbgValues();
1092 }
1093 }
1094#endif
1095 InlineFunction(*CI, IFI);
1096 }
1097}
1098
1099// Simplify all extractions to use inserted values, if possible.
1100void simplifyExtractions(Function *NewF) {
1101 // First rewrite/remove any extractions
1102 for (auto &BB : *NewF) {
1103 IRBuilder<> B(&BB);
1104 auto first = BB.begin();
1105 auto last = BB.empty() ? BB.end() : std::prev(BB.end());
1106 for (auto it = first; it != last;) {
1107 auto inst = &*it;
1108 // We iterate first here, since we may delete the instruction
1109 // in the body
1110 ++it;
1111 if (auto E = dyn_cast<ExtractValueInst>(inst)) {
1112 auto rep = GradientUtils::extractMeta(B, E->getAggregateOperand(),
1113 E->getIndices(), E->getName(),
1114 /*fallback*/ false);
1115 if (rep) {
1116 E->replaceAllUsesWith(rep);
1117 E->eraseFromParent();
1118 }
1119 }
1120 }
1121 }
1122 // Now that there may be unused insertions, delete them. We keep a list of
1123 // todo's since deleting an insertvalue may cause a different insertvalue to
1124 // have no uses
1125 SmallVector<InsertValueInst *, 1> todo;
1126 for (auto &BB : *NewF) {
1127 for (auto &inst : BB)
1128 if (auto I = dyn_cast<InsertValueInst>(&inst)) {
1129 if (I->getNumUses() == 0)
1130 todo.push_back(I);
1131 }
1132 }
1133 while (todo.size()) {
1134 auto I = todo.pop_back_val();
1135 auto op = I->getAggregateOperand();
1136 I->eraseFromParent();
1137 if (auto I2 = dyn_cast<InsertValueInst>(op))
1138 if (I2->getNumUses() == 0)
1139 todo.push_back(I2);
1140 }
1141}
1142
1144 simplifyExtractions(NewF);
1145 SmallVector<Instruction *, 1> InitialTodo;
1146 for (auto &BB : *NewF) {
1147 for (auto &I : BB) {
1148 if (hasMetadata(&I, "enzyme_backstack")) {
1149 InitialTodo.push_back(&I);
1150 // TODO
1151 // I.eraseMetadata("enzyme_backstack");
1152 }
1153 }
1154 }
1155 SmallVector<std::tuple<Value *, Value *, Instruction *>, 1> Todo;
1156 SmallVector<Instruction *, 1> toErase;
1157 for (auto T : InitialTodo) {
1158 auto T0 = T->getOperand(0);
1159 if (auto CI = dyn_cast<BitCastInst>(T0))
1160 T0 = CI->getOperand(0);
1161 auto AI = cast<AllocaInst>(T0);
1162 llvm::Value *AIV = AI;
1163#if LLVM_VERSION_MAJOR < 17
1164 if (AIV->getContext().supportsTypedPointers() &&
1165 AIV->getType()->getPointerElementType() !=
1166 T->getType()->getPointerElementType()) {
1167 IRBuilder<> B(AI->getNextNode());
1168 AIV = B.CreateBitCast(
1169 AIV,
1170 getPointerType(T->getType()->getPointerElementType(),
1171 cast<PointerType>(AI->getType())->getAddressSpace()));
1172 }
1173#endif
1174 for (auto U : T->users()) {
1175 Todo.push_back(
1176 std::make_tuple((Value *)AIV, (Value *)T, cast<Instruction>(U)));
1177 }
1178 if (auto I = dyn_cast<Instruction>(T)) {
1179 assert(I);
1180 toErase.push_back(I);
1181 }
1182 }
1183 RecursivelyReplaceAddressSpace(Todo, toErase, /*legal*/ true);
1184
1185#if LLVM_VERSION_MAJOR >= 22
1186 {
1187 auto start_lifetime =
1188 NewF->getParent()->getFunction("llvm.enzyme.lifetime_start");
1189 auto end_lifetime =
1190 NewF->getParent()->getFunction("llvm.enzyme.lifetime_end");
1191
1192 SmallVector<CallInst *, 1> Todo;
1193 for (auto &BB : *NewF) {
1194 for (auto &I : BB) {
1195 if (auto CB = dyn_cast<CallInst>(&I)) {
1196 if (!CB->getCalledFunction())
1197 continue;
1198 if (CB->getCalledFunction() == start_lifetime ||
1199 CB->getCalledFunction() == end_lifetime) {
1200 Todo.push_back(CB);
1201 }
1202 }
1203 }
1204 }
1205
1206 for (auto CB : Todo) {
1207 if (!isa<AllocaInst>(CB->getArgOperand(1))) {
1208 CB->eraseFromParent();
1209 continue;
1210 }
1211 IRBuilder<> B(CB);
1212 if (CB->getCalledFunction() == start_lifetime) {
1213 B.CreateLifetimeStart(cast<ConstantInt>(CB->getArgOperand(0)));
1214 } else {
1215 B.CreateLifetimeEnd(cast<ConstantInt>(CB->getArgOperand(0)));
1216 }
1217 CB->eraseFromParent();
1218 }
1219 }
1220#endif
1221}
1222
1223/// Calls to realloc with an appropriate implementation
1224void PreProcessCache::ReplaceReallocs(Function *NewF, bool mem2reg) {
1225 if (mem2reg) {
1226 auto PA = PromotePass().run(*NewF, FAM);
1227 FAM.invalidate(*NewF, PA);
1228#if !defined(FLANG)
1229 PA = GVNPass().run(*NewF, FAM);
1230#else
1231 PA = GVN().run(*NewF, FAM);
1232#endif
1233 FAM.invalidate(*NewF, PA);
1234 }
1235
1236 SmallVector<CallInst *, 4> ToConvert;
1237 std::map<CallInst *, Value *> reallocSizes;
1238 IntegerType *T = nullptr;
1239
1240 for (auto &BB : *NewF) {
1241 for (auto &I : BB) {
1242 if (auto CI = dyn_cast<CallInst>(&I)) {
1243 if (auto F = CI->getCalledFunction()) {
1244 if (F->getName() == "realloc") {
1245 ToConvert.push_back(CI);
1246 IRBuilder<> B(CI->getNextNode());
1247 T = cast<IntegerType>(CI->getArgOperand(1)->getType());
1248 reallocSizes[CI] = B.CreatePHI(T, 0);
1249 }
1250 }
1251 }
1252 }
1253 }
1254
1255 SmallVector<AllocaInst *, 4> memoryLocations;
1256
1257 for (auto CI : ToConvert) {
1258 assert(T);
1259 AllocaInst *AI =
1260 OldAllocationSize(CI->getArgOperand(0), CI, NewF, T, reallocSizes);
1261
1262 BasicBlock *resize =
1263 BasicBlock::Create(CI->getContext(), "resize" + CI->getName(), NewF);
1264 assert(resize->getParent() == NewF);
1265
1266 BasicBlock *splitParent = CI->getParent();
1267 BasicBlock *nextBlock = splitParent->splitBasicBlock(CI);
1268
1269 splitParent->getTerminator()->eraseFromParent();
1270 IRBuilder<> B(splitParent);
1271
1272 Value *p = CI->getArgOperand(0);
1273 Value *req = CI->getArgOperand(1);
1274 Value *old = B.CreateLoad(AI->getAllocatedType(), AI);
1275 Value *cmp = B.CreateICmpULE(req, old);
1276 // if (req < old)
1277 B.CreateCondBr(cmp, nextBlock, resize);
1278
1279 B.SetInsertPoint(resize);
1280 // size_t newsize = nextPowerOfTwo(req);
1281 // void* next = malloc(newsize);
1282 // memcpy(next, p, newsize);
1283 // free(p);
1284 // return { next, newsize };
1285
1286 Value *newsize = nextPowerOfTwo(B, req);
1287
1288 Module *M = NewF->getParent();
1289 Type *BPTy = getInt8PtrTy(NewF->getContext());
1290 auto MallocFunc =
1291 M->getOrInsertFunction("malloc", BPTy, newsize->getType());
1292 auto next = B.CreateCall(MallocFunc, newsize);
1293 B.SetInsertPoint(resize);
1294
1295 auto volatile_arg = ConstantInt::getFalse(CI->getContext());
1296
1297 Value *nargs[] = {next, p, old, volatile_arg};
1298
1299 Type *tys[] = {next->getType(), p->getType(), old->getType()};
1300
1301 auto memcpyF =
1302 getIntrinsicDeclaration(NewF->getParent(), Intrinsic::memcpy, tys);
1303
1304 auto mem = cast<CallInst>(B.CreateCall(memcpyF, nargs));
1305 mem->setCallingConv(memcpyF->getCallingConv());
1306
1307 Type *VoidTy = Type::getVoidTy(M->getContext());
1308 auto FreeFunc = M->getOrInsertFunction("free", VoidTy, BPTy);
1309 B.CreateCall(FreeFunc, p);
1310 B.SetInsertPoint(resize);
1311
1312 B.CreateBr(nextBlock);
1313
1314 // else
1315 // return { p, old }
1316 B.SetInsertPoint(&*nextBlock->begin());
1317
1318 PHINode *retPtr = B.CreatePHI(CI->getType(), 2);
1319 retPtr->addIncoming(p, splitParent);
1320 retPtr->addIncoming(next, resize);
1321 CI->replaceAllUsesWith(retPtr);
1322 std::string nam = CI->getName().str();
1323 CI->setName("");
1324 retPtr->setName(nam);
1325 Value *nextSize = B.CreateSelect(cmp, old, req);
1326 reallocSizes[CI]->replaceAllUsesWith(nextSize);
1327 cast<PHINode>(reallocSizes[CI])->eraseFromParent();
1328 reallocSizes[CI] = nextSize;
1329 }
1330
1331 for (auto CI : ToConvert) {
1332 CI->eraseFromParent();
1333 }
1334
1335 PreservedAnalyses PA;
1336 FAM.invalidate(*NewF, PA);
1337
1338 PA = PromotePass().run(*NewF, FAM);
1339 FAM.invalidate(*NewF, PA);
1340}
1341
1342Function *CreateMPIWrapper(Function *F) {
1343 std::string name = ("enzyme_wrapmpi$$" + F->getName() + "#").str();
1344 if (auto W = F->getParent()->getFunction(name))
1345 return W;
1346
1347 // MPI_Comm_rank(MPI_Comm comm, int *rank)
1348 // MPI_Comm_size(MPI_Comm comm, int *size)
1349 Type *ReturnType = Type::getInt32Ty(F->getContext());
1350 Type *types = {F->getFunctionType()->getParamType(0)}; // MPI_Comm
1351 auto FT = FunctionType::get(ReturnType, types, false);
1352 Function *W = Function::Create(FT, GlobalVariable::InternalLinkage, name,
1353 F->getParent());
1354 llvm::Attribute::AttrKind attrs[] = {
1355 Attribute::WillReturn,
1356 Attribute::MustProgress,
1357#if LLVM_VERSION_MAJOR < 16
1358 Attribute::ReadOnly,
1359#endif
1360 Attribute::Speculatable,
1361 Attribute::NoUnwind,
1362 Attribute::AlwaysInline,
1363 Attribute::NoFree,
1364 Attribute::NoSync,
1365#if LLVM_VERSION_MAJOR < 16
1366 Attribute::InaccessibleMemOnly
1367#endif
1368 };
1369 for (auto attr : attrs) {
1370 W->addFnAttr(attr);
1371 }
1372#if LLVM_VERSION_MAJOR >= 16
1373 W->setOnlyAccessesInaccessibleMemory();
1374 W->setOnlyReadsMemory();
1375#endif
1376 W->addFnAttr(Attribute::get(F->getContext(), "enzyme_inactive"));
1377 BasicBlock *entry = BasicBlock::Create(W->getContext(), "entry", W);
1378 IRBuilder<> B(entry);
1379 auto alloc = B.CreateAlloca(ReturnType);
1380 Value *args[] = {W->arg_begin(), alloc};
1381
1382 auto T = F->getFunctionType()->getParamType(1);
1383 if (!isa<PointerType>(T)) {
1384 assert(isa<IntegerType>(T));
1385 args[1] = B.CreatePtrToInt(args[1], T);
1386 }
1387 B.CreateCall(F, args);
1388 B.CreateRet(B.CreateLoad(ReturnType, alloc));
1389 return W;
1390}
1391
1392static void SimplifyMPIQueries(Function &NewF, FunctionAnalysisManager &FAM) {
1393 DominatorTree &DT = FAM.getResult<DominatorTreeAnalysis>(NewF);
1394 SmallVector<CallBase *, 4> Todo;
1395 SmallVector<CallBase *, 4> OMPBounds;
1396 for (auto &BB : NewF) {
1397 for (auto &I : BB) {
1398 if (auto CI = dyn_cast<CallBase>(&I)) {
1399 Function *Fn = CI->getCalledFunction();
1400 if (Fn == nullptr)
1401 continue;
1402 auto name = getFuncName(Fn);
1403 if (name == "MPI_Comm_rank" || name == "PMPI_Comm_rank" ||
1404 name == "MPI_Comm_size" || name == "PMPI_Comm_size") {
1405 Todo.push_back(CI);
1406 }
1407 if (name == "__kmpc_for_static_init_4" ||
1408 name == "__kmpc_for_static_init_4u" ||
1409 name == "__kmpc_for_static_init_8" ||
1410 name == "__kmpc_for_static_init_8u") {
1411 OMPBounds.push_back(CI);
1412 }
1413 }
1414 }
1415 }
1416 if (Todo.size() == 0 && OMPBounds.size() == 0)
1417 return;
1418 for (auto CI : Todo) {
1419 IRBuilder<> B(CI);
1420 Value *arg[] = {CI->getArgOperand(0)};
1421 SmallVector<OperandBundleDef, 2> Defs;
1422 CI->getOperandBundlesAsDefs(Defs);
1423 CallBase *res = nullptr;
1424 if (auto II = dyn_cast<InvokeInst>(CI))
1425 res = B.CreateInvoke(CreateMPIWrapper(CI->getCalledFunction()),
1426 II->getNormalDest(), II->getUnwindDest(), arg, Defs);
1427 else
1428 res = B.CreateCall(CreateMPIWrapper(CI->getCalledFunction()), arg, Defs);
1429 Value *storePointer = CI->getArgOperand(1);
1430
1431 // Comm_rank and Comm_size return Err, assume 0 is success
1432 CI->replaceAllUsesWith(ConstantInt::get(CI->getType(), 0));
1433 CI->eraseFromParent();
1434
1435 while (auto Cast = dyn_cast<CastInst>(storePointer)) {
1436 storePointer = Cast->getOperand(0);
1437 if (Cast->use_empty())
1438 Cast->eraseFromParent();
1439 }
1440
1441 B.SetInsertPoint(res);
1442
1443 if (auto PT = dyn_cast<PointerType>(storePointer->getType())) {
1444 (void)PT;
1445#if LLVM_VERSION_MAJOR < 17
1446 if (PT->getContext().supportsTypedPointers()) {
1447 if (PT->getPointerElementType() != res->getType())
1448 storePointer = B.CreateBitCast(
1449 storePointer,
1450 getPointerType(res->getType(), PT->getAddressSpace()));
1451 }
1452#endif
1453 } else {
1454 assert(isa<IntegerType>(storePointer->getType()));
1455 storePointer = B.CreateIntToPtr(storePointer, getUnqual(res->getType()));
1456 }
1457 if (isa<AllocaInst>(storePointer)) {
1458 // If this is only loaded from, immedaitely replace
1459 // Immediately replace all dominated stores.
1460 SmallVector<LoadInst *, 2> LI;
1461 bool nonload = false;
1462 for (auto &U : storePointer->uses()) {
1463 if (auto L = dyn_cast<LoadInst>(U.getUser())) {
1464 LI.push_back(L);
1465 } else
1466 nonload = true;
1467 }
1468 if (!nonload) {
1469 for (auto L : LI) {
1470 if (DT.dominates(res, L)) {
1471 L->replaceAllUsesWith(res);
1472 L->eraseFromParent();
1473 }
1474 }
1475 }
1476 }
1477 if (auto II = dyn_cast<InvokeInst>(res)) {
1478 B.SetInsertPoint(getFirstNonPHI(II->getNormalDest()));
1479 } else {
1480 B.SetInsertPoint(res->getNextNode());
1481 }
1482 B.CreateStore(res, storePointer);
1483 }
1484 for (auto Bound : OMPBounds) {
1485 for (int i = 4; i <= 6; i++) {
1486 auto AI = cast<AllocaInst>(Bound->getArgOperand(i));
1487 IRBuilder<> B(AI);
1488 auto AI2 = B.CreateAlloca(AI->getAllocatedType(), nullptr,
1489 AI->getName() + "_smpl");
1490 B.SetInsertPoint(Bound);
1491 B.CreateStore(B.CreateLoad(AI->getAllocatedType(), AI), AI2);
1492 Bound->setArgOperand(i, AI2);
1493 if (auto II = dyn_cast<InvokeInst>(Bound)) {
1494 B.SetInsertPoint(getFirstNonPHI(II->getNormalDest()));
1495 } else {
1496 B.SetInsertPoint(Bound->getNextNode());
1497 }
1498 B.CreateStore(B.CreateLoad(AI2->getAllocatedType(), AI2), AI);
1499 addCallSiteNoCapture(Bound, i);
1500 }
1501 }
1502 PreservedAnalyses PA;
1503 PA.preserve<AssumptionAnalysis>();
1504 PA.preserve<TargetLibraryAnalysis>();
1505 PA.preserve<LoopAnalysis>();
1506 PA.preserve<DominatorTreeAnalysis>();
1507 PA.preserve<PostDominatorTreeAnalysis>();
1508 FAM.invalidate(NewF, PA);
1509}
1510
1511/// Perform recursive inlinining on NewF up to the given limit
1512static void ForceRecursiveInlining(Function *NewF, size_t Limit) {
1513 std::map<const Function *, RecurType> RecurResults;
1514 for (size_t count = 0; count < Limit; count++) {
1515 for (auto &BB : *NewF) {
1516 for (auto &I : BB) {
1517 if (auto CI = dyn_cast<CallInst>(&I)) {
1518 if (CI->getCalledFunction() == nullptr)
1519 continue;
1520 if (CI->getCalledFunction()->empty())
1521 continue;
1522 if (startsWith(CI->getCalledFunction()->getName(),
1523 "_ZN3std2io5stdio6_print"))
1524 continue;
1525 if (startsWith(CI->getCalledFunction()->getName(), "_ZN4core3fmt"))
1526 continue;
1527 if (startsWith(CI->getCalledFunction()->getName(),
1528 "enzyme_wrapmpi$$"))
1529 continue;
1530 if (CI->getCalledFunction()->hasFnAttribute(
1531 Attribute::ReturnsTwice) ||
1532 CI->getCalledFunction()->hasFnAttribute(Attribute::NoInline))
1533 continue;
1534 if (IsFunctionRecursive(CI->getCalledFunction(), RecurResults)) {
1535 LLVM_DEBUG(llvm::dbgs()
1536 << "not inlining recursive "
1537 << CI->getCalledFunction()->getName() << "\n");
1538 continue;
1539 }
1540 InlineFunctionInfo IFI;
1541 InlineFunction(*CI, IFI);
1542 goto outermostContinue;
1543 }
1544 }
1545 }
1546
1547 // No functions were inlined, break
1548 break;
1549
1550 outermostContinue:;
1551 }
1552}
1553
1554void CanonicalizeLoops(Function *F, FunctionAnalysisManager &FAM) {
1555 LoopSimplifyPass().run(*F, FAM);
1556 DominatorTree &DT = FAM.getResult<DominatorTreeAnalysis>(*F);
1557 LoopInfo &LI = FAM.getResult<LoopAnalysis>(*F);
1558 AssumptionCache &AC = FAM.getResult<AssumptionAnalysis>(*F);
1559 TargetLibraryInfo &TLI = FAM.getResult<TargetLibraryAnalysis>(*F);
1560 MustExitScalarEvolution SE(*F, TLI, AC, DT, LI);
1561 for (Loop *L : LI.getLoopsInPreorder()) {
1562 auto pair =
1563 InsertNewCanonicalIV(L, Type::getInt64Ty(F->getContext()), "iv");
1564 PHINode *CanonicalIV = pair.first;
1565 assert(CanonicalIV);
1567 L, CanonicalIV, pair.second, SE,
1568 [&](Instruction *I, Value *V) { I->replaceAllUsesWith(V); },
1569 [&](Instruction *I) { I->eraseFromParent(); });
1570 }
1571 PreservedAnalyses PA;
1572 PA.preserve<AssumptionAnalysis>();
1573 PA.preserve<TargetLibraryAnalysis>();
1574 PA.preserve<LoopAnalysis>();
1575 PA.preserve<DominatorTreeAnalysis>();
1576 PA.preserve<PostDominatorTreeAnalysis>();
1577 PA.preserve<TypeBasedAA>();
1578 PA.preserve<BasicAA>();
1579 PA.preserve<ScopedNoAliasAA>();
1580 FAM.invalidate(*F, PA);
1581}
1582
1583void RemoveRedundantPHI(Function *F, FunctionAnalysisManager &FAM) {
1584 DominatorTree &DT = FAM.getResult<DominatorTreeAnalysis>(*F);
1585 for (BasicBlock &BB : *F) {
1586 for (BasicBlock::iterator II = BB.begin(); isa<PHINode>(II);) {
1587 PHINode *PN = cast<PHINode>(II);
1588 ++II;
1589 SmallPtrSet<Value *, 2> vals;
1590 SmallPtrSet<PHINode *, 2> done;
1591 SmallVector<PHINode *, 2> todo = {PN};
1592 while (todo.size() > 0) {
1593 PHINode *N = todo.back();
1594 todo.pop_back();
1595 if (done.count(N))
1596 continue;
1597 done.insert(N);
1598 if (vals.size() == 0 && todo.size() == 0 && PN != N &&
1599 DT.dominates(N, PN)) {
1600 vals.insert(N);
1601 break;
1602 }
1603 for (auto &v : N->incoming_values()) {
1604 if (isa<UndefValue>(v))
1605 continue;
1606 if (auto NN = dyn_cast<PHINode>(v)) {
1607 todo.push_back(NN);
1608 continue;
1609 }
1610 vals.insert(v);
1611 if (vals.size() > 1)
1612 break;
1613 }
1614 if (vals.size() > 1)
1615 break;
1616 }
1617 if (vals.size() == 1) {
1618 auto V = *vals.begin();
1619 if (!isa<Instruction>(V) || DT.dominates(cast<Instruction>(V), PN)) {
1620 PN->replaceAllUsesWith(V);
1621 PN->eraseFromParent();
1622 }
1623 }
1624 }
1625 }
1626}
1627
1629 // Explicitly chose AA passes that are stateless
1630 // and will not be invalidated
1631 FAM.registerPass([] { return TypeBasedAA(); });
1632 FAM.registerPass([] { return BasicAA(); });
1633 MAM.registerPass([] { return GlobalsAA(); });
1634 // CallGraphAnalysis required for GlobalsAA
1635 MAM.registerPass([] { return CallGraphAnalysis(); });
1636
1637 FAM.registerPass([] { return ScopedNoAliasAA(); });
1638
1639 // SCEVAA causes some breakage/segfaults
1640 // disable for now, consider enabling in future
1641 // FAM.registerPass([] { return SCEVAA(); });
1642
1643#if LLVM_VERSION_MAJOR < 16
1645 FAM.registerPass([] { return CFLSteensAA(); });
1646#endif
1647
1648 MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
1649 FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });
1650
1651 LAM.registerPass([&] { return FunctionAnalysisManagerLoopProxy(FAM); });
1652 FAM.registerPass([&] { return LoopAnalysisManagerFunctionProxy(LAM); });
1653
1654 FAM.registerPass([] {
1655 auto AM = AAManager();
1656 AM.registerFunctionAnalysis<BasicAA>();
1657 AM.registerFunctionAnalysis<TypeBasedAA>();
1658 AM.registerModuleAnalysis<GlobalsAA>();
1659 AM.registerFunctionAnalysis<ScopedNoAliasAA>();
1660
1661 // broken for different reasons
1662 // AM.registerFunctionAnalysis<SCEVAA>();
1663
1664#if LLVM_VERSION_MAJOR < 16
1666 AM.registerFunctionAnalysis<CFLSteensAA>();
1667#endif
1668
1669 return AM;
1670 });
1671
1672 PassBuilder PB;
1673 PB.registerModuleAnalyses(MAM);
1674 PB.registerFunctionAnalyses(FAM);
1675 PB.registerLoopAnalyses(LAM);
1676}
1677
1678llvm::AAResults &
1680 return FAM.getResult<AAManager>(*NewF);
1681}
1682
1683void setFullWillReturn(Function *NewF) {
1684 for (auto &BB : *NewF) {
1685 for (auto &I : BB) {
1686 if (auto CI = dyn_cast<CallInst>(&I)) {
1687 CI->addFnAttr(Attribute::WillReturn);
1688 CI->addFnAttr(Attribute::MustProgress);
1689 }
1690 if (auto CI = dyn_cast<InvokeInst>(&I)) {
1691 CI->addFnAttr(Attribute::WillReturn);
1692 CI->addFnAttr(Attribute::MustProgress);
1693 }
1694 }
1695 }
1696}
1697
1698void SplitPHIs(llvm::Function &F) {
1699 SetVector<Instruction *> todo;
1700 for (auto &BB : F) {
1701 for (auto &I : BB) {
1702 if (isa<PHINode>(&I)) {
1703 todo.insert(&I);
1704 } else if (isa<SelectInst>(&I)) {
1705 todo.insert(&I);
1706 }
1707 }
1708 }
1709 while (todo.size()) {
1710 auto cur = todo.pop_back_val();
1711 IRBuilder<> B(cur);
1712 auto ST = dyn_cast<StructType>(cur->getType());
1713 if (!ST)
1714 continue;
1715 bool justExtract = true;
1716 for (auto U : cur->users()) {
1717 if (!isa<ExtractValueInst>(U)) {
1718 justExtract = false;
1719 break;
1720 }
1721 if (cast<ExtractValueInst>(U)->getIndices().size() == 0) {
1722 justExtract = false;
1723 break;
1724 }
1725 }
1726 if (!justExtract)
1727 continue;
1728
1729 SmallVector<Value *, 1> replacements;
1730 for (size_t i = 0, e = ST->getNumElements(); i < e; i++) {
1731 if (auto cur2 = dyn_cast<PHINode>(cur)) {
1732 auto nPhi =
1733 B.CreatePHI(ST->getElementType(i), cur2->getNumIncomingValues(),
1734 cur->getName() + ".extract." + std::to_string(i));
1735 for (auto &&[blk, val] :
1736 llvm::zip(cur2->blocks(), cur2->incoming_values())) {
1737 IRBuilder B2(blk->getTerminator());
1738 nPhi->addIncoming(GradientUtils::extractMeta(B2, val, i), blk);
1739 }
1740 replacements.push_back(nPhi);
1741 todo.insert(nPhi);
1742 } else {
1743 auto cur3 = cast<SelectInst>(cur);
1744 auto falseV = GradientUtils::extractMeta(B, cur3->getFalseValue(), i);
1745 auto trueV = GradientUtils::extractMeta(B, cur3->getTrueValue(), i);
1746 auto rep =
1747 B.CreateSelect(cur3->getCondition(), trueV, falseV,
1748 cur->getName() + ".extract." + std::to_string(i));
1749 replacements.push_back(rep);
1750 if (auto sel = dyn_cast<SelectInst>(rep))
1751 todo.insert(sel);
1752 }
1753 }
1754 for (auto &U : make_early_inc_range(cur->uses())) {
1755 auto user = cast<ExtractValueInst>(U.getUser());
1756 Value *rep = replacements[user->getIndices()[0]];
1757 IRBuilder<> B(user);
1758 if (user->getIndices().size() > 1)
1759 rep = B.CreateExtractValue(rep, user->getIndices().slice(1));
1760 assert(rep->getType() == user->getType());
1761 user->replaceAllUsesWith(rep);
1762 user->eraseFromParent();
1763 }
1764 cur->eraseFromParent();
1765 }
1766}
1767
1768// returns if newly changed, subject to the pending calls
1769// Whether a parameter carries the function's result: an sret (also one still
1770// marked for Enzyme.jl's calling-convention fixup), a Julia sret union, or the
1771// roots of a returned value.
1772static bool isReturnLikeParam(const Function &F, unsigned argno) {
1773 if (F.hasParamAttribute(argno, Attribute::StructRet))
1774 return true;
1775 for (auto name : {"enzyme_sret", "enzyme_sret_v", "enzymejl_returnRoots",
1776 "enzymejl_sret_union_bytes"})
1777 if (F.getAttribute(argno + AttributeList::FirstArgIndex, name).isValid())
1778 return true;
1779 return false;
1780}
1781
1782// Whether a derivative of F comes from a custom rule rather than from F's
1783// body: one registered by a frontend (Enzyme.jl marks the function
1784// enzyme_math=enzyme_custom) or through metadata
1785// (__enzyme_register_derivative and friends). Such a rule (augmented forward,
1786// reverse, or the forward-mode replacement) may read a parameter the body
1787// never reads, so the body alone cannot justify marking a parameter writeonly
1788// or readnone, except for one that only carries the result. A marking the
1789// function already carries is kept: it was stated deliberately. A function
1790// marked enzyme_custom_full_attributes is exempt: the frontend states that its
1791// rule accesses no more than the body does (Enzyme.jl sets it for @easy_rule,
1792// whose rule only combines the inputs the way the body does).
1793static bool mayReadThroughCustomRule(Function &F, unsigned argno) {
1794 if (getFuncName(&F) != "enzyme_custom" && !hasCustomRuleMetadata(&F))
1795 return false;
1796 if (F.hasFnAttribute("enzyme_custom_full_attributes"))
1797 return false;
1798 return !isReturnLikeParam(F, argno);
1799}
1800
1801bool DetectPointerArgOfFn(llvm::Function &F,
1802 SmallPtrSetImpl<Function *> &calls_todo) {
1803 if (F.empty())
1804 return false;
1805 bool changed = false;
1806 for (auto &arg : F.args()) {
1807 if (!arg.getType()->isPointerTy())
1808 continue;
1809 // Store list of values we need to check
1810 std::deque<Value *> todo = {&arg};
1811 SmallPtrSet<Value *, 1> seen;
1812
1813 bool captured = false;
1814 bool read = false;
1815 bool written = false;
1816
1817 AttributeList Attrs = arg.getParent()->getAttributes();
1818
1819 // We have already hit the max state.
1820
1821 if (Attrs.hasParamAttr(arg.getArgNo(), Attribute::ReadNone) &&
1822 arg.hasNoCaptureAttr())
1823 continue;
1824
1825 while (!todo.empty()) {
1826 auto cur = todo.back();
1827 todo.pop_back();
1828 if (seen.contains(cur))
1829 continue;
1830 seen.insert(cur);
1831 for (auto &U : cur->uses()) {
1832 auto I = cast<Instruction>(U.getUser());
1833 if (isPointerArithmeticInst(I)) {
1834 todo.push_back(I);
1835 continue;
1836 }
1837 if (isa<LoadInst>(I)) {
1838 read = true;
1839 continue;
1840 }
1841 if (auto SI = dyn_cast<StoreInst>(I)) {
1842 if (SI->getValueOperand() == cur) {
1843 captured = true;
1844 read = true;
1845 written = true;
1846 break;
1847 }
1848 if (SI->getPointerOperand() == cur) {
1849 written = true;
1850 continue;
1851 }
1852 }
1853 if (auto MSI = dyn_cast<MemSetInst>(I)) {
1854 if (MSI->getRawDest() == cur) {
1855 written = true;
1856 }
1857 continue;
1858 }
1859 if (auto MTI = dyn_cast<MemTransferInst>(I)) {
1860 if (MTI->getRawDest() == cur) {
1861 written = true;
1862 }
1863 if (MTI->getRawSource() == cur) {
1864 read = true;
1865 }
1866 continue;
1867 }
1868 if (auto CB = dyn_cast<CallBase>(I)) {
1869
1870 if (CB->getCalledOperand() == cur) {
1871 captured = true;
1872 read = true;
1873 written = true;
1874 break;
1875 }
1876
1877 auto F2 = dyn_cast<Function>(CB->getCalledOperand());
1878
1879 if (F2 == &F && U.getOperandNo() == arg.getArgNo()) {
1880 continue;
1881 }
1882
1883 auto name = getFuncNameFromCall(CB);
1884
1885 if (name == "julia.write_barrier") {
1886 continue;
1887 }
1888
1889 // Used as operand bundle
1890 if (U.getOperandNo() >= CB->arg_size()) {
1891 captured = true;
1892 read = true;
1893 written = true;
1894 break;
1895 }
1896
1897 if (!isNoCapture(CB, U.getOperandNo()) && !arg.hasNoCaptureAttr()) {
1898 captured = true;
1899 if (F2)
1900 calls_todo.insert(F2);
1901 }
1902
1903 if (!isReadOnly(CB, U.getOperandNo()) &&
1904 !Attrs.hasParamAttr(arg.getArgNo(), Attribute::ReadNone) &&
1905 !Attrs.hasParamAttr(arg.getArgNo(), Attribute::ReadOnly)) {
1906 written = true;
1907 if (F2)
1908 calls_todo.insert(F2);
1909 }
1910
1911 if (!isWriteOnly(CB, U.getOperandNo()) &&
1912 !Attrs.hasParamAttr(arg.getArgNo(), Attribute::ReadNone) &&
1913 !Attrs.hasParamAttr(arg.getArgNo(), Attribute::WriteOnly)) {
1914 read = true;
1915 if (F2)
1916 calls_todo.insert(F2);
1917 }
1918 continue;
1919 }
1920
1921 captured = true;
1922 read = true;
1923 written = true;
1924 break;
1925 }
1926 }
1927
1928 if (!captured && !arg.hasNoCaptureAttr()) {
1929 addFunctionNoCapture(arg.getParent(), arg.getArgNo());
1930 changed = true;
1931 }
1932
1933 if (mayReadThroughCustomRule(F, arg.getArgNo())) {
1934 // Whatever the function was already marked with stays; only readonly
1935 // may be concluded from the body.
1936 if (!written &&
1937 !Attrs.hasParamAttr(arg.getArgNo(), Attribute::ReadOnly) &&
1938 !Attrs.hasParamAttr(arg.getArgNo(), Attribute::ReadNone) &&
1939 !Attrs.hasParamAttr(arg.getArgNo(), Attribute::WriteOnly)) {
1940 arg.addAttr(Attribute::ReadOnly);
1941 changed = true;
1942 }
1943 } else if ((!read && !written) ||
1944 Attrs.hasParamAttr(arg.getArgNo(), Attribute::ReadNone)) {
1945 if (!Attrs.hasParamAttr(arg.getArgNo(), Attribute::ReadNone)) {
1946 if (Attrs.hasParamAttr(arg.getArgNo(), Attribute::ReadOnly)) {
1947 arg.removeAttr(Attribute::ReadOnly);
1948 }
1949 if (Attrs.hasParamAttr(arg.getArgNo(), Attribute::WriteOnly)) {
1950 arg.removeAttr(Attribute::WriteOnly);
1951 }
1952#if LLVM_VERSION_MAJOR >= 18
1953 if (Attrs.hasParamAttr(arg.getArgNo(), Attribute::Writable)) {
1954 arg.removeAttr(Attribute::Writable);
1955 }
1956#endif
1957 arg.addAttr(Attribute::ReadNone);
1958 changed = true;
1959 }
1960 } else if (!written) {
1961 if (!Attrs.hasParamAttr(arg.getArgNo(), Attribute::ReadOnly)) {
1962 arg.addAttr(Attribute::ReadOnly);
1963 changed = true;
1964 }
1965#if LLVM_VERSION_MAJOR >= 18
1966 if (Attrs.hasParamAttr(arg.getArgNo(), Attribute::Writable)) {
1967 arg.removeAttr(Attribute::Writable);
1968 }
1969#endif
1970 } else if (!read) {
1971 if (!Attrs.hasParamAttr(arg.getArgNo(), Attribute::WriteOnly)) {
1972 arg.addAttr(Attribute::WriteOnly);
1973 changed = true;
1974 }
1975 }
1976 }
1977 return changed;
1978}
1979
1980// returns if newly changed, subject to the pending calls
1981bool DetectNoUnwindOfFn(llvm::Function &F,
1982 SmallPtrSetImpl<Function *> &calls_todo) {
1983 if (F.empty())
1984 return false;
1985 if (F.doesNotThrow())
1986 return false;
1987
1988 bool mayThrow = false;
1989
1990 for (auto &BB : F) {
1991 for (auto &I : BB) {
1992#if LLVM_VERSION_MAJOR >= 17
1993 if (!I.mayThrow(/*IncludePhaseOneUnwind*/ true)) {
1994 continue;
1995 }
1996#else
1997 if (!I.mayThrow()) {
1998 continue;
1999 }
2000#endif
2001 if (auto CB = dyn_cast<CallBase>(&I)) {
2002 if (auto F2 = CB->getCalledFunction()) {
2003 if (F2 == &F)
2004 continue;
2005 if (F2->doesNotThrow())
2006 continue;
2007 calls_todo.insert(F2);
2008 }
2009 }
2010 mayThrow = true;
2011 break;
2012 }
2013 }
2014 if (mayThrow)
2015 return false;
2016 F.setDoesNotThrow();
2017 return true;
2018}
2019
2020// Checks whether a function is read only or throw, and whether only locally
2021// (see `enzyme_LocalReadOnlyOrThrow`). Calls to functions not yet known to be
2022// read only or throw are collected in `calls_todo`, and the function is only
2023// marked once there are none.
2025public:
2026 ReadOnlyOrThrowDetector(llvm::Function &F,
2027 SmallPtrSetImpl<Function *> &calls_todo,
2028 llvm::TargetLibraryInfo &TLI, bool &local)
2029 : F(F), calls_todo(calls_todo), TLI(TLI), local(local),
2030 canHandBack(!cannotHandBackPointer(F)) {}
2031
2032 // Returns if newly legal, subject to the pending calls.
2033 bool run();
2034
2035private:
2036 using FreshFieldSet = SmallSet<std::pair<Value *, int64_t>, 4>;
2037 // Objects with the offset of a field of theirs holding fresh data.
2038 using FreshFields = SmallVectorImpl<std::pair<Value *, int64_t>>;
2039 using Fields = SmallVector<std::pair<Value *, int64_t>, 2>;
2040 // Values that may be a pointer to written memory (Holds false), or that point
2041 // to memory that may hold such a pointer (Holds true). With Deep, memory
2042 // reachable through pointers loaded from the value was written too.
2043 using EscapeEntry = std::tuple<Value *, bool, bool>;
2044 using EscapeWorklist = SmallVector<EscapeEntry, 4>;
2045
2046 llvm::Function &F;
2047 SmallPtrSetImpl<Function *> &calls_todo;
2048 llvm::TargetLibraryInfo &TLI;
2049 bool &local;
2050 const bool canHandBack;
2051
2052 // The escape queries are cached per written object, as a function often
2053 // writes the same object many times.
2054 std::map<std::tuple<Value *, bool, Instruction *>, bool> reachCache;
2055 std::map<Value *, std::optional<Fields>> freshCache;
2056 std::map<std::pair<Value *, int64_t>, bool> fieldCache;
2057
2058 // Whether F is local only matters once all its callees are known: with a
2059 // callee pending, F is checked again later, so the escape queries deciding
2060 // it are skipped. They are also skipped if F cannot hand back a pointer at
2061 // all.
2062 bool needLocal() const { return canHandBack && !local && calls_todo.empty(); }
2063
2064 // Whether Obj may reach the return value (see mayReachReturn).
2065 bool reaches(Value *Obj, bool Deep, Instruction *Ignore = nullptr) {
2066 auto key = std::make_tuple(Obj, Deep, Ignore);
2067 auto found = reachCache.find(key);
2068 if (found != reachCache.end())
2069 return found->second;
2070 bool res = mayReachReturn(Obj, Deep, Ignore);
2071 reachCache[key] = res;
2072 return res;
2073 }
2074
2075 // For written data that is fresh (see isFreshData), the fields holding it;
2076 // null if it is not fresh.
2077 const Fields *freshFields(Value *Obj) {
2078 auto found = freshCache.find(Obj);
2079 if (found == freshCache.end()) {
2080 Fields bases;
2081 std::optional<Fields> entry;
2082 if (isFreshData(Obj, bases))
2083 entry = std::move(bases);
2084 found = freshCache.emplace(Obj, std::move(entry)).first;
2085 }
2086 return found->second ? &*found->second : nullptr;
2087 }
2088
2089 // Whether written fresh data Obj, held in fields, may reach the return
2090 // value: by itself, or through one of those fields.
2091 bool freshReaches(Value *Obj, const Fields &fields) {
2092 if (reaches(Obj, /*Deep*/ false))
2093 return true;
2094 for (auto field : fields) {
2095 auto found = fieldCache.find(field);
2096 if (found == fieldCache.end())
2097 found = fieldCache
2098 .emplace(field, freshFieldMayReachReturn(field.first,
2099 field.second))
2100 .first;
2101 if (found->second)
2102 return true;
2103 }
2104 return false;
2105 }
2106
2107 void addReadOnlyOrThrowAttributes();
2108 static bool isJuliaMemoryAllocation(Value *V);
2109 static bool isJuliaArrayAllocation(Value *V);
2110 bool allocatorLeavesFieldsUninitialized(CallBase *CB);
2111 bool fieldOnlyHoldsFreshData(Instruction *Obj, int64_t Field,
2112 FreshFieldSet &seen, FreshFields *bases);
2113 bool isFreshField(Value *V, int64_t Field, FreshFieldSet &seen,
2114 FreshFields *bases);
2115 bool isFreshData(Value *Obj, FreshFieldSet &seen, FreshFields *bases);
2116 bool isFreshData(Value *Obj, FreshFields &bases);
2117 static bool typeTreeRulesOutPointer(const TypeTree &TT, Type *T,
2118 const DataLayout &DL);
2119 static bool enzymeTypeRulesOutPointer(Attribute attr, Type *T,
2120 const DataLayout &DL);
2121 static bool typeCannotHoldPointer(Type *T, const DataLayout &DL);
2122 static bool cannotHoldPointer(Value *V);
2123 static bool returnCannotHoldPointer(ReturnInst *RI);
2124 static bool cannotHandBackPointer(Function &F);
2125 static bool hasSRetLikeArgument(CallBase *CB);
2126 static bool mayHandBackPointer(CallBase *CB);
2127 static void pushMayBePointer(EscapeWorklist &todo, Value *V, bool Deep);
2128 bool mayReachReturn(Value *Obj, bool Deep, Instruction *Ignore = nullptr);
2129 bool freshFieldMayReachReturn(Value *Base, int64_t Field);
2130};
2131
2132// Restate what `enzyme_ReadOnlyOrThrow` / `enzyme_LocalReadOnlyOrThrow` mean in
2133// attributes LLVM's alias analysis understands: the function reads any memory,
2134// writes only inaccessible memory (the exception it may allocate, GC
2135// bookkeeping) and, for the local variant, its `sret`-like arguments; every
2136// other pointer argument is `readonly`. Effects a location already had are
2137// kept when they are tighter. The derivative of a function marked here does
2138// not inherit them: `CloneFunctionWithReturns` builds it afresh and only
2139// copies `readonly` onto the primal arguments, which the derivative reads.
2140//
2141// This is only valid with Julia's setjmp/longjmp exceptions: the analysis
2142// permits writes on paths that throw, and there such a write is only
2143// observable after control re-enters through a `returns_twice` call, which
2144// LLVM already treats as clobbering all memory. With `invoke`/landingpad
2145// exception handling the landing pad succeeds the throwing call itself, so
2146// the attributes would be wrong.
2147void ReadOnlyOrThrowDetector::addReadOnlyOrThrowAttributes() {
2149 return;
2150#if LLVM_VERSION_MAJOR >= 16
2151 MemoryEffects derived =
2152 MemoryEffects::readOnly() |
2153 MemoryEffects::inaccessibleMemOnly(ModRefInfo::ModRef);
2154 if (local)
2155 derived |= MemoryEffects::argMemOnly(ModRefInfo::ModRef);
2156 F.setMemoryEffects(F.getMemoryEffects() & derived);
2157#endif
2158 // Before LLVM 16 there is no attribute for "reads anything, writes only
2159 // inaccessible memory": `readonly` would also let an unused `nounwind` call
2160 // be deleted, so only the parameters are annotated there.
2161 for (auto &arg : F.args()) {
2162 if (!arg.getType()->isPointerTy())
2163 continue;
2164 if (local) {
2165 if (arg.hasStructRetAttr() ||
2166 F.getAttribute(arg.getArgNo() + AttributeList::FirstArgIndex,
2167 "enzymejl_returnRoots")
2168 .isValid() ||
2169 F.getAttribute(arg.getArgNo() + AttributeList::FirstArgIndex,
2170 "enzymejl_sret_union_bytes")
2171 .isValid())
2172 continue;
2173 }
2174 unsigned argno = arg.getArgNo();
2175 if (F.hasParamAttribute(argno, Attribute::ReadNone) ||
2176 F.hasParamAttribute(argno, Attribute::ReadOnly))
2177 continue;
2178 // Never written by us and never read per the existing attribute.
2179 if (F.hasParamAttribute(argno, Attribute::WriteOnly)) {
2180 if (!mayReadThroughCustomRule(F, argno)) {
2181 F.removeParamAttr(argno, Attribute::WriteOnly);
2182 F.addParamAttr(argno, Attribute::ReadNone);
2183 }
2184 continue;
2185 }
2186 F.addParamAttr(argno, Attribute::ReadOnly);
2187 }
2188}
2189
2190// Whether V is allocated by Julia's allocator of Memory objects, or of arrays
2191// on Julia 1.10. These are recognized by name, as Enzyme.jl may not have
2192// registered them as allocation functions.
2193bool ReadOnlyOrThrowDetector::isJuliaMemoryAllocation(Value *V) {
2194 auto CB = dyn_cast<CallBase>(V);
2195 if (!CB)
2196 return false;
2197 auto name = getFuncNameFromCall(CB);
2198 return name == "jl_alloc_genericmemory" ||
2199 name == "ijl_alloc_genericmemory" ||
2200 name == "jl_alloc_genericmemory_unchecked" ||
2201 name == "ijl_alloc_genericmemory_unchecked";
2202}
2203bool ReadOnlyOrThrowDetector::isJuliaArrayAllocation(Value *V) {
2204 auto CB = dyn_cast<CallBase>(V);
2205 if (!CB)
2206 return false;
2207 auto name = getFuncNameFromCall(CB);
2208 return name == "jl_alloc_array_1d" || name == "ijl_alloc_array_1d" ||
2209 name == "jl_alloc_array_2d" || name == "ijl_alloc_array_2d" ||
2210 name == "jl_alloc_array_3d" || name == "ijl_alloc_array_3d" ||
2211 name == "jl_new_array" || name == "ijl_new_array";
2212}
2213
2214// Whether allocation call CB returns memory whose fields it leaves
2215// uninitialized (or zeroed): the C and C++ allocators and Julia's object
2216// allocators. Allocators that initialize fields themselves (swift_allocObject
2217// sets a metadata pointer, custom enzyme_allocator and registered allocation
2218// handlers may do anything) do not qualify, as a field may then point to
2219// memory that existed before the call.
2220bool ReadOnlyOrThrowDetector::allocatorLeavesFieldsUninitialized(CallBase *CB) {
2221 auto name = getFuncNameFromCall(CB);
2222 if (name == "julia.gc_alloc_obj" || name == "jl_gc_alloc_typed" ||
2223 name == "ijl_gc_alloc_typed")
2224 return true;
2225 if (name == "swift_allocObject" || name == "enzyme_allocator" ||
2226 name == "__size_returning_new_experiment" ||
2227 name == "_mlir_memref_to_llvm_alloc")
2228 return false;
2229 if (shadowHandlers.find(name) != shadowHandlers.end())
2230 return false;
2231 return isAllocationFunction(name, TLI);
2232}
2233
2234// Whether the field at offset Field of Obj, an object allocated in its
2235// function, only ever holds fresh data (see isFreshData) while the function
2236// runs. Every write to the field must store such data (or null, through which
2237// nothing is written), and Obj's address must not be stored or passed to a
2238// call that may capture or write through it, so that no other code can write
2239// the field. Returning Obj is fine: the field may then change only after the
2240// function returns.
2241bool ReadOnlyOrThrowDetector::fieldOnlyHoldsFreshData(Instruction *Obj,
2242 int64_t Field,
2243 FreshFieldSet &seen,
2244 FreshFields *bases) {
2245 auto &DL = Obj->getModule()->getDataLayout();
2246 int64_t fieldSize = DL.getPointerSize();
2247 // Pointers derived from Obj, with their offset from it if constant.
2248 SmallVector<std::pair<Value *, std::optional<int64_t>>, 4> todo;
2249 SmallPtrSet<Value *, 8> visited;
2250 todo.emplace_back(Obj, 0);
2251 while (!todo.empty()) {
2252 auto [V, off] = todo.pop_back_val();
2253 if (!visited.insert(V).second)
2254 continue;
2255 for (auto U : V->users()) {
2256 auto I = dyn_cast<Instruction>(U);
2257 if (!I)
2258 return false;
2259 if (isa<LoadInst>(I) || isa<ICmpInst>(I) || isa<ReturnInst>(I))
2260 continue;
2261 if (auto CI = dyn_cast<CastInst>(I)) {
2262 if (!CI->getDestTy()->isPointerTy())
2263 return false;
2264 todo.emplace_back(CI, off);
2265 continue;
2266 }
2267 if (auto GEP = dyn_cast<GetElementPtrInst>(I)) {
2268 if (GEP->getPointerOperand() != V)
2269 return false;
2270 std::optional<int64_t> gepOff;
2271 APInt gepOffset(DL.getIndexSizeInBits(GEP->getPointerAddressSpace()),
2272 0);
2273 if (off && GEP->accumulateConstantOffset(DL, gepOffset))
2274 gepOff = *off + gepOffset.getSExtValue();
2275 todo.emplace_back(GEP, gepOff);
2276 continue;
2277 }
2278 if (isa<PHINode>(I) || isa<SelectInst>(I)) {
2279 if (auto SI = dyn_cast<SelectInst>(I))
2280 if (SI->getCondition() == V)
2281 return false;
2282 todo.emplace_back(I, off);
2283 continue;
2284 }
2285 if (auto SI = dyn_cast<StoreInst>(I)) {
2286 if (SI->getValueOperand() == V)
2287 return false;
2288 if (!off)
2289 return false;
2290 int64_t size = DL.getTypeStoreSize(SI->getValueOperand()->getType());
2291 if (*off + size <= Field || *off >= Field + fieldSize)
2292 continue;
2293 if (*off != Field || size != fieldSize)
2294 return false;
2295 auto val = getBaseObject(SI->getValueOperand());
2296 if (isa<ConstantPointerNull>(val) || isa<UndefValue>(val))
2297 continue;
2298 if (!isFreshData(val, seen, bases))
2299 return false;
2300 continue;
2301 }
2302 if (auto MI = dyn_cast<MemIntrinsic>(I)) {
2303 if (MI->getRawDest() != V)
2304 continue;
2305 auto len = dyn_cast<ConstantInt>(MI->getLength());
2306 if (!off || !len)
2307 return false;
2308 if (*off + (int64_t)len->getZExtValue() <= Field ||
2309 *off >= Field + fieldSize)
2310 continue;
2311 return false;
2312 }
2313 if (auto CB = dyn_cast<CallBase>(I)) {
2314 auto name = getFuncNameFromCall(CB);
2315 if (name == "julia.write_barrier")
2316 continue;
2317 if (CB->isLifetimeStartOrEnd())
2318 continue;
2319 for (unsigned i = 0; i < CB->arg_size(); ++i) {
2320 if (CB->getArgOperand(i) != V)
2321 continue;
2322 if (!CB->doesNotCapture(i) || !CB->onlyReadsMemory(i))
2323 return false;
2324 }
2325 if (CB->getCalledOperand() == V)
2326 return false;
2327 continue;
2328 }
2329 return false;
2330 }
2331 }
2332 return true;
2333}
2334
2335// Whether the field at offset Field of the base object V, which did not exist
2336// before this function, only ever holds fresh data (see isFreshData) while the
2337// function runs:
2338// * the data field, which follows the length, of a Julia Memory allocated
2339// here, or of the empty Memory singleton (a global Enzyme.jl marks
2340// enzymejl_empty_memory), which no in-bounds access writes. The field is
2341// set when the object is allocated and never changed.
2342// * the data field, at offset 0, of a Julia 1.10 array allocated here by
2343// jl_alloc_array_*, which sets it to data it allocates, or any field of an
2344// object allocated here by an allocator that leaves its fields
2345// uninitialized (such as a Julia 1.11+ array; see
2346// allocatorLeavesFieldsUninitialized), provided that every write to the
2347// field in the function sets it to fresh data (see
2348// fieldOnlyHoldsFreshData).
2349bool ReadOnlyOrThrowDetector::isFreshField(Value *V, int64_t Field,
2350 FreshFieldSet &seen,
2351 FreshFields *bases) {
2352 // A field we are already checking, reached again through a store to such a
2353 // field: it holds fresh data if everything else does.
2354 if (!seen.insert(std::make_pair(V, Field)).second)
2355 return true;
2356 if (auto GV = dyn_cast<GlobalVariable>(V))
2357 return GV->getMetadata("enzymejl_empty_memory") &&
2358 Field == GV->getParent()->getDataLayout().getPointerSize();
2359 auto I = dyn_cast<Instruction>(V);
2360 if (!I)
2361 return false;
2362 // Julia's runtime allocators initialize the other fields of the objects they
2363 // allocate themselves, possibly to memory that existed before the call.
2364 if (isJuliaMemoryAllocation(V))
2365 return Field == I->getModule()->getDataLayout().getPointerSize();
2366 if (isJuliaArrayAllocation(V))
2367 return Field == 0 && fieldOnlyHoldsFreshData(I, Field, seen, bases);
2368 // Other allocators may leave the fields uninitialized.
2369 if (isAllocationCall(V, TLI) &&
2370 allocatorLeavesFieldsUninitialized(cast<CallBase>(V)))
2371 return fieldOnlyHoldsFreshData(I, Field, seen, bases);
2372 return false;
2373}
2374
2375// Whether Obj, the base object of a written pointer, is data that did not
2376// exist before this function: a pointer loaded from a field holding only
2377// fresh data (see isFreshField) of an object, or of a phi or select of
2378// objects. If so, appends those objects with the field's offset to bases,
2379// along with the objects whose fields the fresh data stored into those fields
2380// was itself loaded from.
2381bool ReadOnlyOrThrowDetector::isFreshData(Value *Obj, FreshFieldSet &seen,
2382 FreshFields *bases) {
2384 return false;
2385 auto LI = dyn_cast<LoadInst>(Obj);
2386 if (!LI)
2387 return false;
2388 auto &DL = LI->getModule()->getDataLayout();
2389 Value *ptr = LI->getPointerOperand();
2390 APInt offset(DL.getIndexTypeSizeInBits(ptr->getType()), 0);
2391 ptr = ptr->stripAndAccumulateConstantOffsets(DL, offset,
2392 /*AllowNonInbounds*/ true);
2393 if (offset.isNegative())
2394 return false;
2395 for (auto base : getBaseObjects(ptr, /*offsetAllowed*/ false)) {
2396 if (!isFreshField(base, offset.getSExtValue(), seen, bases))
2397 return false;
2398 if (bases)
2399 bases->emplace_back(base, offset.getSExtValue());
2400 }
2401 return true;
2402}
2403
2404bool ReadOnlyOrThrowDetector::isFreshData(Value *Obj, FreshFields &bases) {
2405 FreshFieldSet seen;
2406 return isFreshData(Obj, seen, &bases);
2407}
2408
2409// Whether type info TT for a value of type T says every byte of it is an
2410// integer or a float, so that it cannot be or hold a pointer.
2411bool ReadOnlyOrThrowDetector::typeTreeRulesOutPointer(const TypeTree &TT,
2412 Type *T,
2413 const DataLayout &DL) {
2414 if (T->isVoidTy() || T->isTokenTy())
2415 return true;
2416 for (int i = 0, size = DL.getTypeStoreSize(T); i < size; ++i) {
2417 auto CT = TT[{i}];
2418 if (CT != BaseType::Integer && !CT.isFloat())
2419 return false;
2420 }
2421 return true;
2422}
2423
2424bool ReadOnlyOrThrowDetector::enzymeTypeRulesOutPointer(Attribute attr, Type *T,
2425 const DataLayout &DL) {
2426 return attr.isValid() &&
2427 typeTreeRulesOutPointer(
2428 TypeTree::parse(attr.getValueAsString(), T->getContext()), T, DL);
2429}
2430
2431// Whether a value of type T cannot be or hold a pointer: void and token
2432// values, floating-point values, and integers narrower than a pointer.
2433// Integers as wide as a pointer can hold one (ptrtoint, Julia's Ptr).
2434bool ReadOnlyOrThrowDetector::typeCannotHoldPointer(Type *T,
2435 const DataLayout &DL) {
2436 if (T->isVoidTy() || T->isTokenTy())
2437 return true;
2438 if (T->isFPOrFPVectorTy())
2439 return true;
2440 if (auto IT = dyn_cast<IntegerType>(T))
2441 return IT->getBitWidth() < DL.getPointerSizeInBits();
2442 return false;
2443}
2444
2445// Whether value V cannot be or hold a pointer by its type, or by the
2446// enzyme_type info given for it.
2447bool ReadOnlyOrThrowDetector::cannotHoldPointer(Value *V) {
2448 auto T = V->getType();
2449 if (T->isVoidTy() || T->isTokenTy())
2450 return true;
2451 auto I = dyn_cast<Instruction>(V);
2452 if (!I)
2453 return false;
2454 auto &DL = I->getModule()->getDataLayout();
2455 if (typeCannotHoldPointer(T, DL))
2456 return true;
2457 if (auto MD = I->getMetadata("enzyme_type"))
2458 if (typeTreeRulesOutPointer(TypeTree::fromMD(MD), T, DL))
2459 return true;
2460 if (auto CB = dyn_cast<CallBase>(I)) {
2461 if (enzymeTypeRulesOutPointer(
2462 CB->getAttributes().getAttribute(AttributeList::ReturnIndex,
2463 "enzyme_type"),
2464 T, DL))
2465 return true;
2466 if (auto F = CB->getCalledFunction())
2467 if (enzymeTypeRulesOutPointer(
2468 F->getAttributes().getAttribute(AttributeList::ReturnIndex,
2469 "enzyme_type"),
2470 T, DL))
2471 return true;
2472 }
2473 return false;
2474}
2475
2476// Whether returning V from its function cannot return a pointer, by the
2477// enzyme_type info given for V or for the function's return value.
2478bool ReadOnlyOrThrowDetector::returnCannotHoldPointer(ReturnInst *RI) {
2479 auto V = RI->getReturnValue();
2480 if (!V || cannotHoldPointer(V))
2481 return true;
2482 auto F = RI->getParent()->getParent();
2483 return enzymeTypeRulesOutPointer(
2484 F->getAttributes().getAttribute(AttributeList::ReturnIndex,
2485 "enzyme_type"),
2486 V->getType(), F->getParent()->getDataLayout());
2487}
2488
2489// Whether F cannot hand a pointer back to its caller at all: its return value
2490// cannot hold one, by its type or enzyme_type info, and it has no sret-like
2491// argument (sret, returnRoots or sret_union_bytes). Then no memory it writes
2492// can reach its caller other than by a write that disqualifies it anyway.
2493bool ReadOnlyOrThrowDetector::cannotHandBackPointer(Function &F) {
2494 auto T = F.getReturnType();
2495 auto &DL = F.getParent()->getDataLayout();
2496 if (!T->isVoidTy() && !typeCannotHoldPointer(T, DL) &&
2497 !enzymeTypeRulesOutPointer(F.getAttributes().getAttribute(
2498 AttributeList::ReturnIndex, "enzyme_type"),
2499 T, DL))
2500 return false;
2501 for (auto &A : F.args()) {
2502 if (A.hasStructRetAttr())
2503 return false;
2504 auto idx = A.getArgNo() + AttributeList::FirstArgIndex;
2505 if (F.getAttribute(idx, "enzymejl_returnRoots").isValid() ||
2506 F.getAttribute(idx, "enzymejl_sret_union_bytes").isValid())
2507 return false;
2508 }
2509 return true;
2510}
2511
2512// Whether call CB has an sret-like argument (sret, returnRoots or
2513// sret_union_bytes), marked on the call or on the called function.
2514bool ReadOnlyOrThrowDetector::hasSRetLikeArgument(CallBase *CB) {
2516 return true;
2517 auto F = getFunctionFromCall(CB);
2518 for (unsigned i = 0; i < CB->arg_size(); ++i) {
2519 // Checks the called function too.
2520 if (CB->paramHasAttr(i, Attribute::StructRet))
2521 return true;
2522 if (F && i < F->arg_size() &&
2523 (F->getAttribute(i + AttributeList::FirstArgIndex,
2524 "enzymejl_returnRoots")
2525 .isValid() ||
2526 F->getAttribute(i + AttributeList::FirstArgIndex,
2527 "enzymejl_sret_union_bytes")
2528 .isValid()))
2529 return true;
2530 }
2531 return false;
2532}
2533
2534// Whether call CB may hand a pointer back to its caller, through its result or
2535// an sret-like argument.
2536bool ReadOnlyOrThrowDetector::mayHandBackPointer(CallBase *CB) {
2537 return !cannotHoldPointer(CB) || hasSRetLikeArgument(CB);
2538}
2539
2540void ReadOnlyOrThrowDetector::pushMayBePointer(EscapeWorklist &todo, Value *V,
2541 bool Deep) {
2542 todo.emplace_back(V, false, Deep);
2543 // Memory reachable through pointers loaded from it was written too.
2544 if (Deep)
2545 todo.emplace_back(V, true, Deep);
2546}
2547
2548// Whether Obj, memory written within this function, may reach its return
2549// value, so that the function returns memory holding data it wrote. With
2550// Deep, memory reachable from Obj through pointers loaded from it was written
2551// too (e.g. by a callee that handed Obj to us), and counts as well. Ignore is
2552// a use of Obj not to consider (e.g. the call that wrote it).
2553//
2554// Storing a pointer to written memory into memory that existed before the
2555// call hands it to the caller (or is a write that disqualifies the function
2556// anyway). Storing it into memory of the function (an alloca, an allocation,
2557// or data of one) makes that memory hold it, which is followed through what
2558// is loaded or copied from that memory, and from any object owning it.
2559bool ReadOnlyOrThrowDetector::mayReachReturn(Value *Obj, bool Deep,
2560 Instruction *Ignore) {
2561 EscapeWorklist todo;
2562 SmallSet<EscapeEntry, 8> seen;
2563 pushMayBePointer(todo, Obj, Deep);
2564 while (!todo.empty()) {
2565 auto Entry = todo.pop_back_val();
2566 if (!seen.insert(Entry).second)
2567 continue;
2568 auto [V, Holds, Deep] = Entry;
2569 for (auto U : V->users()) {
2570 auto I = dyn_cast<Instruction>(U);
2571 if (!I)
2572 return true;
2573 if (I == Ignore)
2574 continue;
2575 if (auto RI = dyn_cast<ReturnInst>(I)) {
2576 if (returnCannotHoldPointer(RI))
2577 continue;
2578 return true;
2579 }
2580 if (isa<ICmpInst>(I))
2581 continue;
2582 if (auto SI = dyn_cast<SelectInst>(I)) {
2583 if (SI->getCondition() != V)
2584 todo.emplace_back(SI, Holds, Deep);
2585 continue;
2586 }
2587 if (isPointerArithmeticInst(I, /*includephi*/ true,
2588 /*includebin*/ false) ||
2589 isa<InsertValueInst>(I) || isa<ExtractValueInst>(I)) {
2590 todo.emplace_back(I, Holds, Deep);
2591 continue;
2592 }
2593 if (auto LI = dyn_cast<LoadInst>(I)) {
2594 if (Holds && !cannotHoldPointer(LI))
2595 pushMayBePointer(todo, LI, Deep);
2596 continue;
2597 }
2598 if (auto SI = dyn_cast<StoreInst>(I)) {
2599 if (SI->getValueOperand() != V)
2600 continue;
2601 // The address of memory holding a written pointer escapes.
2602 if (Holds)
2603 return true;
2604 auto Dst = getBaseObject(SI->getPointerOperand());
2605 if (isa<Argument>(Dst) || isa<GlobalValue>(Dst))
2606 return true;
2607 if (isa<AllocaInst>(Dst)) {
2608 todo.emplace_back(Dst, true, Deep);
2609 continue;
2610 }
2611 // Memory of ours now holds the pointer: anything loaded from it, or
2612 // reached from an object whose fresh data it is, may be the pointer.
2613 todo.emplace_back(Dst, true, /*Deep*/ true);
2614 SmallVector<std::pair<Value *, int64_t>, 2> owners;
2615 if (isFreshData(Dst, owners))
2616 for (auto owner : owners)
2617 if (!isa<GlobalVariable>(owner.first))
2618 todo.emplace_back(owner.first, true, /*Deep*/ true);
2619 continue;
2620 }
2621 if (auto MTI = dyn_cast<MemTransferInst>(I)) {
2622 if (Holds && MTI->getRawSource() == V) {
2623 auto Dst = getBaseObject(MTI->getRawDest());
2624 if (!isa<AllocaInst>(Dst))
2625 return true;
2626 todo.emplace_back(Dst, true, Deep);
2627 }
2628 continue;
2629 }
2630 if (isa<MemSetInst>(I))
2631 continue;
2632 if (auto CB = dyn_cast<CallBase>(I)) {
2633 if (CB->getCalledOperand() == V)
2634 return true;
2635 if (CB->isLifetimeStartOrEnd() || isDeallocationCall(CB, TLI))
2636 continue;
2637 if (getFuncNameFromCall(CB) == "julia.write_barrier")
2638 continue;
2639 if (!mayHandBackPointer(CB))
2640 continue;
2641 // A call may hand back a pointer it was passed unless it does not
2642 // capture it, but may hand back any pointer it loads.
2643 if (!Holds) {
2644 bool noCapture = true;
2645 for (unsigned i = 0; i < CB->arg_size(); ++i)
2646 if (CB->getArgOperand(i) == V && !isNoCapture(CB, i))
2647 noCapture = false;
2648 if (noCapture)
2649 continue;
2650 }
2651 return true;
2652 }
2653 // Integer arithmetic on a value that may hold a pointer may compute
2654 // another one; anything else that neither writes memory nor produces a
2655 // value able to hold a pointer (e.g. a conversion to floating point)
2656 // cannot pass the pointer on.
2657 if (isPointerArithmeticInst(I, /*includephi*/ true,
2658 /*includebin*/ true)) {
2659 todo.emplace_back(I, Holds, Deep);
2660 continue;
2661 }
2662 if (!I->mayWriteToMemory() && cannotHoldPointer(I))
2663 continue;
2664 return true;
2665 }
2666 }
2667 return false;
2668}
2669
2670// Whether written fresh data, held in the field at offset Field of Base (an
2671// object allocated in this function, or the empty Julia Memory singleton), may
2672// reach the function's return value: with Base itself, or through a load of
2673// that field (any load of it yields the written data), a copy of it, or a call
2674// that may hand back what it loads from Base.
2675bool ReadOnlyOrThrowDetector::freshFieldMayReachReturn(Value *Base,
2676 int64_t Field) {
2677 if (isa<GlobalVariable>(Base))
2678 return false;
2679 if (mayReachReturn(Base, /*Deep*/ false))
2680 return true;
2681 auto &DL = cast<Instruction>(Base)->getModule()->getDataLayout();
2682 int64_t fieldSize = DL.getPointerSize();
2683 auto overlaps = [&](std::optional<int64_t> off, uint64_t size) {
2684 if (!off)
2685 return true;
2686 int64_t start = *off;
2687 int64_t end = start + (int64_t)size;
2688 return start < Field + fieldSize && end > Field;
2689 };
2690 // Pointers derived from Base, with their offset from it if constant.
2691 SmallVector<std::pair<Value *, std::optional<int64_t>>, 4> todo;
2692 SmallPtrSet<Value *, 8> visited;
2693 todo.emplace_back(Base, 0);
2694 while (!todo.empty()) {
2695 auto [V, off] = todo.pop_back_val();
2696 if (!visited.insert(V).second)
2697 continue;
2698 for (auto U : V->users()) {
2699 auto I = dyn_cast<Instruction>(U);
2700 if (!I)
2701 return true;
2702 if (auto LI = dyn_cast<LoadInst>(I)) {
2703 if (overlaps(off, DL.getTypeStoreSize(LI->getType())) &&
2704 !cannotHoldPointer(LI) && mayReachReturn(LI, /*Deep*/ false))
2705 return true;
2706 continue;
2707 }
2708 if (auto GEP = dyn_cast<GetElementPtrInst>(I)) {
2709 if (GEP->getPointerOperand() != V)
2710 continue;
2711 std::optional<int64_t> gepOff;
2712 APInt gepOffset(DL.getIndexSizeInBits(GEP->getPointerAddressSpace()),
2713 0);
2714 if (off && GEP->accumulateConstantOffset(DL, gepOffset))
2715 gepOff = *off + gepOffset.getSExtValue();
2716 todo.emplace_back(GEP, gepOff);
2717 continue;
2718 }
2719 if (isa<CastInst>(I) || isa<PHINode>(I) || isa<SelectInst>(I)) {
2720 if (auto SI = dyn_cast<SelectInst>(I))
2721 if (SI->getCondition() == V)
2722 continue;
2723 if (I->getType()->isPointerTy())
2724 todo.emplace_back(I, off);
2725 continue;
2726 }
2727 if (auto MTI = dyn_cast<MemTransferInst>(I)) {
2728 if (MTI->getRawSource() != V)
2729 continue;
2730 auto len = dyn_cast<ConstantInt>(MTI->getLength());
2731 if (!len || overlaps(off, len->getZExtValue()))
2732 return true;
2733 continue;
2734 }
2735 if (auto CB = dyn_cast<CallBase>(I)) {
2736 if (CB->isLifetimeStartOrEnd() || isDeallocationCall(CB, TLI))
2737 continue;
2738 auto name = getFuncNameFromCall(CB);
2739 if (name == "julia.write_barrier")
2740 continue;
2741 // julia.gc_loaded(obj, ptr) returns ptr, keeping obj alive: passing
2742 // Base as the object hands back nothing loaded from it (a load of the
2743 // field passed as ptr is checked as a load above).
2744 if (name == "julia.gc_loaded" && CB->getArgOperand(0) == V &&
2745 CB->getArgOperand(1) != V)
2746 continue;
2747 if (mayHandBackPointer(CB))
2748 return true;
2749 continue;
2750 }
2751 // Stores into Base, comparisons, and the like do not read the field.
2752 // Storing a pointer to Base elsewhere is followed by mayReachReturn
2753 // above.
2754 }
2755 }
2756 return false;
2757}
2758
2760 if (isReadOnlyOrThrow(&F))
2761 return false;
2762 if (F.empty())
2763 return false;
2764
2765 const auto unreachable = getGuaranteedUnreachable(&F);
2766 for (auto &BB : F) {
2767 if (unreachable.find(&BB) != unreachable.end()) {
2768 continue;
2769 }
2770 for (auto &I : BB) {
2771 if (!I.mayWriteToMemory())
2772 continue;
2773 if (hasMetadata(&I, "enzyme_ReadOnlyOrThrow"))
2774 continue;
2775 // A call marked local read-only-or-throw is handled like a call to a
2776 // local read-only-or-throw function below, as it may hand us memory it
2777 // wrote.
2778 bool localMD = hasMetadata(&I, "enzyme_LocalReadOnlyOrThrow");
2779 if (localMD && !isa<CallBase>(&I))
2780 continue;
2781
2782 if (auto MTI = dyn_cast<MemTransferInst>(&I)) {
2783 auto Obj = getBaseObject(MTI->getOperand(0));
2784 // Writing the data of a fresh Julia Memory or array writes memory
2785 // that did not exist before the call.
2786 if (auto fields = freshFields(Obj)) {
2787 if (needLocal() && freshReaches(Obj, *fields))
2788 local = true;
2789 continue;
2790 }
2791 // Storing into local memory is fine since it definitionally will not be
2792 // seen outside the function. Note, even if one stored into x =
2793 // malloc(..), and stored x into a global/arg pointer, that second store
2794 // would trigger not readonly.
2795 if (isa<AllocaInst>(Obj))
2796 continue;
2797 if (isAllocationCall(Obj, TLI)) {
2798 if (needLocal() && reaches(Obj, /*Deep*/ false))
2799 local = true;
2800 continue;
2801 }
2802 if (auto arg = dyn_cast<Argument>(Obj)) {
2803 if (arg->hasStructRetAttr() ||
2804 arg->getParent()
2805 ->getAttribute(arg->getArgNo() + AttributeList::FirstArgIndex,
2806 "enzymejl_returnRoots")
2807 .isValid() ||
2808 arg->getParent()
2809 ->getAttribute(arg->getArgNo() + AttributeList::FirstArgIndex,
2810 "enzymejl_sret_union_bytes")
2811 .isValid()) {
2812 local = true;
2813 continue;
2814 }
2815 }
2816 }
2817 if (auto MSI = dyn_cast<MemSetInst>(&I)) {
2818 auto Obj = getBaseObject(MSI->getOperand(0));
2819 // Writing the data of a fresh Julia Memory or array writes memory
2820 // that did not exist before the call.
2821 if (auto fields = freshFields(Obj)) {
2822 if (needLocal() && freshReaches(Obj, *fields))
2823 local = true;
2824 continue;
2825 }
2826 // Storing into local memory is fine since it definitionally will not be
2827 // seen outside the function. Note, even if one stored into x =
2828 // malloc(..), and stored x into a global/arg pointer, that second store
2829 // would trigger not readonly.
2830 if (isa<AllocaInst>(Obj))
2831 continue;
2832 if (isAllocationCall(Obj, TLI)) {
2833 if (needLocal() && reaches(Obj, /*Deep*/ false))
2834 local = true;
2835 continue;
2836 }
2837 if (auto arg = dyn_cast<Argument>(Obj)) {
2838 if (arg->hasStructRetAttr() ||
2839 arg->getParent()
2840 ->getAttribute(arg->getArgNo() + AttributeList::FirstArgIndex,
2841 "enzymejl_returnRoots")
2842 .isValid() ||
2843 arg->getParent()
2844 ->getAttribute(arg->getArgNo() + AttributeList::FirstArgIndex,
2845 "enzymejl_sret_union_bytes")
2846 .isValid()) {
2847 local = true;
2848 continue;
2849 }
2850 }
2851 }
2852
2853 if (auto CI = dyn_cast<CallBase>(&I)) {
2854 if (localMD || isLocalReadOnlyOrThrow(CI)) {
2855 // A local read-only-or-throw callee may still write through its
2856 // sret-like arguments, and those writes land in memory of ours.
2857 // Classify them as we would a store of our own: memory local to us
2858 // is fine, our own sret-like argument makes us local too (e.g. an
2859 // sret passed straight through to the callee after call-slot
2860 // optimization), and anything else disqualifies us.
2861 if (!isReadOnlyOrThrow(CI)) {
2862 // Unlike a fully read-only-or-throw callee, a local one may also
2863 // write memory it allocates and hand it to us, through its result
2864 // or its sret-like arguments. Like an allocation of ours that was
2865 // written to, if that memory, or memory a pointer loaded from it
2866 // points to, may escape us, we are only local read-only-or-throw
2867 // too.
2868 if (needLocal() && !cannotHoldPointer(CI) &&
2869 reaches(CI, /*Deep*/ true))
2870 local = true;
2871 auto Callee = CI->getCalledFunction();
2872#if LLVM_VERSION_MAJOR >= 14
2873 size_t nargs = CI->arg_size();
2874#else
2875 size_t nargs = CI->getNumArgOperands();
2876#endif
2877 for (size_t i = 0; i < nargs; i++) {
2878 Value *arg = CI->getArgOperand(i);
2879 if (!arg->getType()->isPointerTy())
2880 continue;
2881 bool sret = CI->paramHasAttr(i, Attribute::StructRet);
2882 bool roots = false;
2883 bool unionBytes = false;
2884 if (Callee && i < Callee->arg_size()) {
2885 roots = Callee
2886 ->getAttribute(i + AttributeList::FirstArgIndex,
2887 "enzymejl_returnRoots")
2888 .isValid();
2889 unionBytes =
2890 Callee
2891 ->getAttribute(i + AttributeList::FirstArgIndex,
2892 "enzymejl_sret_union_bytes")
2893 .isValid();
2894 }
2895 if (!sret && !roots && !unionBytes)
2896 continue;
2897 auto Obj = getBaseObject(arg);
2898 if (isa<AllocaInst>(Obj) || isAllocationCall(Obj, TLI)) {
2899 if (needLocal() && reaches(Obj, /*Deep*/ true, CI))
2900 local = true;
2901 continue;
2902 }
2903 if (auto A = dyn_cast<Argument>(Obj)) {
2904 if (A->hasStructRetAttr() ||
2905 A->getParent()
2906 ->getAttribute(A->getArgNo() +
2907 AttributeList::FirstArgIndex,
2908 "enzymejl_returnRoots")
2909 .isValid() ||
2910 A->getParent()
2911 ->getAttribute(A->getArgNo() +
2912 AttributeList::FirstArgIndex,
2913 "enzymejl_sret_union_bytes")
2914 .isValid()) {
2915 local = true;
2916 continue;
2917 }
2918 }
2919 if (EnzymePrintPerf) {
2920 EmitWarning("WritingInstruction", I,
2921 "Instruction could write forcing ", F.getName(),
2922 " to not be marked readonly_or_throw per "
2923 "argument ",
2924 i, " of local read-only-or-throw call ", I);
2925 }
2926 return false;
2927 }
2928 }
2929 continue;
2930 }
2931 if (isAllocationCall(CI, TLI)) {
2932 continue;
2933 }
2934 if (getFuncNameFromCall(CI) == "enzyme_zerotype") {
2935 auto Obj = getBaseObject(CI->getArgOperand(0));
2936 // Storing into local memory is fine since it definitionally will not
2937 // be seen outside the function. Note, even if one stored into x =
2938 // malloc(..), and stored x into a global/arg pointer, that second
2939 // store would trigger not readonly.
2940 if (isa<AllocaInst>(Obj))
2941 continue;
2942 if (isAllocationCall(Obj, TLI)) {
2943 if (needLocal() && reaches(Obj, /*Deep*/ false))
2944 local = true;
2945 continue;
2946 }
2947 if (auto arg = dyn_cast<Argument>(Obj)) {
2948 if (arg->hasStructRetAttr() ||
2949 arg->getParent()
2950 ->getAttribute(arg->getArgNo() +
2951 AttributeList::FirstArgIndex,
2952 "enzymejl_returnRoots")
2953 .isValid() ||
2954 arg->getParent()
2955 ->getAttribute(arg->getArgNo() +
2956 AttributeList::FirstArgIndex,
2957 "enzymejl_sret_union_bytes")
2958 .isValid()) {
2959 local = true;
2960 continue;
2961 }
2962 }
2963 }
2964 if (auto F2 = CI->getCalledFunction()) {
2965 if (isDebugFunction(F2))
2966 continue;
2967 if (F2->getName() == "julia.write_barrier")
2968 continue;
2969 if (F2->getCallingConv() == CI->getCallingConv()) {
2970 if (F2 == &F)
2971 continue;
2972 if (isReadOnlyOrThrow(F2))
2973 continue;
2974 if (!F2->empty()) {
2975 if (EnzymePrintPerf) {
2977 "WritingInstruction", I, "Instruction could write forcing ",
2978 F.getName(),
2979 " to not be marked readonly_or_throw per sub-call of ",
2980 F2->getName());
2981 }
2982 calls_todo.insert(F2);
2983 continue;
2984 }
2985 }
2986 }
2987 }
2988 if (auto SI = dyn_cast<StoreInst>(&I)) {
2989 auto Obj = getBaseObject(SI->getPointerOperand());
2990 // Writing the data of a fresh Julia Memory or array writes memory
2991 // that did not exist before the call.
2992 if (auto fields = freshFields(Obj)) {
2993 if (needLocal() && freshReaches(Obj, *fields))
2994 local = true;
2995 continue;
2996 }
2997 // Storing into local memory is fine since it definitionally will not be
2998 // seen outside the function. Note, even if one stored into x =
2999 // malloc(..), and stored x into a global/arg pointer, that second store
3000 // would trigger not readonly.
3001 if (isa<AllocaInst>(Obj))
3002 continue;
3003 if (isAllocationCall(Obj, TLI)) {
3004 if (needLocal() && reaches(Obj, /*Deep*/ false))
3005 local = true;
3006 continue;
3007 }
3008 if (auto arg = dyn_cast<Argument>(Obj)) {
3009 if (arg->hasStructRetAttr() ||
3010 arg->getParent()
3011 ->getAttribute(arg->getArgNo() + AttributeList::FirstArgIndex,
3012 "enzymejl_returnRoots")
3013 .isValid() ||
3014 arg->getParent()
3015 ->getAttribute(arg->getArgNo() + AttributeList::FirstArgIndex,
3016 "enzymejl_sret_union_bytes")
3017 .isValid()) {
3018 local = true;
3019 continue;
3020 }
3021 }
3022 }
3023 // ignore atomic load impacts
3024 if (isa<LoadInst>(&I))
3025 continue;
3026 if (EnzymeJuliaAddrLoad && isa<FenceInst>(&I)) {
3027 if (auto prev = dyn_cast_or_null<CallBase>(I.getPrevNode())) {
3028 if (auto F = prev->getCalledFunction())
3029 if (F->getName() == "julia.safepoint")
3030 continue;
3031 }
3032 if (auto prev = dyn_cast_or_null<CallBase>(I.getNextNode())) {
3033 if (auto F = prev->getCalledFunction())
3034 if (F->getName() == "julia.safepoint")
3035 continue;
3036 }
3037 }
3038
3039 if (EnzymePrintPerf) {
3040 EmitWarning("WritingInstruction", I, "Instruction could write forcing ",
3041 F.getName(), " to not be marked readonly_or_throw", I);
3042 }
3043 return false;
3044 }
3045 }
3046
3047 if (calls_todo.size() == 0) {
3048 if (local) {
3049 F.addFnAttr("enzyme_LocalReadOnlyOrThrow");
3050 } else {
3051 // A function is either local or fully read-only-or-throw, never both:
3052 // an earlier run, e.g. on less optimized IR, may have marked it local.
3053 F.removeFnAttr("enzyme_LocalReadOnlyOrThrow");
3054 F.addFnAttr("enzyme_ReadOnlyOrThrow");
3055 }
3056 addReadOnlyOrThrowAttributes();
3057 }
3058 return true;
3059}
3060
3061bool DetectReadonlyOrThrow(Module &M) {
3062
3063 bool changed = false;
3064
3065 {
3066 // Set of functions newly deduced readonly/nocapture/etc by this pass
3067 SmallVector<llvm::Function *> todo;
3068
3069 // Map of functions which could be readonly if all functions in the set are
3070 // marked readonly
3071 DenseMap<llvm::Function *, SmallPtrSet<Function *, 1>> todo_map;
3072
3073 for (Function &F : M) {
3074 SmallPtrSet<Function *, 1> calls_todo;
3075 if (DetectNoUnwindOfFn(F, calls_todo)) {
3076 changed = true;
3077 for (auto F2 : todo_map[&F]) {
3078 todo.push_back(F2);
3079 }
3080 todo_map.erase(&F);
3081 }
3082 for (auto tocheck : calls_todo) {
3083 todo_map[tocheck].insert(&F);
3084 todo.push_back(tocheck);
3085 }
3086 }
3087
3088 while (!todo.empty()) {
3089 auto cur = todo.pop_back_val();
3090
3091 SmallPtrSet<Function *, 1> calls_todo;
3092
3093 if (!DetectNoUnwindOfFn(*cur, calls_todo))
3094 continue;
3095
3096 for (auto F2 : todo_map[cur]) {
3097 todo.push_back(F2);
3098 }
3099
3100 todo_map.erase(cur);
3101
3102 for (auto tocheck : calls_todo) {
3103 todo_map[tocheck].insert(cur);
3104 todo.push_back(tocheck);
3105 }
3106
3107 changed = true;
3108 }
3109 }
3110
3111 {
3112 // Set of functions newly deduced readonly/nocapture/etc by this pass
3113 SmallVector<llvm::Function *> todo;
3114
3115 // Map of functions which could be readonly if all functions in the set are
3116 // marked readonly
3117 DenseMap<llvm::Function *, SmallPtrSet<Function *, 1>> todo_map;
3118
3119 for (Function &F : M) {
3120 SmallPtrSet<Function *, 1> calls_todo;
3121 if (DetectPointerArgOfFn(F, calls_todo)) {
3122 changed = true;
3123 for (auto F2 : todo_map[&F]) {
3124 todo.push_back(F2);
3125 }
3126 todo_map.erase(&F);
3127 }
3128 for (auto tocheck : calls_todo) {
3129 todo_map[tocheck].insert(&F);
3130 todo.push_back(tocheck);
3131 }
3132 }
3133
3134 while (!todo.empty()) {
3135 auto cur = todo.pop_back_val();
3136
3137 SmallPtrSet<Function *, 1> calls_todo;
3138
3139 if (!DetectPointerArgOfFn(*cur, calls_todo))
3140 continue;
3141
3142 for (auto F2 : todo_map[cur]) {
3143 todo.push_back(F2);
3144 }
3145
3146 todo_map.erase(cur);
3147
3148 for (auto tocheck : calls_todo) {
3149 todo_map[tocheck].insert(cur);
3150 todo.push_back(tocheck);
3151 }
3152
3153 changed = true;
3154 }
3155 }
3156
3157 PassBuilder PB;
3158 LoopAnalysisManager LAM;
3159 FunctionAnalysisManager FAM;
3160 CGSCCAnalysisManager CGAM;
3161 ModuleAnalysisManager MAM;
3162 PB.registerModuleAnalyses(MAM);
3163 PB.registerFunctionAnalyses(FAM);
3164 PB.registerLoopAnalyses(LAM);
3165 PB.registerCGSCCAnalyses(CGAM);
3166 PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
3167
3168 // Set of functions newly deduced readonlyorthrow by this pass
3169 SmallVector<llvm::Function *> todo;
3170
3171 // Map of functions which could be readonly if all functions in the set are
3172 // marked readonly
3173 DenseMap<llvm::Function *, SmallPtrSet<Function *, 1>> todo_map;
3174
3175 // Map from a function `f` to all the functions that have `f` as a
3176 // prerequisite for being readonly. Inverse of `todo_map`
3177 DenseMap<llvm::Function *, SmallPtrSet<Function *, 1>> inverse_todo_map;
3178
3179 for (Function &F : M) {
3180 SmallPtrSet<Function *, 1> calls_todo;
3181 auto &TLI = FAM.getResult<TargetLibraryAnalysis>(F);
3182 bool local = false;
3183 if (ReadOnlyOrThrowDetector(F, calls_todo, TLI, local).run()) {
3184 if (calls_todo.size() == 0) {
3185 changed = true;
3186 todo.push_back(&F);
3187 } else {
3188 for (auto F2 : calls_todo) {
3189 inverse_todo_map[F2].insert(&F);
3190 }
3191 }
3192 todo_map[&F] = std::move(calls_todo);
3193 }
3194 }
3195
3196 while (todo.size()) {
3197 auto cur = todo.pop_back_val();
3198 auto found = inverse_todo_map.find(cur);
3199
3200 // Nobody needs cur as a prerequisite
3201 if (found == inverse_todo_map.end()) {
3202 continue;
3203 }
3204 for (auto F2 : found->second) {
3205 auto found2 = todo_map.find(F2);
3206 assert(found2 != todo_map.end());
3207 auto &fwd_set = found2->second;
3208 fwd_set.erase(cur);
3209 if (fwd_set.size() == 0) {
3210 todo_map.erase(found2);
3211 // Check F2 again now that all its callees are known to be read only or
3212 // throw. The first check treated them as unknown, so it could not
3213 // tell whether a callee that turned out to be only local makes F2
3214 // local too (e.g. by passing its sret through, or by returning memory
3215 // the callee wrote), or rules it out (e.g. by passing an argument that
3216 // is not its sret as the callee's).
3217 SmallPtrSet<Function *, 1> calls_todo;
3218 auto &TLI = FAM.getResult<TargetLibraryAnalysis>(*F2);
3219 bool local = false;
3220 if (ReadOnlyOrThrowDetector(*F2, calls_todo, TLI, local).run() &&
3221 calls_todo.size() == 0) {
3222 changed = true;
3223 todo.push_back(F2);
3224 }
3225 }
3226 }
3227
3228 inverse_todo_map.erase(found);
3229 }
3230 return changed;
3231}
3232
3233namespace {
3234
3235/// Post-dominance over the CFG with the blocks that can only lead to an abort
3236/// (see `getGuaranteedUnreachable`) removed.
3237///
3238/// LLVM's PostDominatorTree treats every `unreachable` as a function exit, and
3239/// Julia ends every bounds check in one, so with them left in almost nothing
3240/// post-dominates anything. Those paths never return, which is exactly why
3241/// ignoring them is the right model here: what runs after a preserve region
3242/// closes only matters on paths that do return.
3243class AbortIgnoringPostDom {
3244 static constexpr unsigned Undef = ~0U;
3245
3246 const SmallPtrSetImpl<BasicBlock *> &Unreachable;
3247 SmallVector<BasicBlock *, 32> Nodes;
3248 DenseMap<BasicBlock *, unsigned> Ids;
3249 SmallVector<SmallVector<unsigned, 2>, 32> Succs;
3250 SmallVector<SmallVector<unsigned, 2>, 32> Preds;
3251 SmallVector<unsigned, 32> Rank; // post-order number in the reversed CFG
3252 SmallVector<unsigned, 32> IPDom;
3253 unsigned Root; // virtual exit, sitting behind every returning block
3254
3255 unsigned intersect(unsigned A, unsigned B) const {
3256 while (A != B) {
3257 while (Rank[A] < Rank[B])
3258 A = IPDom[A];
3259 while (Rank[B] < Rank[A])
3260 B = IPDom[B];
3261 }
3262 return A;
3263 }
3264
3265public:
3266 /// `Unreachable` is the abort-only block set of `F` (see
3267 /// `getGuaranteedUnreachable`); it must outlive this object.
3268 AbortIgnoringPostDom(Function &F,
3269 const SmallPtrSetImpl<BasicBlock *> &Unreachable)
3270 : Unreachable(Unreachable) {
3271 Root = 0;
3272 if (F.empty() || Unreachable.count(&F.getEntryBlock()))
3273 return;
3274
3275 SmallVector<BasicBlock *, 32> Todo = {&F.getEntryBlock()};
3276 Ids[&F.getEntryBlock()] = 0;
3277 Nodes.push_back(&F.getEntryBlock());
3278 while (!Todo.empty()) {
3279 auto BB = Todo.pop_back_val();
3280 for (auto Succ : successors(BB)) {
3281 if (Unreachable.count(Succ) || Ids.count(Succ))
3282 continue;
3283 Ids[Succ] = Nodes.size();
3284 Nodes.push_back(Succ);
3285 Todo.push_back(Succ);
3286 }
3287 }
3288
3289 Root = Nodes.size();
3290 Succs.resize(Root + 1);
3291 Preds.resize(Root + 1);
3292 for (unsigned I = 0; I != Root; ++I) {
3293 for (auto Succ : successors(Nodes[I])) {
3294 auto Found = Ids.find(Succ);
3295 if (Found == Ids.end())
3296 continue;
3297 Succs[I].push_back(Found->second);
3298 Preds[Found->second].push_back(I);
3299 }
3300 // Nothing left to go to in the pruned CFG means this block returns, as
3301 // far as this analysis is concerned.
3302 if (Succs[I].empty()) {
3303 Succs[I].push_back(Root);
3304 Preds[Root].push_back(I);
3305 }
3306 }
3307
3308 // Post-order of the reversed CFG, walking back from the virtual exit.
3309 Rank.assign(Root + 1, Undef);
3310 SmallVector<unsigned, 32> Order;
3311 SmallVector<std::pair<unsigned, unsigned>, 32> Stack;
3312 SmallVector<bool, 32> Seen(Root + 1, false);
3313 Stack.push_back({Root, 0});
3314 Seen[Root] = true;
3315 while (!Stack.empty()) {
3316 unsigned Node = Stack.back().first;
3317 unsigned &Idx = Stack.back().second;
3318 if (Idx < Preds[Node].size()) {
3319 unsigned Child = Preds[Node][Idx++];
3320 if (!Seen[Child]) {
3321 Seen[Child] = true;
3322 Stack.push_back({Child, 0});
3323 }
3324 continue;
3325 }
3326 Rank[Node] = Order.size();
3327 Order.push_back(Node);
3328 Stack.pop_back();
3329 }
3330
3331 // Cooper-Harvey-Kennedy, on the reversed CFG.
3332 IPDom.assign(Root + 1, Undef);
3333 IPDom[Root] = Root;
3334 bool Changed = true;
3335 while (Changed) {
3336 Changed = false;
3337 for (auto It = Order.rbegin(), End = Order.rend(); It != End; ++It) {
3338 unsigned Node = *It;
3339 if (Node == Root)
3340 continue;
3341 unsigned New = Undef;
3342 for (unsigned Succ : Succs[Node]) {
3343 if (IPDom[Succ] == Undef)
3344 continue;
3345 New = (New == Undef) ? Succ : intersect(Succ, New);
3346 }
3347 if (New != Undef && New != IPDom[Node]) {
3348 IPDom[Node] = New;
3349 Changed = true;
3350 }
3351 }
3352 }
3353 }
3354
3355 /// Whether every path through `BB` ends in an abort rather than a return.
3356 bool isAbort(BasicBlock *BB) const { return Unreachable.count(BB); }
3357
3358 /// The nearest block that every returning path out of each of `BBs` runs
3359 /// through, or null if there is none.
3360 BasicBlock *nearestCommonPostDominator(ArrayRef<BasicBlock *> BBs) const {
3361 unsigned Cur = Undef;
3362 for (auto BB : BBs) {
3363 auto Found = Ids.find(BB);
3364 if (Found == Ids.end() || IPDom[Found->second] == Undef)
3365 return nullptr;
3366 Cur = (Cur == Undef) ? Found->second : intersect(Found->second, Cur);
3367 }
3368 if (Cur == Undef || Cur == Root)
3369 return nullptr;
3370 return Nodes[Cur];
3371 }
3372};
3373
3374enum class MergeResult { Merged, NoMergePoint, Failed };
3375
3376} // namespace
3377
3378/// Whether a returning path leads from the end of `From` back into `To`
3379/// without passing through `Avoid`.
3380static bool reachesAvoiding(BasicBlock *From, BasicBlock *To, BasicBlock *Avoid,
3381 const AbortIgnoringPostDom &PDT) {
3382 SmallPtrSet<BasicBlock *, 16> Seen;
3383 SmallVector<BasicBlock *, 16> Todo(succ_begin(From), succ_end(From));
3384 while (!Todo.empty()) {
3385 auto Cur = Todo.pop_back_val();
3386 if (Cur == To)
3387 return true;
3388 if (Cur == Avoid || PDT.isAbort(Cur) || !Seen.insert(Cur).second)
3389 continue;
3390 Todo.append(succ_begin(Cur), succ_end(Cur));
3391 }
3392 return false;
3393}
3394
3395/// Route every `ret` of `F` through one new return block. Unlike
3396/// UnifyFunctionExitNodesPass this leaves the `unreachable`s alone, which the
3397/// augmented primal relies on to spot calls that never return.
3398static bool unifyReturnBlocks(Function &F) {
3399 SmallVector<BasicBlock *, 4> Returning;
3400 for (auto &BB : F)
3401 if (isa<ReturnInst>(BB.getTerminator()))
3402 Returning.push_back(&BB);
3403 if (Returning.size() < 2)
3404 return false;
3405
3406 auto NewRet = BasicBlock::Create(F.getContext(), "UnifiedReturnBlock", &F);
3407 IRBuilder<> B(NewRet);
3408 PHINode *PN = nullptr;
3409 if (F.getReturnType()->isVoidTy()) {
3410 B.CreateRetVoid();
3411 } else {
3412 PN = B.CreatePHI(F.getReturnType(), Returning.size(), "UnifiedRetVal");
3413 B.CreateRet(PN);
3414 }
3415 for (auto BB : Returning) {
3416 auto Ret = BB->getTerminator();
3417 if (PN)
3418 PN->addIncoming(Ret->getOperand(0), BB);
3419 IRBuilder<>(Ret).CreateBr(NewRet);
3420 Ret->eraseFromParent();
3421 }
3422 return true;
3423}
3424
3425/// Give `Begin` a single returning `llvm.julia.gc_preserve_end` that
3426/// post-dominates it, replacing the several ends it currently has. Ends on
3427/// abort-only paths are left as they are: those paths never return, and the
3428/// reverse pass drops their blocks wholesale rather than visiting them.
3429/// Returns NoMergePoint when only the function exit post-dominates the ends,
3430/// and Failed for any other reason not to merge; the function is then left
3431/// untouched but for a possible edge split.
3432static MergeResult MergeGCPreserveEnds(CallInst *Begin,
3433 ArrayRef<CallInst *> AllEnds,
3434 DominatorTree &DT,
3435 AbortIgnoringPostDom &PDT,
3436 bool &CFGChanged) {
3437 auto BB = Begin->getParent();
3438 if (!DT.getNode(BB))
3439 return MergeResult::Failed;
3440
3441 SmallVector<CallInst *, 2> Ends;
3442 for (auto End : AllEnds)
3443 if (!PDT.isAbort(End->getParent()))
3444 Ends.push_back(End);
3445 if (Ends.size() < 2)
3446 return MergeResult::Failed;
3447
3448 auto EndFn = Ends[0]->getCalledFunction();
3449 if (!EndFn)
3450 return MergeResult::Failed;
3451
3452 // Every returning path out of the region runs through `Merge`. The begin's
3453 // own block takes part so that a path leaving the begin without ever meeting
3454 // an end still ends up covered; `Merge` is then a legal home for the end as
3455 // long as the begin dominates it.
3456 SmallVector<BasicBlock *, 3> Blocks = {BB};
3457 for (auto End : Ends)
3458 Blocks.push_back(End->getParent());
3459 auto Merge = PDT.nearestCommonPostDominator(Blocks);
3460 if (!Merge)
3461 return MergeResult::NoMergePoint;
3462
3463 if (!DT.dominates(BB, Merge)) {
3464 // `Merge` is also reachable without ever running the begin. The paths that
3465 // did run it all enter `Merge` from inside the begin's dominator subtree,
3466 // so splitting those edges off into a block of their own gives a home that
3467 // the begin does dominate -- as long as they are the only returning way
3468 // out of that subtree, otherwise a path could leave the region without
3469 // ever reaching the split block.
3470 SmallVector<BasicBlock *, 2> InRegion;
3471 for (auto Pred : predecessors(Merge))
3472 if (DT.dominates(BB, Pred))
3473 InRegion.push_back(Pred);
3474 if (InRegion.empty())
3475 return MergeResult::Failed;
3476
3477 for (auto Node : depth_first(DT.getNode(BB)))
3478 for (auto Succ : successors(Node->getBlock()))
3479 if (Succ != Merge && !DT.dominates(BB, Succ) && !PDT.isAbort(Succ))
3480 return MergeResult::Failed;
3481
3482 Merge = SplitBlockPredecessors(Merge, InRegion, ".gcpreserve", &DT);
3483 if (!Merge)
3484 return MergeResult::Failed;
3485 CFGChanged = true;
3486 }
3487
3488 // Post-dominance does not stop the begin from running again before `Merge`,
3489 // e.g. when one end sat on a loop back edge. Each begin must meet exactly
3490 // one end before the next begin, and vice versa.
3491 if (Merge != BB && (reachesAvoiding(BB, BB, Merge, PDT) ||
3492 reachesAvoiding(Merge, Merge, BB, PDT)))
3493 return MergeResult::Failed;
3494
3495 // Placing the end at the top of `Merge` is only right if none of the old
3496 // ends sit in `Merge` themselves: the region used to cover everything up to
3497 // the last of those, so that is where the merged end has to go.
3498 Instruction *InsertPt = nullptr;
3499 for (auto End : Ends)
3500 if (End->getParent() == Merge && (!InsertPt || InsertPt->comesBefore(End)))
3501 InsertPt = End;
3502 if (!InsertPt)
3503 InsertPt = &*Merge->getFirstInsertionPt();
3504 if (!DT.dominates(Begin, InsertPt))
3505 return MergeResult::Failed;
3506
3507 IRBuilder<> B(InsertPt);
3508 Value *Args[] = {Begin};
3509 auto NewEnd = B.CreateCall(EndFn, Args);
3510 NewEnd->setCallingConv(Ends[0]->getCallingConv());
3511 NewEnd->setAttributes(Ends[0]->getAttributes());
3512 NewEnd->setDebugLoc(Ends[0]->getDebugLoc());
3513
3514 for (auto End : Ends)
3515 End->eraseFromParent();
3516 return MergeResult::Merged;
3517}
3518
3519/// A pass such as JumpThreading is free to clone a basic block holding an
3520/// `llvm.julia.gc_preserve_end`: the token it consumes may legally have many
3521/// uses, and only the instruction *producing* a token blocks duplication. That
3522/// leaves a single `llvm.julia.gc_preserve_begin` paired with several ends.
3523///
3524/// Reverse mode mirrors a preserve region by emitting a `gc_preserve_begin` at
3525/// the invert of the end and the matching `gc_preserve_end` at the invert of
3526/// the begin, so it needs the end to post-dominate the begin. With several
3527/// ends no mirrored begin dominates the mirrored end, and the differentiated
3528/// function fails verification.
3529///
3530/// Restore the one-end invariant. The preserved region can only grow, which is
3531/// always safe.
3532static bool CanonicalizeGCPreserveEnds(Function &F,
3533 FunctionAnalysisManager &FAM) {
3534 SmallVector<CallInst *, 1> Begins;
3535 for (auto &BB : F)
3536 for (auto &I : BB)
3537 if (auto CI = dyn_cast<CallInst>(&I))
3538 if (getFuncNameFromCall(CI) == "llvm.julia.gc_preserve_begin")
3539 Begins.push_back(CI);
3540
3541 bool changed = false;
3542 bool CFGChanged = false;
3543 DominatorTree *DT = nullptr;
3544 // The merge only ever splits edges into a returning block, and the return
3545 // unification only adds one, so the abort-only set stays valid across the
3546 // post-dominator rebuilds below.
3547 SmallPtrSet<BasicBlock *, 4> Unreachable;
3548 std::unique_ptr<AbortIgnoringPostDom> PDT;
3549 bool UnifiedExits = false;
3550
3551 for (auto Begin : Begins) {
3552 SmallVector<CallInst *, 2> Ends;
3553 for (auto U : Begin->users())
3554 if (auto CI = dyn_cast<CallInst>(U))
3555 if (getFuncNameFromCall(CI) == "llvm.julia.gc_preserve_end")
3556 Ends.push_back(CI);
3557 if (Ends.size() < 2)
3558 continue;
3559
3560 if (!DT) {
3561 DT = &FAM.getResult<DominatorTreeAnalysis>(F);
3562 Unreachable = getGuaranteedUnreachable(&F);
3563 }
3564 if (!PDT)
3565 PDT = std::make_unique<AbortIgnoringPostDom>(F, Unreachable);
3566
3567 bool Split = false;
3568 auto Result = MergeGCPreserveEnds(Begin, Ends, *DT, *PDT, Split);
3569 if (Split) {
3570 CFGChanged = true;
3571 PDT.reset();
3572 }
3573
3574 // Ends that were cloned into separate `end; ret` tails have no common
3575 // post-dominator short of the function exit itself. Give the function a
3576 // single return block and try once more; that block, or the edges from the
3577 // region into it, is then the place for the merged end.
3578 if (Result == MergeResult::NoMergePoint && !UnifiedExits) {
3579 UnifiedExits = true;
3580 if (unifyReturnBlocks(F)) {
3581 FAM.invalidate(F, PreservedAnalyses::none());
3582 CFGChanged = true;
3583 DT = &FAM.getResult<DominatorTreeAnalysis>(F);
3584 PDT = std::make_unique<AbortIgnoringPostDom>(F, Unreachable);
3585 Split = false;
3586 Result = MergeGCPreserveEnds(Begin, Ends, *DT, *PDT, Split);
3587 if (Split)
3588 PDT.reset();
3589 }
3590 }
3591 changed |= Result == MergeResult::Merged;
3592 }
3593
3594 if (changed || CFGChanged) {
3595 PreservedAnalyses PA;
3596 if (CFGChanged)
3597 PA.preserve<DominatorTreeAnalysis>();
3598 else
3599 PA.preserveSet<CFGAnalyses>();
3600 FAM.invalidate(F, PA);
3601 }
3602
3603 return changed;
3604}
3605
3607 DerivativeMode mode) {
3608
3609 TimeTraceScope timeScope("preprocessForClone", F->getName());
3610
3615
3616 // If we've already processed this, return the previous version
3617 // and derive aliasing information
3618 if (cache.find(std::make_pair(F, mode)) != cache.end()) {
3619 Function *NewF = cache[std::make_pair(F, mode)];
3620 return NewF;
3621 }
3622
3623 Function *NewF =
3624 Function::Create(F->getFunctionType(), F->getLinkage(),
3625 "preprocess_" + F->getName(), F->getParent());
3626
3627 ValueToValueMapTy VMap;
3628 for (auto i = F->arg_begin(), j = NewF->arg_begin(); i != F->arg_end();) {
3629 VMap[i] = j;
3630 j->setName(i->getName());
3631 if (EnzymeNoAlias && j->getType()->isPointerTy()) {
3632 j->addAttr(Attribute::NoAlias);
3633 }
3634 ++i;
3635 ++j;
3636 }
3637
3638 SmallVector<ReturnInst *, 4> Returns;
3639
3640 if (!F->empty()) {
3641 CloneFunctionInto(
3642 NewF, F, VMap,
3643 /*ModuleLevelChanges*/ CloneFunctionChangeType::LocalChangesOnly,
3644 Returns, "", nullptr);
3645 resetClonedGUID(NewF);
3646 }
3647 CloneOrigin[NewF] = F;
3648 NewF->setAttributes(F->getAttributes());
3649 if (EnzymeNoAlias)
3650 for (auto j = NewF->arg_begin(); j != NewF->arg_end(); j++) {
3651 if (j->getType()->isPointerTy()) {
3652 j->addAttr(Attribute::NoAlias);
3653 }
3654 }
3655 NewF->addFnAttr(Attribute::WillReturn);
3656 NewF->addFnAttr(Attribute::MustProgress);
3657 setFullWillReturn(NewF);
3658
3659 if (EnzymePreopt) {
3660 if (EnzymeInline) {
3662 setFullWillReturn(NewF);
3663 PreservedAnalyses PA;
3664 FAM.invalidate(*NewF, PA);
3665
3666 OptimizationLevel Level = OptimizationLevel::O0;
3667
3668 switch (EnzymePostInlineOpt) {
3669 default:
3670 case 0:
3671 Level = OptimizationLevel::O0;
3672 break;
3673 case 1:
3674 Level = OptimizationLevel::O1;
3675 break;
3676 case 2:
3677 Level = OptimizationLevel::O2;
3678 break;
3679 case 3:
3680 Level = OptimizationLevel::O3;
3681 break;
3682 }
3683 if (Level != OptimizationLevel::O0) {
3684 PassBuilder PB;
3685 FunctionPassManager FPM = PB.buildFunctionSimplificationPipeline(
3686 Level, ThinOrFullLTOPhase::None);
3687 PA = FPM.run(*NewF, FAM);
3688 FAM.invalidate(*NewF, PA);
3689 }
3690 }
3691 }
3692
3693 {
3694 SmallVector<CallInst *, 4> ItersToErase;
3695 for (auto &BB : *NewF) {
3696 for (auto &I : BB) {
3697
3698 if (auto CI = dyn_cast<CallInst>(&I)) {
3699
3700 Function *called = CI->getCalledFunction();
3701 if (auto castinst = dyn_cast<ConstantExpr>(CI->getCalledOperand())) {
3702 if (castinst->isCast()) {
3703 if (auto fn = dyn_cast<Function>(castinst->getOperand(0)))
3704 called = fn;
3705 }
3706 }
3707
3708 if (called && called->getName().contains("__enzyme_iter")) {
3709 ItersToErase.push_back(CI);
3710 }
3711 }
3712 }
3713 }
3714 for (auto Call : ItersToErase) {
3715 IRBuilder<> B(Call);
3716 Call->setArgOperand(
3717 0, B.CreateAdd(Call->getArgOperand(0), Call->getArgOperand(1)));
3718 }
3719 }
3720
3721 // Assume allocations do not return null
3722 {
3723 TargetLibraryInfo &TLI = FAM.getResult<TargetLibraryAnalysis>(*F);
3724 SmallVector<Instruction *, 4> CmpsToErase;
3725 SmallVector<BasicBlock *, 4> BranchesToErase;
3726 for (auto &BB : *NewF) {
3727 for (auto &I : BB) {
3728 if (auto IC = dyn_cast<ICmpInst>(&I)) {
3729 if (!IC->isEquality())
3730 continue;
3731 for (int i = 0; i < 2; i++) {
3732 if (isa<ConstantPointerNull>(IC->getOperand(1 - i)))
3733 if (isAllocationCall(IC->getOperand(i), TLI)) {
3734 for (auto U : IC->users()) {
3735 if (auto BI =
3736 (isAnyBranch(U) ? cast<Instruction>(U) : nullptr))
3737 BranchesToErase.push_back(BI->getParent());
3738 }
3739 IC->replaceAllUsesWith(
3740 IC->getPredicate() == ICmpInst::ICMP_NE
3741 ? ConstantInt::getTrue(I.getContext())
3742 : ConstantInt::getFalse(I.getContext()));
3743 CmpsToErase.push_back(&I);
3744 break;
3745 }
3746 }
3747 }
3748 }
3749 }
3750 for (auto I : CmpsToErase)
3751 I->eraseFromParent();
3752 for (auto BE : BranchesToErase)
3753 ConstantFoldTerminator(BE);
3754 }
3755
3756 SimplifyMPIQueries(*NewF, FAM);
3757 {
3758 auto PA = PromotePass().run(*NewF, FAM);
3759 FAM.invalidate(*NewF, PA);
3760 }
3761
3762 if (EnzymeLowerGlobals) {
3763 SmallVector<CallInst *, 4> Calls;
3764 SmallVector<ReturnInst *, 4> Returns;
3765 for (BasicBlock &BB : *NewF) {
3766 for (Instruction &I : BB) {
3767 if (auto CI = dyn_cast<CallInst>(&I)) {
3768 Calls.push_back(CI);
3769 }
3770 if (auto RI = dyn_cast<ReturnInst>(&I)) {
3771 Returns.push_back(RI);
3772 }
3773 }
3774 }
3775
3776 // TODO consider using TBAA and globals as well
3777 // instead of just BasicAA
3778 AAResults AA2(FAM.getResult<TargetLibraryAnalysis>(*NewF));
3779 AA2.addAAResult(FAM.getResult<BasicAA>(*NewF));
3780 AA2.addAAResult(FAM.getResult<TypeBasedAA>(*NewF));
3781 AA2.addAAResult(FAM.getResult<ScopedNoAliasAA>(*NewF));
3782
3783 for (auto &g : NewF->getParent()->globals()) {
3784 bool inF = false;
3785 {
3786 std::set<Constant *> seen;
3787 std::deque<Constant *> todo = {(Constant *)&g};
3788 while (todo.size()) {
3789 auto GV = todo.front();
3790 todo.pop_front();
3791 if (!seen.insert(GV).second)
3792 continue;
3793 for (auto u : GV->users()) {
3794 if (auto C = dyn_cast<Constant>(u)) {
3795 todo.push_back(C);
3796 } else if (auto I = dyn_cast<Instruction>(u)) {
3797 if (I->getParent()->getParent() == NewF) {
3798 inF = true;
3799 goto doneF;
3800 }
3801 }
3802 }
3803 }
3804 }
3805 doneF:;
3806 if (inF) {
3807 bool activeCall = false;
3808 bool hasWrite = false;
3809 MemoryLocation Loc =
3810 MemoryLocation(&g, LocationSize::beforeOrAfterPointer());
3811
3812 for (CallInst *CI : Calls) {
3813 if (isa<IntrinsicInst>(CI))
3814 continue;
3815 Function *F = CI->getCalledFunction();
3816 if (auto castinst = dyn_cast<ConstantExpr>(CI->getCalledOperand())) {
3817 if (castinst->isCast())
3818 if (auto fn = dyn_cast<Function>(castinst->getOperand(0))) {
3819 F = fn;
3820 }
3821 }
3822 if (F && isMemFreeLibMFunction(F->getName())) {
3823 continue;
3824 }
3825 if (F && F->getName().contains("__enzyme_integer")) {
3826 continue;
3827 }
3828 if (F && F->getName().contains("__enzyme_pointer")) {
3829 continue;
3830 }
3831 if (F && F->getName().contains("__enzyme_float")) {
3832 continue;
3833 }
3834 if (F && F->getName().contains("__enzyme_double")) {
3835 continue;
3836 }
3837 if (F && (startsWith(F->getName(), "f90io") ||
3838 F->getName() == "ftnio_fmt_write64" ||
3839 F->getName() == "__mth_i_ipowi" ||
3840 F->getName() == "f90_pausea")) {
3841 continue;
3842 }
3843 if (llvm::isModOrRefSet(AA2.getModRefInfo(CI, Loc))) {
3844 llvm::errs() << " failed to inline global: " << g << " due to "
3845 << *CI << "\n";
3846 activeCall = true;
3847 break;
3848 }
3849 }
3850
3851 if (!activeCall) {
3852 std::set<Value *> seen;
3853 std::deque<Value *> todo = {(Value *)&g};
3854 while (todo.size()) {
3855 auto GV = todo.front();
3856 todo.pop_front();
3857 if (!seen.insert(GV).second)
3858 continue;
3859 for (auto u : GV->users()) {
3860 if (isa<Constant>(u) || isa<GetElementPtrInst>(u) ||
3861 isa<CastInst>(u) || isa<LoadInst>(u)) {
3862 todo.push_back(u);
3863 continue;
3864 }
3865
3866 if (auto II = dyn_cast<IntrinsicInst>(u)) {
3867 if (isIntelSubscriptIntrinsic(*II)) {
3868 todo.push_back(u);
3869 continue;
3870 }
3871 }
3872
3873 if (auto CI = dyn_cast<CallInst>(u)) {
3874 Function *F = CI->getCalledFunction();
3875 if (auto castinst =
3876 dyn_cast<ConstantExpr>(CI->getCalledOperand())) {
3877 if (castinst->isCast())
3878 if (auto fn = dyn_cast<Function>(castinst->getOperand(0))) {
3879 F = fn;
3880 }
3881 }
3882 if (F && isMemFreeLibMFunction(F->getName())) {
3883 continue;
3884 }
3885 if (F && F->getName().contains("__enzyme_integer")) {
3886 continue;
3887 }
3888 if (F && F->getName().contains("__enzyme_pointer")) {
3889 continue;
3890 }
3891 if (F && F->getName().contains("__enzyme_float")) {
3892 continue;
3893 }
3894 if (F && F->getName().contains("__enzyme_double")) {
3895 continue;
3896 }
3897 if (F && (startsWith(F->getName(), "f90io") ||
3898 F->getName() == "ftnio_fmt_write64" ||
3899 F->getName() == "__mth_i_ipowi" ||
3900 F->getName() == "f90_pausea")) {
3901 continue;
3902 }
3903
3904 if (couldFunctionArgumentCapture(CI, GV)) {
3905 hasWrite = true;
3906 goto endCheck;
3907 }
3908
3909 if (llvm::isModSet(AA2.getModRefInfo(CI, Loc))) {
3910 hasWrite = true;
3911 goto endCheck;
3912 }
3913 }
3914
3915 else if (auto I = dyn_cast<Instruction>(u)) {
3916 if (llvm::isModSet(AA2.getModRefInfo(I, Loc))) {
3917 hasWrite = true;
3918 goto endCheck;
3919 }
3920 }
3921 }
3922 }
3923 }
3924
3925 endCheck:;
3926 if (!activeCall && hasWrite) {
3927 IRBuilder<> bb(&NewF->getEntryBlock(), NewF->getEntryBlock().begin());
3928 AllocaInst *antialloca = bb.CreateAlloca(
3929 g.getValueType(), g.getType()->getPointerAddressSpace(), nullptr,
3930 g.getName() + "_local");
3931
3932 if (g.getAlign()) {
3933 antialloca->setAlignment(*g.getAlign());
3934 }
3935
3936 std::map<Constant *, Value *> remap;
3937 remap[&g] = antialloca;
3938
3939 std::deque<Constant *> todo = {&g};
3940 while (todo.size()) {
3941 auto GV = todo.front();
3942 todo.pop_front();
3943 if (&g != GV && remap.find(GV) != remap.end())
3944 continue;
3945 Value *replaced = nullptr;
3946 if (remap.find(GV) != remap.end()) {
3947 replaced = remap[GV];
3948 } else if (auto CE = dyn_cast<ConstantExpr>(GV)) {
3949 auto I = CE->getAsInstruction();
3950 bb.Insert(I);
3951 assert(isa<Constant>(I->getOperand(0)));
3952 assert(remap[cast<Constant>(I->getOperand(0))]);
3953 I->setOperand(0, remap[cast<Constant>(I->getOperand(0))]);
3954 replaced = remap[GV] = I;
3955 }
3956 assert(replaced && "unhandled constantexpr");
3957
3958 SmallVector<std::pair<Instruction *, size_t>, 4> uses;
3959 for (Use &U : GV->uses()) {
3960 if (auto I = dyn_cast<Instruction>(U.getUser())) {
3961 if (I->getParent()->getParent() == NewF) {
3962 uses.emplace_back(I, U.getOperandNo());
3963 }
3964 }
3965 if (auto C = dyn_cast<Constant>(U.getUser())) {
3966 assert(C != &g);
3967 todo.push_back(C);
3968 }
3969 }
3970 for (auto &U : uses) {
3971 U.first->setOperand(U.second, replaced);
3972 }
3973 }
3974
3975 Value *args[] = {
3976 bb.CreateBitCast(antialloca, getInt8PtrTy(g.getContext())),
3977 bb.CreateBitCast(&g, getInt8PtrTy(g.getContext())),
3978 ConstantInt::get(
3979 Type::getInt64Ty(g.getContext()),
3980 g.getParent()->getDataLayout().getTypeAllocSizeInBits(
3981 g.getValueType()) /
3982 8),
3983 ConstantInt::getFalse(g.getContext())};
3984
3985 Type *tys[] = {args[0]->getType(), args[1]->getType(),
3986 args[2]->getType()};
3987 auto intr =
3988 getIntrinsicDeclaration(g.getParent(), Intrinsic::memcpy, tys);
3989 {
3990
3991 auto cal = bb.CreateCall(intr, args);
3992 if (g.getAlign()) {
3993 cal->addParamAttr(0, Attribute::getWithAlignment(g.getContext(),
3994 *g.getAlign()));
3995 cal->addParamAttr(1, Attribute::getWithAlignment(g.getContext(),
3996 *g.getAlign()));
3997 }
3998 }
3999
4000 std::swap(args[0], args[1]);
4001
4002 for (ReturnInst *RI : Returns) {
4003 IRBuilder<> IB(RI);
4004 auto cal = IB.CreateCall(intr, args);
4005 if (g.getAlign()) {
4006 cal->addParamAttr(0, Attribute::getWithAlignment(g.getContext(),
4007 *g.getAlign()));
4008 cal->addParamAttr(1, Attribute::getWithAlignment(g.getContext(),
4009 *g.getAlign()));
4010 }
4011 }
4012 }
4013 }
4014 }
4015
4016 auto Level = OptimizationLevel::O2;
4017
4018 PassBuilder PB;
4019 FunctionPassManager FPM =
4020 PB.buildFunctionSimplificationPipeline(Level, ThinOrFullLTOPhase::None);
4021 auto PA = FPM.run(*F, FAM);
4022 FAM.invalidate(*F, PA);
4023 }
4024
4025 if (EnzymePreopt) {
4026 {
4027 auto PA = LowerInvokePass().run(*NewF, FAM);
4028 FAM.invalidate(*NewF, PA);
4029 }
4030 {
4031 auto PA = UnreachableBlockElimPass().run(*NewF, FAM);
4032 FAM.invalidate(*NewF, PA);
4033 }
4034
4035 {
4036 auto PA = PromotePass().run(*NewF, FAM);
4037 FAM.invalidate(*NewF, PA);
4038 }
4039
4040 {
4041#if LLVM_VERSION_MAJOR >= 16 && !defined(FLANG)
4042 auto PA = SROAPass(llvm::SROAOptions::ModifyCFG).run(*NewF, FAM);
4043#elif !defined(FLANG)
4044 auto PA = SROAPass().run(*NewF, FAM);
4045#else
4046 auto PA = SROA().run(*NewF, FAM);
4047#endif
4048 FAM.invalidate(*NewF, PA);
4049 }
4050
4051 if (mode != DerivativeMode::ForwardMode)
4052 ReplaceReallocs(NewF);
4053
4054 {
4055#if LLVM_VERSION_MAJOR >= 16 && !defined(FLANG)
4056 auto PA = SROAPass(llvm::SROAOptions::PreserveCFG).run(*NewF, FAM);
4057#elif !defined(FLANG)
4058 auto PA = SROAPass().run(*NewF, FAM);
4059#else
4060 auto PA = SROA().run(*NewF, FAM);
4061#endif
4062 FAM.invalidate(*NewF, PA);
4063 }
4064
4065 SimplifyCFGOptions scfgo;
4066 {
4067 auto PA = SimplifyCFGPass(scfgo).run(*NewF, FAM);
4068 FAM.invalidate(*NewF, PA);
4069 }
4070 }
4071
4072 {
4073 SplitPHIs(*NewF);
4074 PreservedAnalyses PA;
4075 PA.preserve<AssumptionAnalysis>();
4076 PA.preserve<TargetLibraryAnalysis>();
4077 PA.preserve<LoopAnalysis>();
4078 PA.preserve<DominatorTreeAnalysis>();
4079 PA.preserve<PostDominatorTreeAnalysis>();
4080 PA.preserve<TypeBasedAA>();
4081 PA.preserve<BasicAA>();
4082 PA.preserve<ScopedNoAliasAA>();
4083 PA.preserve<ScalarEvolutionAnalysis>();
4084 PA.preserve<PhiValuesAnalysis>();
4085 }
4086
4087 if (mode != DerivativeMode::ForwardMode)
4088 ReplaceReallocs(NewF);
4089
4093 // For subfunction calls upgrade stack allocations to mallocs
4094 // to ensure availability in the reverse pass
4095 auto unreachable = getGuaranteedUnreachable(NewF);
4096 UpgradeAllocasToMallocs(NewF, mode, unreachable);
4097 }
4098
4099 CanonicalizeLoops(NewF, FAM);
4100 RemoveRedundantPHI(NewF, FAM);
4101
4102 // Run LoopSimplifyPass to ensure preheaders exist on all loops
4103 {
4104 auto PA = LoopSimplifyPass().run(*NewF, FAM);
4105 FAM.invalidate(*NewF, PA);
4106 }
4107
4108 {
4109 for (auto &BB : *NewF) {
4110 for (auto &I : make_early_inc_range(BB)) {
4111 if (auto MTI = dyn_cast<MemTransferInst>(&I)) {
4112
4113 if (auto CI = dyn_cast<ConstantInt>(MTI->getOperand(2))) {
4114 if (CI->getValue() == 0) {
4115 MTI->eraseFromParent();
4116 }
4117 }
4118 }
4119 }
4120 }
4121
4122 PreservedAnalyses PA;
4123 PA.preserve<AssumptionAnalysis>();
4124 PA.preserve<TargetLibraryAnalysis>();
4125 PA.preserve<LoopAnalysis>();
4126 PA.preserve<DominatorTreeAnalysis>();
4127 PA.preserve<PostDominatorTreeAnalysis>();
4128 PA.preserve<TypeBasedAA>();
4129 PA.preserve<BasicAA>();
4130 PA.preserve<ScopedNoAliasAA>();
4131 PA.preserve<ScalarEvolutionAnalysis>();
4132 PA.preserve<PhiValuesAnalysis>();
4133
4134 FAM.invalidate(*NewF, PA);
4135
4137 for (auto &Arg : NewF->args()) {
4138 if (!Arg.hasName())
4139 Arg.setName("arg");
4140 }
4141 for (BasicBlock &BB : *NewF) {
4142 if (!BB.hasName())
4143 BB.setName("bb");
4144
4145 for (Instruction &I : BB) {
4146 if (!I.hasName() && !I.getType()->isVoidTy())
4147 I.setName("i");
4148 }
4149 }
4150 }
4151 }
4152
4154 if (false) {
4155 reset:;
4156 PreservedAnalyses PA;
4157 FAM.invalidate(*NewF, PA);
4158 }
4159
4160 SmallVector<BasicBlock *, 4> MultiBlocks;
4161 for (auto &B : *NewF) {
4162 if (B.hasNPredecessorsOrMore(3))
4163 MultiBlocks.push_back(&B);
4164 }
4165
4166 LoopInfo &LI = FAM.getResult<LoopAnalysis>(*NewF);
4167 for (BasicBlock *B : MultiBlocks) {
4168
4169 // Map of function edges to list of values possible
4170 std::map<std::pair</*pred*/ BasicBlock *, /*successor*/ BasicBlock *>,
4171 std::set<BasicBlock *>>
4172 done;
4173 {
4174 std::deque<std::tuple<
4175 std::pair</*pred*/ BasicBlock *, /*successor*/ BasicBlock *>,
4176 BasicBlock *>>
4177 Q; // newblock, target
4178
4179 for (auto P : predecessors(B)) {
4180 Q.emplace_back(std::make_pair(P, B), P);
4181 }
4182
4183 for (std::tuple<
4184 std::pair</*pred*/ BasicBlock *, /*successor*/ BasicBlock *>,
4185 BasicBlock *>
4186 trace;
4187 Q.size() > 0;) {
4188 trace = Q.front();
4189 Q.pop_front();
4190 auto edge = std::get<0>(trace);
4191 auto block = edge.first;
4192 auto target = std::get<1>(trace);
4193
4194 if (done[edge].count(target))
4195 continue;
4196 done[edge].insert(target);
4197
4198 Loop *blockLoop = LI.getLoopFor(block);
4199
4200 for (BasicBlock *Pred : predecessors(block)) {
4201 // Don't go up the backedge as we can use the last value if desired
4202 // via lcssa
4203 if (blockLoop && blockLoop->getHeader() == block &&
4204 blockLoop == LI.getLoopFor(Pred))
4205 continue;
4206
4207 Q.push_back(
4208 std::tuple<std::pair<BasicBlock *, BasicBlock *>, BasicBlock *>(
4209 std::make_pair(Pred, block), target));
4210 }
4211 }
4212 }
4213
4214 SmallPtrSet<BasicBlock *, 4> Preds;
4215 for (auto &pair : done) {
4216 Preds.insert(pair.first.first);
4217 }
4218
4219 for (auto BB : Preds) {
4220 bool illegal = false;
4221 SmallPtrSet<BasicBlock *, 2> UnionSet;
4222 size_t numSuc = 0;
4223 for (BasicBlock *sucI : successors(BB)) {
4224 numSuc++;
4225 const auto &SI = done[std::make_pair(BB, sucI)];
4226 if (SI.size() == 0) {
4227 // sucI->getName();
4228 illegal = true;
4229 break;
4230 }
4231 for (auto si : SI) {
4232 UnionSet.insert(si);
4233
4234 for (BasicBlock *sucJ : successors(BB)) {
4235 if (sucI == sucJ)
4236 continue;
4237 if (done[std::make_pair(BB, sucJ)].count(si)) {
4238 illegal = true;
4239 goto endIllegal;
4240 }
4241 }
4242 }
4243 }
4244 endIllegal:;
4245
4246 if (!illegal && numSuc > 1 && !B->hasNPredecessors(UnionSet.size())) {
4247 BasicBlock *Ins =
4248 BasicBlock::Create(BB->getContext(), "tmpblk", BB->getParent());
4249 IRBuilder<> Builder(Ins);
4250 for (auto &phi : B->phis()) {
4251 auto nphi = Builder.CreatePHI(phi.getType(), 2);
4252 SmallVector<BasicBlock *, 4> Blocks;
4253
4254 for (auto blk : UnionSet) {
4255 nphi->addIncoming(phi.getIncomingValueForBlock(blk), blk);
4256 phi.removeIncomingValue(blk, /*deleteifempty*/ false);
4257 }
4258
4259 phi.addIncoming(nphi, Ins);
4260 }
4261 Builder.CreateBr(B);
4262 for (auto blk : UnionSet) {
4263 auto term = blk->getTerminator();
4264 for (unsigned Idx = 0, NumSuccessors = term->getNumSuccessors();
4265 Idx != NumSuccessors; ++Idx)
4266 if (term->getSuccessor(Idx) == B)
4267 term->setSuccessor(Idx, Ins);
4268 }
4269 goto reset;
4270 }
4271 }
4272 }
4273 }
4274
4275 // Only the reverse pass mirrors preserve regions, so only it needs the
4276 // one-end invariant. The augmented primal and split forward mode share
4277 // their preprocessing with it, so everything but plain forward mode runs it.
4278 if (mode != DerivativeMode::ForwardMode)
4280
4281 {
4282 SmallPtrSet<Function *, 1> calls_todo;
4283 bool local = false;
4284 ReadOnlyOrThrowDetector(*NewF, calls_todo,
4285 FAM.getResult<TargetLibraryAnalysis>(*NewF), local)
4286 .run();
4287 }
4288
4289 if (EnzymePrint)
4290 llvm::errs() << "after simplification :\n" << *NewF << "\n";
4291
4292 if (llvm::verifyFunction(*NewF, &llvm::errs())) {
4293 llvm::errs() << *NewF << "\n";
4294 report_fatal_error("function failed verification (1)");
4295 }
4296 cache[std::make_pair(F, mode)] = NewF;
4297 return NewF;
4298}
4299
4300FunctionType *getFunctionTypeForClone(llvm::FunctionType *FTy,
4301 DerivativeMode mode, unsigned width,
4302 llvm::Type *additionalArg,
4303 llvm::ArrayRef<DIFFE_TYPE> constant_args,
4304 bool diffeReturnArg, bool returnTape,
4305 bool returnPrimal, bool returnShadow) {
4306 SmallVector<Type *, 4> RetTypes;
4307 if (returnPrimal)
4308 RetTypes.push_back(FTy->getReturnType());
4309 if (returnShadow)
4310 RetTypes.push_back(
4311 GradientUtils::getShadowType(FTy->getReturnType(), width));
4312 SmallVector<Type *, 4> ArgTypes;
4313
4314 // The user might be deleting arguments to the function by specifying them in
4315 // the VMap. If so, we need to not add the arguments to the arg ty vector
4316 unsigned argno = 0;
4317
4318 for (auto &I : FTy->params()) {
4319 ArgTypes.push_back(I);
4320 if (constant_args[argno] == DIFFE_TYPE::DUP_ARG ||
4321 constant_args[argno] == DIFFE_TYPE::DUP_NONEED) {
4322 ArgTypes.push_back(GradientUtils::getShadowType(I, width));
4323 } else if (constant_args[argno] == DIFFE_TYPE::OUT_DIFF && !returnTape) {
4324 RetTypes.push_back(GradientUtils::getShadowType(I, width));
4325 }
4326 ++argno;
4327 }
4328
4329 if (diffeReturnArg) {
4330 assert(!FTy->getReturnType()->isVoidTy());
4331 ArgTypes.push_back(
4332 GradientUtils::getShadowType(FTy->getReturnType(), width));
4333 }
4334 if (additionalArg) {
4335 ArgTypes.push_back(additionalArg);
4336 }
4337 Type *RetType = StructType::get(FTy->getContext(), RetTypes);
4338 if (returnTape) {
4339 RetTypes.insert(RetTypes.begin(),
4340 getDefaultAnonymousTapeType(FTy->getContext()));
4341 }
4342
4343 if (RetTypes.size() == 0)
4344 RetType = Type::getVoidTy(RetType->getContext());
4345 else if (RetTypes.size() == 1 && (returnPrimal || returnShadow) &&
4347 RetType = RetTypes[0];
4348 else
4349 RetType = StructType::get(FTy->getContext(), RetTypes);
4350
4351 bool noReturn = RetTypes.size() == 0;
4352 if (noReturn)
4353 RetType = Type::getVoidTy(RetType->getContext());
4354
4355 // Create a new function type...
4356 return FunctionType::get(RetType, ArgTypes, FTy->isVarArg());
4357}
4358
4360 DerivativeMode mode, unsigned width, Function *&F,
4361 ValueToValueMapTy &ptrInputs, ArrayRef<DIFFE_TYPE> constant_args,
4362 SmallPtrSetImpl<Value *> &constants, SmallPtrSetImpl<Value *> &nonconstant,
4363 SmallPtrSetImpl<Value *> &returnvals, bool returnTape, bool returnPrimal,
4364 bool returnShadow, const Twine &name,
4365 llvm::ValueMap<const llvm::Value *, AssertingReplacingVH> *VMapO,
4366 bool diffeReturnArg, llvm::Type *additionalArg) {
4367 if (!F->empty())
4368 F = preprocessForClone(F, mode);
4369 llvm::ValueToValueMapTy VMap;
4370 llvm::FunctionType *FTy = getFunctionTypeForClone(
4371 F->getFunctionType(), mode, width, additionalArg, constant_args,
4372 diffeReturnArg, returnTape, returnPrimal, returnShadow);
4373
4374 for (BasicBlock &BB : *F) {
4375 if (auto ri = dyn_cast<ReturnInst>(BB.getTerminator())) {
4376 if (auto rv = ri->getReturnValue()) {
4377 returnvals.insert(rv);
4378 }
4379 }
4380 }
4381
4382 // Create the new function...
4383 Function *NewF = Function::Create(FTy, F->getLinkage(), name, F->getParent());
4384 if (diffeReturnArg) {
4385 auto I = NewF->arg_end();
4386 I--;
4387 if (additionalArg)
4388 I--;
4389 I->setName("differeturn");
4390 }
4391 if (additionalArg) {
4392 auto I = NewF->arg_end();
4393 I--;
4394 I->setName("tapeArg");
4395 }
4396
4397 {
4398 unsigned ii = 0;
4399 for (auto i = F->arg_begin(), j = NewF->arg_begin(); i != F->arg_end();) {
4400 VMap[i] = j;
4401 ++j;
4402 ++i;
4403 if (constant_args[ii] == DIFFE_TYPE::DUP_ARG ||
4404 constant_args[ii] == DIFFE_TYPE::DUP_NONEED) {
4405 ++j;
4406 }
4407 ++ii;
4408 }
4409 }
4410
4411 // Loop over the arguments, copying the names of the mapped arguments over...
4412 Function::arg_iterator DestI = NewF->arg_begin();
4413
4414 for (const Argument &I : F->args())
4415 if (VMap.count(&I) == 0) { // Is this argument preserved?
4416 DestI->setName(I.getName()); // Copy the name over...
4417 VMap[&I] = &*DestI++; // Add mapping to VMap
4418 }
4419 SmallVector<ReturnInst *, 4> Returns;
4420 if (!F->empty()) {
4421 CloneFunctionInto(NewF, F, VMap, CloneFunctionChangeType::LocalChangesOnly,
4422 Returns, "", nullptr);
4423 resetClonedGUID(NewF);
4424 }
4425 if (NewF->empty()) {
4426 auto entry = BasicBlock::Create(NewF->getContext(), "entry", NewF);
4427 IRBuilder<> B(entry);
4428 B.CreateUnreachable();
4429 }
4430 CloneOrigin[NewF] = F;
4431 if (VMapO) {
4432 for (const auto &data : VMap)
4433 VMapO->insert(std::pair<const llvm::Value *, AssertingReplacingVH>(
4434 data.first, (llvm::Value *)data.second));
4435 VMapO->getMDMap() = VMap.getMDMap();
4436 }
4437
4438 for (auto attr : {"enzyme_ta_norecur", "frame-pointer"})
4439 if (F->getAttributes().hasAttribute(AttributeList::FunctionIndex, attr)) {
4440 NewF->addAttribute(
4441 AttributeList::FunctionIndex,
4442 F->getAttributes().getAttribute(AttributeList::FunctionIndex, attr));
4443 }
4444
4445 for (auto attr :
4446 {"enzyme_type", "enzymejl_parmtype", "enzymejl_parmtype_ref"})
4447 if (F->getAttributes().hasAttribute(AttributeList::ReturnIndex, attr)) {
4448 NewF->addAttribute(
4449 AttributeList::ReturnIndex,
4450 F->getAttributes().getAttribute(AttributeList::ReturnIndex, attr));
4451 }
4452
4453 bool hasPtrInput = false;
4454 unsigned ii = 0, jj = 0;
4455
4456 for (auto i = F->arg_begin(), j = NewF->arg_begin(); i != F->arg_end();) {
4457 if (F->hasParamAttribute(ii, Attribute::StructRet)) {
4458 NewF->addParamAttr(
4459 jj, Attribute::get(F->getContext(), "enzyme_sret",
4461 F->getParamAttribute(ii, Attribute::StructRet)
4462 .getValueAsType())));
4463 }
4464 for (auto attr : {"enzymejl_parmtype", "enzymejl_parmtype_ref",
4465 "enzyme_type", "enzymejl_rooted_typ",
4466 "enzymejl_returnRoots", "enzymejl_sret_union_bytes"})
4467 if (F->getAttributes().hasParamAttr(ii, attr)) {
4468 NewF->addParamAttr(jj, F->getAttributes().getParamAttr(ii, attr));
4469 }
4470 for (auto ty : PrimalParamAttrsToPreserve)
4471 if (F->getAttributes().hasParamAttr(ii, ty)) {
4472 auto attr = F->getAttributes().getParamAttr(ii, ty);
4473 NewF->addParamAttr(jj, attr);
4474 }
4475 if (constant_args[ii] == DIFFE_TYPE::CONSTANT) {
4476 if (!i->hasByValAttr())
4477 constants.insert(i);
4479 llvm::errs() << "in new function " << NewF->getName()
4480 << " constant arg " << *j << "\n";
4481 } else {
4482 nonconstant.insert(i);
4484 llvm::errs() << "in new function " << NewF->getName()
4485 << " nonconstant arg " << *j << "\n";
4486 }
4487
4488 // Always remove nonnull/noundef since the caller may choose to pass
4489 // undef as an arg if provably it will not be used in the reverse pass
4490 if (constant_args[ii] == DIFFE_TYPE::DUP_NONEED ||
4492 if (F->hasParamAttribute(ii, Attribute::NonNull)) {
4493 NewF->removeParamAttr(jj, Attribute::NonNull);
4494 }
4495 if (F->hasParamAttribute(ii, Attribute::NoUndef)) {
4496 NewF->removeParamAttr(jj, Attribute::NoUndef);
4497 }
4498 if (F->hasParamAttribute(ii, Attribute::Dereferenceable)) {
4499 NewF->removeParamAttr(jj, Attribute::Dereferenceable);
4500 }
4501 if (F->hasParamAttribute(ii, Attribute::DereferenceableOrNull)) {
4502 NewF->removeParamAttr(jj, Attribute::DereferenceableOrNull);
4503 }
4504 }
4505
4506 if (constant_args[ii] == DIFFE_TYPE::DUP_ARG ||
4507 constant_args[ii] == DIFFE_TYPE::DUP_NONEED) {
4508 hasPtrInput = true;
4509 ptrInputs[i] = (j + 1);
4510 // TODO: find a way to keep the attributes in vector mode.
4511 if (width == 1) {
4512 for (auto ty : ShadowParamAttrsToPreserve) {
4513 if (F->getAttributes().hasParamAttr(ii, ty)) {
4514 auto attr = F->getAttributes().getParamAttr(ii, ty);
4515 NewF->addParamAttr(jj + 1, attr);
4516 }
4517 }
4518 }
4519
4520 for (auto attr : {"enzymejl_parmtype", "enzymejl_parmtype_ref",
4521 "enzyme_type", "enzymejl_rooted_typ",
4522 "enzymejl_returnRoots", "enzymejl_sret_union_bytes"})
4523 if (F->getAttributes().hasParamAttr(ii, attr)) {
4524 if (width == 1) {
4525 NewF->addParamAttr(jj + 1,
4526 F->getAttributes().getParamAttr(ii, attr));
4527 } else {
4528 NewF->addParamAttr(jj + 1,
4529 Attribute::get(F->getContext(),
4530 attr + std::string("_v"),
4531 F->getAttributes()
4532 .getParamAttr(ii, attr)
4533 .getValueAsString()));
4534 }
4535 }
4536
4537 if (F->hasParamAttribute(ii, Attribute::StructRet)) {
4538 if (width == 1) {
4539 NewF->addParamAttr(
4540 jj + 1,
4541 Attribute::get(F->getContext(), "enzyme_sret",
4543 F->getParamAttribute(ii, Attribute::StructRet)
4544 .getValueAsType())));
4545 } else {
4546 NewF->addParamAttr(
4547 jj + 1,
4548 Attribute::get(F->getContext(), "enzyme_sret_v",
4550 F->getParamAttribute(ii, Attribute::StructRet)
4551 .getValueAsType())));
4552 }
4553 }
4554
4555 j->setName(i->getName());
4556 ++j;
4557 j->setName(i->getName() + "'");
4558 nonconstant.insert(j);
4559 ++j;
4560 jj += 2;
4561
4562 ++i;
4563
4564 } else {
4565 j->setName(i->getName());
4566 ++j;
4567 ++jj;
4568 ++i;
4569 }
4570 ++ii;
4571 }
4572
4573 if (hasPtrInput && (mode == DerivativeMode::ReverseModeCombined ||
4575 if (NewF->hasFnAttribute(Attribute::ReadOnly)) {
4576 NewF->removeFnAttr(Attribute::ReadOnly);
4577 }
4578#if LLVM_VERSION_MAJOR >= 16
4579 auto eff = NewF->getMemoryEffects();
4580 for (auto loc : MemoryEffects::locations()) {
4581 if (loc == MemoryEffects::Location::InaccessibleMem)
4582 continue;
4583 auto mr = eff.getModRef(loc);
4584 if (isModSet(mr))
4585 eff |= MemoryEffects(loc, ModRefInfo::Ref);
4586 if (isRefSet(mr))
4587 eff |= MemoryEffects(loc, ModRefInfo::Mod);
4588 }
4589 NewF->setMemoryEffects(eff);
4590#endif
4591 }
4592 NewF->setLinkage(Function::LinkageTypes::InternalLinkage);
4594 NewF->addFnAttr(Attribute::AlwaysInline);
4595 assert(NewF->hasLocalLinkage());
4596
4597 return NewF;
4598}
4599
4600void CoaleseTrivialMallocs(Function &F, DominatorTree &DT) {
4601 std::map<BasicBlock *, std::vector<std::pair<CallInst *, CallInst *>>>
4602 LegalMallocs;
4603
4604 std::map<Metadata *, std::vector<CallInst *>> frees;
4605 for (BasicBlock &BB : F) {
4606 for (Instruction &I : BB) {
4607 if (auto CI = dyn_cast<CallInst>(&I)) {
4608 if (auto MD = hasMetadata(CI, "enzyme_cache_free")) {
4609 Metadata *op = MD->getOperand(0);
4610 if (cast<ConstantInt>(
4611 cast<ConstantAsMetadata>(cast<MDNode>(op)->getOperand(0))
4612 ->getValue())
4613 ->isOne()) {
4614 frees[op].push_back(CI);
4615 }
4616 }
4617 }
4618 }
4619 }
4620
4621 for (BasicBlock &BB : F) {
4622 for (Instruction &I : BB) {
4623 if (auto CI = dyn_cast<CallInst>(&I)) {
4624 if (auto F = CI->getCalledFunction()) {
4625 if (F->getName() == "malloc") {
4626 CallInst *freeCall = nullptr;
4627 for (auto U : CI->users()) {
4628 if (auto CI2 = dyn_cast<CallInst>(U)) {
4629 if (auto F2 = CI2->getCalledFunction()) {
4630 if (F2->getName() == "free") {
4631 if (DT.dominates(CI, CI2)) {
4632 freeCall = CI2;
4633 break;
4634 }
4635 }
4636 }
4637 }
4638 }
4639 if (!freeCall) {
4640 if (auto MD = hasMetadata(CI, "enzyme_cache_alloc")) {
4641 Metadata *op = MD->getOperand(0);
4642 if (cast<ConstantInt>(cast<ConstantAsMetadata>(
4643 cast<MDNode>(op)->getOperand(0))
4644 ->getValue())
4645 ->isOne()) {
4646 if (frees[op].size() == 1)
4647 freeCall = frees[op][0];
4648 }
4649 }
4650 }
4651 if (freeCall)
4652 LegalMallocs[&BB].emplace_back(CI, freeCall);
4653 }
4654 }
4655 }
4656 }
4657 }
4658 for (auto &pair : LegalMallocs) {
4659 if (pair.second.size() < 2)
4660 continue;
4661 CallInst *First = pair.second[0].first;
4662 for (auto &z : pair.second) {
4663 if (!DT.dominates(First, z.first))
4664 First = z.first;
4665 }
4666 bool legal = true;
4667 for (auto &z : pair.second) {
4668 if (auto inst = dyn_cast<Instruction>(z.first->getArgOperand(0)))
4669 if (!DT.dominates(inst, First))
4670 legal = false;
4671 }
4672 if (!legal)
4673 continue;
4674 IRBuilder<> B(First);
4675 Value *Size = First->getArgOperand(0);
4676 for (auto &z : pair.second) {
4677 if (z.first == First)
4678 continue;
4679 Size = B.CreateAdd(
4680 B.CreateOr(B.CreateSub(Size, ConstantInt::get(Size->getType(), 1)),
4681 ConstantInt::get(Size->getType(), 15)),
4682 ConstantInt::get(Size->getType(), 1));
4683 z.second->eraseFromParent();
4684 IRBuilder<> B2(z.first);
4685 Value *gepPtr = B2.CreateInBoundsGEP(Type::getInt8Ty(First->getContext()),
4686 First, Size);
4687 z.first->replaceAllUsesWith(gepPtr);
4688 Size = B.CreateAdd(Size, z.first->getArgOperand(0));
4689 z.first->eraseFromParent();
4690 }
4691 auto NewMalloc =
4692 cast<CallInst>(B.CreateCall(First->getCalledFunction(), Size));
4693 NewMalloc->copyIRFlags(First);
4694 NewMalloc->setMetadata("enzyme_cache_alloc",
4695 hasMetadata(First, "enzyme_cache_alloc"));
4696 First->replaceAllUsesWith(NewMalloc);
4697 First->eraseFromParent();
4698 }
4699}
4700
4701void SelectOptimization(Function *F) {
4702 DominatorTree DT(*F);
4703 for (auto &BB : *F) {
4704 if (auto BI = (isAnyBranch(BB.getTerminator())
4705 ? cast<Instruction>(BB.getTerminator())
4706 : nullptr)) {
4707 if (isConditionalBranch(BI)) {
4708 for (auto &I : BB) {
4709 if (auto SI = dyn_cast<SelectInst>(&I)) {
4710 if (SI->getCondition() == getBranchCondition(BI)) {
4711 for (Value::use_iterator UI = SI->use_begin(), E = SI->use_end();
4712 UI != E;) {
4713 Use &U = *UI;
4714 ++UI;
4715 if (DT.dominates(BasicBlockEdge(&BB, BI->getSuccessor(0)), U))
4716 U.set(SI->getTrueValue());
4717 else if (DT.dominates(BasicBlockEdge(&BB, BI->getSuccessor(1)),
4718 U))
4719 U.set(SI->getFalseValue());
4720 }
4721 }
4722 }
4723 }
4724 }
4725 }
4726 }
4727}
4728
4730 for (Function &Impl : M) {
4731 for (auto attr : {"implements", "implements2"}) {
4732 if (!Impl.hasFnAttribute(attr))
4733 continue;
4734 const Attribute &A = Impl.getFnAttribute(attr);
4735
4736 const StringRef SpecificationName = A.getValueAsString();
4737 Function *Specification = M.getFunction(SpecificationName);
4738 if (!Specification) {
4739 LLVM_DEBUG(dbgs() << "Found implementation '" << Impl.getName()
4740 << "' but no matching specification with name '"
4741 << SpecificationName
4742 << "', potentially inlined and/or eliminated.\n");
4743 continue;
4744 }
4745 LLVM_DEBUG(dbgs() << "Replace specification '" << Specification->getName()
4746 << "' with implementation '" << Impl.getName()
4747 << "'\n");
4748
4749 for (auto I = Specification->use_begin(), UE = Specification->use_end();
4750 I != UE;) {
4751 auto &use = *I;
4752 ++I;
4753 auto cext = ConstantExpr::getBitCast(&Impl, Specification->getType());
4754 if (cast<Instruction>(use.getUser())->getParent()->getParent() == &Impl)
4755 continue;
4756 use.set(cext);
4757 if (auto CI = dyn_cast<CallInst>(use.getUser())) {
4758 if (CI->getCalledOperand() == cext ||
4759 CI->getCalledFunction() == &Impl) {
4760 CI->setCallingConv(Impl.getCallingConv());
4761 }
4762 }
4763 }
4764 }
4765 }
4766}
4767
4769 PreservedAnalyses PA;
4770 PA = PromotePass().run(*F, FAM);
4771 FAM.invalidate(*F, PA);
4772#if !defined(FLANG)
4773 PA = GVNPass().run(*F, FAM);
4774#else
4775 PA = GVN().run(*F, FAM);
4776#endif
4777 FAM.invalidate(*F, PA);
4778#if LLVM_VERSION_MAJOR >= 16 && !defined(FLANG)
4779 PA = SROAPass(llvm::SROAOptions::PreserveCFG).run(*F, FAM);
4780#elif !defined(FLANG)
4781 PA = SROAPass().run(*F, FAM);
4782#else
4783 PA = SROA().run(*F, FAM);
4784#endif
4785 FAM.invalidate(*F, PA);
4786
4787 if (EnzymeSelectOpt) {
4788 SimplifyCFGOptions scfgo;
4789 PA = SimplifyCFGPass(scfgo).run(*F, FAM);
4790 FAM.invalidate(*F, PA);
4791 PA = CorrelatedValuePropagationPass().run(*F, FAM);
4792 FAM.invalidate(*F, PA);
4794 }
4795 // EarlyCSEPass(/*memoryssa*/ true).run(*F, FAM);
4796
4797 if (EnzymeCoalese)
4798 CoaleseTrivialMallocs(*F, FAM.getResult<DominatorTreeAnalysis>(*F));
4799
4800 ReplaceFunctionImplementation(*F->getParent());
4801
4802 {
4803 PreservedAnalyses PA;
4804 FAM.invalidate(*F, PA);
4805 }
4806
4807 OptimizationLevel Level = OptimizationLevel::O0;
4808
4809 switch (EnzymePostOptLevel) {
4810 default:
4811 case 0:
4812 Level = OptimizationLevel::O0;
4813 break;
4814 case 1:
4815 Level = OptimizationLevel::O1;
4816 break;
4817 case 2:
4818 Level = OptimizationLevel::O2;
4819 break;
4820 case 3:
4821 Level = OptimizationLevel::O3;
4822 break;
4823 }
4824 if (Level != OptimizationLevel::O0) {
4825 PassBuilder PB;
4826 FunctionPassManager FPM =
4827 PB.buildFunctionSimplificationPipeline(Level, ThinOrFullLTOPhase::None);
4828 PA = FPM.run(*F, FAM);
4829 FAM.invalidate(*F, PA);
4830 }
4831
4832 // TODO actually run post optimizations.
4833}
4834
4836 LAM.clear();
4837 FAM.clear();
4838 MAM.clear();
4839 cache.clear();
4840}
4841
4842// Returns if a is guaranteed to be equivalent to not b
4843static bool isNot(Value *a, Value *b) {
4844 // cmp pred, a, b and cmp inverse(pred), a, b
4845 if (auto I1 = dyn_cast<CmpInst>(a))
4846 if (auto I2 = dyn_cast<CmpInst>(b))
4847 if (I1->getOperand(0) == I2->getOperand(0) &&
4848 I1->getOperand(1) == I2->getOperand(1) &&
4849 I1->getPredicate() == I2->getInversePredicate())
4850 return true;
4851 // a := xor true, b
4852 if (auto I = dyn_cast<Instruction>(a))
4853 if (I->getOpcode() == Instruction::Xor)
4854 for (int i = 0; i < 2; i++) {
4855 if (I->getOperand(i) == b)
4856 if (auto CI = dyn_cast<ConstantInt>(I->getOperand(1 - i)))
4857#if LLVM_VERSION_MAJOR > 16
4858 if (CI->getValue().isAllOnes())
4859#else
4860 if (CI->getValue().isAllOnesValue())
4861#endif
4862 return true;
4863 }
4864 // b := xor true, a
4865 if (auto I = dyn_cast<Instruction>(b))
4866 if (I->getOpcode() == Instruction::Xor)
4867 for (int i = 0; i < 2; i++) {
4868 if (I->getOperand(i) == a)
4869 if (auto CI = dyn_cast<ConstantInt>(I->getOperand(1 - i)))
4870#if LLVM_VERSION_MAJOR > 16
4871 if (CI->getValue().isAllOnes())
4872#else
4873 if (CI->getValue().isAllOnesValue())
4874#endif
4875 return true;
4876 }
4877 return false;
4878}
4879
4881public:
4882 DominatorTree &DT;
4883 LoopInfo &LI;
4884 compare_insts(DominatorTree &DT, LoopInfo &LI) : DT(DT), LI(LI) {}
4885
4886 // return true if A appears later than B.
4887 bool operator()(Instruction *A, Instruction *B) const {
4888 if (A == B) {
4889 return false;
4890 }
4891 if (A->getParent() == B->getParent()) {
4892 return !A->comesBefore(B);
4893 }
4894 auto AB = A->getParent();
4895 auto BB = B->getParent();
4896 assert(AB->getParent() == BB->getParent());
4897
4898 for (auto prev = BB->getPrevNode(); prev; prev = prev->getPrevNode()) {
4899 if (prev == AB)
4900 return false;
4901 }
4902 return true;
4903 }
4904};
4905
4906class DominatorOrderSet : public std::set<Instruction *, compare_insts> {
4907public:
4908 DominatorOrderSet(DominatorTree &DT, LoopInfo &LI)
4909 : std::set<Instruction *, compare_insts>(compare_insts(DT, LI)) {}
4910 bool contains(Instruction *I) const {
4911 auto __i = find(I);
4912 return __i != end();
4913 }
4914 void remove(Instruction *I) {
4915 auto __i = find(I);
4916 assert(__i != end());
4917 erase(__i);
4918 }
4919 Instruction *pop_back_val() {
4920 auto back = end();
4921 back--;
4922 auto v = *back;
4923 erase(back);
4924 return v;
4925 }
4926};
4927
4928bool directlySparse(Value *z) {
4929 if (isa<UIToFPInst>(z))
4930 return true;
4931 if (isa<SIToFPInst>(z))
4932 return true;
4933 if (isa<ZExtInst>(z))
4934 return true;
4935 if (isa<SExtInst>(z))
4936 return true;
4937 if (auto SI = dyn_cast<SelectInst>(z)) {
4938 if (auto CI = dyn_cast<ConstantInt>(SI->getTrueValue()))
4939 if (CI->isZero())
4940 return true;
4941 if (auto CI = dyn_cast<ConstantInt>(SI->getFalseValue()))
4942 if (CI->isZero())
4943 return true;
4944 }
4945 return false;
4946}
4947
4949
4950Function *getProductIntrinsic(llvm::Module &M, llvm::Type *T) {
4951 std::string name = "__enzyme_product.";
4952 if (T->isFloatTy())
4953 name += "f32";
4954 else if (T->isDoubleTy())
4955 name += "f64";
4956 else if (T->isIntegerTy())
4957 name += "i" + std::to_string(cast<IntegerType>(T)->getBitWidth());
4958 else
4959 assert(0);
4960 auto FT = llvm::FunctionType::get(T, {}, true);
4961 AttributeList AL;
4962#if LLVM_VERSION_MAJOR >= 16
4963 AL = AL.addAttribute(
4964 T->getContext(), AttributeList::FunctionIndex,
4965 Attribute::getWithMemoryEffects(T->getContext(), MemoryEffects::none()));
4966#else
4967 AL = AL.addAttribute(T->getContext(), AttributeList::FunctionIndex,
4968 Attribute::ReadNone);
4969#endif
4970 AL = AL.addAttribute(T->getContext(), AttributeList::FunctionIndex,
4971 Attribute::NoUnwind);
4972 AL = AL.addAttribute(T->getContext(), AttributeList::FunctionIndex,
4973 Attribute::NoFree);
4974 AL = AL.addAttribute(T->getContext(), AttributeList::FunctionIndex,
4975 Attribute::NoSync);
4976 AL = AL.addAttribute(T->getContext(), AttributeList::FunctionIndex,
4977 Attribute::WillReturn);
4978 return cast<Function>(M.getOrInsertFunction(name, FT, AL).getCallee());
4979}
4980
4981Function *getSumIntrinsic(llvm::Module &M, llvm::Type *T) {
4982 std::string name = "__enzyme_sum.";
4983 if (T->isFloatTy())
4984 name += "f32";
4985 else if (T->isDoubleTy())
4986 name += "f64";
4987 else if (T->isIntegerTy())
4988 name += "i" + std::to_string(cast<IntegerType>(T)->getBitWidth());
4989 else
4990 assert(0);
4991 auto FT = llvm::FunctionType::get(T, {}, true);
4992 AttributeList AL;
4993#if LLVM_VERSION_MAJOR >= 16
4994 AL = AL.addAttribute(
4995 T->getContext(), AttributeList::FunctionIndex,
4996 Attribute::getWithMemoryEffects(T->getContext(), MemoryEffects::none()));
4997#else
4998 AL = AL.addAttribute(T->getContext(), AttributeList::FunctionIndex,
4999 Attribute::ReadNone);
5000#endif
5001 AL = AL.addAttribute(T->getContext(), AttributeList::FunctionIndex,
5002 Attribute::NoUnwind);
5003 AL = AL.addAttribute(T->getContext(), AttributeList::FunctionIndex,
5004 Attribute::NoFree);
5005 AL = AL.addAttribute(T->getContext(), AttributeList::FunctionIndex,
5006 Attribute::NoSync);
5007 AL = AL.addAttribute(T->getContext(), AttributeList::FunctionIndex,
5008 Attribute::WillReturn);
5009 return cast<Function>(M.getOrInsertFunction(name, FT, AL).getCallee());
5010}
5011
5012CallInst *isProduct(llvm::Value *v) {
5013 if (auto prod = dyn_cast<CallInst>(v))
5014 if (auto F = getFunctionFromCall(prod))
5015 if (startsWith(F->getName(), "__enzyme_product"))
5016 return prod;
5017 return nullptr;
5018}
5019
5020CallInst *isSum(llvm::Value *v) {
5021 if (auto prod = dyn_cast<CallInst>(v))
5022 if (auto F = getFunctionFromCall(prod))
5023 if (startsWith(F->getName(), "__enzyme_sum"))
5024 return prod;
5025 return nullptr;
5026}
5027
5028SmallVector<Value *, 1> callOperands(llvm::CallBase *CB) {
5029 return SmallVector<Value *, 1>(CB->args().begin(), CB->args().end());
5030}
5031
5033 if (isa<LoadInst>(z))
5034 return true;
5035 if (isa<Constant>(z))
5036 return true;
5037 if (auto BO = dyn_cast<BinaryOperator>(z))
5038 return guaranteedDataDependent(BO->getOperand(0)) &&
5039 guaranteedDataDependent(BO->getOperand(1));
5040 if (auto C = dyn_cast<CastInst>(z))
5041 return guaranteedDataDependent(C->getOperand(0));
5042 if (auto S = isSum(z)) {
5043 for (auto op : callOperands(S))
5045 return true;
5046 return false;
5047 }
5048 if (auto S = isProduct(z)) {
5049 for (auto op : callOperands(S))
5050 if (!guaranteedDataDependent(op))
5051 return false;
5052 return true;
5053 }
5054 if (auto II = dyn_cast<IntrinsicInst>(z)) {
5055 switch (II->getIntrinsicID()) {
5056 case Intrinsic::sqrt:
5057 case Intrinsic::sin:
5058 case Intrinsic::cos:
5059#if LLVM_VERSION_MAJOR >= 19
5060 case Intrinsic::sinh:
5061 case Intrinsic::cosh:
5062 case Intrinsic::tanh:
5063#endif
5064 return guaranteedDataDependent(II->getArgOperand(0));
5065 default:
5066 break;
5067 }
5068 }
5069 return false;
5070}
5071
5072std::optional<std::string> fixSparse_inner(Instruction *cur, llvm::Function &F,
5073 QueueType &Q, DominatorTree &DT,
5074 ScalarEvolution &SE, LoopInfo &LI,
5075 const DataLayout &DL) {
5076 auto push = [&](llvm::Value *V) {
5077 if (V == cur)
5078 return V;
5079 assert(V);
5080 if (auto I = dyn_cast<Instruction>(V)) {
5081 Q.insert(I);
5082 for (auto U : I->users()) {
5083 if (auto I2 = dyn_cast<Instruction>(U)) {
5084 if (I2 == cur)
5085 continue;
5086 Q.insert(I2);
5087 }
5088 }
5089 }
5090 return V;
5091 };
5092 auto pushcse = [&](llvm::Value *V) -> llvm::Value * {
5093 if (auto I = dyn_cast<Instruction>(V)) {
5094 for (size_t i = 0; i < I->getNumOperands(); i++) {
5095 if (auto I2 = dyn_cast<Instruction>(I->getOperand(i))) {
5096 Instruction *candidate = nullptr;
5097 for (auto U : I2->users()) {
5098 candidate = dyn_cast<Instruction>(U);
5099 if (!candidate)
5100 continue;
5101 if (candidate == I && candidate->getType() != I->getType()) {
5102 candidate = nullptr;
5103 continue;
5104 }
5105 bool isSame = candidate->isIdenticalTo(I);
5106 if (!isSame) {
5107 if (auto P1 = isProduct(I))
5108 if (auto P2 = isProduct(I2)) {
5109 std::multiset<llvm::Value *> s1;
5110 std::multiset<llvm::Value *> s2;
5111 for (auto &v : callOperands(P1))
5112 s1.insert(v);
5113 for (auto &v : callOperands(P2))
5114 s2.insert(v);
5115 isSame = s1 == s2;
5116 }
5117 if (auto P1 = isSum(I))
5118 if (auto P2 = isSum(I2)) {
5119 std::multiset<llvm::Value *> s1;
5120 std::multiset<llvm::Value *> s2;
5121 for (auto &v : callOperands(P1))
5122 s1.insert(v);
5123 for (auto &v : callOperands(P2))
5124 s2.insert(v);
5125 isSame = s1 == s2;
5126 }
5127 }
5128 if (!isSame) {
5129 candidate = nullptr;
5130 continue;
5131 }
5132
5133 if (DT.dominates(candidate, I)) {
5134 break;
5135 }
5136 candidate = nullptr;
5137 }
5138 if (candidate) {
5139 I->eraseFromParent();
5140 return candidate;
5141 }
5142 break;
5143 }
5144 }
5145 return push(I);
5146 }
5147 return V;
5148 };
5149 auto replaceAndErase = [&](llvm::Instruction *I, llvm::Value *candidate) {
5150 for (auto U : I->users())
5151 push(U);
5152 I->replaceAllUsesWith(candidate);
5153 push(candidate);
5154
5155 SetVector<Instruction *> operands;
5156 for (size_t i = 0; i < I->getNumOperands(); i++) {
5157 if (auto I2 = dyn_cast<Instruction>(I->getOperand(i))) {
5158 if ((!I2->mayWriteToMemory() ||
5159 (isa<CallInst>(I2) && isReadOnly(cast<CallInst>(I2)))))
5160 operands.insert(I2);
5161 }
5162 }
5163 if (Q.contains(I)) {
5164 Q.remove(I);
5165 }
5166 assert(!Q.contains(I));
5167 I->eraseFromParent();
5168 for (auto op : operands)
5169 if (op->getNumUses() == 0) {
5170 if (Q.contains(op))
5171 Q.remove(op);
5172 op->eraseFromParent();
5173 }
5174 };
5175 if (!cur->getType()->isVoidTy() &&
5176 (!cur->mayWriteToMemory() ||
5177 (isa<CallInst>(cur) && isReadOnly(cast<CallInst>(cur))))) {
5178 // DCE
5179 if (cur->getNumUses() == 0) {
5180 for (size_t i = 0; i < cur->getNumOperands(); i++)
5181 push(cur->getOperand(i));
5182 assert(!Q.contains(cur));
5183 cur->eraseFromParent();
5184 return "DCE";
5185 }
5186 // CSE
5187 {
5188 for (size_t i = 0; i < cur->getNumOperands(); i++) {
5189 if (auto I = dyn_cast<Instruction>(cur->getOperand(i))) {
5190 Instruction *candidate = nullptr;
5191 bool reverse = false;
5192 for (auto U : I->users()) {
5193 candidate = dyn_cast<Instruction>(U);
5194 if (!candidate)
5195 continue;
5196 if (candidate == cur && candidate->getType() != cur->getType()) {
5197 candidate = nullptr;
5198 continue;
5199 }
5200 bool isSame = candidate->isIdenticalTo(cur);
5201 if (!isSame) {
5202 if (auto P1 = isProduct(candidate))
5203 if (auto P2 = isProduct(cur)) {
5204 std::multiset<llvm::Value *> s1;
5205 std::multiset<llvm::Value *> s2;
5206 for (auto &v : callOperands(P1))
5207 s1.insert(v);
5208 for (auto &v : callOperands(P2))
5209 s2.insert(v);
5210 isSame = s1 == s2;
5211 }
5212 if (auto P1 = isSum(candidate))
5213 if (auto P2 = isSum(cur)) {
5214 std::multiset<llvm::Value *> s1;
5215 std::multiset<llvm::Value *> s2;
5216 for (auto &v : callOperands(P1))
5217 s1.insert(v);
5218 for (auto &v : callOperands(P2))
5219 s2.insert(v);
5220 isSame = s1 == s2;
5221 }
5222 }
5223
5224 if (!isSame) {
5225 candidate = nullptr;
5226 continue;
5227 }
5228
5229 if (DT.dominates(candidate, cur)) {
5230 break;
5231 } else if (DT.dominates(cur, candidate)) {
5232 reverse = true;
5233 break;
5234 }
5235 candidate = nullptr;
5236 }
5237 if (candidate) {
5238 if (reverse) {
5239 if (Q.contains(candidate))
5240 Q.remove(candidate);
5241 auto tmp = candidate;
5242 candidate = cur;
5243 cur = tmp;
5244 }
5245 replaceAndErase(cur, candidate);
5246 return "CSE";
5247 }
5248 break;
5249 }
5250 }
5251 }
5252 }
5253
5254 if (auto SI = dyn_cast<SelectInst>(cur))
5255 if (auto CI = dyn_cast<ConstantInt>(SI->getCondition())) {
5256 if (CI->isOne()) {
5257 replaceAndErase(cur, SI->getTrueValue());
5258 return "SelectToTrue";
5259 } else {
5260 replaceAndErase(cur, SI->getFalseValue());
5261 return "SelectToFalse";
5262 }
5263 }
5264 if (cur->getOpcode() == Instruction::Or) {
5265 for (int i = 0; i < 2; i++) {
5266 if (auto C = dyn_cast<ConstantInt>(cur->getOperand(i))) {
5267 // or a, 0 -> a
5268 if (C->isZero()) {
5269 replaceAndErase(cur, cur->getOperand(1 - i));
5270 return "OrZero";
5271 }
5272 // or a, 1 -> 1
5273 if (C->isOne() && cur->getType()->isIntegerTy(1)) {
5274 replaceAndErase(cur, C);
5275 return "OrOne";
5276 }
5277 }
5278 }
5279 }
5280 if (cur->getOpcode() == Instruction::And) {
5281 for (int i = 0; i < 2; i++) {
5282 if (auto C = dyn_cast<ConstantInt>(cur->getOperand(i))) {
5283 // and a, 1 -> a
5284 if (C->isOne() && cur->getType()->isIntegerTy(1)) {
5285 replaceAndErase(cur, cur->getOperand(1 - i));
5286 return "AndOne";
5287 }
5288 // and a, 0 -> 0
5289 if (C->isZero()) {
5290 replaceAndErase(cur, C);
5291 return "AndZero";
5292 }
5293 }
5294 }
5295 }
5296
5297 IRBuilder<> B(cur);
5298 if (auto CI = dyn_cast<CastInst>(cur))
5299 if (auto C = dyn_cast<Constant>(CI->getOperand(0))) {
5300 replaceAndErase(
5301 cur, cast<Constant>(B.CreateCast(CI->getOpcode(), C, CI->getType())));
5302 return "CastConstProp";
5303 }
5304 std::function<Value *(Value *, Value *, Value *)> replace = [&](Value *val,
5305 Value *orig,
5306 Value *with) {
5307 if (val == orig) {
5308 return with;
5309 }
5310 if (isNot(val, orig)) {
5311 return pushcse(B.CreateNot(with));
5312 }
5313 if (isa<PHINode>(val))
5314 return val;
5315
5316 if (auto I = dyn_cast<Instruction>(val)) {
5317 if (I->mayWriteToMemory() &&
5318 !(isa<CallInst>(I) && isReadOnly(cast<CallInst>(I))))
5319 return val;
5320
5321 if (I->getOpcode() == Instruction::Add) {
5322 Value *lhs = replace(I->getOperand(0), orig, with);
5323 Value *rhs = replace(I->getOperand(1), orig, with);
5324 if (lhs == I->getOperand(0) && rhs == I->getOperand(1))
5325 return val;
5326 push(I);
5327 return pushcse(B.CreateAdd(lhs, rhs, "sel." + I->getName(),
5328 I->hasNoUnsignedWrap(),
5329 I->hasNoSignedWrap()));
5330 }
5331
5332 if (I->getOpcode() == Instruction::Sub) {
5333 Value *lhs = replace(I->getOperand(0), orig, with);
5334 Value *rhs = replace(I->getOperand(1), orig, with);
5335 if (lhs == I->getOperand(0) && rhs == I->getOperand(1))
5336 return val;
5337 push(I);
5338 return pushcse(B.CreateSub(lhs, rhs, "sel." + I->getName(),
5339 I->hasNoUnsignedWrap(),
5340 I->hasNoSignedWrap()));
5341 }
5342
5343 if (I->getOpcode() == Instruction::Mul) {
5344 Value *lhs = replace(I->getOperand(0), orig, with);
5345 Value *rhs = replace(I->getOperand(1), orig, with);
5346 if (lhs == I->getOperand(0) && rhs == I->getOperand(1))
5347 return val;
5348 push(I);
5349 return pushcse(B.CreateMul(lhs, rhs, "sel." + I->getName(),
5350 I->hasNoUnsignedWrap(),
5351 I->hasNoSignedWrap()));
5352 }
5353
5354 if (I->getOpcode() == Instruction::And) {
5355 Value *lhs = replace(I->getOperand(0), orig, with);
5356 Value *rhs = replace(I->getOperand(1), orig, with);
5357 if (lhs == I->getOperand(0) && rhs == I->getOperand(1))
5358 return val;
5359 push(I);
5360 return pushcse(B.CreateAnd(lhs, rhs, "sel." + I->getName()));
5361 }
5362
5363 if (I->getOpcode() == Instruction::Or) {
5364 Value *lhs = replace(I->getOperand(0), orig, with);
5365 Value *rhs = replace(I->getOperand(1), orig, with);
5366 if (lhs == I->getOperand(0) && rhs == I->getOperand(1))
5367 return val;
5368 push(I);
5369 return pushcse(B.CreateOr(lhs, rhs, "sel." + I->getName()));
5370 }
5371
5372 if (I->getOpcode() == Instruction::Xor) {
5373 Value *lhs = replace(I->getOperand(0), orig, with);
5374 Value *rhs = replace(I->getOperand(1), orig, with);
5375 if (lhs == I->getOperand(0) && rhs == I->getOperand(1))
5376 return val;
5377 push(I);
5378 return pushcse(B.CreateXor(lhs, rhs, "sel." + I->getName()));
5379 }
5380
5381 if (I->getOpcode() == Instruction::FAdd) {
5382 Value *lhs = replace(I->getOperand(0), orig, with);
5383 Value *rhs = replace(I->getOperand(1), orig, with);
5384 if (lhs == I->getOperand(0) && rhs == I->getOperand(1))
5385 return val;
5386 push(I);
5387 return pushcse(B.CreateFAddFMF(lhs, rhs, I, "sel." + I->getName()));
5388 }
5389
5390 if (I->getOpcode() == Instruction::FSub) {
5391 Value *lhs = replace(I->getOperand(0), orig, with);
5392 Value *rhs = replace(I->getOperand(1), orig, with);
5393 if (lhs == I->getOperand(0) && rhs == I->getOperand(1))
5394 return val;
5395 push(I);
5396 return pushcse(B.CreateFSubFMF(lhs, rhs, I, "sel." + I->getName()));
5397 }
5398
5399 if (I->getOpcode() == Instruction::FMul) {
5400 Value *lhs = replace(I->getOperand(0), orig, with);
5401 Value *rhs = replace(I->getOperand(1), orig, with);
5402 if (lhs == I->getOperand(0) && rhs == I->getOperand(1))
5403 return val;
5404 push(I);
5405 return pushcse(B.CreateFMulFMF(lhs, rhs, I, "sel." + I->getName()));
5406 }
5407
5408 if (I->getOpcode() == Instruction::ZExt) {
5409 Value *op = replace(I->getOperand(0), orig, with);
5410 if (op == I->getOperand(0))
5411 return val;
5412 push(I);
5413 return pushcse(B.CreateZExt(op, I->getType(), "sel." + I->getName()));
5414 }
5415
5416 if (I->getOpcode() == Instruction::SExt) {
5417 Value *op = replace(I->getOperand(0), orig, with);
5418 if (op == I->getOperand(0))
5419 return val;
5420 push(I);
5421 return pushcse(B.CreateSExt(op, I->getType(), "sel." + I->getName()));
5422 }
5423
5424 if (I->getOpcode() == Instruction::UIToFP) {
5425 Value *op = replace(I->getOperand(0), orig, with);
5426 if (op == I->getOperand(0))
5427 return val;
5428 push(I);
5429 return pushcse(B.CreateUIToFP(op, I->getType(), "sel." + I->getName()));
5430 }
5431
5432 if (I->getOpcode() == Instruction::SIToFP) {
5433 Value *op = replace(I->getOperand(0), orig, with);
5434 if (op == I->getOperand(0))
5435 return val;
5436 push(I);
5437 return pushcse(B.CreateSIToFP(op, I->getType(), "sel." + I->getName()));
5438 }
5439
5440 if (auto CI = dyn_cast<CmpInst>(I)) {
5441 Value *lhs = replace(I->getOperand(0), orig, with);
5442 Value *rhs = replace(I->getOperand(1), orig, with);
5443 if (lhs == I->getOperand(0) && rhs == I->getOperand(1))
5444 return val;
5445 push(I);
5446 return pushcse(
5447 B.CreateCmp(CI->getPredicate(), lhs, rhs, "sel." + I->getName()));
5448 }
5449
5450 if (auto SI = dyn_cast<SelectInst>(I)) {
5451 Value *cond = replace(SI->getCondition(), orig, with);
5452 Value *tval = replace(SI->getTrueValue(), orig, with);
5453 Value *fval = replace(SI->getFalseValue(), orig, with);
5454 if (cond == SI->getCondition() && tval == SI->getTrueValue() &&
5455 fval == SI->getFalseValue())
5456 return val;
5457 push(I);
5458 if (auto CI = dyn_cast<ConstantInt>(cond)) {
5459 if (CI->isOne())
5460 return tval;
5461 else
5462 return fval;
5463 }
5464 return pushcse(B.CreateSelect(cond, tval, fval, "sel." + I->getName()));
5465 }
5466
5467 if (isProduct(I) || isSum(I)) {
5468 auto C = cast<CallBase>(I);
5469 auto ops = callOperands(C);
5470 bool changed = false;
5471 for (auto &op : ops) {
5472 auto next = replace(op, orig, with);
5473 if (next != op) {
5474 changed = true;
5475 op = next;
5476 }
5477 }
5478 if (!changed)
5479 return (Value *)I;
5480 push(I);
5481 pushcse(
5482 B.CreateCall(getFunctionFromCall(C), ops, "sel." + I->getName()));
5483 }
5484 }
5485 return val;
5486 };
5487
5488 if (auto II = dyn_cast<IntrinsicInst>(cur))
5489 if (II->getIntrinsicID() == Intrinsic::fmuladd ||
5490 II->getIntrinsicID() == Intrinsic::fma) {
5491 B.setFastMathFlags(getFast());
5492 auto mul = pushcse(B.CreateFMul(II->getOperand(0), II->getOperand(1)));
5493 auto add = pushcse(B.CreateFAdd(mul, II->getOperand(2)));
5494 replaceAndErase(cur, add);
5495 return "FMulAddExpand";
5496 }
5497
5498 if (auto BO = dyn_cast<BinaryOperator>(cur)) {
5499 if (BO->getOpcode() == Instruction::FMul && BO->isFast()) {
5500 Value *args[2] = {BO->getOperand(0), BO->getOperand(1)};
5501 auto mul = pushcse(
5502 B.CreateCall(getProductIntrinsic(*F.getParent(), BO->getType()), args,
5503 cur->getName()));
5504 replaceAndErase(cur, mul);
5505 return "FMulToProduct";
5506 }
5507 if (BO->getOpcode() == Instruction::FDiv && BO->isFast()) {
5508 auto c0 = dyn_cast<ConstantFP>(BO->getOperand(0));
5509 if (!c0 || !c0->isExactlyValue(1.0)) {
5510 B.setFastMathFlags(getFast());
5511 auto div = pushcse(B.CreateFDivFMF(ConstantFP::get(BO->getType(), 1.0),
5512 BO->getOperand(1), BO));
5513 auto mul = pushcse(
5514 B.CreateFMulFMF(BO->getOperand(0), div, BO, cur->getName()));
5515 replaceAndErase(cur, mul);
5516 return "FDivToFMul";
5517 }
5518 }
5519 if (BO->getOpcode() == Instruction::FAdd && BO->isFast()) {
5520 Value *args[2] = {BO->getOperand(0), BO->getOperand(1)};
5521 auto mul = pushcse(
5522 B.CreateCall(getSumIntrinsic(*F.getParent(), BO->getType()), args));
5523 replaceAndErase(cur, mul);
5524 return "FAddToSum";
5525 }
5526 if (BO->getOpcode() == Instruction::FSub && BO->isFast()) {
5527 B.setFastMathFlags(getFast());
5528 Value *args[2] = {BO->getOperand(0),
5529 pushcse(B.CreateFNeg(BO->getOperand(1)))};
5530 auto mul =
5531 pushcse(B.CreateCall(getSumIntrinsic(*F.getParent(), BO->getType()),
5532 args, cur->getName()));
5533 replaceAndErase(cur, mul);
5534 return "FAddToSum";
5535 }
5536 }
5537 if (cur->getOpcode() == Instruction::FNeg) {
5538 B.setFastMathFlags(getFast());
5539 auto mul =
5540 pushcse(B.CreateFMulFMF(ConstantFP::get(cur->getType(), -1.0),
5541 cur->getOperand(0), cur, cur->getName()));
5542 replaceAndErase(cur, mul);
5543 return "FNegToMul";
5544 }
5545
5546 if (auto SI = dyn_cast<SelectInst>(cur)) {
5547 if (auto tc = dyn_cast<ConstantFP>(SI->getTrueValue()))
5548 if (auto fc = dyn_cast<ConstantFP>(SI->getFalseValue()))
5549 if (fc->isZero()) {
5550 if (tc->isExactlyValue(1.0)) {
5551 auto res =
5552 pushcse(B.CreateUIToFP(SI->getCondition(), tc->getType()));
5553 replaceAndErase(cur, res);
5554 return "SelToUIFP";
5555 }
5556 if (tc->isExactlyValue(-1.0)) {
5557 auto res =
5558 pushcse(B.CreateSIToFP(SI->getCondition(), tc->getType()));
5559 replaceAndErase(cur, res);
5560 return "SelToSIFP";
5561 }
5562 }
5563 }
5564
5565 if (auto P = isProduct(cur)) {
5566 SmallVector<Value *, 1> operands;
5567 std::optional<APFloat> constval;
5568 bool changed = false;
5569 for (auto &v : callOperands(P))
5570
5571 {
5572 if (auto P2 = isProduct(v)) {
5573 for (auto &v2 : callOperands(P2)) {
5574 push(v2);
5575 operands.push_back(v2);
5576 }
5577 push(P2);
5578 changed = true;
5579 continue;
5580 }
5581 if (auto C = dyn_cast<ConstantFP>(v)) {
5582 if (C->isExactlyValue(1.0)) {
5583 changed = true;
5584 continue;
5585 }
5586 if (C->isZero()) {
5587 replaceAndErase(cur, C);
5588 return "ZeroProduct";
5589 }
5590 if (!constval) {
5591 constval = C->getValue();
5592 continue;
5593 }
5594 constval = (*constval) * C->getValue();
5595 changed = true;
5596 continue;
5597 }
5598 if (auto op = dyn_cast<SelectInst>(v)) {
5599 if (auto tc = dyn_cast<ConstantFP>(op->getTrueValue()))
5600 if (tc->isZero()) {
5601 operands.push_back(pushcse(B.CreateUIToFP(
5602 pushcse(B.CreateNot(op->getCondition())), op->getType())));
5603 operands.push_back(op->getFalseValue());
5604 changed = true;
5605 continue;
5606 }
5607 if (auto tc = dyn_cast<ConstantFP>(op->getFalseValue()))
5608 if (tc->isZero()) {
5609 operands.push_back(
5610 pushcse(B.CreateUIToFP(op->getCondition(), op->getType())));
5611 operands.push_back(op->getTrueValue());
5612 changed = true;
5613 continue;
5614 }
5615 }
5616 operands.push_back(v);
5617 }
5618 if (constval)
5619 operands.push_back(ConstantFP::get(cur->getType(), *constval));
5620
5621 if (operands.size() == 0) {
5622 replaceAndErase(cur, ConstantFP::get(cur->getType(), 1.0));
5623 return "EmptyProduct";
5624 }
5625 if (operands.size() == 1) {
5626 replaceAndErase(cur, operands[0]);
5627 return "SingleProduct";
5628 }
5629 if (changed) {
5630 auto mul = pushcse(
5631 B.CreateCall(getProductIntrinsic(*F.getParent(), cur->getType()),
5632 operands, cur->getName()));
5633 replaceAndErase(cur, mul);
5634 return "ProductSimplification";
5635 }
5636 }
5637
5638 if (auto P = isSum(cur)) {
5639 // map from operand, to number of counts
5640 std::map<Value *, unsigned> operands;
5641 std::optional<APFloat> constval;
5642 bool changed = false;
5643 for (auto &v : callOperands(P)) {
5644 if (auto P2 = isSum(v)) {
5645 for (auto &v2 : callOperands(P2)) {
5646 push(v2);
5647 operands[v2]++;
5648 }
5649 push(P2);
5650 changed = true;
5651 continue;
5652 }
5653 if (auto C = dyn_cast<ConstantFP>(v)) {
5654 if (C->isExactlyValue(0.0)) {
5655 changed = true;
5656 continue;
5657 }
5658 if (!constval) {
5659 constval = C->getValue();
5660 continue;
5661 }
5662 constval = (*constval) + C->getValue();
5663 changed = true;
5664 continue;
5665 }
5666 operands[v]++;
5667 }
5668 if (constval)
5669 operands[ConstantFP::get(cur->getType(), *constval)]++;
5670
5671 if (operands.size() == 0) {
5672 replaceAndErase(cur, ConstantFP::get(cur->getType(), 0.0));
5673 return "EmptySum";
5674 }
5675 SmallVector<Value *, 1> args;
5676 for (auto &pair : operands) {
5677 if (pair.second == 1) {
5678 args.push_back(pair.first);
5679 continue;
5680 }
5681 changed = true;
5682 Value *sargs[] = {pair.first,
5683 ConstantFP::get(cur->getType(), (double)pair.second)};
5684 args.push_back(pushcse(B.CreateCall(
5685 getProductIntrinsic(*F.getParent(), cur->getType()), sargs)));
5686 }
5687 if (args.size() == 1) {
5688 replaceAndErase(cur, args[0]);
5689 return "SingleSum";
5690 }
5691 if (changed) {
5692 auto sum =
5693 pushcse(B.CreateCall(getSumIntrinsic(*F.getParent(), cur->getType()),
5694 args, cur->getName()));
5695 replaceAndErase(cur, sum);
5696 return "SumSimplification";
5697 }
5698 }
5699
5700 if (auto P = isProduct(cur)) {
5701 SmallVector<Value *, 1> operands;
5702 SmallVector<Value *, 1> conditions;
5703 for (auto &v : callOperands(P)) {
5704 // z = uitofp i1 c to float -> select c, (prod withot z), 0
5705 if (auto op = dyn_cast<UIToFPInst>(v)) {
5706 if (op->getOperand(0)->getType()->isIntegerTy(1)) {
5707 conditions.push_back(op->getOperand(0));
5708 continue;
5709 }
5710 }
5711 // z = sitofp i1 c to float -> select c, (-prod withot z), 0
5712 if (auto op = dyn_cast<SIToFPInst>(v)) {
5713 if (op->getOperand(0)->getType()->isIntegerTy(1)) {
5714 conditions.push_back(op->getOperand(0));
5715 operands.push_back(ConstantFP::get(cur->getType(), -1.0));
5716 continue;
5717 }
5718 }
5719 if (auto op = dyn_cast<SelectInst>(v)) {
5720 if (auto tc = dyn_cast<ConstantFP>(op->getTrueValue()))
5721 if (tc->isZero()) {
5722 conditions.push_back(pushcse(B.CreateNot(op->getCondition())));
5723 operands.push_back(op->getFalseValue());
5724 continue;
5725 }
5726 if (auto tc = dyn_cast<ConstantFP>(op->getFalseValue()))
5727 if (tc->isZero()) {
5728 conditions.push_back(op->getCondition());
5729 operands.push_back(op->getTrueValue());
5730 continue;
5731 }
5732 }
5733 operands.push_back(v);
5734 }
5735
5736 if (conditions.size()) {
5737 auto mul = pushcse(B.CreateCall(
5738 getProductIntrinsic(*F.getParent(), cur->getType()), operands));
5739 Value *condition = nullptr;
5740 for (auto v : conditions) {
5741 assert(v->getType()->isIntegerTy(1));
5742 if (condition == nullptr) {
5743 condition = v;
5744 continue;
5745 }
5746 condition = pushcse(B.CreateAnd(condition, v));
5747 }
5748 auto zero = ConstantFP::get(cur->getType(), 0.0);
5749 auto sel = pushcse(B.CreateSelect(condition, mul, zero, cur->getName()));
5750 replaceAndErase(cur, sel);
5751 return "ProductSelect";
5752 }
5753 }
5754
5755 // TODO
5756 if (auto P = isSum(cur)) {
5757 // whether negated
5758 SmallVector<std::pair<Value *, bool>, 1> conditions;
5759 bool legal = true;
5760 for (auto &v : callOperands(P)) {
5761 // z = uitofp i1 c to float -> select c, (prod withot z), 0
5762 if (auto op = dyn_cast<UIToFPInst>(v)) {
5763 if (op->getOperand(0)->getType()->isIntegerTy(1)) {
5764 conditions.emplace_back(op->getOperand(0), false);
5765 continue;
5766 }
5767 }
5768 // z = sitofp i1 c to float -> select c, (-prod withot z), 0
5769 if (auto op = dyn_cast<SIToFPInst>(v)) {
5770 if (op->getOperand(0)->getType()->isIntegerTy(1)) {
5771 conditions.emplace_back(op->getOperand(0), false);
5772 continue;
5773 }
5774 }
5775 if (auto op = dyn_cast<SelectInst>(v)) {
5776 if (auto tc = dyn_cast<ConstantFP>(op->getTrueValue()))
5777 if (tc->isZero()) {
5778 conditions.emplace_back(op->getCondition(), true);
5779 continue;
5780 }
5781 if (auto tc = dyn_cast<ConstantFP>(op->getFalseValue()))
5782 if (tc->isZero()) {
5783 conditions.emplace_back(op->getCondition(), false);
5784 continue;
5785 }
5786 }
5787 legal = false;
5788 break;
5789 }
5790 Value *condition = nullptr;
5791 if (legal)
5792 for (size_t i = 0; i < conditions.size(); i++) {
5793 size_t count = 0;
5794 for (size_t j = 0; j < conditions.size(); j++) {
5795 if (((conditions[i].first == conditions[j].first) &&
5796 (conditions[i].second == conditions[i].second)) ||
5797 ((isNot(conditions[i].first, conditions[j].first) &&
5798 (conditions[i].second != conditions[i].second))))
5799 count++;
5800 }
5801 if (count == conditions.size() && count > 1) {
5802 condition = conditions[i].first;
5803 if (conditions[i].second)
5804 condition = pushcse(B.CreateNot(condition, "sumpnot"));
5805 break;
5806 }
5807 }
5808
5809 if (condition) {
5810
5811 SmallVector<Value *, 1> operands;
5812 for (auto &v : callOperands(P)) {
5813 // z = uitofp i1 c to float -> select c, (prod withot z), 0
5814 if (auto op = dyn_cast<UIToFPInst>(v)) {
5815 if (op->getOperand(0)->getType()->isIntegerTy(1)) {
5816 operands.push_back(ConstantFP::get(cur->getType(), 1.0));
5817 continue;
5818 }
5819 }
5820 // z = sitofp i1 c to float -> select c, (-prod withot z), 0
5821 if (auto op = dyn_cast<SIToFPInst>(v)) {
5822 if (op->getOperand(0)->getType()->isIntegerTy(1)) {
5823 operands.push_back(ConstantFP::get(cur->getType(), -1.0));
5824 continue;
5825 }
5826 }
5827 if (auto op = dyn_cast<SelectInst>(v)) {
5828 if (auto tc = dyn_cast<ConstantFP>(op->getTrueValue()))
5829 if (tc->isZero()) {
5830 operands.push_back(op->getFalseValue());
5831 continue;
5832 }
5833 if (auto tc = dyn_cast<ConstantFP>(op->getFalseValue()))
5834 if (tc->isZero()) {
5835 operands.push_back(op->getTrueValue());
5836 continue;
5837 }
5838 }
5839 llvm::errs() << " unhandled call op sumselect: " << *v << "\n";
5840 assert(0);
5841 }
5842
5843 if (conditions.size()) {
5844 auto sum = pushcse(B.CreateCall(
5845 getSumIntrinsic(*F.getParent(), cur->getType()), operands));
5846 auto zero = ConstantFP::get(cur->getType(), 0.0);
5847 auto sel =
5848 pushcse(B.CreateSelect(condition, sum, zero, cur->getName()));
5849 replaceAndErase(cur, sel);
5850 return "SumSelect";
5851 }
5852 }
5853 }
5854 // (a1*b1) + (a1*c1) + (a1*d1 ) + ... -> a1 * (b1 + c1 + d1 + ...)
5855 if (auto S = isSum(cur)) {
5856 SmallVector<Value *, 1> allOps;
5857 auto combine = [](const SmallVector<Value *, 1> &lhs,
5858 SmallVector<Value *, 1> rhs) {
5859 SmallVector<Value *, 1> out;
5860 for (auto v : lhs) {
5861 bool seen = false;
5862 for (auto &v2 : rhs) {
5863 if (v == v2) {
5864 v2 = nullptr;
5865 seen = true;
5866 break;
5867 }
5868 }
5869 if (seen) {
5870 out.push_back(v);
5871 }
5872 }
5873 return out;
5874 };
5875 auto subtract = [](SmallVector<Value *, 1> lhs,
5876 const SmallVector<Value *, 1> &rhs) {
5877 for (auto v : rhs) {
5878 auto found = find(lhs, v);
5879 assert(found != lhs.end());
5880 lhs.erase(found);
5881 }
5882 return lhs;
5883 };
5884 bool seen = false;
5885 bool legal = true;
5886 for (auto op : callOperands(S)) {
5887 auto P = isProduct(op);
5888 if (!P) {
5889 legal = false;
5890 break;
5891 }
5892 if (!seen) {
5893 allOps = callOperands(P);
5894 seen = true;
5895 continue;
5896 }
5897 allOps = combine(allOps, callOperands(P));
5898 }
5899
5900 if (legal && allOps.size() > 0) {
5901 SmallVector<Value *, 1> operands;
5902 for (auto op : callOperands(S)) {
5903 auto P = isProduct(op);
5904 push(op);
5905 auto sub = subtract(callOperands(P), allOps);
5906 auto newprod = pushcse(B.CreateCall(
5907 getProductIntrinsic(*F.getParent(), S->getType()), sub));
5908 operands.push_back(newprod);
5909 }
5910 auto newsum = pushcse(B.CreateCall(
5911 getSumIntrinsic(*F.getParent(), S->getType()), operands));
5912 allOps.push_back(newsum);
5913 auto fprod = pushcse(B.CreateCall(
5914 getProductIntrinsic(*F.getParent(), S->getType()), allOps));
5915 replaceAndErase(cur, fprod);
5916 return "SumFactor";
5917 }
5918 }
5919
5920 /*
5921 // add (ext (x == expr )), ( ext (x == expr + 1)) -> -expr == c2 ) and c1
5922 != c2 -> false if (cur->getOpcode() == Instruction::Add) for (int j=0; j<2;
5923 j++) if (auto c0 = dyn_cast<ZExtInst>(cur->getOperand(j))) if (auto cmp0 =
5924 dyn_cast<ICmpInst>(c0->getOperand(0))) if (auto c1 =
5925 dyn_cast<CastInst>(cur->getOperand(1-j))) if (auto cmp1 =
5926 dyn_cast<ICmpInst>(c0->getOperand(0))) if (cmp0->getPredicate() ==
5927 ICmpInst::ICMP_EQ && cmp1->getPredicate() == ICmpInst::ICMP_EQ)
5928 {
5929 for (size_t i0 = 0; i0 < 2; i0++)
5930 for (size_t i1 = 0; i1 < 2; i1++)
5931 if (cmp0->getOperand(1 - i0) == cmp1->getOperand(1 - i1))
5932 auto e0 = SE.getSCEV(cmp0->getOperand(i0));
5933 auto e1 = SE.getSCEV(cmp1->getOperand(i1));
5934#if LLVM_VERSION_MAJOR >= 24
5935 auto m = SE.getMinusSCEV(e0, e1, SCEV::FlagsMask);
5936#else
5937 auto m = SE.getMinusSCEV(e0, e1, SCEV::NoWrapMask);
5938#endif
5939 if (auto C = dyn_cast<SCEVConstant>(m)) {
5940 // if c1 == c2 don't need the and they are equivalent
5941 if (C->getValue()->isZero()) {
5942 } else {
5943 auto sel0 = pushcse(B.CreateSelect(cmp0,
5944 ConstantInt::get(cur->getType(), isa<ZExtInst>(cmp0) ? 1 : -1),
5945 ConstantInt::get(cur->getType(), 0));
5946 // if non one constant they must be distinct.
5947 replaceAndErase(cur,
5948 ConstantInt::getFalse(cur->getContext()));
5949 return "AndNEExpr";
5950 }
5951 }
5952 }
5953 }
5954 */
5955
5956 if (auto fcmp = dyn_cast<FCmpInst>(cur)) {
5957 auto predicate = fcmp->getPredicate();
5958 if (predicate == FCmpInst::FCMP_OEQ || predicate == FCmpInst::FCMP_UEQ ||
5959 predicate == FCmpInst::FCMP_UNE || predicate == FCmpInst::FCMP_ONE) {
5960 for (int i = 0; i < 2; i++)
5961 if (auto C = dyn_cast<ConstantFP>(fcmp->getOperand(i))) {
5962 if (C->isZero()) {
5963 // (a1*a2*...an) == 0 -> (a1 == 0) || (a2 == 0) || ... (a2 == 0)
5964 // (a1*a2*...an) != 0 -> ![ (a1 == 0) || (a2 == 0) || ... (a2 ==
5965 // 0)
5966 // ]
5967 if (auto P = isProduct(fcmp->getOperand(1 - i))) {
5968 Value *res = nullptr;
5969
5970 auto eq_predicate = predicate;
5971 if (predicate == FCmpInst::FCMP_UNE ||
5972 predicate == FCmpInst::FCMP_ONE)
5973 eq_predicate = fcmp->getInversePredicate();
5974
5975 for (auto &v : callOperands(P)) {
5976 auto ncmp1 = pushcse(B.CreateFCmp(eq_predicate, v, C));
5977 if (!res)
5978 res = ncmp1;
5979 else
5980 res = pushcse(B.CreateOr(res, ncmp1));
5981 }
5982
5983 if (predicate == FCmpInst::FCMP_UNE ||
5984 predicate == FCmpInst::FCMP_ONE) {
5985 res = pushcse(B.CreateNot(res));
5986 }
5987
5988 replaceAndErase(cur, res);
5989 return "CmpProductSplit";
5990 }
5991
5992 // (a1*b1) + (a1*c1) + (a1*d1 ) + ... ?= 0 -> a1 * (b1 + c1 + d1 +
5993 // ...) ?= 0
5994 if (auto S = isSum(fcmp->getOperand(1 - i))) {
5995 SmallVector<Value *, 1> allOps;
5996 auto combine = [](const SmallVector<Value *, 1> &lhs,
5997 SmallVector<Value *, 1> rhs) {
5998 SmallVector<Value *, 1> out;
5999 for (auto v : lhs) {
6000 bool seen = false;
6001 for (auto &v2 : rhs) {
6002 if (v == v2) {
6003 v2 = nullptr;
6004 seen = true;
6005 break;
6006 }
6007 }
6008 if (seen) {
6009 out.push_back(v);
6010 }
6011 }
6012 return out;
6013 };
6014 auto subtract = [](SmallVector<Value *, 1> lhs,
6015 const SmallVector<Value *, 1> &rhs) {
6016 for (auto v : rhs) {
6017 auto found = find(lhs, v);
6018 assert(found != lhs.end());
6019 lhs.erase(found);
6020 }
6021 return lhs;
6022 };
6023 bool seen = false;
6024 bool legal = true;
6025 for (auto op : callOperands(S)) {
6026 auto P = isProduct(op);
6027 if (!P) {
6028 legal = false;
6029 break;
6030 }
6031 if (!seen) {
6032 allOps = callOperands(P);
6033 seen = true;
6034 continue;
6035 }
6036 allOps = combine(allOps, callOperands(P));
6037 }
6038
6039 if (legal && allOps.size() > 0) {
6040 SmallVector<Value *, 1> operands;
6041 for (auto op : callOperands(S)) {
6042 auto P = isProduct(op);
6043 push(op);
6044 auto sub = subtract(callOperands(P), allOps);
6045 auto newprod = pushcse(B.CreateCall(
6046 getProductIntrinsic(*F.getParent(), C->getType()), sub));
6047 operands.push_back(newprod);
6048 }
6049 auto newsum = pushcse(B.CreateCall(
6050 getSumIntrinsic(*F.getParent(), C->getType()), operands));
6051 allOps.push_back(newsum);
6052 auto fprod = pushcse(B.CreateCall(
6053 getProductIntrinsic(*F.getParent(), C->getType()), allOps));
6054 auto fcmp = pushcse(B.CreateCmp(predicate, fprod, C));
6055 replaceAndErase(cur, fcmp);
6056 return "CmpSumFactor";
6057 }
6058 }
6059 }
6060 }
6061 }
6062 }
6063
6064 if (auto fcmp = dyn_cast<FCmpInst>(cur)) {
6065 auto predicate = fcmp->getPredicate();
6066 if (predicate == FCmpInst::FCMP_OEQ || predicate == FCmpInst::FCMP_UEQ ||
6067 predicate == FCmpInst::FCMP_UNE || predicate == FCmpInst::FCMP_ONE) {
6068 for (int i = 0; i < 2; i++)
6069 if (auto C = dyn_cast<ConstantFP>(fcmp->getOperand(i))) {
6070 if (C->isZero()) {
6071 // a + b == 0 -> ( (a == 0 & b == 0) || a == -b)
6072 if (auto S = isSum(fcmp->getOperand(1 - i))) {
6073 auto allOps = callOperands(S);
6074 if (!llvm::any_of(allOps, guaranteedDataDependent)) {
6075 auto eq_predicate = predicate;
6076 if (predicate == FCmpInst::FCMP_UNE ||
6077 predicate == FCmpInst::FCMP_ONE)
6078 eq_predicate = fcmp->getInversePredicate();
6079
6080 Value *op_checks = nullptr;
6081 for (auto a : allOps) {
6082 auto a_e0 = pushcse(B.CreateFCmp(eq_predicate, a, C));
6083 if (op_checks == nullptr)
6084 op_checks = a_e0;
6085 else
6086 op_checks = pushcse(B.CreateAnd(op_checks, a_e0));
6087 }
6088 SmallVector<Value *, 1> slice;
6089 for (size_t i = 1; i < allOps.size(); i++)
6090 slice.push_back(allOps[i]);
6091 auto sliceCall =
6092 pushcse(B.CreateCall(getFunctionFromCall(S), slice));
6093 auto negOp = pushcse(B.CreateFNeg(allOps[0]));
6094 auto ane =
6095 pushcse(B.CreateFCmp(eq_predicate, negOp, sliceCall));
6096 auto ori = pushcse(B.CreateOr(op_checks, ane));
6097 if (predicate == FCmpInst::FCMP_UNE ||
6098 predicate == FCmpInst::FCMP_ONE) {
6099 ori = pushcse(B.CreateNot(ori));
6100 }
6101 replaceAndErase(cur, ori);
6102 return "Sum2ZeroSplit";
6103 }
6104 }
6105 }
6106 }
6107 }
6108 }
6109
6110 // (zext a) + (zext b) ?= 0 -> zext a ?= - zext b
6111 if (auto icmp = dyn_cast<CmpInst>(cur)) {
6112 if (icmp->getPredicate() == CmpInst::ICMP_EQ ||
6113 icmp->getPredicate() == CmpInst::ICMP_NE) {
6114 for (int i = 0; i < 2; i++)
6115 if (auto C = dyn_cast<ConstantInt>(icmp->getOperand(i)))
6116 if (C->isZero())
6117 if (auto add = dyn_cast<BinaryOperator>(icmp->getOperand(1 - i)))
6118 if (add->getOpcode() == Instruction::Add)
6119 if (auto a0 = dyn_cast<CastInst>(add->getOperand(0)))
6120 if (auto a1 = dyn_cast<CastInst>(add->getOperand(1)))
6121 if (a0->getOperand(0)->getType() ==
6122 a1->getOperand(0)->getType() &&
6123 (isa<ZExtInst>(a0) || isa<SExtInst>(a0))) {
6124 auto cmp2 = pushcse(B.CreateCmp(
6125 icmp->getPredicate(), a0, pushcse(B.CreateNeg(a1))));
6126 replaceAndErase(cur, cmp2);
6127 return "CmpExt0Shuffle";
6128 }
6129 }
6130 }
6131
6132 // sub 0, (zext i1 to N) -> sext i1 to N
6133 // sub 0, (sext i1 to N) -> zext i1 to N
6134 if (auto sub = dyn_cast<BinaryOperator>(cur))
6135 if (sub->getOpcode() == Instruction::Sub)
6136 if (auto C = dyn_cast<ConstantInt>(sub->getOperand(0)))
6137 if (C->isZero())
6138 if (auto a0 = dyn_cast<CastInst>(sub->getOperand(1)))
6139 if (a0->getOperand(0)->getType()->isIntegerTy(1)) {
6140
6141 Value *tmp = nullptr;
6142 if (isa<ZExtInst>(a0))
6143 tmp = pushcse(B.CreateSExt(a0->getOperand(0), a0->getType()));
6144 else if (isa<SExtInst>(a0))
6145 tmp = pushcse(B.CreateZExt(a0->getOperand(0), a0->getType()));
6146 else
6147 assert(0);
6148 replaceAndErase(cur, tmp);
6149 return "NegSZExtI1";
6150 }
6151
6152 if ((cur->getOpcode() == Instruction::LShr ||
6153 cur->getOpcode() == Instruction::SDiv ||
6154 cur->getOpcode() == Instruction::UDiv) &&
6155 cur->isExact())
6156 if (auto C2 = dyn_cast<ConstantInt>(cur->getOperand(1)))
6157 if (auto mul = dyn_cast<BinaryOperator>(cur->getOperand(0))) {
6158 // (lshr exact (mul a, C1), C2), C -> mul a, (lhsr exact C1, C2) if
6159 // C2 divides C1
6160 if (mul->getOpcode() == Instruction::Mul)
6161 for (int i0 = 0; i0 < 2; i0++)
6162 if (auto C1 = dyn_cast<ConstantInt>(mul->getOperand(i0))) {
6163 auto lhs = C1->getValue();
6164 APInt rhs = C2->getValue();
6165 if (cur->getOpcode() == Instruction::LShr) {
6166 rhs = APInt(rhs.getBitWidth(), 1) << rhs;
6167 }
6168
6169 APInt div, rem;
6170 if (cur->getOpcode() == Instruction::LShr ||
6171 cur->getOpcode() == Instruction::UDiv)
6172 APInt::udivrem(lhs, rhs, div, rem);
6173 else
6174 APInt::sdivrem(lhs, rhs, div, rem);
6175 if (rem == 0) {
6176 auto res = pushcse(B.CreateMul(
6177 mul->getOperand(1 - i0),
6178 ConstantInt::get(cur->getType(), div),
6179 "mdiv." + cur->getName(), mul->hasNoUnsignedWrap(),
6180 mul->hasNoSignedWrap()));
6181 push(mul);
6182 replaceAndErase(cur, res);
6183 return "IMulDivConst";
6184 }
6185 }
6186 // (lshr exact (add a, C1), C2), C -> add a, (lhsr exact C1, C2) if
6187 // C2
6188 if (mul->getOpcode() == Instruction::Add)
6189 for (int i0 = 0; i0 < 2; i0++)
6190 if (auto C1 = dyn_cast<ConstantInt>(mul->getOperand(i0))) {
6191 auto lhs = C1->getValue();
6192 APInt rhs = C2->getValue();
6193 if (cur->getOpcode() == Instruction::LShr) {
6194 rhs = APInt(rhs.getBitWidth(), 1) << rhs;
6195 }
6196
6197 APInt div, rem;
6198 if (cur->getOpcode() == Instruction::LShr ||
6199 cur->getOpcode() == Instruction::UDiv)
6200 APInt::udivrem(lhs, rhs, div, rem);
6201 else
6202 APInt::sdivrem(lhs, rhs, div, rem);
6203 if (rem == 0 && ((mul->hasNoUnsignedWrap() &&
6204 (cur->getOpcode() == Instruction::LShr ||
6205 cur->getOpcode() == Instruction::UDiv)) ||
6206 (mul->hasNoSignedWrap() &&
6207 (cur->getOpcode() == Instruction::AShr ||
6208 cur->getOpcode() == Instruction::SDiv)))) {
6209 auto res = pushcse(B.CreateAdd(
6210 mul->getOperand(1 - i0),
6211 ConstantInt::get(cur->getType(), div),
6212 "madd." + cur->getName(), mul->hasNoUnsignedWrap(),
6213 mul->hasNoSignedWrap()));
6214 push(mul);
6215 replaceAndErase(cur, res);
6216 return "IAddDivConst";
6217 }
6218 }
6219 }
6220
6221 // mul (mul a, const1), (mul b, const2) -> mul (mul a, b), (const1, const2)
6222 if (cur->getOpcode() == Instruction::FMul)
6223 if (cur->isFast())
6224 if (auto mul1 = dyn_cast<Instruction>(cur->getOperand(0)))
6225 if (mul1->getOpcode() == Instruction::FMul && mul1->isFast())
6226 if (auto mul2 = dyn_cast<Instruction>(cur->getOperand(1)))
6227 if (mul2->getOpcode() == Instruction::FMul && mul2->isFast()) {
6228 for (auto i1 = 0; i1 < 2; i1++)
6229 for (auto i2 = 0; i2 < 2; i2++)
6230 if (isa<Constant>(mul1->getOperand(i1)))
6231 if (isa<Constant>(mul2->getOperand(i2))) {
6232
6233 auto n0 = pushcse(
6234 B.CreateFMulFMF(mul1->getOperand(1 - i1),
6235 mul2->getOperand(1 - i2), cur));
6236 auto n1 = pushcse(B.CreateFMulFMF(
6237 mul1->getOperand(i1), mul2->getOperand(i2), cur));
6238 auto n2 = pushcse(B.CreateFMulFMF(n0, n1, cur));
6239 push(mul1);
6240 push(mul2);
6241 replaceAndErase(cur, n2);
6242 return "MulMulConstConst";
6243 }
6244 }
6245
6246 // mul (mul a, const1), const2 -> mul a, (mul const1, const2)
6247 if ((cur->getOpcode() == Instruction::FMul && cur->isFast()) ||
6248 cur->getOpcode() == Instruction::Mul)
6249 for (auto i1 = 0; i1 < 2; i1++)
6250 if (auto mul1 = dyn_cast<Instruction>(cur->getOperand(i1)))
6251 if (((mul1->getOpcode() == Instruction::FMul && mul1->isFast())) ||
6252 mul1->getOpcode() == Instruction::FMul)
6253 if (auto const2 = dyn_cast<Constant>(cur->getOperand(1 - i1)))
6254 for (auto i2 = 0; i2 < 2; i2++)
6255 if (auto const1 = dyn_cast<Constant>(mul1->getOperand(i2))) {
6256 Value *res = nullptr;
6257 if (cur->getOpcode() == Instruction::FMul) {
6258 auto const3 = pushcse(B.CreateFMulFMF(const1, const2, mul1));
6259 res = pushcse(
6260 B.CreateFMulFMF(mul1->getOperand(1 - i2), const3, cur));
6261 } else {
6262 auto const3 = pushcse(B.CreateMul(const1, const2));
6263 res = pushcse(B.CreateMul(mul1->getOperand(1 - i2), const3));
6264 }
6265 push(mul1);
6266 replaceAndErase(cur, res);
6267 return "MulConstConst";
6268 }
6269
6270 if (auto fcmp = dyn_cast<FCmpInst>(cur)) {
6271 if (fcmp->getPredicate() == FCmpInst::FCMP_OEQ) {
6272 for (int i = 0; i < 2; i++)
6273 if (auto C = dyn_cast<ConstantFP>(fcmp->getOperand(i))) {
6274 if (C->isZero()) {
6275 if (auto fmul = dyn_cast<BinaryOperator>(fcmp->getOperand(1 - i))) {
6276 // (a*b) == 0 -> (a == 0) || (b == 0)
6277 if (fmul->getOpcode() == Instruction::FMul) {
6278 auto ncmp1 = pushcse(
6279 B.CreateFCmp(fcmp->getPredicate(), fmul->getOperand(0), C));
6280 auto ncmp2 = pushcse(
6281 B.CreateFCmp(fcmp->getPredicate(), fmul->getOperand(1), C));
6282 auto ori = pushcse(B.CreateOr(ncmp1, ncmp2));
6283 replaceAndErase(cur, ori);
6284 return "CmpFMulSplit";
6285 }
6286 // (a/b) == 0 -> (a == 0)
6287 if (fmul->getOpcode() == Instruction::FDiv) {
6288 auto ncmp1 = pushcse(
6289 B.CreateFCmp(fcmp->getPredicate(), fmul->getOperand(0), C));
6290 replaceAndErase(cur, ncmp1);
6291 return "CmpFDivSplit";
6292 }
6293 // (a - b) ?= 0 -> a ?= b
6294 if (fmul->getOpcode() == Instruction::FSub) {
6295 auto ncmp1 = pushcse(B.CreateFCmp(fcmp->getPredicate(),
6296 fmul->getOperand(0),
6297 fmul->getOperand(1)));
6298 replaceAndErase(cur, ncmp1);
6299 return "CmpFSubSplit";
6300 }
6301 }
6302 if (auto cast = dyn_cast<SIToFPInst>(fcmp->getOperand(1 - i))) {
6303 auto ncmp1 = pushcse(B.CreateICmp(
6304 ICmpInst::ICMP_EQ, cast->getOperand(0),
6305 ConstantInt::get(cast->getOperand(0)->getType(), 0)));
6306 replaceAndErase(cur, ncmp1);
6307 return "SFCmpToICmp";
6308 }
6309 if (auto cast = dyn_cast<UIToFPInst>(fcmp->getOperand(1 - i))) {
6310 auto ncmp1 = pushcse(B.CreateICmp(
6311 ICmpInst::ICMP_EQ, cast->getOperand(0),
6312 ConstantInt::get(cast->getOperand(0)->getType(), 0)));
6313 replaceAndErase(cur, ncmp1);
6314 return "UFCmpToICmp";
6315 }
6316 if (auto SI = dyn_cast<SelectInst>(fcmp->getOperand(1 - i))) {
6317 auto falseCmp = pushcse(
6318 B.CreateCmp(fcmp->getPredicate(), C, SI->getFalseValue()));
6319 auto trueCmp = pushcse(
6320 B.CreateCmp(fcmp->getPredicate(), C, SI->getTrueValue()));
6321 auto res = pushcse(
6322 B.CreateSelect(SI->getCondition(), trueCmp, falseCmp));
6323 replaceAndErase(cur, res);
6324 return "FCmpSelect";
6325 }
6326 }
6327 }
6328 }
6329 }
6330 if (auto fcmp = dyn_cast<CmpInst>(cur)) {
6331 if (fcmp->getPredicate() == CmpInst::ICMP_EQ ||
6332 fcmp->getPredicate() == CmpInst::ICMP_NE ||
6333 fcmp->getPredicate() == CmpInst::FCMP_OEQ ||
6334 fcmp->getPredicate() == CmpInst::FCMP_ONE) {
6335
6336 // a + c ?= a -> c ?= 0 , if fast
6337 for (int i = 0; i < 2; i++)
6338 if (auto inst = dyn_cast<Instruction>(fcmp->getOperand(i)))
6339 if (inst->getOpcode() == Instruction::FAdd && inst->isFast())
6340 for (int i2 = 0; i2 < 2; i2++)
6341 if (inst->getOperand(i2) == fcmp->getOperand(1 - i)) {
6342 auto res = pushcse(
6343 B.CreateCmp(fcmp->getPredicate(), inst->getOperand(1 - i2),
6344 ConstantFP::get(inst->getType(), 0)));
6345 replaceAndErase(cur, res);
6346 return "CmpFAddSame";
6347 }
6348
6349 // a == b -> a & b | !a & !b
6350 // a != b -> a & !b | !a & b
6351 if (fcmp->getOperand(0)->getType()->isIntegerTy(1)) {
6352 auto a = fcmp->getOperand(0);
6353 auto b = fcmp->getOperand(1);
6354 if (fcmp->getPredicate() == CmpInst::ICMP_EQ) {
6355 auto notB = pushcse(B.CreateNot(b));
6356 auto notA = pushcse(B.CreateNot(a));
6357 auto neitherAB = pushcse(B.CreateAnd(notA, notB));
6358 auto bothAB = pushcse(B.CreateAnd(a, b));
6359 auto res = pushcse(B.CreateOr(bothAB, neitherAB));
6360 replaceAndErase(cur, res);
6361 return "CmpI1EQ";
6362 }
6363 if (fcmp->getPredicate() == CmpInst::ICMP_NE) {
6364 auto notB = pushcse(B.CreateNot(b));
6365 auto aNotB = pushcse(B.CreateAnd(a, notB));
6366 auto notA = pushcse(B.CreateNot(a));
6367 auto notAB = pushcse(B.CreateAnd(notA, b));
6368 auto res = pushcse(B.CreateOr(notAB, aNotB));
6369 replaceAndErase(cur, res);
6370 return "CmpI1NE";
6371 }
6372 }
6373
6374 for (int i = 0; i < 2; i++)
6375 if (auto CI = dyn_cast<ConstantInt>(fcmp->getOperand(i)))
6376 if (CI->isZero()) {
6377 // a + a ?= 0 -> a ?= 0
6378 if (auto addI = dyn_cast<Instruction>(fcmp->getOperand(1 - i))) {
6379 if (addI->getOpcode() == Instruction::Add &&
6380 addI->getOperand(0) == addI->getOperand(1)) {
6381 Value *res = pushcse(
6382 B.CreateCmp(fcmp->getPredicate(), addI->getOperand(0), CI));
6383 replaceAndErase(cur, res);
6384 return "CmpAddAdd";
6385 }
6386 // (a-b) ?= 0 -> a ?= b
6387 if (addI->getOpcode() == Instruction::Sub) {
6388 auto ncmp1 = pushcse(B.CreateICmp(fcmp->getPredicate(),
6389 addI->getOperand(0),
6390 addI->getOperand(1)));
6391 replaceAndErase(cur, ncmp1);
6392 return "CmpISubSplit";
6393 }
6394 }
6395 }
6396
6397 // (a * b) == (c * b) -> (a == c) || b == 0
6398 // (a * b) != (c * b) -> (a != c) && b != 0
6399 // auto S1 = SE.getSCEV(cur->getOperand(0));
6400 // auto S2 = SE.getSCEV(cur->getOperand(1));
6401 // llvm::errs() <<" attempting push: " << *cur << " S1: " << *S1 << "
6402 // S2: " << *S2 << " and " << *cur->getOperand(0) << " " <<
6403 // *cur->getOperand(1) << "\n";
6404 if (auto mul1 = dyn_cast<Instruction>(cur->getOperand(0)))
6405 if (auto mul2 = dyn_cast<Instruction>(cur->getOperand(1))) {
6406 if (mul1->getOpcode() == Instruction::Mul &&
6407 mul2->getOpcode() == Instruction::Mul &&
6408 mul1->hasNoUnsignedWrap() && mul1->hasNoSignedWrap() &&
6409 mul2->hasNoUnsignedWrap() && mul2->hasNoSignedWrap()) {
6410 for (int i = 0; i < 2; i++) {
6411 if (mul1->getOperand(i) == mul2->getOperand(i)) {
6412 Value *res = pushcse(B.CreateICmp(fcmp->getPredicate(),
6413 mul1->getOperand(1 - i),
6414 mul2->getOperand(1 - i)));
6415 auto b = mul1->getOperand(i);
6416 if (fcmp->getPredicate() == CmpInst::ICMP_EQ) {
6417 Value *bZero = pushcse(B.CreateICmp(
6418 CmpInst::ICMP_EQ, b, ConstantInt::get(b->getType(), 0)));
6419 res = pushcse(B.CreateOr(res, bZero));
6420 } else {
6421 Value *bZero = pushcse(B.CreateICmp(
6422 ICmpInst::ICMP_NE, b, ConstantInt::get(b->getType(), 0)));
6423 res = pushcse(B.CreateAnd(res, bZero));
6424 }
6425 replaceAndErase(cur, res);
6426 return "CmpMulCommon";
6427 }
6428 }
6429 }
6430 // same as above but now with floats
6431 if (mul1->getOpcode() == Instruction::FMul &&
6432 mul2->getOpcode() == Instruction::FMul && mul1->isFast() &&
6433 mul2->isFast()) {
6434 for (int i = 0; i < 2; i++) {
6435 if (mul1->getOperand(i) == mul2->getOperand(i)) {
6436 Value *res = pushcse(B.CreateFCmp(fcmp->getPredicate(),
6437 mul1->getOperand(1 - i),
6438 mul2->getOperand(1 - i)));
6439 auto b = mul1->getOperand(i);
6440 if (fcmp->getPredicate() == CmpInst::FCMP_OEQ) {
6441 Value *bZero = pushcse(B.CreateCmp(
6442 CmpInst::FCMP_OEQ, b, ConstantFP::get(b->getType(), 0)));
6443 res = pushcse(B.CreateOr(res, bZero));
6444 } else {
6445 Value *bZero = pushcse(B.CreateCmp(
6446 CmpInst::FCMP_ONE, b, ConstantFP::get(b->getType(), 0)));
6447 res = pushcse(B.CreateAnd(res, bZero));
6448 }
6449 replaceAndErase(cur, res);
6450 return "CmpMulfCommon";
6451 }
6452 }
6453 }
6454
6455 // (uitofp a ) ?= (uitofp b) -> a ?= b
6456 for (auto cond : {Instruction::UIToFP, Instruction::SIToFP})
6457 if (mul1->getOpcode() == cond && mul2->getOpcode() == cond &&
6458 mul1->getOperand(0)->getType() ==
6459 mul2->getOperand(0)->getType()) {
6460 Value *res = pushcse(B.CreateICmp(
6461 fcmp->getPredicate() == CmpInst::FCMP_OEQ ? CmpInst::ICMP_EQ
6462 : CmpInst::ICMP_NE,
6463 mul1->getOperand(0), mul2->getOperand(0)));
6464 replaceAndErase(cur, res);
6465 return "CmpUIToFP";
6466 }
6467
6468 // (zext a ) ?= (zext b) -> a ?= b
6469 if (mul1->getOpcode() == Instruction::ZExt &&
6470 mul2->getOpcode() == Instruction::ZExt &&
6471 mul1->getOperand(0)->getType() ==
6472 mul2->getOperand(0)->getType()) {
6473 Value *res =
6474 pushcse(B.CreateICmp(fcmp->getPredicate(), mul1->getOperand(0),
6475 mul2->getOperand(0)));
6476 replaceAndErase(cur, res);
6477 return "CmpZExt";
6478 }
6479
6480 // (zext i1 a ) == (sext i1 b) -> (!a & !b)
6481 // (zext i1 a ) != (sext i1 b) -> (a | b)
6482 if (auto mul1 = dyn_cast<Instruction>(cur->getOperand(0)))
6483 if (auto mul2 = dyn_cast<Instruction>(cur->getOperand(1)))
6484 if (((mul1->getOpcode() == Instruction::ZExt &&
6485 mul2->getOpcode() == Instruction::SExt) ||
6486 (mul1->getOpcode() == Instruction::SExt &&
6487 mul2->getOpcode() == Instruction::ZExt)) &&
6488 mul1->getOperand(0)->getType() ==
6489 mul2->getOperand(0)->getType() &&
6490 mul1->getOperand(0)->getType()->isIntegerTy(1)) {
6491
6492 Value *na = mul1->getOperand(0);
6493 Value *nb = mul2->getOperand(0);
6494
6495 if (fcmp->getPredicate() == ICmpInst::ICMP_EQ) {
6496 na = pushcse(B.CreateNot(na));
6497 nb = pushcse(B.CreateNot(nb));
6498 }
6499
6500 Value *res = nullptr;
6501 if (fcmp->getPredicate() == ICmpInst::ICMP_EQ)
6502 res = pushcse(B.CreateAnd(na, nb));
6503 else
6504 res = pushcse(B.CreateOr(na, nb));
6505
6506 replaceAndErase(cur, res);
6507 return "CmpZExtSExt";
6508 }
6509 }
6510 }
6511 if (fcmp->getPredicate() == ICmpInst::ICMP_EQ) {
6512 for (int i = 0; i < 2; i++) {
6513 if (auto C = dyn_cast<ConstantInt>(fcmp->getOperand(i))) {
6514 if (C->isZero()) {
6515 if (auto fmul = dyn_cast<BinaryOperator>(fcmp->getOperand(1 - i))) {
6516 // (a*b) == 0 -> (a == 0) || (b == 0)
6517 if (fmul->getOpcode() == Instruction::Mul) {
6518 auto ncmp1 = pushcse(
6519 B.CreateICmp(fcmp->getPredicate(), fmul->getOperand(0), C));
6520 auto ncmp2 = pushcse(
6521 B.CreateICmp(fcmp->getPredicate(), fmul->getOperand(1), C));
6522 auto ori = pushcse(B.CreateOr(ncmp1, ncmp2));
6523 replaceAndErase(cur, ori);
6524 return "CmpIMulSplit";
6525 }
6526 }
6527 }
6528 }
6529 }
6530 }
6531 }
6532
6533 if (cur->getOpcode() == Instruction::FAdd) {
6534 // add x, x -> mul 2.0
6535 if (cur->getOperand(0) == cur->getOperand(1) && cur->isFast()) {
6536 auto res = pushcse(B.CreateFMulFMF(
6537 cur->getOperand(0), ConstantFP::get(cur->getType(), 2.0), cur));
6538 replaceAndErase(cur, res);
6539 return "AddToMul2";
6540 }
6541 }
6542
6543 if (cur->getOpcode() == Instruction::Add) {
6544 // add x, (y * -1) -> sub x, y
6545 for (int i = 0; i < 2; i++) {
6546 if (auto mul1 = dyn_cast<Instruction>(cur->getOperand(i)))
6547 if (mul1->getOpcode() == Instruction::Mul) {
6548 for (int j = 0; j < 2; j++) {
6549 if (auto C = dyn_cast<ConstantInt>(mul1->getOperand(j))) {
6550 if (C->isMinusOne()) {
6551 auto res = pushcse(B.CreateSub(cur->getOperand(1 - i),
6552 mul1->getOperand(1 - j)));
6553 push(mul1);
6554
6555 replaceAndErase(cur, res);
6556 return "AddToSub";
6557 }
6558 }
6559 }
6560 }
6561 }
6562 }
6563
6564 if (auto SI = dyn_cast<SelectInst>(cur)) {
6565 auto shouldMove = [](Value *v) { return isa<Constant>(v); };
6566
6567 /*
6568 // select c, 0, x -> fmul (uitofp (!c)), x
6569 if (auto C1 = dyn_cast<ConstantFP>(SI->getTrueValue())) {
6570 if (C1->isZero()) {
6571 auto n = pushcse(B.CreateNot(SI->getCondition()));
6572 auto val = pushcse(B.CreateUIToFP(n, SI->getType()));
6573 auto res = pushcse(B.CreateFMul(val, SI->getFalseValue()));
6574 if (auto I = dyn_cast<Instruction>(res))
6575 I->setFast(true);
6576 replaceAndErase(cur, res);
6577 return true;
6578 }
6579 }
6580 // select c, x, 0 -> fmul (uitofp c), x
6581 if (auto C1 = dyn_cast<ConstantFP>(SI->getFalseValue())) {
6582 if (C1->isZero()) {
6583 auto val = pushcse(B.CreateUIToFP(SI->getCondition(), SI->getType()));
6584 auto res = pushcse(B.CreateFMul(val, SI->getTrueValue()));
6585 if (auto I = dyn_cast<Instruction>(res))
6586 I->setFast(true);
6587 replaceAndErase(cur, res);
6588 return true;
6589 }
6590 }
6591 */
6592
6593 // select c, (mul x y), 0 -> mul x, (select c, y, 0)
6594 for (int i = 0; i < 2; i++)
6595 if (auto inst = dyn_cast<Instruction>(SI->getOperand(1 + i)))
6596 if (inst->getOpcode() == Instruction::Mul)
6597 // inst->getOpcode() == Instruction::FMul)
6598 if (auto C = dyn_cast<Constant>(SI->getOperand(1 + (1 - i))))
6599 if ((isa<ConstantInt>(C) && cast<ConstantInt>(C)->isZero()) ||
6600 (isa<ConstantFP>(C) && cast<ConstantFP>(C)->isZero()))
6601 for (int j = 0; j < 2; j++)
6602 if (shouldMove(inst->getOperand(j))) {
6603 auto x = inst->getOperand(j);
6604 auto y = inst->getOperand(1 - j);
6605 auto isel = pushcse(B.CreateSelect(
6606 SI->getCondition(), (i == 0) ? y : C, (i == 0) ? C : y,
6607 "smulmove." + SI->getName()));
6608 Value *imul;
6609 if (cur->getType()->isIntegerTy())
6610 imul = pushcse(B.CreateMul(isel, x, "",
6611 inst->hasNoUnsignedWrap(),
6612 inst->hasNoSignedWrap()));
6613 else
6614 imul = pushcse(B.CreateFMulFMF(isel, x, inst, ""));
6615
6616 replaceAndErase(cur, imul);
6617 return "SelMulMove";
6618 }
6619
6620 // select c, (sitofp x), (sitofp y) -> sitofp (select c, x, y)
6621 // select c, c5, (sitofp y) -> sitofp (select c, c5, y)
6622 {
6623 Value *ops[2] = {nullptr, nullptr};
6624 bool legal = true;
6625 for (int i = 0; i < 2; i++) {
6626 if (isa<ConstantFP>(SI->getOperand(1 + i))) {
6627 ops[i] = nullptr;
6628 continue;
6629 }
6630 if (auto CI = dyn_cast<CastInst>(SI->getOperand(1 + i))) {
6631 if (CI->getOpcode() == Instruction::SIToFP) {
6632 ops[i] = CI->getOperand(0);
6633 continue;
6634 }
6635 }
6636 legal = false;
6637 break;
6638 }
6639 for (int i = 0; i < 2; i++) {
6640 if (!ops[i] && ops[1 - i])
6641 ops[i] = ConstantInt::get(ops[1 - i]->getType(), 0);
6642 }
6643 for (int i = 0; i < 2; i++) {
6644 if (ops[i] == nullptr || ops[i]->getType() != ops[0]->getType()) {
6645 legal = false;
6646 break;
6647 }
6648 }
6649 if (legal) {
6650 auto isel = pushcse(B.CreateSelect(SI->getCondition(), ops[0], ops[1],
6651 "seltofp." + SI->getName()));
6652 auto res = pushcse(B.CreateSIToFP(isel, SI->getType()));
6653
6654 replaceAndErase(cur, res);
6655 return "SelSIMerge";
6656 }
6657 }
6658 }
6659
6660 if (cur->getOpcode() == Instruction::Mul) {
6661 for (int i = 0; i < 2; i++) {
6662 // mul (x, 1) -> x
6663 if (auto C = dyn_cast<ConstantInt>(cur->getOperand(i)))
6664 if (C->isOne()) {
6665 replaceAndErase(cur, cur->getOperand(1 - i));
6666 return "MulIdent";
6667 }
6668
6669 // mul (zext i1 x), y -> mul (zext i1 x) y[x->1]
6670 if (auto Z = dyn_cast<ZExtInst>(cur->getOperand(i)))
6671 if (Z->getOperand(0)->getType()->isIntegerTy(1)) {
6672 auto prev = cur->getOperand(1 - i);
6673 auto next = replace(prev, Z->getOperand(0),
6674 ConstantInt::getTrue(cur->getContext()));
6675 if (next != prev) {
6676 auto res = pushcse(B.CreateMul(Z, next, "postmul." + cur->getName(),
6677 cur->hasNoUnsignedWrap(),
6678 cur->hasNoSignedWrap()));
6679 replaceAndErase(cur, res);
6680 return "MulReplaceZExt";
6681 }
6682 }
6683 }
6684
6685 /*
6686 // mul x, (select c, 0, y) -> select c (mul x 0), (mul x y)
6687 for (int i=0; i<2; i++)
6688 if (auto SI = dyn_cast<SelectInst>(cur->getOperand(i)))
6689 for (int j=0; j<2; j++)
6690 if (auto CI = dyn_cast<ConstantInt>(SI->getOperand(1+j)))
6691 if (CI->isZero()) {
6692 auto tval = (j == 0) ? CI :
6693 pushcse(B.CreateMul(SI->getTrueValue(), cur->getOperand(1-i), "tval." +
6694 cur->getName(), cur->hasNoUnsignedWrap(), cur->hasNoSignedWrap())); auto
6695 fval = (j == 1) ? CI : pushcse(B.CreateMul(SI->getFalseValue(),
6696 cur->getOperand(1-i), "fval." + cur->getName(), cur->hasNoUnsignedWrap(),
6697 cur->hasNoSignedWrap()));
6698
6699 auto res = pushcse(B.CreateSelect(SI->getCondition(), tval, fval));
6700
6701 replaceAndErase(cur, res);
6702 return true;
6703 }
6704 */
6705
6706 // mul (sub x, y), -c -> mul (sub, y, x), c
6707 for (int i = 0; i < 2; i++)
6708 if (auto inst = dyn_cast<Instruction>(cur->getOperand(i)))
6709 if (inst->getOpcode() == Instruction::Sub)
6710 if (auto CI = dyn_cast<ConstantInt>(cur->getOperand(1 - i)))
6711 if (CI->isNegative()) {
6712 auto sub2 = pushcse(B.CreateSub(
6713 inst->getOperand(1), inst->getOperand(0), "",
6714 inst->hasNoUnsignedWrap(), inst->hasNoSignedWrap()));
6715 auto mul2 = pushcse(B.CreateMul(
6716 sub2, ConstantInt::get(CI->getType(), -CI->getValue()), "",
6717 cur->hasNoUnsignedWrap(), cur->hasNoSignedWrap()));
6718
6719 replaceAndErase(cur, mul2);
6720 return "MulSubNegConst";
6721 }
6722 }
6723
6724 if (cur->getOpcode() == Instruction::Sub)
6725 if (auto CI = dyn_cast<ConstantInt>(cur->getOperand(0)))
6726 if (CI->isZero())
6727 if (auto zext = dyn_cast<Instruction>(cur->getOperand(1))) {
6728 // sub 0, (zext i1 x) -> sext x
6729 if (zext->getOpcode() == Instruction::ZExt &&
6730 zext->getOperand(0)->getType()->isIntegerTy(1)) {
6731 auto res =
6732 pushcse(B.CreateSExt(zext->getOperand(0), cur->getType()));
6733 replaceAndErase(cur, res);
6734 return "SubZExt";
6735 }
6736 // sub 0, (mul nsw nuw constant, x) -> mul nsw nuw -constant, x
6737 if (zext->getOpcode() == Instruction::Mul &&
6738 zext->hasNoUnsignedWrap() && zext->hasNoSignedWrap()) {
6739 for (int i = 0; i < 2; i++)
6740 if (auto CI = dyn_cast<ConstantInt>(zext->getOperand(i))) {
6741 auto res = pushcse(B.CreateMul(
6742 zext->getOperand(1 - i),
6743 ConstantInt::get(CI->getType(), -CI->getValue()),
6744 "neg." + zext->getName(), true, true));
6745 replaceAndErase(cur, res);
6746 return "SubMulConstant";
6747 }
6748 }
6749 }
6750
6751 // add (zext (and c1, x) ), (zext (and c1, y)) -> select c1, (add (zext x),
6752 // (zext y)), 0
6753 /*
6754 if (cur->getOpcode() == Instruction::Add ||
6755 cur->getOpcode() == Instruction::Sub ||
6756 cur->getOpcode() == Instruction::Mul)
6757 if (auto inst1 = dyn_cast<Instruction>(cur->getOperand(0)))
6758 if (auto inst2 = dyn_cast<Instruction>(cur->getOperand(1)))
6759 if (inst1->getOpcode() == Instruction::ZExt && inst2->getOpcode() ==
6760 Instruction::ZExt) if (auto and1 =
6761 dyn_cast<Instruction>(inst1->getOperand(0))) if (auto and2 =
6762 dyn_cast<Instruction>(inst2->getOperand(0))) if
6763 (and1->getType()->isIntegerTy(1) && and2->getType()->isIntegerTy(1) &&
6764 and1->getOpcode() == Instruction::And && and2->getOpcode() ==
6765 Instruction::And) { bool done = false; for (int i1=0; i1<2; i1++) for (int
6766 i2=0; i2<2; i2++) if (and1->getOperand(i1) == and2->getOperand(i2)) { auto
6767 c1 = and1->getOperand(i1); auto x = and1->getOperand(1-i1); x =
6768 pushcse(B.CreateZExt(x, inst1->getType())); auto y =
6769 and2->getOperand(1-i2);
6770
6771 y = pushcse(B.CreateZExt(y, inst2->getType()));
6772
6773 Value *res = nullptr;
6774 switch (cur->getOpcode()) {
6775 case Instruction::Add:
6776 res = pushcse(B.CreateAdd(x, y, "", cur->hasNoUnsignedWrap(),
6777 cur->hasNoSignedWrap())); break; case Instruction::Sub: res = B.CreateSub(x,
6778 y,
6779 "", cur->hasNoUnsignedWrap(), cur->hasNoSignedWrap()); break; case
6780 Instruction::Mul: res = B.CreateMul(x, y, "", cur->hasNoUnsignedWrap(),
6781 cur->hasNoSignedWrap()); break; default: llvm_unreachable("Illegal opcode");
6782 }
6783 res = pushcse(B.CreateSelect(c1, res,
6784 Constant::getNullValue(cur->getType())));
6785
6786 replaceAndErase(cur, res);
6787 return;
6788 }
6789 }
6790 */
6791
6792 // add (select %c c0, x), (select %c, c1, y) -> select %c, (add c0, c1),
6793 // (add x, y) and for sub/mul/cmp
6794 if (cur->getOpcode() == Instruction::Add ||
6795 cur->getOpcode() == Instruction::Sub ||
6796 cur->getOpcode() == Instruction::Mul ||
6797 cur->getOpcode() == Instruction::FAdd ||
6798 cur->getOpcode() == Instruction::FSub ||
6799 cur->getOpcode() == Instruction::FMul ||
6800 // cur->getOpcode() == Instruction::SIToFP ||
6801 // cur->getOpcode() == Instruction::UIToFP ||
6802 cur->getOpcode() == Instruction::ICmp ||
6803 cur->getOpcode() == Instruction::FCmp) {
6804
6805 Value *SI1cond = nullptr;
6806 Value *SI1tval = nullptr;
6807 Value *SI1fval = nullptr;
6808 if (auto SI1 = dyn_cast<SelectInst>(cur->getOperand(0))) {
6809 SI1cond = SI1->getCondition();
6810 SI1tval = SI1->getTrueValue();
6811 SI1fval = SI1->getFalseValue();
6812 }
6813 if (auto SI1 = dyn_cast<ZExtInst>(cur->getOperand(0)))
6814 if (SI1->getOperand(0)->getType()->isIntegerTy(1)) {
6815 SI1cond = SI1->getOperand(0);
6816 SI1tval = SI1;
6817 SI1fval = ConstantInt::get(SI1->getType(), 0);
6818 }
6819 if (auto SI1 = dyn_cast<SExtInst>(cur->getOperand(0)))
6820 if (SI1->getOperand(0)->getType()->isIntegerTy(1)) {
6821 SI1cond = SI1->getOperand(0);
6822 SI1tval = SI1;
6823 SI1fval = ConstantInt::get(SI1->getType(), 0);
6824 }
6825 Value *SI2cond = nullptr;
6826 Value *SI2tval = nullptr;
6827 Value *SI2fval = nullptr;
6828
6829 auto op2 = cur->getOperand((cur->getOpcode() == Instruction::SIToFP ||
6830 cur->getOpcode() == Instruction::UIToFP)
6831 ? 0
6832 : 1);
6833 if (auto SI2 = dyn_cast<SelectInst>(op2)) {
6834 SI2cond = SI2->getCondition();
6835 SI2tval = SI2->getTrueValue();
6836 SI2fval = SI2->getFalseValue();
6837 }
6838 if (auto SI2 = dyn_cast<ZExtInst>(op2))
6839 if (SI2->getOperand(0)->getType()->isIntegerTy(1)) {
6840 SI2cond = SI2->getOperand(0);
6841 SI2tval = SI2;
6842 SI2fval = ConstantInt::get(SI2->getType(), 0);
6843 }
6844 if (auto SI2 = dyn_cast<SExtInst>(op2))
6845 if (SI2->getOperand(0)->getType()->isIntegerTy(1)) {
6846 SI2cond = SI2->getOperand(0);
6847 SI2tval = SI2;
6848 SI2fval = ConstantInt::get(SI2->getType(), 0);
6849 }
6850
6851 if (SI1cond && SI2cond && (SI1cond == SI2cond || isNot(SI1cond, SI2cond)))
6852 if ((SI1cond == SI2cond &&
6853 ((isa<Constant>(SI1tval) && isa<Constant>(SI2tval)) ||
6854 (isa<Constant>(SI1fval) && isa<Constant>(SI2fval)))) ||
6855 (SI1cond != SI2cond &&
6856 ((isa<Constant>(SI1tval) && isa<Constant>(SI2fval)) ||
6857 (isa<Constant>(SI1fval) && isa<Constant>(SI2tval))))
6858
6859 ) {
6860 Value *tval = nullptr;
6861 Value *fval = nullptr;
6862 bool inverted = SI1cond != SI2cond;
6863 switch (cur->getOpcode()) {
6864 case Instruction::SIToFP:
6865 tval =
6866 B.CreateSIToFP(SI1tval, cur->getType(), "tval." + cur->getName());
6867 fval =
6868 B.CreateSIToFP(SI1fval, cur->getType(), "fval." + cur->getName());
6869 break;
6870 case Instruction::UIToFP:
6871 tval =
6872 B.CreateUIToFP(SI1tval, cur->getType(), "tval." + cur->getName());
6873 fval =
6874 B.CreateUIToFP(SI1fval, cur->getType(), "fval." + cur->getName());
6875 break;
6876 case Instruction::FAdd:
6877 tval = B.CreateFAddFMF(SI1tval, inverted ? SI2fval : SI2tval, cur,
6878 "tval." + cur->getName());
6879 fval = B.CreateFAddFMF(SI1fval, inverted ? SI2tval : SI2fval, cur,
6880 "fval." + cur->getName());
6881 break;
6882 case Instruction::FSub:
6883 tval = B.CreateFSubFMF(SI1tval, inverted ? SI2fval : SI2tval, cur,
6884 "tval." + cur->getName());
6885 fval = B.CreateFSubFMF(SI1fval, inverted ? SI2tval : SI2fval, cur,
6886 "fval." + cur->getName());
6887 break;
6888 case Instruction::FMul:
6889 tval = B.CreateFMulFMF(SI1tval, inverted ? SI2fval : SI2tval, cur,
6890 "tval." + cur->getName());
6891 fval = B.CreateFMulFMF(SI1fval, inverted ? SI2tval : SI2fval, cur,
6892 "fval." + cur->getName());
6893 break;
6894 case Instruction::Add:
6895 tval = B.CreateAdd(SI1tval, inverted ? SI2fval : SI2tval,
6896 "tval." + cur->getName(), cur->hasNoUnsignedWrap(),
6897 cur->hasNoSignedWrap());
6898 fval = B.CreateAdd(SI1fval, inverted ? SI2tval : SI2fval,
6899 "fval." + cur->getName(), cur->hasNoUnsignedWrap(),
6900 cur->hasNoSignedWrap());
6901 break;
6902 case Instruction::Sub:
6903 tval = B.CreateSub(SI1tval, inverted ? SI2fval : SI2tval,
6904 "tval." + cur->getName(), cur->hasNoUnsignedWrap(),
6905 cur->hasNoSignedWrap());
6906 fval = B.CreateSub(SI1fval, inverted ? SI2tval : SI2fval,
6907 "fval." + cur->getName(), cur->hasNoUnsignedWrap(),
6908 cur->hasNoSignedWrap());
6909 break;
6910 case Instruction::Mul:
6911 tval = B.CreateMul(SI1tval, inverted ? SI2fval : SI2tval,
6912 "tval." + cur->getName(), cur->hasNoUnsignedWrap(),
6913 cur->hasNoSignedWrap());
6914 fval = B.CreateMul(SI1fval, inverted ? SI2tval : SI2fval,
6915 "fval." + cur->getName(), cur->hasNoUnsignedWrap(),
6916 cur->hasNoSignedWrap());
6917 break;
6918 case Instruction::ICmp:
6919 case Instruction::FCmp:
6920 tval = B.CreateCmp(cast<CmpInst>(cur)->getPredicate(), SI1tval,
6921 inverted ? SI2fval : SI2tval,
6922 "tval." + cur->getName());
6923 fval = B.CreateCmp(cast<CmpInst>(cur)->getPredicate(), SI1fval,
6924 inverted ? SI2tval : SI2fval,
6925 "fval." + cur->getName());
6926 break;
6927 default:
6928 llvm_unreachable("illegal opcode");
6929 }
6930 tval = pushcse(tval);
6931 fval = pushcse(fval);
6932
6933 auto res = pushcse(
6934 B.CreateSelect(SI1cond, tval, fval, "selmerge." + cur->getName()));
6935
6936 push(cur->getOperand(0));
6937 push(cur->getOperand(1));
6938 replaceAndErase(cur, res);
6939 return "BinopSelFuse";
6940 }
6941 }
6942
6943 /*
6944 // and (i == c), (i != d) -> and (i == c) && (c != d)
6945 if (cur->getOpcode() == Instruction::And) {
6946 auto lhs = replace(cur->getOperand(0), cur->getOperand(1),
6947 ConstantInt::getTrue(cur->getContext()));
6948 auto rhs = replace(cur->getOperand(1), cur->getOperand(0),
6949 ConstantInt::getTrue(cur->getContext()));
6950 if (lhs != cur->getOperand(0) || rhs != cur->getOperand(1)) {
6951 auto res = pushcse(B.CreateAnd(lhs, rhs, "postand." + cur->getName()));
6952 replaceAndErase(cur, res);
6953 return "AndReplace2";
6954 }
6955 }
6956 */
6957
6958 // and a, (or q, (not a)) -> and a q
6959 if (cur->getOpcode() == Instruction::And) {
6960 for (size_t i1 = 0; i1 < 2; i1++)
6961 if (auto inst2 = dyn_cast<Instruction>(cur->getOperand(1 - i1)))
6962 if (inst2->getOpcode() == Instruction::Or)
6963 for (size_t i2 = 0; i2 < 2; i2++)
6964 if (isNot(cur->getOperand(i1), inst2->getOperand(i2))) {
6965 auto q = inst2->getOperand(1 - i2);
6966 cur->setOperand(1 - i1, q);
6967 push(cur);
6968 push(q);
6969 push(inst2);
6970 push(cur->getOperand(i1));
6971 push(inst2->getOperand(i2));
6972 Q.insert(cur);
6973 for (auto U : cur->users())
6974 push(U);
6975 return "AndOrProp";
6976 }
6977 }
6978
6979 // and (and a, b), a) -> and a, b
6980 if (cur->getOpcode() == Instruction::And) {
6981 for (size_t i1 = 0; i1 < 2; i1++)
6982 if (auto inst2 = dyn_cast<Instruction>(cur->getOperand(i1)))
6983 if (inst2->getOpcode() == Instruction::And)
6984 for (size_t i2 = 0; i2 < 2; i2++)
6985 if (inst2->getOperand(i2) == cur->getOperand(1 - i1)) {
6986 replaceAndErase(cur, inst2);
6987 return "AndAndProp";
6988 }
6989 }
6990
6991 // or a, (and q, (not a)) -> and a q
6992 if (cur->getOpcode() == Instruction::And) {
6993 for (size_t i1 = 0; i1 < 2; i1++)
6994 if (auto inst2 = dyn_cast<Instruction>(cur->getOperand(1 - i1)))
6995 if (inst2->getOpcode() == Instruction::Or)
6996 for (size_t i2 = 0; i2 < 2; i2++)
6997 if (isNot(cur->getOperand(i1), inst2->getOperand(i2))) {
6998 auto q = inst2->getOperand(1 - i2);
6999 cur->setOperand(1 - i1, q);
7000 push(cur);
7001 push(q);
7002 push(inst2);
7003 push(cur->getOperand(i1));
7004 push(inst2->getOperand(i2));
7005 Q.insert(cur);
7006 for (auto U : cur->users())
7007 push(U);
7008 return "OrAndProp";
7009 }
7010 }
7011
7012 // and ( (a +/- b) != c ), ( (d +/- b) != c ) -> and ( a != (c -/+ b) ), (
7013 // d != (c -/+ b) )
7014 // also with or
7015 if (cur->getOpcode() == Instruction::And ||
7016 cur->getOpcode() == Instruction::Or) {
7017 for (auto cmpOp : {ICmpInst::ICMP_EQ, ICmpInst::ICMP_NE})
7018 for (auto interOp : {Instruction::Add, Instruction::Sub})
7019 if (auto cmp1 = dyn_cast<ICmpInst>(cur->getOperand(0)))
7020 if (auto cmp2 = dyn_cast<ICmpInst>(cur->getOperand(1)))
7021 for (size_t i1 = 0; i1 < 2; i1++)
7022 for (size_t i2 = 0; i2 < 2; i2++)
7023 if (cmp1->getOperand(1 - i1) == cmp2->getOperand(1 - i2) &&
7024 cmp1->getPredicate() == cmpOp &&
7025 cmp2->getPredicate() == cmpOp)
7026 if (auto add1 = dyn_cast<Instruction>(cmp1->getOperand(i1)))
7027 if (auto add2 = dyn_cast<Instruction>(cmp2->getOperand(i2)))
7028 if (add1->getOpcode() == interOp &&
7029 add2->getOpcode() == interOp)
7030 for (size_t ia = 0; ia < 2; ia++)
7031 if (add1->getOperand(ia) == add2->getOperand(ia)) {
7032
7033 auto b = add1->getOperand(ia);
7034 auto c = cmp1->getOperand(1 - i1);
7035 auto a = add1->getOperand(1 - ia);
7036 auto d = add2->getOperand(1 - ia);
7037
7038 Value *res = nullptr;
7039 if (interOp == Instruction::Add)
7040 // a + b == c <=> a == c - b
7041 res = pushcse(B.CreateSub(c, b));
7042 else if (ia == 0)
7043 // b - a == c <=> a == b - c
7044 res = pushcse(B.CreateSub(b, c));
7045 else
7046 // a - b == c <=> a == c + b
7047 res = pushcse(B.CreateAdd(c, b));
7048
7049 auto lhs = pushcse(B.CreateCmp(cmpOp, a, res));
7050 auto rhs = pushcse(B.CreateCmp(cmpOp, d, res));
7051
7052 Value *fres = nullptr;
7053 if (cur->getOpcode() == Instruction::And)
7054 fres = pushcse(B.CreateAnd(lhs, rhs));
7055 else
7056 fres = pushcse(B.CreateOr(lhs, rhs));
7057
7058 replaceAndErase(cur, fres);
7059 return "AndLinearShift";
7060 }
7061 }
7062
7063 // and ( expr == c1 ), ( expr == c2 ) and c1 != c2 -> false
7064 if (cur->getOpcode() == Instruction::And) {
7065 for (auto cmpOp : {ICmpInst::ICMP_EQ})
7066 if (auto cmp1 = dyn_cast<ICmpInst>(cur->getOperand(0)))
7067 if (auto cmp2 = dyn_cast<ICmpInst>(cur->getOperand(1)))
7068 for (size_t i1 = 0; i1 < 2; i1++)
7069 for (size_t i2 = 0; i2 < 2; i2++)
7070 if (cmp1->getOperand(1 - i1) == cmp2->getOperand(1 - i2) &&
7071 cmp1->getPredicate() == cmpOp &&
7072 cmp2->getPredicate() == cmpOp) {
7073 auto c1 = SE.getSCEV(cmp1->getOperand(i1));
7074 auto c2 = SE.getSCEV(cmp2->getOperand(i2));
7075#if LLVM_VERSION_MAJOR >= 24
7076 auto m = SE.getMinusSCEV(c1, c2, SCEV::FlagsMask);
7077#else
7078 auto m = SE.getMinusSCEV(c1, c2, SCEV::NoWrapMask);
7079#endif
7080 if (auto C = dyn_cast<SCEVConstant>(m)) {
7081 // if c1 == c2 don't need the and they are equivalent
7082 if (C->getValue()->isZero()) {
7083 push(cmp1);
7084 push(cmp2);
7085 replaceAndErase(cur, cmp1);
7086 return "AndEQExpr";
7087 } else {
7088 // if non one constant they must be distinct.
7089 replaceAndErase(cur,
7090 ConstantInt::getFalse(cur->getContext()));
7091 return "AndNEExpr";
7092 }
7093 }
7094 }
7095 }
7096
7097 // (a | b) == 0 -> a == 0 & b == 0
7098 if (auto icmp = dyn_cast<ICmpInst>(cur))
7099 if (icmp->getPredicate() == ICmpInst::ICMP_EQ &&
7100 cur->getType()->isIntegerTy(1))
7101 for (int i = 0; i < 2; i++)
7102 if (auto C = dyn_cast<ConstantInt>(icmp->getOperand(i)))
7103 if (C->isZero())
7104 if (auto z = dyn_cast<BinaryOperator>(icmp->getOperand(1 - i)))
7105 if (z->getOpcode() == BinaryOperator::Or) {
7106 auto a0 = pushcse(B.CreateICmpEQ(z->getOperand(0), C));
7107 auto b0 = pushcse(B.CreateICmpEQ(z->getOperand(1), C));
7108 auto res = pushcse(B.CreateAnd(a0, b0));
7109 push(z);
7110 push(icmp);
7111 replaceAndErase(cur, res);
7112 return "OrEQZero";
7113 }
7114
7115 // add (mul a b), (mul c, b) -> mul (add a, c), b
7116 if (cur->getOpcode() == Instruction::Sub ||
7117 cur->getOpcode() == Instruction::Add) {
7118 if (auto mul1 = dyn_cast<Instruction>(cur->getOperand(0)))
7119 if (auto mul2 = dyn_cast<Instruction>(cur->getOperand(1)))
7120 if ((mul1->getOpcode() == Instruction::Mul &&
7121 mul2->getOpcode() == Instruction::Mul) ||
7122 (mul1->getOpcode() == Instruction::FMul &&
7123 mul2->getOpcode() == Instruction::FMul && mul1->isFast() &&
7124 mul2->isFast() && cur->isFast())) {
7125 for (int i1 = 0; i1 < 2; i1++)
7126 for (int i2 = 0; i2 < 2; i2++) {
7127 if (mul1->getOperand(i1) == mul2->getOperand(i2)) {
7128 Value *res = nullptr;
7129 switch (cur->getOpcode()) {
7130 case Instruction::Add:
7131 res = B.CreateAdd(mul1->getOperand(1 - i1),
7132 mul2->getOperand(1 - i2));
7133 break;
7134 case Instruction::Sub:
7135 res = B.CreateSub(mul1->getOperand(1 - i1),
7136 mul2->getOperand(1 - i2));
7137 break;
7138 case Instruction::FAdd:
7139 res = B.CreateFAddFMF(mul1->getOperand(1 - i1),
7140 mul2->getOperand(1 - i2), cur);
7141 break;
7142 case Instruction::FSub:
7143 res = B.CreateFSubFMF(mul1->getOperand(1 - i1),
7144 mul2->getOperand(1 - i2), cur);
7145 break;
7146 default:
7147 llvm_unreachable("Illegal opcode");
7148 }
7149 res = pushcse(res);
7150 Value *res2 = nullptr;
7151 if (cur->getType()->isIntegerTy())
7152 res2 = B.CreateMul(
7153 res, mul1->getOperand(i1), "",
7154 mul1->hasNoUnsignedWrap() && mul1->hasNoUnsignedWrap(),
7155 mul2->hasNoSignedWrap() && mul2->hasNoSignedWrap());
7156 else
7157 res2 = B.CreateFMulFMF(res, mul1->getOperand(i1), cur);
7158
7159 res2 = pushcse(res2);
7160
7161 replaceAndErase(cur, res2);
7162 return "InvDistributive";
7163 }
7164 }
7165 }
7166 }
7167
7168 // fadd (ext a), (ext b) -> ext (a + b)
7169 // fsub (ext a), (ext b) -> ext (a - b)
7170 // fmul (ext a), (ext b) -> ext (a * b)
7171 if (cur->getOpcode() == Instruction::FSub ||
7172 cur->getOpcode() == Instruction::FAdd ||
7173 cur->getOpcode() == Instruction::FMul ||
7174 cur->getOpcode() == Instruction::FNeg ||
7175 (isSum(cur) && callOperands(cast<CallBase>(cur)).size() == 2)) {
7176 auto opcode = cur->getOpcode();
7177 if (isSum(cur))
7178 opcode = Instruction::FAdd;
7179 auto Ty = B.getInt64Ty();
7180 SmallPtrSet<Instruction *, 1> temporaries;
7181 SmallVector<Instruction *, 1> precasts;
7182 Value *lhs = nullptr;
7183
7184 Value *prelhs = (cur->getOpcode() == Instruction::FNeg)
7185 ? ConstantFP::get(cur->getType(), 0.0)
7186 : cur->getOperand(0);
7187 Value *prerhs = (cur->getOpcode() == Instruction::FNeg)
7188 ? cur->getOperand(0)
7189 : cur->getOperand(1);
7190
7191 APInt minval(64, 0);
7192 APInt maxval(64, 0);
7193 if (auto C = dyn_cast<ConstantFP>(prelhs)) {
7194 APSInt Tmp(64);
7195 bool isExact = false;
7196 C->getValue().convertToInteger(Tmp, llvm::RoundingMode::TowardZero,
7197 &isExact);
7198 if (isExact || C->isZero()) {
7199 minval = maxval = Tmp;
7200 lhs = ConstantInt::get(Ty, Tmp);
7201 }
7202 }
7203 if (auto ext = dyn_cast<CastInst>(prelhs)) {
7204 if (ext->getOpcode() == Instruction::UIToFP ||
7205 ext->getOpcode() == Instruction::SIToFP) {
7206 precasts.push_back(ext);
7207 auto ity = cast<IntegerType>(ext->getOperand(0)->getType());
7208 bool md = false;
7209 if (auto I = dyn_cast<Instruction>(ext->getOperand(0)))
7210 if (auto MD = hasMetadata(I, LLVMContext::MD_range)) {
7211 md = true;
7212 minval =
7213 cast<ConstantInt>(
7214 cast<ConstantAsMetadata>(MD->getOperand(0))->getValue())
7215 ->getValue()
7216 .zextOrTrunc(64);
7217 maxval =
7218 cast<ConstantInt>(
7219 cast<ConstantAsMetadata>(MD->getOperand(1))->getValue())
7220 ->getValue()
7221 .zextOrTrunc(64);
7222 }
7223 if (!md) {
7224 if (ext->getOpcode() == Instruction::UIToFP)
7225 maxval = APInt::getMaxValue(ity->getBitWidth()).zextOrTrunc(64);
7226 else {
7227 maxval =
7228 APInt::getSignedMaxValue(ity->getBitWidth()).zextOrTrunc(64);
7229 minval =
7230 APInt::getSignedMinValue(ity->getBitWidth()).zextOrTrunc(64);
7231 }
7232 }
7233 if (ext->getOperand(0)->getType() == Ty)
7234 lhs = ext->getOperand(0);
7235 else if (ity->getBitWidth() < Ty->getBitWidth()) {
7236 if (ext->getOpcode() == Instruction::UIToFP)
7237 lhs = B.CreateZExt(ext->getOperand(0), Ty);
7238 else
7239 lhs = B.CreateSExt(ext->getOperand(0), Ty);
7240 if (auto I = dyn_cast<Instruction>(lhs))
7241 if (I != ext->getOperand(0))
7242 temporaries.insert(I);
7243 }
7244 }
7245 }
7246
7247 Value *rhs = nullptr;
7248
7249 if (auto C = dyn_cast<ConstantFP>(prerhs)) {
7250 APSInt Tmp(64);
7251 bool isExact = false;
7252 C->getValue().convertToInteger(Tmp, llvm::RoundingMode::TowardZero,
7253 &isExact);
7254 if (isExact || C->isZero()) {
7255 rhs = ConstantInt::get(Ty, Tmp);
7256 switch (opcode) {
7257 case Instruction::FAdd:
7258 minval += Tmp;
7259 maxval += Tmp;
7260 break;
7261 case Instruction::FSub:
7262 case Instruction::FNeg:
7263 minval -= Tmp;
7264 maxval -= Tmp;
7265 break;
7266 case Instruction::FMul:
7267 minval *= Tmp;
7268 maxval *= Tmp;
7269 break;
7270 default:
7271 llvm_unreachable("Illegal opcode");
7272 }
7273 }
7274 }
7275 if (auto ext = dyn_cast<CastInst>(prerhs)) {
7276 if (ext->getOpcode() == Instruction::UIToFP ||
7277 ext->getOpcode() == Instruction::SIToFP) {
7278 precasts.push_back(ext);
7279 auto ity = cast<IntegerType>(ext->getOperand(0)->getType());
7280 bool md = false;
7281 APInt rhsMin(64, 0);
7282 APInt rhsMax(64, 0);
7283 if (auto I = dyn_cast<Instruction>(ext->getOperand(0)))
7284 if (auto MD = hasMetadata(I, LLVMContext::MD_range)) {
7285 md = true;
7286 rhsMin =
7287 cast<ConstantInt>(
7288 cast<ConstantAsMetadata>(MD->getOperand(0))->getValue())
7289 ->getValue()
7290 .zextOrTrunc(64);
7291 rhsMax =
7292 cast<ConstantInt>(
7293 cast<ConstantAsMetadata>(MD->getOperand(1))->getValue())
7294 ->getValue()
7295 .zextOrTrunc(64);
7296 }
7297 if (!md) {
7298 if (ext->getOpcode() == Instruction::UIToFP) {
7299 rhsMax = APInt::getMaxValue(ity->getBitWidth()).zextOrTrunc(64);
7300 rhsMin = APInt(64, 0);
7301 } else {
7302 rhsMax =
7303 APInt::getSignedMaxValue(ity->getBitWidth()).zextOrTrunc(64);
7304 rhsMin =
7305 APInt::getSignedMinValue(ity->getBitWidth()).zextOrTrunc(64);
7306 }
7307 }
7308 switch (opcode) {
7309 case Instruction::FAdd:
7310 minval += rhsMin;
7311 maxval += rhsMax;
7312 break;
7313 case Instruction::FSub:
7314 case Instruction::FNeg:
7315 minval -= rhsMax;
7316 maxval -= rhsMin;
7317 break;
7318 case Instruction::FMul: {
7319 auto minf = [&](APInt a, APInt b) { return a.sle(b) ? a : b; };
7320 auto maxf = [&](APInt a, APInt b) { return a.sle(b) ? b : b; };
7321 minval = minf(
7322 minval * rhsMin,
7323 minf(minval * rhsMax, minf(maxval * rhsMin, maxval * rhsMax)));
7324 maxval = maxf(
7325 minval * rhsMin,
7326 maxf(minval * rhsMax, maxf(maxval * rhsMin, maxval * rhsMax)));
7327 break;
7328 }
7329 default:
7330 llvm_unreachable("Illegal opcode");
7331 }
7332 if (ext->getOperand(0)->getType() == Ty)
7333 rhs = ext->getOperand(0);
7334 else if (ity->getBitWidth() < Ty->getBitWidth()) {
7335 if (ext->getOpcode() == Instruction::UIToFP)
7336 rhs = B.CreateZExt(ext->getOperand(0), Ty);
7337 else
7338 rhs = B.CreateSExt(ext->getOperand(0), Ty);
7339 if (auto I = dyn_cast<Instruction>(rhs))
7340 if (I != ext->getOperand(0))
7341 temporaries.insert(I);
7342 }
7343 }
7344 }
7345
7346 if (lhs && rhs) {
7347 Value *res = nullptr;
7348 if (temporaries.count(dyn_cast<Instruction>(lhs)))
7349 lhs = pushcse(lhs);
7350 if (temporaries.count(dyn_cast<Instruction>(rhs)))
7351 rhs = pushcse(rhs);
7352 switch (opcode) {
7353 case Instruction::FAdd:
7354 res = B.CreateAdd(lhs, rhs, "", false, true);
7355 break;
7356 case Instruction::FSub:
7357 case Instruction::FNeg:
7358 res = B.CreateSub(lhs, rhs, "", false, true);
7359 break;
7360 case Instruction::FMul:
7361 res = B.CreateMul(lhs, rhs, "", false, true);
7362 break;
7363 default:
7364 llvm_unreachable("Illegal opcode");
7365 }
7366 res = pushcse(res);
7367 for (auto I : precasts)
7368 push(I);
7369 /*
7370 if (auto I = dyn_cast<Instruction>(res)) {
7371 Q.insert(I);
7372 Metadata *vals[] = {(Metadata *)ConstantAsMetadata::get(
7373 ConstantInt::get(Ty, minval)),
7374 (Metadata *)ConstantAsMetadata::get(
7375 ConstantInt::get(Ty, maxval))};
7376 I->setMetadata(LLVMContext::MD_range,
7377 MDNode::get(I->getContext(), vals));
7378 }
7379 */
7380 auto ext = pushcse(B.CreateSIToFP(res, cur->getType()));
7381 replaceAndErase(cur, ext);
7382 return "BinopExtToExtBinop";
7383
7384 } else {
7385 for (auto I : temporaries)
7386 I->eraseFromParent();
7387 }
7388 }
7389
7390 // select(cond, const1, b) ?= const2 -> select(cond, const1 ?= const2, b ?=
7391 // const2)
7392 if (auto fcmp = dyn_cast<FCmpInst>(cur))
7393 for (int i = 0; i < 2; i++)
7394 if (auto const2 = dyn_cast<Constant>(fcmp->getOperand(i)))
7395 if (auto sel = dyn_cast<SelectInst>(fcmp->getOperand(1 - i)))
7396 if (isa<Constant>(sel->getTrueValue()) ||
7397 isa<Constant>(sel->getFalseValue())) {
7398 auto tval = pushcse(B.CreateFCmp(fcmp->getPredicate(),
7399 sel->getTrueValue(), const2));
7400 auto fval = pushcse(B.CreateFCmp(fcmp->getPredicate(),
7401 sel->getFalseValue(), const2));
7402 auto res = pushcse(B.CreateSelect(sel->getCondition(), tval, fval));
7403 replaceAndErase(cur, res);
7404 return "FCmpSelectConst";
7405 }
7406
7407 // mul (mul a, const), b:not_sparse_or_const -> mul (mul a, b), const
7408 // note we avoid the case where b = (mul a, const) since otherwise
7409 // we create an infinite recursion
7410 // and also we make sure b isn't sparse, since sparse is the first
7411 // precedence for pushing, then constant, then others
7412 if (cur->getOpcode() == Instruction::FMul)
7413 if (cur->isFast() && cur->getOperand(0) != cur->getOperand(1))
7414 for (auto ic = 0; ic < 2; ic++)
7415 if (auto mul = dyn_cast<Instruction>(cur->getOperand(ic)))
7416 if (mul->getOpcode() == Instruction::FMul && mul->isFast()) {
7417 auto b = cur->getOperand(1 - ic);
7418 if (!isa<Constant>(b) && !directlySparse(b)) {
7419
7420 for (int i = 0; i < 2; i++)
7421 if (auto C = dyn_cast<Constant>(mul->getOperand(i))) {
7422 auto n0 =
7423 pushcse(B.CreateFMulFMF(mul->getOperand(1 - i), b, mul));
7424 auto n1 = pushcse(B.CreateFMulFMF(n0, C, cur));
7425 push(mul);
7426
7427 replaceAndErase(cur, n1);
7428 return "MulMulConst";
7429 }
7430 }
7431 }
7432
7433 // (mul c, a) +/- (mul c, b) -> mul c, (a +/- b)
7434 if (cur->getOpcode() == Instruction::FAdd ||
7435 cur->getOpcode() == Instruction::FSub) {
7436 if (auto mul1 = dyn_cast<BinaryOperator>(cur->getOperand(0))) {
7437 if (mul1->getOpcode() == Instruction::FMul && mul1->isFast()) {
7438 if (auto mul2 = dyn_cast<BinaryOperator>(cur->getOperand(1))) {
7439 if (mul2->getOpcode() == Instruction::FMul && mul2->isFast()) {
7440 for (int i = 0; i < 2; i++) {
7441 for (int j = 0; j < 2; j++) {
7442 if (mul1->getOperand(i) == mul2->getOperand(j)) {
7443 auto c = mul1->getOperand(i);
7444 auto a = mul1->getOperand(1 - i);
7445 auto b = mul2->getOperand(1 - j);
7446 Value *intermediate = nullptr;
7447
7448 if (cur->getOpcode() == Instruction::FAdd)
7449 intermediate = pushcse(B.CreateFAddFMF(a, b, cur));
7450 else
7451 intermediate = pushcse(B.CreateFSubFMF(a, b, cur));
7452
7453 auto res = pushcse(B.CreateFMulFMF(c, intermediate, cur));
7454 push(mul1);
7455 push(mul2);
7456 replaceAndErase(cur, res);
7457 return "FAddMulConstMulConst";
7458 }
7459 }
7460 }
7461 }
7462 }
7463 }
7464 }
7465 }
7466
7467 // fmul a, (sitofp (imul c:const, b)) -> fmul (fmul (a, (sitofp c))),
7468 // (sitofp b)
7469
7470 if (cur->getOpcode() == Instruction::FMul && cur->isFast()) {
7471 for (int i = 0; i < 2; i++)
7472 if (auto z = dyn_cast<Instruction>(cur->getOperand(i)))
7473 if (isa<SIToFPInst>(z) || isa<UIToFPInst>(z))
7474 if (auto imul = dyn_cast<BinaryOperator>(z->getOperand(0)))
7475 if (imul->getOpcode() == Instruction::Mul)
7476 for (int j = 0; j < 2; j++)
7477 if (auto c = dyn_cast<Constant>(imul->getOperand(j))) {
7478 auto b = imul->getOperand(1 - j);
7479 auto a = cur->getOperand(1 - i);
7480
7481 auto c_fp = pushcse(B.CreateSIToFP(c, cur->getType()));
7482 auto b_fp = pushcse(B.CreateSIToFP(b, cur->getType()));
7483 auto n_mul = pushcse(B.CreateFMulFMF(a, c_fp, cur));
7484 auto res = pushcse(
7485 B.CreateFMulFMF(n_mul, b_fp, cur, cur->getName()));
7486 push(imul);
7487 push(z);
7488 replaceAndErase(cur, res);
7489 return "FMulIMulConstRotate";
7490 }
7491 }
7492
7493 if (cur->getOpcode() == Instruction::FDiv) {
7494 Value *prelhs = cur->getOperand(0);
7495 Value *b = cur->getOperand(1);
7496
7497 // fdiv (sitofp a), b -> select (a == 0), 0 [ (fdiv 1 / b) * sitofp a]
7498 if (auto ext = dyn_cast<CastInst>(prelhs)) {
7499 if (ext->getOpcode() == Instruction::UIToFP ||
7500 ext->getOpcode() == Instruction::SIToFP) {
7501 push(ext);
7502
7503 Value *condition = pushcse(
7504 B.CreateICmpEQ(ext->getOperand(0),
7505 ConstantInt::get(ext->getOperand(0)->getType(), 0),
7506 "sdivcmp." + cur->getName()));
7507
7508 Value *fdiv = pushcse(
7509 B.CreateFMulFMF(pushcse(B.CreateFDivFMF(
7510 ConstantFP::get(cur->getType(), 1.0), b, cur)),
7511 ext, cur));
7512
7513 Value *sel = pushcse(
7514 B.CreateSelect(condition, ConstantFP::get(cur->getType(), 0.0),
7515 fdiv, "sfdiv." + cur->getName()));
7516
7517 replaceAndErase(cur, sel);
7518 return "FDivSIToFPProp";
7519 }
7520 }
7521 // fdiv (select c, 0, a), b -> select c, 0 (fdiv a, b)
7522 if (auto SI = dyn_cast<SelectInst>(prelhs)) {
7523 auto tvalC = dyn_cast<ConstantFP>(SI->getTrueValue());
7524 auto fvalC = dyn_cast<ConstantFP>(SI->getFalseValue());
7525 if ((tvalC && tvalC->isZero()) || (fvalC && fvalC->isZero())) {
7526 push(SI);
7527 auto ntval =
7528 (tvalC && tvalC->isZero())
7529 ? tvalC
7530 : pushcse(B.CreateFDivFMF(SI->getTrueValue(), b, cur,
7531 "sfdiv2_t." + cur->getName()));
7532 auto nfval =
7533 (fvalC && fvalC->isZero())
7534 ? fvalC
7535 : pushcse(B.CreateFDivFMF(SI->getFalseValue(), b, cur,
7536 "sfdiv2_f." + cur->getName()));
7537
7538 // Work around bad fdivfmf, fixed in LLVM 16+
7539 // https://github.com/llvm/llvm-project/commit/4f3b1c6dd6ef6c7b5bb79f058e3b7ba4bcdf4566
7540#if LLVM_VERSION_MAJOR < 16
7541 for (auto v : {ntval, nfval})
7542 if (auto I = dyn_cast<Instruction>(v))
7543 I->setFastMathFlags(cur->getFastMathFlags());
7544#endif
7545
7546 auto res = pushcse(B.CreateSelect(SI->getCondition(), ntval, nfval,
7547 "sfdiv2." + cur->getName()));
7548
7549 replaceAndErase(cur, res);
7550 return "FDivSelectProp";
7551 }
7552 }
7553 }
7554
7555 // div (mul a:not_sparse, b:is_sparse), c -> mul (div, a, c), b:is_sparse
7556 if (cur->getOpcode() == Instruction::FDiv) {
7557 auto c = cur->getOperand(1);
7558 if (auto z = dyn_cast<BinaryOperator>(cur->getOperand(0))) {
7559 if (z->getOpcode() == Instruction::FMul) {
7560 for (int i = 0; i < 2; i++) {
7561
7562 Value *a = z->getOperand(i);
7563 Value *b = z->getOperand(1 - i);
7564 if (directlySparse(a))
7565 continue;
7566 if (!directlySparse(b))
7567 continue;
7568
7569 Value *inner_fdiv = pushcse(B.CreateFDivFMF(a, c, cur));
7570 Value *outer_fmul = pushcse(B.CreateFMulFMF(inner_fdiv, b, z));
7571 push(z);
7572 replaceAndErase(cur, outer_fmul);
7573 return "FDivFMulSparseProp";
7574 }
7575 }
7576 }
7577 }
7578
7579 if (cur->getOpcode() == Instruction::FMul)
7580 for (int i = 0; i < 2; i++) {
7581
7582 Value *prelhs = cur->getOperand(i);
7583 Value *b = cur->getOperand(1 - i);
7584
7585 // fmul (fmul x:constant, y):z, b:constant .
7586 if (isa<Constant>(b))
7587 if (auto z = dyn_cast<BinaryOperator>(prelhs)) {
7588 if (z->getOpcode() == Instruction::FMul) {
7589 for (int j = 0; j < 2; j++) {
7590 auto x = z->getOperand(i);
7591 if (!isa<Constant>(x))
7592 continue;
7593 auto y = z->getOperand(1 - i);
7594 Value *inner_fmul = pushcse(B.CreateFMulFMF(x, b, cur));
7595 Value *outer_fmul = pushcse(B.CreateFMulFMF(inner_fmul, y, z));
7596 push(z);
7597 replaceAndErase(cur, outer_fmul);
7598 return "FMulFMulConstantReorder";
7599 }
7600 }
7601 }
7602
7603 auto integralFloat = [](Value *z) {
7604 if (auto C = dyn_cast<ConstantFP>(z)) {
7605 APSInt Tmp(64);
7606 bool isExact = false;
7607 C->getValue().convertToInteger(Tmp, llvm::RoundingMode::TowardZero,
7608 &isExact);
7609 if (isExact || C->isZero()) {
7610 return true;
7611 }
7612 }
7613 return false;
7614 };
7615
7616 // fmul (fmul x:sparse, y):z, b
7617 // 1) If x and y are both sparse, do nothing and let the inner fmul be
7618 // simplified into a single sparse instruction. Thus, we may assume
7619 // y is not sparse.
7620 // 2) if b is sparse, swap it to be fmul (fmul x, b), y so the inner
7621 // sparsity can be simplified.
7622 // 3) otherwise b is not sparse and we should push the sparsity to
7623 // be the outermost value
7624 if (auto z = dyn_cast<BinaryOperator>(prelhs)) {
7625 if (z->getOpcode() == Instruction::FMul) {
7626 for (int j = 0; j < 2; j++) {
7627 auto x = z->getOperand(j);
7628 if (!directlySparse(x))
7629 continue;
7630 auto y = z->getOperand(1 - j);
7631 if (directlySparse(y))
7632 continue;
7633
7634 if (directlySparse(b) || integralFloat(b)) {
7635 push(z);
7636 Value *inner_fmul = pushcse(
7637 B.CreateFMulFMF(x, b, cur, "mulisr." + cur->getName()));
7638 Value *outer_fmul = pushcse(
7639 B.CreateFMulFMF(inner_fmul, y, z, "mulisr." + z->getName()));
7640 replaceAndErase(cur, outer_fmul);
7641 return "FMulFMulSparseReorder";
7642 } else {
7643 push(z);
7644 Value *inner_fmul = pushcse(
7645 B.CreateFMulFMF(y, b, cur, "mulisp." + cur->getName()));
7646 Value *outer_fmul = pushcse(
7647 B.CreateFMulFMF(inner_fmul, x, z, "mulisp." + z->getName()));
7648 replaceAndErase(cur, outer_fmul);
7649 return "FMulFMulSparsePush";
7650 }
7651 }
7652 }
7653 }
7654
7655 /*
7656 auto contains = [](MDNode *MD, Value *V) {
7657 if (!MD)
7658 return false;
7659 for (auto &op : MD->operands()) {
7660 auto V2 = cast<ValueAsMetadata>(op)->getValue();
7661 if (V == V2)
7662 return true;
7663 }
7664 return false;
7665 };
7666
7667 // fmul (sitofp a), b -> select (a == 0), 0 [noprop fmul ( sitofp a), b]
7668 if (true || !contains(hasMetadata(cur, "enzyme_fmulnoprop"), prelhs))
7669 if (auto ext = dyn_cast<CastInst>(prelhs)) {
7670 if (ext->getOpcode() == Instruction::UIToFP ||
7671 ext->getOpcode() == Instruction::SIToFP) {
7672 push(ext);
7673
7674 Value *condition = pushcse(B.CreateICmpEQ(
7675 ext->getOperand(0),
7676 ConstantInt::get(ext->getOperand(0)->getType(), 0),
7677 "mulcsicmp." + cur->getName()));
7678
7679 Value *fmul = pushcse(B.CreateFMulFMF(ext, b, cur));
7680 if (auto I = dyn_cast<Instruction>(fmul)) {
7681 SmallVector<Metadata *, 1> nodes;
7682 if (auto MD = hasMetadata(cur, "enzyme_fmulnoprop")) {
7683 for (auto &M : MD->operands()) {
7684 nodes.push_back(M.get());
7685 }
7686 }
7687 nodes.push_back(ValueAsMetadata::get(ext));
7688 I->setMetadata("enzyme_fmulnoprop",
7689 MDNode::get(I->getContext(), nodes));
7690 }
7691
7692 Value *sel = pushcse(
7693 B.CreateSelect(condition, ConstantFP::get(cur->getType(),
7694 0.0), fmul, "mulcsi." + cur->getName()));
7695
7696 replaceAndErase(cur, sel);
7697 return "FMulSIToFPProp";
7698 }
7699 }
7700 */
7701
7702 // fmul (select c, 0, a), b -> select c, 0 (fmul a, b)
7703 if (auto SI = dyn_cast<SelectInst>(prelhs)) {
7704 auto tvalC = dyn_cast<ConstantFP>(SI->getTrueValue());
7705 auto fvalC = dyn_cast<ConstantFP>(SI->getFalseValue());
7706 if ((tvalC && tvalC->isZero()) || (fvalC && fvalC->isZero())) {
7707 push(SI);
7708 auto ntval =
7709 (tvalC && tvalC->isZero())
7710 ? tvalC
7711 : pushcse(B.CreateFMulFMF(SI->getTrueValue(), b, cur));
7712 auto nfval =
7713 (fvalC && fvalC->isZero())
7714 ? fvalC
7715 : pushcse(B.CreateFMulFMF(SI->getFalseValue(), b, cur));
7716 auto res = pushcse(B.CreateSelect(SI->getCondition(), ntval, nfval,
7717 "mulsi." + cur->getName()));
7718
7719 replaceAndErase(cur, res);
7720 return "FMulSelectProp";
7721 }
7722 }
7723 }
7724
7725 if (auto icmp = dyn_cast<BinaryOperator>(cur)) {
7726 if (icmp->getOpcode() == Instruction::Xor) {
7727 for (int i = 0; i < 2; i++) {
7728 if (auto C = dyn_cast<ConstantInt>(icmp->getOperand(i))) {
7729 // !(cmp a, b) -> inverse(cmp), a, b
7730 if (C->isOne()) {
7731 if (auto scmp = dyn_cast<CmpInst>(icmp->getOperand(1 - i))) {
7732 auto next = pushcse(
7733 B.CreateCmp(scmp->getInversePredicate(), scmp->getOperand(0),
7734 scmp->getOperand(1), "not." + scmp->getName()));
7735 replaceAndErase(cur, next);
7736 return "NotCmp";
7737 }
7738 }
7739 }
7740 }
7741 }
7742 }
7743
7744 // select cmp, (ext tval), (ext fval) -> (cmp & tval) | (!cmp & fval)
7745 if (auto SI = dyn_cast<SelectInst>(cur)) {
7746
7747 Value *trueVal = nullptr;
7748 if (auto C = dyn_cast<ConstantFP>(SI->getTrueValue())) {
7749 if (C->isZero()) {
7750 trueVal = ConstantInt::getFalse(SI->getContext());
7751 }
7752 if (C->isExactlyValue(1.0)) {
7753 trueVal = ConstantInt::getTrue(SI->getContext());
7754 }
7755 }
7756 if (auto ext = dyn_cast<CastInst>(SI->getTrueValue())) {
7757 if (ext->getOperand(0)->getType()->isIntegerTy(1))
7758 trueVal = ext->getOperand(0);
7759 }
7760 Value *falseVal = nullptr;
7761 if (auto C = dyn_cast<ConstantFP>(SI->getFalseValue())) {
7762 if (C->isZero()) {
7763 falseVal = ConstantInt::getFalse(SI->getContext());
7764 }
7765 if (C->isExactlyValue(1.0)) {
7766 falseVal = ConstantInt::getTrue(SI->getContext());
7767 }
7768 }
7769 if (auto ext = dyn_cast<CastInst>(SI->getFalseValue())) {
7770 if (ext->getOperand(0)->getType()->isIntegerTy(1))
7771 falseVal = ext->getOperand(0);
7772 }
7773 if (trueVal && falseVal) {
7774 auto ncmp1 = pushcse(B.CreateAnd(SI->getCondition(), trueVal));
7775 auto notV = pushcse(B.CreateNot(SI->getCondition()));
7776 auto ncmp2 = pushcse(B.CreateAnd(notV, falseVal));
7777 auto ori = pushcse(B.CreateOr(ncmp1, ncmp2));
7778 auto ext = pushcse(B.CreateUIToFP(ori, SI->getType()));
7779 replaceAndErase(cur, ext);
7780 return "SelectI1Ext";
7781 }
7782 }
7783 // select cmp, (i1 tval), (i1 fval) -> (cmp & tval) | (!cmp & fval)
7784 if (cur->getType()->isIntegerTy(1))
7785 if (auto SI = dyn_cast<SelectInst>(cur)) {
7786 auto ncmp1 = pushcse(B.CreateAnd(SI->getCondition(), SI->getTrueValue()));
7787 auto notV = pushcse(B.CreateNot(SI->getCondition()));
7788 auto ncmp2 = pushcse(B.CreateAnd(notV, SI->getFalseValue()));
7789 auto ori = pushcse(B.CreateOr(ncmp1, ncmp2));
7790 replaceAndErase(cur, ori);
7791 return "SelectI1";
7792 }
7793
7794 if (auto PN = dyn_cast<PHINode>(cur)) {
7795 B.SetInsertPoint(getFirstNonPHI(PN->getParent()));
7796 if (SE.isSCEVable(PN->getType())) {
7797 auto S = SE.getSCEV(PN);
7798
7799 bool legal = false;
7800 if (auto SV = dyn_cast<SCEVUnknown>(S)) {
7801 auto val = SV->getValue();
7802 legal |= isa<Constant>(val) || isa<Argument>(val);
7803 if (auto I = dyn_cast<Instruction>(val)) {
7804 auto L = LI.getLoopFor(I->getParent());
7805 if ((!L || L->getCanonicalInductionVariable() != I) && I != PN)
7806 legal = true;
7807 }
7808 }
7809 if (isa<SCEVAddRecExpr>(S)) {
7810 auto L = LI.getLoopFor(PN->getParent());
7811 assert(L);
7812 if (L->getCanonicalInductionVariable() != PN)
7813 legal = true;
7814 }
7815
7816 if (legal) {
7817 for (auto U : cur->users()) {
7818 push(U);
7819 }
7820 auto point = getFirstNonPHI(PN->getParent());
7821 auto tmp = cast<PHINode>(pushcse(B.CreatePHI(cur->getType(), 1)));
7822 cur->replaceAllUsesWith(tmp);
7823 cur->eraseFromParent();
7824
7825 Value *newIV = nullptr;
7826 {
7827#if LLVM_VERSION_MAJOR >= 22
7828 SCEVExpander Exp(SE, "sparseenzyme");
7829#else
7830 SCEVExpander Exp(SE, DL, "sparseenzyme");
7831#endif
7832 // We place that at first non phi as it may produce a non-phi
7833 // instruction and must thus be expanded after all phi's
7834 newIV = Exp.expandCodeFor(S, tmp->getType(), point);
7835 // sadly this doesn't exist on 11
7836 for (auto I : Exp.getAllInsertedInstructions())
7837 Q.insert(I);
7838 }
7839
7840 tmp->replaceAllUsesWith(newIV);
7841 tmp->eraseFromParent();
7842 return "InductVarSCEV";
7843 }
7844 }
7845 // phi a, a -> a
7846 {
7847 bool legal = true;
7848 for (size_t i = 1; i < PN->getNumIncomingValues(); i++) {
7849 auto v = PN->getIncomingValue(i);
7850 if (v != PN->getIncomingValue(0)) {
7851 legal = false;
7852 break;
7853 }
7854 }
7855 if (legal) {
7856 auto val = PN->getIncomingValue(0);
7857 replaceAndErase(cur, val);
7858 return "PhiMerge";
7859 }
7860 }
7861 // phi (idx=0) ? b, a, a -> select (idx == 0), b, a
7862 if (auto L = LI.getLoopFor(PN->getParent()))
7863 if (L->getHeader() == PN->getParent())
7864 if (auto idx = L->getCanonicalInductionVariable())
7865 if (auto PH = L->getLoopPreheader()) {
7866 bool legal = idx != PN;
7867 auto ph_idx = PN->getBasicBlockIndex(PH);
7868 assert(ph_idx >= 0);
7869 for (size_t i = 0; i < PN->getNumIncomingValues(); i++) {
7870 if ((int)i == ph_idx)
7871 continue;
7872 auto v = PN->getIncomingValue(i);
7873 if (v != PN->getIncomingValue(1 - ph_idx)) {
7874 legal = false;
7875 break;
7876 }
7877 // The given var must dominate the loop
7878 if (isa<Constant>(v))
7879 continue;
7880 if (isa<Argument>(v))
7881 continue;
7882 // exception for the induction itself, which we handle specially
7883 if (v == idx)
7884 continue;
7885 auto I = cast<Instruction>(v);
7886 if (!DT.dominates(I, PN)) {
7887 legal = false;
7888 break;
7889 }
7890 }
7891 if (legal) {
7892 auto val = PN->getIncomingValue(1 - ph_idx);
7893 push(val);
7894 if (val == idx) {
7895 val = pushcse(
7896 B.CreateSub(idx, ConstantInt::get(idx->getType(), 1)));
7897 }
7898
7899 auto val2 = PN->getIncomingValue(ph_idx);
7900 push(val2);
7901
7902 auto c0 = ConstantInt::get(idx->getType(), 0);
7903 // if (val2 == c0 && PN->getIncomingValue(1 - ph_idx) == idx) {
7904 // val = B.CreateBinaryIntrinsic(Intrinsic::umax, c0, val);
7905 //} else {
7906 auto eq = pushcse(B.CreateICmpEQ(idx, c0));
7907 val = pushcse(
7908 B.CreateSelect(eq, val2, val, "phisel." + cur->getName()));
7909 //}
7910
7911 replaceAndErase(cur, val);
7912 return "PhiLoop0Sel";
7913 }
7914 }
7915 // phi (sitofp a), (sitofp b) -> sitofp (phi a, b)
7916 {
7917 SmallVector<Value *, 1> negOps;
7918 SmallVector<Instruction *, 1> prevNegOps;
7919 bool legal = true;
7920 for (size_t i = 0; i < PN->getNumIncomingValues(); i++) {
7921 auto v = PN->getIncomingValue(i);
7922 if (auto C = dyn_cast<ConstantFP>(v)) {
7923 APSInt Tmp(64);
7924 bool isExact = false;
7925 C->getValue().convertToInteger(Tmp, llvm::RoundingMode::TowardZero,
7926 &isExact);
7927 if (isExact || C->isZero()) {
7928 negOps.push_back(ConstantInt::get(B.getInt64Ty(), Tmp));
7929 continue;
7930 }
7931 }
7932 if (auto fneg = dyn_cast<Instruction>(v)) {
7933 if (fneg->getOpcode() == Instruction::SIToFP &&
7934 cast<IntegerType>(fneg->getOperand(0)->getType())
7935 ->getBitWidth() == 64) {
7936 negOps.push_back(fneg->getOperand(0));
7937 prevNegOps.push_back(fneg);
7938 continue;
7939 }
7940 }
7941 legal = false;
7942 }
7943 if (legal) {
7944 auto PN2 = cast<PHINode>(
7945 pushcse(B.CreatePHI(B.getInt64Ty(), PN->getNumIncomingValues())));
7946 PN2->takeName(PN);
7947 for (auto val : llvm::enumerate(negOps))
7948 PN2->addIncoming(val.value(), PN->getIncomingBlock(val.index()));
7949
7950 push(PN2);
7951
7952 auto fneg = pushcse(B.CreateSIToFP(PN2, PN->getType()));
7953
7954 for (auto I : prevNegOps)
7955 push(I);
7956 replaceAndErase(cur, fneg);
7957 return "PhiSIToFP";
7958 }
7959 }
7960 // phi (fneg a), (fneg b) -> fneg (phi a, b)
7961 {
7962 SmallVector<Value *, 1> negOps;
7963 SmallVector<Instruction *, 1> prevNegOps;
7964 bool legal = true;
7965 bool hasNeg = false;
7966 for (size_t i = 0; i < PN->getNumIncomingValues(); i++) {
7967 auto v = PN->getIncomingValue(i);
7968 if (auto C = dyn_cast<ConstantFP>(v)) {
7969 negOps.push_back(C->isZero() ? C : pushcse(B.CreateFNeg(C)));
7970 continue;
7971 }
7972 if (auto fneg = dyn_cast<Instruction>(v)) {
7973 if (fneg->getOpcode() == Instruction::FNeg) {
7974 negOps.push_back(fneg->getOperand(0));
7975 prevNegOps.push_back(fneg);
7976 continue;
7977 }
7978 }
7979 legal = false;
7980 }
7981 if (legal && hasNeg) {
7982 for (auto val : llvm::enumerate(negOps))
7983 PN->setIncomingValue(val.index(), val.value());
7984
7985 push(PN);
7986
7987 auto fneg = pushcse(B.CreateFNeg(PN));
7988
7989 for (auto &U : cur->uses()) {
7990 if (U.getUser() == fneg)
7991 continue;
7992 push(U.getUser());
7993 U.set(fneg);
7994 }
7995 for (auto I : prevNegOps)
7996 push(I);
7997 return "PhiFNeg";
7998 }
7999 }
8000 // phi (neg a), (neg b) -> neg (phi a, b)
8001 {
8002 SmallVector<Value *, 1> negOps;
8003 SmallVector<Instruction *, 1> prevNegOps;
8004 bool legal = true;
8005 bool hasNeg = false;
8006 for (size_t i = 0; i < PN->getNumIncomingValues(); i++) {
8007 auto v = PN->getIncomingValue(i);
8008 if (auto C = dyn_cast<ConstantInt>(v)) {
8009 negOps.push_back(pushcse(B.CreateNeg(C)));
8010 continue;
8011 }
8012 if (auto fneg = dyn_cast<BinaryOperator>(v)) {
8013 if (auto CI = dyn_cast<ConstantInt>(fneg->getOperand(0)))
8014 if (fneg->getOpcode() == Instruction::Sub && CI->isZero()) {
8015 negOps.push_back(fneg->getOperand(1));
8016 prevNegOps.push_back(fneg);
8017 hasNeg = true;
8018 continue;
8019 }
8020 }
8021 legal = false;
8022 }
8023 if (legal && hasNeg) {
8024 for (auto val : llvm::enumerate(negOps))
8025 PN->setIncomingValue(val.index(), val.value());
8026
8027 push(PN);
8028
8029 auto fneg = pushcse(B.CreateNeg(PN));
8030
8031 for (auto &U : cur->uses()) {
8032 if (U.getUser() == fneg)
8033 continue;
8034 push(U.getUser());
8035 U.set(fneg);
8036 }
8037 for (auto I : prevNegOps)
8038 push(I);
8039 return "PHINeg";
8040 }
8041 }
8042 // p = phi (mul a, c), (mul b, d) -> mul (phi a, b), (phi c, d) if
8043 // a,b,c != p
8044 {
8045 for (auto code :
8046 {(unsigned)Instruction::Mul, (unsigned)Instruction::Sub,
8047 (unsigned)Instruction::Add, (unsigned)Instruction::ZExt,
8048 (unsigned)Instruction::UIToFP, (unsigned)Instruction::ICmp,
8049 (unsigned)Instruction::FMul, (unsigned)Instruction::Or,
8050 (unsigned)Instruction::And}) {
8051 SmallVector<Value *, 1> lhsOps;
8052 SmallVector<Value *, 1> rhsOps;
8053 SmallVector<Instruction *, 1> prevOps;
8054 bool legal = true;
8055 bool fast = false;
8056 bool NUW = false;
8057 bool NSW = false;
8058 size_t numOps = 0;
8059 std::optional<llvm::CmpInst::Predicate> cmpPredicate;
8060 switch (code) {
8061 case Instruction::FMul:
8062 case Instruction::FSub:
8063 case Instruction::FAdd:
8064 fast = true;
8065 numOps = 2;
8066 break;
8067 case Instruction::Mul:
8068 case Instruction::Add:
8069 NUW = NSW = true;
8070 numOps = 2;
8071 break;
8072 case Instruction::Sub:
8073 NSW = true;
8074 numOps = 2;
8075 break;
8076 case Instruction::ICmp:
8077 case Instruction::FCmp:
8078 case Instruction::Or:
8079 case Instruction::And:
8080 numOps = 2;
8081 break;
8082 case Instruction::ZExt:
8083 case Instruction::UIToFP:
8084 numOps = 1;
8085 break;
8086 default:;
8087 llvm_unreachable("unknown opcode");
8088 }
8089 bool changed = false;
8090 for (size_t i = 0; i < PN->getNumIncomingValues(); i++) {
8091 auto v = PN->getIncomingValue(i);
8092 if (auto C = dyn_cast<ConstantInt>(v)) {
8093 if (code == Instruction::ZExt) {
8094 lhsOps.push_back(ConstantInt::getFalse(C->getContext()));
8095 continue;
8096 } else if (C->isZero()) {
8097 rhsOps.push_back(C);
8098 lhsOps.push_back(C);
8099 continue;
8100 }
8101 }
8102 if (auto C = dyn_cast<ConstantFP>(v)) {
8103 if (code == Instruction::UIToFP) {
8104 if (C->isZero()) {
8105 lhsOps.push_back(ConstantInt::getFalse(C->getContext()));
8106 }
8107 } else if (code == Instruction::FMul || code == Instruction::FSub ||
8108 code == Instruction::FAdd) {
8109 if (C->isZero()) {
8110 rhsOps.push_back(C);
8111 lhsOps.push_back(C);
8112 continue;
8113 }
8114 }
8115 }
8116 if (auto fneg = dyn_cast<Instruction>(v)) {
8117 if (fneg->getOpcode() == code) {
8118 switch (code) {
8119 case Instruction::FMul:
8120 case Instruction::FSub:
8121 case Instruction::FAdd:
8122 fast &= fneg->isFast();
8123 if (fneg->getOperand(0) == PN)
8124 legal = false;
8125 if (fneg->getOperand(1) == PN)
8126 legal = false;
8127 lhsOps.push_back(fneg->getOperand(0));
8128 rhsOps.push_back(fneg->getOperand(1));
8129 break;
8130 case Instruction::Mul:
8131 case Instruction::Sub:
8132 case Instruction::Add:
8133 NUW &= fneg->hasNoUnsignedWrap();
8134 NSW &= fneg->hasNoSignedWrap();
8135 if (fneg->getOperand(0) == PN)
8136 legal = false;
8137 if (fneg->getOperand(1) == PN)
8138 legal = false;
8139 lhsOps.push_back(fneg->getOperand(0));
8140 rhsOps.push_back(fneg->getOperand(1));
8141 break;
8142 case Instruction::Or:
8143 case Instruction::And:
8144 if (fneg->getOperand(0) == PN)
8145 legal = false;
8146 if (fneg->getOperand(1) == PN)
8147 legal = false;
8148 lhsOps.push_back(fneg->getOperand(0));
8149 rhsOps.push_back(fneg->getOperand(1));
8150 break;
8151 case Instruction::ICmp:
8152 case Instruction::FCmp:
8153 if (fneg->getOperand(0) == PN)
8154 legal = false;
8155 if (fneg->getOperand(1) == PN)
8156 legal = false;
8157 if (cmpPredicate) {
8158 if (*cmpPredicate != cast<CmpInst>(fneg)->getPredicate())
8159 legal = false;
8160 } else {
8161 cmpPredicate = cast<CmpInst>(fneg)->getPredicate();
8162 }
8163 lhsOps.push_back(fneg->getOperand(0));
8164 rhsOps.push_back(fneg->getOperand(1));
8165 break;
8166 case Instruction::ZExt:
8167 case Instruction::UIToFP:
8168 if (cast<IntegerType>(fneg->getOperand(0)->getType())
8169 ->getBitWidth() != 1)
8170 legal = false;
8171 lhsOps.push_back(fneg->getOperand(0));
8172 break;
8173 default:
8174 llvm_unreachable("unhandled opcode");
8175 }
8176 prevOps.push_back(fneg);
8177 changed = true;
8178 continue;
8179 }
8180 }
8181 legal = false;
8182 }
8183
8184 int preheader_fix = -1;
8185
8186 if (code == Instruction::ICmp || code == Instruction::FCmp) {
8187 if (!cmpPredicate)
8188 legal = false;
8189 auto L = LI.getLoopFor(PN->getParent());
8190 if (legal && L && L->getLoopPreheader() &&
8191 L->getCanonicalInductionVariable() &&
8192 L->getHeader() == PN->getParent()) {
8193 auto ph_idx = PN->getBasicBlockIndex(L->getLoopPreheader());
8194 if (isa<ConstantInt>(PN->getIncomingValue(ph_idx))) {
8195 lhsOps[ph_idx] =
8196 Constant::getNullValue(lhsOps[1 - ph_idx]->getType());
8197 rhsOps[ph_idx] =
8198 Constant::getNullValue(rhsOps[1 - ph_idx]->getType());
8199 preheader_fix = ph_idx;
8200 }
8201 }
8202 for (auto v : lhsOps)
8203 if (v->getType() != lhsOps[0]->getType())
8204 legal = false;
8205 for (auto v : rhsOps)
8206 if (v->getType() != rhsOps[0]->getType())
8207 legal = false;
8208 }
8209
8210 if (legal && changed) {
8211 auto lhsPN = cast<PHINode>(pushcse(
8212 B.CreatePHI(lhsOps[0]->getType(), PN->getNumIncomingValues())));
8213 PHINode *rhsPN = nullptr;
8214 if (numOps == 2)
8215 rhsPN = cast<PHINode>(pushcse(
8216 B.CreatePHI(rhsOps[0]->getType(), PN->getNumIncomingValues())));
8217
8218 for (auto val : llvm::enumerate(lhsOps))
8219 lhsPN->addIncoming(val.value(), PN->getIncomingBlock(val.index()));
8220
8221 if (numOps == 2) {
8222 for (auto val : llvm::enumerate(rhsOps))
8223 rhsPN->addIncoming(val.value(),
8224 PN->getIncomingBlock(val.index()));
8225 }
8226
8227 Value *fneg = nullptr;
8228 switch (code) {
8229 case Instruction::FMul:
8230 fneg = B.CreateFMul(lhsPN, rhsPN);
8231 if (auto I = dyn_cast<Instruction>(fneg))
8232 I->setFast(fast);
8233 break;
8234 case Instruction::FAdd:
8235 fneg = B.CreateFAdd(lhsPN, rhsPN);
8236 if (auto I = dyn_cast<Instruction>(fneg))
8237 I->setFast(fast);
8238 break;
8239 case Instruction::FSub:
8240 fneg = B.CreateFSub(lhsPN, rhsPN);
8241 if (auto I = dyn_cast<Instruction>(fneg))
8242 I->setFast(fast);
8243 break;
8244 case Instruction::Mul:
8245 fneg = B.CreateMul(lhsPN, rhsPN, "", NUW, NSW);
8246 break;
8247 case Instruction::Add:
8248 fneg = B.CreateAdd(lhsPN, rhsPN, "", NUW, NSW);
8249 break;
8250 case Instruction::Sub:
8251 fneg = B.CreateSub(lhsPN, rhsPN, "", NUW, NSW);
8252 break;
8253 case Instruction::ZExt:
8254 fneg = B.CreateZExt(lhsPN, PN->getType());
8255 break;
8256 case Instruction::FCmp:
8257 case Instruction::ICmp:
8258 fneg = B.CreateCmp(*cmpPredicate, lhsPN, rhsPN);
8259 break;
8260 case Instruction::UIToFP:
8261 fneg = B.CreateUIToFP(lhsPN, PN->getType());
8262 break;
8263 case Instruction::Or:
8264 fneg = B.CreateOr(lhsPN, rhsPN);
8265 break;
8266 case Instruction::And:
8267 fneg = B.CreateAnd(lhsPN, rhsPN);
8268 break;
8269 default:
8270 llvm_unreachable("unhandled opcode");
8271 }
8272
8273 push(fneg);
8274
8275 if (preheader_fix != -1) {
8276 auto L = LI.getLoopFor(PN->getParent());
8277 auto idx = L->getCanonicalInductionVariable();
8278 auto eq = pushcse(
8279 B.CreateICmpEQ(idx, ConstantInt::get(idx->getType(), 0)));
8280 fneg =
8281 pushcse(B.CreateSelect(eq, PN->getIncomingValue(preheader_fix),
8282 fneg, "phphisel." + cur->getName()));
8283 }
8284
8285 replaceAndErase(cur, fneg);
8286 return "PHIBinop";
8287 }
8288 }
8289 }
8290 // phi -> select
8291 if (PN->getNumIncomingValues() == 2) {
8292 for (int i = 0; i < 2; i++) {
8293 auto prev = PN->getIncomingBlock(i);
8294 if (!DT.dominates(prev, PN->getParent())) {
8295 continue;
8296 }
8297 auto br = (isAnyBranch(prev->getTerminator())
8298 ? cast<Instruction>(prev->getTerminator())
8299 : nullptr);
8300 if (!br) {
8301 continue;
8302 }
8303 if (!isConditionalBranch(br)) {
8304 continue;
8305 }
8306 if (br->getSuccessor(0) != PN->getParent()) {
8307 continue;
8308 }
8309 if (br->getSuccessor(1) != PN->getIncomingBlock(1 - i)) {
8310 continue;
8311 }
8312
8313 Value *specVal = PN->getIncomingValue(1 - i);
8314 SetVector<Value *, std::deque<Value *>> todo;
8315 todo.insert(specVal);
8316 SetVector<Instruction *> toMove;
8317 bool legal = true;
8318 while (!todo.empty()) {
8319 auto cur = *todo.begin();
8320 todo.erase(todo.begin());
8321 auto I = dyn_cast<Instruction>(cur);
8322 if (!I)
8323 continue;
8324 if (I->mayReadOrWriteMemory()) {
8325 legal = false;
8326 break;
8327 }
8328 if (DT.dominates(I, PN))
8329 continue;
8330 for (size_t i = 0; i < I->getNumOperands(); i++)
8331 todo.insert(I->getOperand(i));
8332 toMove.insert(I);
8333 }
8334 if (!legal)
8335 continue;
8336 for (auto iter = toMove.rbegin(), end = toMove.rend(); iter != end;
8337 iter++) {
8338 moveBeforeInst(*iter, br);
8339 }
8340 auto sel = pushcse(B.CreateSelect(
8341 getBranchCondition(br), PN->getIncomingValueForBlock(prev),
8342 PN->getIncomingValueForBlock(br->getSuccessor(1)),
8343 "tphisel." + cur->getName()));
8344
8345 replaceAndErase(cur, sel);
8346 return "TPhiSel";
8347 }
8348 }
8349 }
8350
8351 if (auto SI = dyn_cast<SelectInst>(cur)) {
8352 auto tval = replace(SI->getTrueValue(), SI->getCondition(),
8353 ConstantInt::getTrue(SI->getContext()));
8354 auto fval = replace(SI->getFalseValue(), SI->getCondition(),
8355 ConstantInt::getFalse(SI->getContext()));
8356 if (tval != SI->getTrueValue() || fval != SI->getFalseValue()) {
8357 auto res = pushcse(B.CreateSelect(SI->getCondition(), tval, fval,
8358 "postsel." + SI->getName()));
8359 replaceAndErase(cur, res);
8360 return "SelectReplace";
8361 }
8362 }
8363
8364 // and a, b -> and a b[with a true]
8365 if (cur->getOpcode() == Instruction::And) {
8366 auto lhs = replace(cur->getOperand(0), cur->getOperand(1),
8367 ConstantInt::getTrue(cur->getContext()));
8368 if (lhs != cur->getOperand(0)) {
8369 auto res = pushcse(
8370 B.CreateAnd(lhs, cur->getOperand(1), "postand." + cur->getName()));
8371 replaceAndErase(cur, res);
8372 return "AndReplaceLHS";
8373 }
8374 auto rhs = replace(cur->getOperand(1), cur->getOperand(0),
8375 ConstantInt::getTrue(cur->getContext()));
8376 if (rhs != cur->getOperand(1)) {
8377 auto res = pushcse(
8378 B.CreateAnd(cur->getOperand(0), rhs, "postand." + cur->getName()));
8379 replaceAndErase(cur, res);
8380 return "AndReplaceRHS";
8381 }
8382 }
8383
8384 // or a, b -> or a b[with a false]
8385 if (cur->getOpcode() == Instruction::Or) {
8386 auto lhs = replace(cur->getOperand(0), cur->getOperand(1),
8387 ConstantInt::getFalse(cur->getContext()));
8388 if (lhs != cur->getOperand(0)) {
8389 auto res = pushcse(
8390 B.CreateOr(lhs, cur->getOperand(1), "postor." + cur->getName()));
8391 replaceAndErase(cur, res);
8392 return "OrReplaceLHS";
8393 }
8394 auto rhs = replace(cur->getOperand(1), cur->getOperand(0),
8395 ConstantInt::getFalse(cur->getContext()));
8396 if (rhs != cur->getOperand(1)) {
8397 auto res = pushcse(
8398 B.CreateOr(cur->getOperand(0), rhs, "postor." + cur->getName()));
8399 replaceAndErase(cur, res);
8400 return "OrReplaceRHS";
8401 }
8402 }
8403 return {};
8404}
8405
8406class Constraints;
8407raw_ostream &operator<<(raw_ostream &os, const Constraints &c);
8408
8410 bool operator()(std::shared_ptr<const Constraints> lhs,
8411 std::shared_ptr<const Constraints> rhs) const;
8412};
8413
8415 ScalarEvolution &SE;
8416 const Loop *loopToSolve;
8417 const SmallVectorImpl<Instruction *> &Assumptions;
8418 DominatorTree &DT;
8419 using InnerTy = std::shared_ptr<const Constraints>;
8420 using SetTy = std::set<InnerTy, ConstraintComparator>;
8422 ConstraintContext(ScalarEvolution &SE, const Loop *loopToSolve,
8423 const SmallVectorImpl<Instruction *> &Assumptions,
8424 DominatorTree &DT)
8426 assert(loopToSolve);
8427 }
8431 DT(ctx.DT), seen(ctx.seen) {
8432 seen.insert(lhs);
8433 }
8436 DT(ctx.DT), seen(ctx.seen) {
8437 seen.insert(lhs);
8438 seen.insert(rhs);
8439 }
8440 bool contains(InnerTy x) const { return seen.count(x) != 0; }
8441};
8442
8443bool cannotDependOnLoopIV(const SCEV *S, const Loop *L) {
8444 assert(L);
8445 if (isa<SCEVConstant>(S))
8446 return true;
8447 if (auto M = dyn_cast<SCEVAddExpr>(S)) {
8448 for (auto o : M->operands())
8449 if (!cannotDependOnLoopIV(o, L))
8450 return false;
8451 return true;
8452 }
8453 if (auto M = dyn_cast<SCEVMulExpr>(S)) {
8454 for (auto o : M->operands())
8455 if (!cannotDependOnLoopIV(o, L))
8456 return false;
8457 return true;
8458 }
8459 if (auto M = dyn_cast<SCEVUDivExpr>(S)) {
8460 for (auto o : {M->getLHS(), M->getRHS()})
8461 if (!cannotDependOnLoopIV(o, L))
8462 return false;
8463 return true;
8464 }
8465 if (auto UV = dyn_cast<SCEVUnknown>(S)) {
8466 auto U = UV->getValue();
8467 if (isa<Argument>(U))
8468 return true;
8469 if (isa<Constant>(U))
8470 return true;
8471 auto I = cast<Instruction>(U);
8472 return !L->contains(I->getParent());
8473 }
8474 if (auto addrec = dyn_cast<SCEVAddRecExpr>(S)) {
8475 if (addrec->getLoop() == L)
8476 return false;
8477 for (auto o : addrec->operands())
8478 if (!cannotDependOnLoopIV(o, L))
8479 return false;
8480 return true;
8481 }
8482 if (auto expr = dyn_cast<SCEVSignExtendExpr>(S)) {
8483 return cannotDependOnLoopIV(expr->getOperand(), L);
8484 }
8485 llvm::errs() << " cannot tell if depends on loop iv: " << *S << "\n";
8486 return false;
8487}
8488
8489const SCEV *evaluateAtLoopIter(const SCEV *V, ScalarEvolution &SE,
8490 const Loop *find, const SCEV *replace) {
8491 assert(find);
8492 if (cannotDependOnLoopIV(V, find))
8493 return V;
8494 if (auto addrec = dyn_cast<SCEVAddRecExpr>(V)) {
8495 if (addrec->getLoop() == find) {
8496 auto V2 = evaluateAtIterationWithoutExt(addrec, replace, SE);
8497 return evaluateAtLoopIter(V2, SE, find, replace);
8498 }
8499 }
8500 if (auto div = dyn_cast<SCEVUDivExpr>(V)) {
8501 auto lhs = evaluateAtLoopIter(div->getLHS(), SE, find, replace);
8502 if (!lhs)
8503 return nullptr;
8504 auto rhs = evaluateAtLoopIter(div->getRHS(), SE, find, replace);
8505 if (!rhs)
8506 return nullptr;
8507 return SE.getUDivExpr(lhs, rhs);
8508 }
8509 return nullptr;
8510}
8511
8512class Constraints : public std::enable_shared_from_this<Constraints> {
8513public:
8514 const enum class Type {
8515 Union = 0,
8516 Intersect = 1,
8517 Compare = 2,
8518 All = 3,
8519 None = 4
8521
8522 using InnerTy = std::shared_ptr<const Constraints>;
8523
8524 using SetTy = std::set<InnerTy, ConstraintComparator>;
8525
8527
8528 const SCEV *const node;
8529 // whether equal to the node, or not equal to the node
8531 // the loop of the iv comparing against.
8532 const llvm::Loop *const Loop;
8533 // using SetTy = SmallVector<InnerTy, 0>;
8534 // using SetTy = SetVector<InnerTy, SmallVector<InnerTy, 0>,
8535 // std::set<InnerTy>>;
8536
8538 : ty(Type::Union), values(), node(nullptr), isEqual(false),
8539 Loop(nullptr) {}
8540
8541private:
8542 Constraints(const SCEV *v, bool isEqual, const llvm::Loop *Loop, bool)
8543 : ty(Type::Compare), values(), node(v), isEqual(isEqual), Loop(Loop) {}
8544
8545public:
8546 static InnerTy make_compare(const SCEV *v, bool isEqual,
8547 const llvm::Loop *Loop,
8548 const ConstraintContext &ctx);
8549
8551 : ty(t), values(), node(nullptr), isEqual(false), Loop(nullptr) {
8552 assert(t == Type::All || t == Type::None);
8553 }
8554 Constraints(Type t, const SetTy &c, bool check = true)
8555 : ty(t), values(c), node(nullptr), isEqual(false), Loop(nullptr) {
8556 assert(t != Type::All);
8557 assert(t != Type::None);
8558 assert(c.size() != 0);
8559 assert(c.size() != 1);
8560#ifndef NDEBUG
8561 SmallVector<InnerTy, 1> tmp(c.begin(), c.end());
8562 for (unsigned i = 0; i < tmp.size(); i++)
8563 for (unsigned j = 0; j < i; j++)
8564 assert(*tmp[i] != *tmp[j]);
8565 if (t == Type::Intersect) {
8566 for (auto &v : c) {
8567 assert(v->ty != Type::Intersect);
8568 }
8569 }
8570 if (t == Type::Union) {
8571 for (auto &v : c) {
8572 assert(v->ty != Type::Union);
8573 }
8574 }
8575 if (t == Type::Intersect && check) {
8576 for (unsigned i = 0; i < tmp.size(); i++)
8577 if (tmp[i]->ty == Type::Compare && tmp[i]->isEqual && tmp[i]->Loop)
8578 for (unsigned j = 0; j < tmp.size(); j++)
8579 if (tmp[j]->ty == Type::Compare)
8580 if (auto s = dyn_cast<SCEVAddRecExpr>(tmp[j]->node))
8581 assert(s->getLoop() != tmp[i]->Loop);
8582 }
8583#endif
8584 }
8585
8586 bool operator==(const Constraints &rhs) const {
8587 if (ty != rhs.ty) {
8588 return false;
8589 }
8590 if (node != rhs.node) {
8591 return false;
8592 }
8593 if (isEqual != rhs.isEqual) {
8594 return false;
8595 }
8596 if (Loop != rhs.Loop) {
8597 return false;
8598 }
8599 if (values.size() != rhs.values.size()) {
8600 return false;
8601 }
8602 for (auto pair : llvm::zip(values, rhs.values)) {
8603 if (*std::get<0>(pair) != *std::get<1>(pair))
8604 return false;
8605 }
8606 return true;
8607 //) && !(rhs.values < values)
8608 /*
8609for (size_t i=0; i<values.size(); i++)
8610if (*values[i] != *rhs.values[i]) return false;
8611return true;
8612 */
8613 }
8614 bool operator>(const Constraints &rhs) const { return rhs < *this; }
8615 bool operator<(const Constraints &rhs) const {
8616 if (ty < rhs.ty) {
8617 return true;
8618 }
8619 if (ty > rhs.ty) {
8620 return false;
8621 }
8622 if (node < rhs.node) {
8623 return true;
8624 }
8625 if (node > rhs.node) {
8626 return false;
8627 }
8628 if (isEqual < rhs.isEqual) {
8629 return true;
8630 }
8631 if (isEqual > rhs.isEqual) {
8632 return false;
8633 }
8634 if (Loop < rhs.Loop) {
8635 return true;
8636 }
8637 if (Loop > rhs.Loop) {
8638 return false;
8639 }
8640 if (values.size() < rhs.values.size()) {
8641 return true;
8642 }
8643 if (values.size() > rhs.values.size()) {
8644 return false;
8645 }
8646 for (auto pair : llvm::zip(values, rhs.values)) {
8647 if (*std::get<0>(pair) < *std::get<1>(pair))
8648 return true;
8649 if (*std::get<0>(pair) > *std::get<1>(pair))
8650 return false;
8651 }
8652 return false;
8653 }
8654 unsigned hash() const {
8655 unsigned res = 5 * (unsigned)ty +
8656 DenseMapInfo<const SCEV *>::getHashValue(node) + isEqual;
8657 res = llvm::detail::combineHashValue(res, (unsigned)(size_t)Loop);
8658 for (auto v : values)
8659 res = llvm::detail::combineHashValue(res, v->hash());
8660 return res;
8661 }
8662 bool operator!=(const Constraints &rhs) const { return !(*this == rhs); }
8663 static InnerTy all() {
8664 static auto allv = std::make_shared<Constraints>(Type::All);
8665 return allv;
8666 }
8667 static InnerTy none() {
8668 static auto nonev = std::make_shared<Constraints>(Type::None);
8669 return nonev;
8670 }
8671 bool isNone() const { return ty == Type::None; }
8672 bool isAll() const { return ty == Type::All; }
8673 static void insert(SetTy &set, InnerTy ty) {
8674 set.insert(ty);
8675 int mcount = 0;
8676 for (auto &v : set)
8677 if (*v == *ty)
8678 mcount++;
8679 assert(mcount == 1);
8680 /*
8681 for (auto &v : set)
8682 if (*v == *ty)
8683 return;
8684 set.push_back(ty);
8685 */
8686 }
8687 static SetTy intersect(const SetTy &lhs, const SetTy &rhs) {
8688 SetTy res;
8689 for (auto &v : lhs)
8690 if (rhs.count(v))
8691 res.insert(v);
8692 return res;
8693 }
8694 static void set_subtract(SetTy &set, const SetTy &rhs) {
8695 for (auto &v : rhs)
8696 if (set.count(v))
8697 set.erase(v);
8698 /*
8699 for (const auto &val : rhs)
8700 for (auto I = set.begin(); I != set.end(); I++) {
8701 if (**I == *val) {
8702 set.erase(I);
8703 break;
8704 }
8705 }
8706*/
8707 }
8708 __attribute__((noinline)) void dump() const { llvm::errs() << *this << "\n"; }
8709 InnerTy notB(const ConstraintContext &ctx) const {
8710 switch (ty) {
8711 case Type::None:
8712 return Constraints::all();
8713 case Type::All:
8714 return Constraints::none();
8715 case Type::Compare:
8716 return make_compare(node, !isEqual, Loop, ctx);
8717 case Type::Union: {
8718 // not of or's is and of not's
8719 SetTy next;
8720 for (const auto &v : values)
8721 insert(next, v->notB(ctx));
8722 if (next.size() == 1)
8723 llvm::errs() << " uold : " << *this << "\n";
8724 return std::make_shared<Constraints>(Type::Intersect, next);
8725 }
8726 case Type::Intersect: {
8727 // not of and's is or of not's
8728 SetTy next;
8729 for (const auto &v : values)
8730 insert(next, v->notB(ctx));
8731 if (next.size() == 1)
8732 llvm::errs() << " old : " << *this << "\n";
8733 return std::make_shared<Constraints>(Type::Union, next);
8734 }
8735 }
8736 return Constraints::none();
8737 }
8738 InnerTy orB(InnerTy rhs, const ConstraintContext &ctx) const {
8739 auto notLHS = notB(ctx);
8740 if (!notLHS)
8741 return nullptr;
8742 auto notRHS = rhs->notB(ctx);
8743 if (!notRHS)
8744 return nullptr;
8745 auto andV = notLHS->andB(notRHS, ctx);
8746 if (!andV)
8747 return nullptr;
8748 auto res = andV->notB(ctx);
8749 return res;
8750 }
8751 InnerTy andB(const InnerTy rhs, const ConstraintContext &ctx) const {
8752 assert(rhs);
8753 if (*rhs == *this)
8754 return shared_from_this();
8755 if (rhs->isNone())
8756 return rhs;
8757 if (rhs->isAll())
8758 return shared_from_this();
8759 if (isNone())
8760 return shared_from_this();
8761 if (isAll())
8762 return rhs;
8763
8764 // llvm::errs() << " anding: " << *this << " with " << *rhs << "\n";
8765 if (ctx.contains(shared_from_this()) || ctx.contains(rhs)) {
8766 // llvm::errs() << " %%% stopping recursion\n";
8767 return nullptr;
8768 }
8769 if (ty == Type::Compare && rhs->ty == Type::Compare) {
8770 auto sub = ctx.SE.getMinusSCEV(node, rhs->node);
8771 if (Loop == rhs->Loop) {
8772 // llvm::errs() << " + sameloop, sub=" << *sub << "\n";
8773 if (auto cst = dyn_cast<SCEVConstant>(sub)) {
8774 // the two solves are equivalent to each other
8775 if (cst->getValue()->isZero()) {
8776 // iv = a and iv = a
8777 // also iv != a and iv != a
8778 if (isEqual == rhs->isEqual)
8779 return shared_from_this();
8780 else {
8781 // iv = a and iv != a
8782 return Constraints::none();
8783 }
8784 } else {
8785 // the two solves are guaranteed to be distinct
8786 // iv == 0 and iv == 1
8787 if (isEqual && rhs->isEqual) {
8788 return Constraints::none();
8789
8790 } else if (!isEqual && !rhs->isEqual) {
8791 // iv != 0 and iv != 1
8792 SetTy vals;
8793 insert(vals, shared_from_this());
8794 insert(vals, rhs);
8795 return std::make_shared<Constraints>(Type::Intersect, vals);
8796 } else if (!isEqual) {
8797 assert(rhs->isEqual);
8798 // iv != 0 and iv == 1
8799 return rhs;
8800 ;
8801 } else {
8802 // iv == 0 and iv != 1
8803 assert(isEqual);
8804 assert(!rhs->isEqual);
8805 return shared_from_this();
8806 }
8807 }
8808 } else if (isEqual || rhs->isEqual) {
8809 // llvm::errs() << " + botheq\n";
8810 // eq(i, a) & i ?= b -> eq(i, a) & (a ?= b)
8811 if (auto addrec = dyn_cast<SCEVAddRecExpr>(sub)) {
8812 // we want a ?= b, but we can only represent loopvar ?= something
8813 // so suppose a-b is of the form X + Y * lv then a-b ?= 0 is
8814 // X + Y * lv ?= 0 -> lv ?= - X / Y
8815 if (addrec->isAffine()) {
8816 auto X = addrec->getStart();
8817 auto Y = addrec->getStepRecurrence(ctx.SE);
8818 auto MinusX = X;
8819
8820 if (isa<SCEVConstant>(Y) &&
8821 cast<SCEVConstant>(Y)->getAPInt().isNegative())
8822 Y = ctx.SE.getNegativeSCEV(Y);
8823 else
8824 MinusX = ctx.SE.getNegativeSCEV(X);
8825
8826 auto div = ctx.SE.getUDivExpr(MinusX, Y);
8827 auto div_e = ctx.SE.getUDivExactExpr(MinusX, Y);
8828 // in case of inexact division, check that these exactly equal
8829 // for replacement
8830
8831 if (div == div_e) {
8832 if (isEqual) {
8833 auto res = make_compare(div, /*isEqual*/ rhs->isEqual,
8834 addrec->getLoop(), ctx);
8835 // llvm::errs() << " simplified rhs to: " << *res << "\n";
8836 return andB(res, ctx);
8837 } else {
8838 assert(rhs->isEqual);
8839 auto res = make_compare(div, /*isEqual*/ isEqual,
8840 addrec->getLoop(), ctx);
8841 // llvm::errs() << " simplified lhs to: " << *res << "\n";
8842 return rhs->andB(res, ctx);
8843 }
8844 }
8845 }
8846 }
8847 if (isEqual && rhs->Loop &&
8849 auto res = make_compare(sub, /*isEqual*/ rhs->isEqual,
8850 /*loop*/ nullptr, ctx);
8851 // llvm::errs() << " simplified(noloop) rhs from " << *rhs
8852 // << " to: " << *res << "\n";
8853 return andB(res, ctx);
8854 }
8855 if (rhs->isEqual && Loop &&
8857 auto res =
8858 make_compare(sub, /*isEqual*/ isEqual, /*loop*/ nullptr, ctx);
8859 // llvm::errs() << " simplified(noloop) lhs from " << *rhs
8860 // << " to: " << *res << "\n";
8861 return rhs->andB(res, ctx);
8862 }
8863
8864 llvm::errs() << " warning: potential but unhandled simplification of "
8865 "equalities: "
8866 << *this << " and " << *rhs << " sub: " << *sub << "\n";
8867 }
8868 }
8869
8870 if (isEqual) {
8871 if (Loop)
8872 if (auto rep = evaluateAtLoopIter(rhs->node, ctx.SE, Loop, node))
8873 if (rep != rhs->node) {
8874 auto newrhs = make_compare(rep, rhs->isEqual, rhs->Loop, ctx);
8875 return andB(newrhs, ctx);
8876 }
8877
8878 // not loop -> node == 0
8879 if (!Loop) {
8880 for (auto sub1 : {ctx.SE.getMinusSCEV(node, rhs->node),
8881 ctx.SE.getMinusSCEV(rhs->node, node)}) {
8882 // llvm::errs() << " maybe replace lhs: " << *this << " rhs: " <<
8883 // *rhs
8884 // << " sub1: " << *sub1 << "\n";
8885 auto newrhs = make_compare(sub1, rhs->isEqual, rhs->Loop, ctx);
8886 if (*newrhs == *this)
8887 return shared_from_this();
8888 if (!isa<SCEVConstant>(rhs->node) && isa<SCEVConstant>(sub1)) {
8889 return andB(newrhs, ctx);
8890 }
8891 }
8892 }
8893 }
8894
8895 if (rhs->isEqual) {
8896 if (rhs->Loop)
8897 if (auto rep = evaluateAtLoopIter(node, ctx.SE, rhs->Loop, rhs->node))
8898 if (rep != node) {
8899 auto newlhs = make_compare(rep, isEqual, Loop, ctx);
8900 return newlhs->andB(rhs, ctx);
8901 }
8902
8903 // not loop -> node == 0
8904 if (!rhs->Loop) {
8905 for (auto sub1 : {ctx.SE.getMinusSCEV(node, rhs->node),
8906 ctx.SE.getMinusSCEV(rhs->node, node)}) {
8907 // llvm::errs() << " maybe replace lhs2: " << *this << " rhs: " <<
8908 // *rhs
8909 // << " sub1: " << *sub1 << "\n";
8910 auto newlhs = make_compare(sub1, isEqual, Loop, ctx);
8911 if (*newlhs == *this)
8912 return shared_from_this();
8913 if (!isa<SCEVConstant>(node) && isa<SCEVConstant>(sub1)) {
8914 return newlhs->andB(rhs, ctx);
8915 }
8916 }
8917 }
8918 }
8919
8920 if (!Loop && !rhs->Loop && isEqual == rhs->isEqual) {
8921 if (node == ctx.SE.getNegativeSCEV(rhs->node))
8922 return shared_from_this();
8923 }
8924
8925 SetTy vals;
8926 insert(vals, shared_from_this());
8927 insert(vals, rhs);
8928 if (vals.size() == 1) {
8929 llvm::errs() << "this: " << *this << " rhs: " << *rhs << "\n";
8930 }
8931 auto res = std::make_shared<Constraints>(Type::Intersect, vals);
8932 // llvm::errs() << " naiive comp merge: " << *res << "\n";
8933 return res;
8934 }
8935 if (ty == Type::Intersect && rhs->ty == Type::Intersect) {
8936 auto tmp = shared_from_this();
8937 for (const auto &v : rhs->values) {
8938 auto tmp2 = tmp->andB(v, ctx);
8939 if (!tmp2)
8940 return nullptr;
8941 tmp = std::move(tmp2);
8942 }
8943 return tmp;
8944 }
8945 if (ty == Type::Intersect && rhs->ty == Type::Compare) {
8946 SetTy vals;
8947 // Force internal merging to do individual compares
8948 bool foldedIn = false;
8949 for (auto en : llvm::enumerate(values)) {
8950 auto i = en.index();
8951 auto v = en.value();
8952 assert(v->ty != Type::Intersect);
8953 assert(v->ty != Type::All);
8954 assert(v->ty != Type::None);
8955 assert(v->ty == Type::Compare || v->ty == Type::Union);
8956 if (foldedIn) {
8957 insert(vals, v);
8958 continue;
8959 }
8960 // this is either a compare or a union
8961 auto tmp = rhs->andB(v, ctx);
8962 if (!tmp)
8963 return nullptr;
8964 switch (tmp->ty) {
8965 case Type::Union:
8966 case Type::All:
8967 llvm_unreachable("Impossible");
8968 case Type::None:
8969 return Constraints::none();
8970 case Type::Compare:
8971 insert(vals, tmp);
8972 foldedIn = true;
8973 break;
8974 // if intersected, these two were not foldable, try folding into later
8975 case Type::Intersect: {
8976 SetTy fuse;
8977 insert(fuse, rhs);
8978 insert(fuse, v);
8979
8980 Constraints trivialFuse(Type::Intersect, fuse, false);
8981
8982 // If this is not just making an intersect of the two operands,
8983 // remerge.
8984 if (trivialFuse != *tmp) {
8985 InnerTy newlhs = Constraints::all();
8986 bool legal = true;
8987 for (auto en2 : llvm::enumerate(values)) {
8988 auto i2 = en2.index();
8989 auto v2 = en2.value();
8990 if (i2 == i)
8991 continue;
8992 auto newlhs2 = newlhs->andB(v2, ctx);
8993 if (!newlhs2) {
8994 legal = false;
8995 break;
8996 }
8997 newlhs = std::move(newlhs2);
8998 }
8999 if (legal) {
9000 return newlhs->andB(tmp, ctx);
9001 }
9002 }
9003 insert(vals, v);
9004 }
9005 }
9006 }
9007 if (!foldedIn) {
9008 insert(vals, rhs);
9009 return std::make_shared<Constraints>(Type::Intersect, vals);
9010 } else {
9011 auto cur = Constraints::all();
9012 for (auto &iv : vals) {
9013 auto cur2 = cur->andB(iv, ctx);
9014 if (!cur2)
9015 return nullptr;
9016 cur = std::move(cur2);
9017 }
9018 return cur;
9019 }
9020 }
9021 if ((ty == Type::Intersect || ty == Type::Compare) &&
9022 rhs->ty == Type::Union) {
9023 SetTy unionVals = rhs->values;
9024 bool changed = false;
9025 SetTy ivVals;
9026 if (ty == Type::Intersect)
9027 ivVals = values;
9028 else
9029 insert(ivVals, shared_from_this());
9030
9031 ConstraintContext ctxd(ctx, shared_from_this(), rhs);
9032
9033 for (const auto &iv : ivVals) {
9034 SetTy nextunionVals;
9035 bool midchanged = false;
9036 for (auto &uv : unionVals) {
9037 auto tmp = iv->andB(uv, ctxd);
9038 if (!tmp) {
9039 midchanged = false;
9040 nextunionVals = unionVals;
9041 break;
9042 }
9043 switch (tmp->ty) {
9044 case Type::None:
9045 case Type::Compare:
9046 case Type::Union:
9047 insert(nextunionVals, tmp);
9048 changed |= tmp != uv;
9049 break;
9050 case Type::Intersect: {
9051 SetTy fuse;
9052 if (uv->ty == Type::Intersect)
9053 fuse = uv->values;
9054 else {
9055 assert(uv->ty == Type::Compare);
9056 insert(fuse, uv);
9057 }
9058 insert(fuse, iv);
9059
9060 Constraints trivialFuse(Type::Intersect, fuse, false);
9061 if (trivialFuse != *tmp) {
9062 insert(nextunionVals, tmp);
9063 midchanged = true;
9064 break;
9065 }
9066
9067 insert(nextunionVals, uv);
9068 break;
9069 }
9070 case Type::All:
9071 llvm_unreachable("Impossible");
9072 }
9073 }
9074 if (midchanged) {
9075 unionVals = nextunionVals;
9076 changed = true;
9077 }
9078 }
9079
9080 if (changed) {
9081 auto cur = Constraints::none();
9082 for (auto uv : unionVals) {
9083 cur = cur->orB(uv, ctxd);
9084 if (!cur)
9085 break;
9086 }
9087
9088 if (*cur != *rhs)
9089 return andB(cur, ctx);
9090 }
9091
9092 SetTy vals = ivVals;
9093 insert(vals, rhs);
9094 return std::make_shared<Constraints>(Type::Intersect, vals);
9095 }
9096 // Handled above via symmetry
9097 if (rhs->ty == Type::Intersect || rhs->ty == Type::Compare) {
9098 return rhs->andB(shared_from_this(), ctx);
9099 }
9100 // (m or a or b or d) and (m or a or c or e ...) -> m or a or ( (b or d)
9101 // and (c or e))
9102 if (ty == Type::Union && rhs->ty == Type::Union) {
9103 if (*this == *rhs->notB(ctx)) {
9104 return Constraints::none();
9105 }
9106 SetTy intersection = intersect(values, rhs->values);
9107 if (intersection.size() != 0) {
9108 InnerTy other_lhs = remove(intersection);
9109 InnerTy other_rhs = rhs->remove(intersection);
9110 InnerTy remainder;
9111 if (intersection.size() == 1)
9112 remainder = *intersection.begin();
9113 else {
9114 remainder = std::make_shared<Constraints>(Type::Union, intersection);
9115 }
9116 return remainder->orB(other_lhs->andB(other_rhs, ctx), ctx);
9117 }
9118
9119 bool changed = false;
9120 SetTy lhsVals = values;
9121 SetTy rhsVals = rhs->values;
9122
9123 ConstraintContext ctxd(ctx, shared_from_this(), rhs);
9124
9125 SetTy distributedVals;
9126 for (const auto &l1 : lhsVals) {
9127 bool subchanged = false;
9128 SetTy subDistributedVals;
9129 for (auto &r1 : rhsVals) {
9130 auto tmp = l1->andB(r1, ctxd);
9131 if (!tmp) {
9132 subchanged = false;
9133 break;
9134 }
9135
9136 if (l1->ty == Type::Intersect || r1->ty == Type::Intersect) {
9137 subchanged = true;
9138 insert(subDistributedVals, tmp);
9139 } else {
9140
9141 SetTy fuse;
9142 insert(fuse, l1);
9143 insert(fuse, r1);
9144 assert(fuse.size() == 2);
9145 Constraints trivialFuse(Type::Intersect, fuse);
9146 if ((trivialFuse != *tmp) || distributedVals.count(tmp)) {
9147 subchanged = true;
9148 }
9149 insert(subDistributedVals, tmp);
9150 }
9151 }
9152 if (subchanged) {
9153 for (auto sub : subDistributedVals)
9154 insert(distributedVals, sub);
9155 changed = true;
9156 } else {
9157 auto midand = l1->andB(rhs, ctxd);
9158 if (!midand) {
9159 changed = false;
9160 break;
9161 }
9162 insert(distributedVals, midand);
9163 }
9164 }
9165
9166 if (changed) {
9167 auto cur = Constraints::none();
9168 bool legal = true;
9169 for (auto &uv : distributedVals) {
9170 auto cur2 = cur->orB(uv, ctxd);
9171 if (!cur2) {
9172 legal = false;
9173 break;
9174 }
9175 cur = std::move(cur2);
9176 }
9177 if (legal) {
9178 return cur;
9179 }
9180 }
9181
9182 SetTy vals;
9183 insert(vals, shared_from_this());
9184 insert(vals, rhs);
9185 auto res = std::make_shared<Constraints>(Type::Intersect, vals);
9186 return res;
9187 }
9188 llvm::errs() << " andB this: " << *this << " rhs: " << *rhs << "\n";
9189 llvm_unreachable("Illegal predicate state");
9190 }
9191 // what this would be like when removing the following list of constraints
9192 InnerTy remove(const SetTy &sub) const {
9193 assert(ty == Type::Union || ty == Type::Intersect);
9194 SetTy res = values;
9195 set_subtract(res, sub);
9196 // res.set_subtract(sub);
9197 if (res.size() == 0) {
9198 if (ty == Type::Union)
9199 return Constraints::none();
9200 else
9201 return Constraints::all();
9202 } else if (res.size() == 1) {
9203 return *res.begin();
9204 } else {
9205 return std::make_shared<Constraints>(ty, res);
9206 }
9207 }
9208 SmallVector<std::pair<Value *, Value *>, 1>
9209 allSolutions(SCEVExpander &Exp, llvm::Type *T, Instruction *IP,
9210 const ConstraintContext &ctx, IRBuilder<> &B) const;
9211};
9212
9213void dump(const Constraints &c) { c.dump(); }
9214void dump(std::shared_ptr<const Constraints> c) { c->dump(); }
9215
9217 std::shared_ptr<const Constraints> lhs,
9218 std::shared_ptr<const Constraints> rhs) const {
9219 return *lhs < *rhs;
9220}
9221
9222raw_ostream &operator<<(raw_ostream &os, const Constraints &c) {
9223 switch (c.ty) {
9225 return os << "All";
9227 return os << "None";
9229 os << "(Union ";
9230 for (auto v : c.values)
9231 os << *v << ", ";
9232 os << ")";
9233 return os;
9234 }
9236 os << "(Intersect ";
9237 for (auto v : c.values)
9238 os << *v << ", ";
9239 os << ")";
9240 return os;
9241 }
9243 if (c.isEqual)
9244 os << "(eq ";
9245 else
9246 os << "(ne ";
9247 os << *c.node << ", L=";
9248 if (c.Loop)
9249 os << c.Loop->getHeader()->getName();
9250 else
9251 os << "nullptr";
9252 return os << ")";
9253 }
9254 }
9255 return os;
9256}
9257
9258SmallVector<std::pair<Value *, Value *>, 1>
9259Constraints::allSolutions(SCEVExpander &Exp, llvm::Type *T, Instruction *IP,
9260 const ConstraintContext &ctx, IRBuilder<> &B) const {
9261 switch (ty) {
9262 case Type::None:
9263 return {};
9264 case Type::All:
9265 llvm::errs() << *this << "\n";
9266 llvm_unreachable("All not handled");
9267 case Type::Compare: {
9268 Value *cond = ConstantInt::getTrue(T->getContext());
9269 if (ctx.loopToSolve != Loop) {
9270 assert(ctx.loopToSolve);
9271 Value *ivVal = Exp.expandCodeFor(node, T, IP);
9272 Value *iv = nullptr;
9273 if (Loop) {
9274 iv = Loop->getCanonicalInductionVariable();
9275 assert(iv);
9276 } else {
9277 iv = ConstantInt::getNullValue(ivVal->getType());
9278 }
9279 if (isEqual)
9280 cond = B.CreateICmpEQ(ivVal, iv);
9281 else
9282 cond = B.CreateICmpNE(ivVal, iv);
9283 return {std::make_pair((Value *)nullptr, cond)};
9284 }
9285 if (isEqual) {
9286 return {std::make_pair(Exp.expandCodeFor(node, T, IP), cond)};
9287 }
9288 EmitFailure("NoSparsification", IP->getDebugLoc(), IP,
9289 "Negated solution not handled: ", *this);
9290 assert(0);
9291 return {};
9292 }
9293 case Type::Union: {
9294 SmallVector<std::pair<Value *, Value *>, 1> vals;
9295 for (auto v : values)
9296 for (auto sol : v->allSolutions(Exp, T, IP, ctx, B))
9297 vals.push_back(sol);
9298 return vals;
9299 }
9300 case Type::Intersect: {
9301 {
9302 SmallVector<InnerTy, 1> vals(values.begin(), values.end());
9303 ssize_t unionidx = -1;
9304 for (unsigned i = 0; i < vals.size(); i++) {
9305 if (vals[i]->ty == Type::Union) {
9306 unionidx = i;
9307 bool allne = true;
9308 for (auto &v : vals[i]->values) {
9309 if (v->ty != Type::Compare || v->isEqual) {
9310 allne = false;
9311 break;
9312 }
9313 }
9314 if (allne)
9315 break;
9316 }
9317 }
9318 if (unionidx != -1) {
9319 auto others = Constraints::all();
9320 for (unsigned j = 0; j < vals.size(); j++)
9321 if (unionidx != j)
9322 others = others->andB(vals[j], ctx);
9323 SmallVector<std::pair<Value *, Value *>, 1> resvals;
9324 for (auto &v : vals[unionidx]->values) {
9325 auto tmp = v->andB(others, ctx);
9326 for (const auto &sol : tmp->allSolutions(Exp, T, IP, ctx, B))
9327 resvals.push_back(sol);
9328 }
9329 return resvals;
9330 }
9331 }
9332 Value *solVal = nullptr;
9333 Value *cond = ConstantInt::getTrue(T->getContext());
9334 for (auto v : values) {
9335 auto sols = v->allSolutions(Exp, T, IP, ctx, B);
9336 if (sols.size() != 1) {
9337 llvm::errs() << *this << "\n";
9338 for (auto s : sols)
9339 if (s.first)
9340 llvm::errs() << " + sol: " << *s.first << " " << *s.second << "\n";
9341 else
9342 llvm::errs() << " + sol: " << s.first << " " << *s.second << "\n";
9343 llvm::errs() << " v: " << *v << " this: " << *this << "\n";
9344 llvm_unreachable("Intersect not handled (solsize>1)");
9345 }
9346 auto sol = sols[0];
9347 if (sol.first) {
9348 if (solVal != nullptr) {
9349 llvm::errs() << *this << "\n";
9350 llvm::errs() << " prevsolVal: " << *solVal << "\n";
9351 llvm_unreachable("Intersect not handled (prevsolval)");
9352 }
9353 assert(solVal == nullptr);
9354 solVal = sol.first;
9355 }
9356 cond = B.CreateAnd(cond, sol.second);
9357 }
9358 return {std::make_pair(solVal, cond)};
9359 }
9360 }
9361 return {};
9362}
9363
9364constexpr bool SparseDebug = false;
9365std::shared_ptr<const Constraints>
9366getSparseConditions(bool &legal, Value *val,
9367 std::shared_ptr<const Constraints> defaultFloat,
9368 Instruction *scope, const ConstraintContext &ctx) {
9369 if (auto I = dyn_cast<Instruction>(val)) {
9370 // Binary `and` is a bit-wise `umin`.
9371 if (I->getOpcode() == Instruction::And) {
9372 auto lhs = getSparseConditions(legal, I->getOperand(0),
9373 Constraints::all(), I, ctx);
9374 auto rhs = getSparseConditions(legal, I->getOperand(1),
9375 Constraints::all(), I, ctx);
9376 auto res = lhs->andB(rhs, ctx);
9377 assert(res);
9378 assert(ctx.seen.size() == 0);
9379 if (SparseDebug) {
9380 llvm::errs() << " getSparse(and, " << *I << "), lhs("
9381 << *I->getOperand(0) << ") = " << *lhs << "\n";
9382 llvm::errs() << " getSparse(and, " << *I << "), rhs("
9383 << *I->getOperand(1) << ") = " << *rhs << "\n";
9384 llvm::errs() << " getSparse(and, " << *I << ") = " << *res << "\n";
9385 }
9386 return res;
9387 }
9388
9389 // Binary `or` is a bit-wise `umax`.
9390 if (I->getOpcode() == Instruction::Or) {
9391 auto lhs = getSparseConditions(legal, I->getOperand(0),
9392 Constraints::none(), I, ctx);
9393 auto rhs = getSparseConditions(legal, I->getOperand(1),
9394 Constraints::none(), I, ctx);
9395 auto res = lhs->orB(rhs, ctx);
9396 if (SparseDebug) {
9397 llvm::errs() << " getSparse(or, " << *I << "), lhs("
9398 << *I->getOperand(0) << ") = " << *lhs << "\n";
9399 llvm::errs() << " getSparse(or, " << *I << "), rhs("
9400 << *I->getOperand(1) << ") = " << *rhs << "\n";
9401 llvm::errs() << " getSparse(or, " << *I << ") = " << *res << "\n";
9402 }
9403 return res;
9404 }
9405
9406 if (I->getOpcode() == Instruction::Xor) {
9407 for (int i = 0; i < 2; i++) {
9408 if (auto C = dyn_cast<ConstantInt>(I->getOperand(i)))
9409 if (C->isOne()) {
9410 auto pres =
9411 getSparseConditions(legal, I->getOperand(1 - i),
9412 defaultFloat->notB(ctx), scope, ctx);
9413 auto res = pres->notB(ctx);
9414 if (SparseDebug) {
9415 llvm::errs() << " getSparse(not, " << *I << "), prev ("
9416 << *I->getOperand(0) << ") = " << *pres << "\n";
9417 llvm::errs() << " getSparse(not, " << *I << ") = " << *res
9418 << "\n";
9419 }
9420 return res;
9421 }
9422 }
9423 }
9424
9425 if (auto icmp = dyn_cast<ICmpInst>(I)) {
9426 auto L = ctx.loopToSolve;
9427 auto lhs = ctx.SE.getSCEVAtScope(icmp->getOperand(0), L);
9428 auto rhs = ctx.SE.getSCEVAtScope(icmp->getOperand(1), L);
9429 if (SparseDebug) {
9430 llvm::errs() << " lhs: " << *lhs << "\n";
9431 llvm::errs() << " rhs: " << *rhs << "\n";
9432 }
9433
9434 auto sub1 = ctx.SE.getMinusSCEV(lhs, rhs);
9435
9436 if (icmp->getPredicate() == ICmpInst::ICMP_EQ ||
9437 icmp->getPredicate() == ICmpInst::ICMP_NE) {
9438 if (auto add = dyn_cast<SCEVAddRecExpr>(sub1)) {
9439 if (add->isAffine()) {
9440 // 0 === A + B * inc -> -A / B = inc
9441 auto A = add->getStart();
9442 if (auto B =
9443 dyn_cast<SCEVConstant>(add->getStepRecurrence(ctx.SE))) {
9444
9445 auto MA = A;
9446 if (B->getAPInt().isNegative())
9447 B = cast<SCEVConstant>(ctx.SE.getNegativeSCEV(B));
9448 else
9449 MA = ctx.SE.getNegativeSCEV(A);
9450 auto div = ctx.SE.getUDivExpr(MA, B);
9451 auto div_e = ctx.SE.getUDivExactExpr(MA, B);
9452 if (div == div_e) {
9453 auto res = Constraints::make_compare(
9454 div, icmp->getPredicate() == ICmpInst::ICMP_EQ,
9455 add->getLoop(), ctx);
9456 if (SparseDebug) {
9457 llvm::errs()
9458 << " getSparse(icmp, " << *I << ") = " << *res << "\n";
9459 }
9460 return res;
9461 }
9462 }
9463 }
9464 }
9465 if (cannotDependOnLoopIV(sub1, ctx.loopToSolve)) {
9466 auto res = Constraints::make_compare(
9467 sub1, icmp->getPredicate() == ICmpInst::ICMP_EQ, nullptr, ctx);
9468 llvm::errs() << " getSparse(icmp_noloop, " << *I << ") = " << *res
9469 << "\n";
9470 return res;
9471 }
9472 }
9473 if (scope)
9474 EmitWarning("NoSparsification", *I,
9475 " No sparsification: not sparse solvable(icmp): ", *I,
9476 " via ", *sub1);
9477 if (SparseDebug) {
9478 llvm::errs() << " getSparse(icmp_dflt, " << *I
9479 << ") = " << *defaultFloat << "\n";
9480 }
9481 return defaultFloat;
9482 }
9483
9484 // cmp x, 1.0 -> false/true
9485 if (auto fcmp = dyn_cast<FCmpInst>(I)) {
9486 auto res = defaultFloat;
9487 if (SparseDebug) {
9488 llvm::errs() << " getSparse(fcmp, " << *I << ") = " << *res << "\n";
9489 }
9490 return res;
9491
9492 if (fcmp->getPredicate() == CmpInst::FCMP_OEQ ||
9493 fcmp->getPredicate() == CmpInst::FCMP_UEQ) {
9494 return Constraints::all();
9495 } else if (fcmp->getPredicate() == CmpInst::FCMP_ONE ||
9496 fcmp->getPredicate() == CmpInst::FCMP_UNE) {
9497 return Constraints::none();
9498 }
9499 }
9500 }
9501
9502 if (scope) {
9503 EmitFailure("NoSparsification", scope->getDebugLoc(), scope,
9504 " No sparsification: not sparse solvable: ", *val);
9505 }
9506 legal = false;
9507 return defaultFloat;
9508}
9509
9511 const llvm::Loop *Loop,
9512 const ConstraintContext &ctx) {
9513 if (!Loop) {
9514 assert(!isa<SCEVAddRecExpr>(v));
9515 SmallVector<Instruction *, 1> noassumption;
9516 ConstraintContext ctx2(ctx.SE, ctx.loopToSolve, noassumption, ctx.DT);
9517 for (auto I : ctx.Assumptions) {
9518 bool legal = true;
9519 if (I->getParent()->getParent() !=
9520 ctx.loopToSolve->getHeader()->getParent())
9521 continue;
9522 auto parsedCond = getSparseConditions(legal, I->getOperand(0),
9523 Constraints::none(), nullptr, ctx2);
9524 bool dominates = ctx.DT.dominates(I, ctx.loopToSolve->getHeader());
9525 if (legal && dominates) {
9526 if (parsedCond->ty == Type::Compare && !parsedCond->Loop) {
9527 if (parsedCond->node == v ||
9528 parsedCond->node == ctx.SE.getNegativeSCEV(v)) {
9529 InnerTy res;
9530 if (parsedCond->isEqual == isEqual)
9531 res = Constraints::all();
9532 else
9533 res = Constraints::none();
9534 return res;
9535 }
9536 }
9537 }
9538 }
9539 }
9540 // cannot have negative loop canonical induction var
9541 if (Loop)
9542 if (auto C = dyn_cast<SCEVConstant>(v))
9543 if (C->getAPInt().isNegative()) {
9544 if (isEqual)
9545 return Constraints::none();
9546 else
9547 return Constraints::all();
9548 }
9549 return InnerTy(new Constraints(v, isEqual, Loop, false));
9550}
9551
9552// Automatic sparsity rewrites sums and products as calls to the
9553// __enzyme_sum / __enzyme_product placeholders. Turn them back into
9554// arithmetic, whether or not the sparsification succeeded.
9555static void lowerSparsePlaceholders(llvm::Function &F) {
9556 for (auto &F2 : F.getParent()->functions()) {
9557 if (startsWith(F2.getName(), "__enzyme_product")) {
9558 SmallVector<Instruction *, 1> toErase;
9559 for (llvm::User *I : F2.users()) {
9560 auto CB = cast<CallBase>(I);
9561 IRBuilder<> B(CB);
9562 B.setFastMathFlags(getFast());
9563 Value *res = nullptr;
9564 for (auto v : callOperands(CB)) {
9565 if (res == nullptr)
9566 res = v;
9567 else {
9568 res = v->getType()->isIntegerTy() ? B.CreateMul(res, v)
9569 : B.CreateFMul(res, v);
9570 }
9571 }
9572 CB->replaceAllUsesWith(res);
9573 toErase.push_back(CB);
9574 }
9575 for (auto CB : toErase)
9576 CB->eraseFromParent();
9577 } else if (startsWith(F2.getName(), "__enzyme_sum")) {
9578 SmallVector<Instruction *, 1> toErase;
9579 for (llvm::User *I : F2.users()) {
9580 auto CB = cast<CallBase>(I);
9581 IRBuilder<> B(CB);
9582 B.setFastMathFlags(getFast());
9583 Value *res = nullptr;
9584 for (auto v : callOperands(CB)) {
9585 if (res == nullptr)
9586 res = v;
9587 else {
9588 res = v->getType()->isIntegerTy() ? B.CreateAdd(res, v)
9589 : B.CreateFAdd(res, v);
9590 }
9591 }
9592 CB->replaceAllUsesWith(res);
9593 toErase.push_back(CB);
9594 }
9595 for (auto CB : toErase)
9596 CB->eraseFromParent();
9597 }
9598 }
9599}
9600
9601// Remove the enzyme.sparse.inbounds markers of a sparsification that was
9602// abandoned.
9603static void eraseSparseInbounds(llvm::Function &F) {
9604 SmallVector<CallInst *, 1> toErase;
9605 for (auto &BB : F)
9606 for (auto &I : BB)
9607 if (auto CI = dyn_cast<CallInst>(&I))
9608 if (auto Fn = CI->getCalledFunction())
9609 if (Fn->getName() == "enzyme.sparse.inbounds")
9610 toErase.push_back(CI);
9611 for (auto CI : toErase)
9612 CI->eraseFromParent();
9613}
9614
9615void fixSparseIndices(llvm::Function &F, llvm::FunctionAnalysisManager &FAM,
9616 SetVector<BasicBlock *> &toDenseBlocks) {
9617
9618 auto &DT = FAM.getResult<DominatorTreeAnalysis>(F);
9619 auto &SE = FAM.getResult<ScalarEvolutionAnalysis>(F);
9620 auto &LI = FAM.getResult<LoopAnalysis>(F);
9621 auto &DL = F.getParent()->getDataLayout();
9622
9623 QueueType Q(DT, LI);
9624 {
9625 llvm::SetVector<BasicBlock *> todoBlocks;
9626 for (auto b : toDenseBlocks) {
9627 auto L = LI.getLoopFor(b);
9628 if (L) {
9629 for (auto B : L->getBlocks())
9630 todoBlocks.insert(B);
9631 }
9632 }
9633 for (auto BB : todoBlocks)
9634 for (auto &I : *BB)
9635 if (!I.getType()->isVoidTy()) {
9636 Q.insert(&I);
9637 assert(Q.contains(&I));
9638 }
9639 }
9640
9641 // llvm::errs() << " pre fix inner: " << F << "\n";
9642
9643 // Full simplification
9644 while (!Q.empty()) {
9645 auto cur = Q.pop_back_val();
9646 /*
9647 std::set<Instruction *> prev;
9648 for (auto v : Q)
9649 prev.insert(v);
9650 // llvm::errs() << "\n\n\n\n" << F << "\n";
9651 llvm::errs() << "cur: " << *cur << "\n";
9652 */
9653 auto changed = fixSparse_inner(cur, F, Q, DT, SE, LI, DL);
9654 (void)changed;
9655 /*
9656 if (changed) {
9657 llvm::errs() << "changed: " << *changed << "\n";
9658
9659 for (auto I : Q)
9660 if (!prev.count(I))
9661 llvm::errs() << " + " << *I << "\n";
9662 // llvm::errs() << F << "\n\n";
9663 }
9664 */
9665 }
9666
9667 // llvm::errs() << " post fix inner " << F << "\n";
9668
9669 SmallVector<std::pair<BasicBlock *, Instruction *>, 1> sparseBlocks;
9670 bool legalToSparse = true;
9671 for (auto &B : F)
9672 if (isConditionalBranch(B.getTerminator())) {
9673 auto br = B.getTerminator();
9674 for (int bidx = 0; bidx < 2; bidx++) {
9675 auto uncond_br = br->getSuccessor(bidx)->getTerminator();
9676 if (isUnconditionalBranch(uncond_br))
9677 if (uncond_br->getSuccessor(0) == br->getSuccessor(1 - bidx)) {
9678 auto blk = br->getSuccessor(bidx);
9679 int countSparse = 0;
9680 for (auto &I : *blk) {
9681 if (auto CI = dyn_cast<CallInst>(&I)) {
9682 if (auto F = CI->getCalledFunction()) {
9683 if (F->hasFnAttribute("enzyme_sparse_accumulate")) {
9684 countSparse++;
9685 }
9686 }
9687 }
9688 }
9689 if (countSparse == 0)
9690 continue;
9691 if (countSparse > 1) {
9692 legalToSparse = false;
9694 "NoSparsification", br->getDebugLoc(), br, "F: ", F,
9695 "\nMultiple distinct sparse stores in same block: ", *blk);
9696 break;
9697 }
9698
9699 for (auto &I : *blk) {
9700 if (auto CI = dyn_cast<CallInst>(&I)) {
9701 if (auto F = CI->getCalledFunction()) {
9702 if (F->hasFnAttribute("enzyme_sparse_accumulate")) {
9703 continue;
9704 }
9705 }
9706 if (isReadOnly(CI))
9707 continue;
9708 }
9709 if (!I.mayWriteToMemory())
9710 continue;
9711
9712 legalToSparse = false;
9713 EmitFailure("NoSparsification", br->getDebugLoc(), br, "F: ", F,
9714 "\nIllegal writing instruction in sparse block: ", I);
9715 break;
9716 }
9717
9718 if (!legalToSparse) {
9719 break;
9720 }
9721
9722 auto L = LI.getLoopFor(blk);
9723 if (!L) {
9724 legalToSparse = false;
9725 EmitFailure("NoSparsification", br->getDebugLoc(), br, "F: ", F,
9726 "\nCould not find loop for: ", *blk);
9727 break;
9728 }
9729 auto idx = L->getCanonicalInductionVariable();
9730 if (!idx) {
9731 legalToSparse = false;
9732 EmitFailure("NoSparsification", br->getDebugLoc(), br, "F: ", F,
9733 "\nL:", *L,
9734 "\nCould not find loop index: ", *L->getHeader());
9735 break;
9736 }
9737 assert(idx);
9738 auto preheader = L->getLoopPreheader();
9739 if (!preheader) {
9740 legalToSparse = false;
9741 EmitFailure("NoSparsification", br->getDebugLoc(), br, "F: ", F,
9742 "\nL:", *L, "\nCould not find loop preheader");
9743 break;
9744 }
9745 sparseBlocks.emplace_back(blk, br);
9746 }
9747 }
9748 }
9749
9750 if (!legalToSparse) {
9752 return;
9753 }
9754
9755 // block, bound, scev for indexset
9756 std::map<Loop *,
9757 std::pair<std::pair<PHINode *, PHINode *>,
9758 SmallVector<std::pair<BasicBlock *,
9759 std::shared_ptr<const Constraints>>,
9760 1>>>
9761 forSparsification;
9762
9763 SmallVector<Instruction *, 1> Assumptions;
9764 for (auto &BB : F)
9765 for (auto &I : BB)
9766 if (auto II = dyn_cast<IntrinsicInst>(&I))
9767 if (II->getIntrinsicID() == Intrinsic::assume)
9768 Assumptions.push_back(II);
9769
9770 bool sawError = false;
9771 SmallVector<std::pair<Instruction *, Value *>, 1> origConditions;
9772
9773 for (auto [blk, br] : sparseBlocks) {
9774 auto L = LI.getLoopFor(blk);
9775 assert(L);
9776 auto idx = L->getCanonicalInductionVariable();
9777 assert(idx);
9778 auto preheader = L->getLoopPreheader();
9779 assert(preheader);
9780
9781 // default is condition avoids sparse, negated is condition goes
9782 // to sparse
9783 auto cond = getBranchCondition(br);
9784 bool negated = br->getSuccessor(0) == blk;
9785
9786 bool legal = true;
9787 // Whether the i1 value does not contain any icmp's
9788 std::function<bool(Value *)> onlyDataDependentValues = [&](Value *val) {
9789 auto I = cast<Instruction>(val);
9790 if (I->getOpcode() == Instruction::Or) {
9791 return onlyDataDependentValues(I->getOperand(0)) &&
9792 onlyDataDependentValues(I->getOperand(1));
9793 }
9794 if (I->getOpcode() == Instruction::And) {
9795 return onlyDataDependentValues(I->getOperand(0)) &&
9796 onlyDataDependentValues(I->getOperand(1));
9797 }
9798 if (isa<FCmpInst>(I))
9799 return true;
9800 if (isa<ICmpInst>(I))
9801 return false;
9802 EmitFailure("NoSparsification", I->getDebugLoc(), I,
9803 " No sparsification: bad datadepedent values check: ", *I);
9804 legal = false;
9805 return true;
9806 };
9807
9808 // Simplify variable val which is known to branch away from the
9809 // actual store (if not negated) or to the store (if negated)
9810 // if! negated the result may become more false if negated the
9811 // result may become more true
9812
9813 //
9814
9815 // default is condition avoids sparse, negated is condition goes
9816 // to sparse
9817 Instruction *context =
9818 isa<Instruction>(cond) ? cast<Instruction>(cond) : idx;
9819 ConstraintContext cctx(SE, L, Assumptions, DT);
9820 auto solutions = getSparseConditions(
9821 legal, cond, negated ? Constraints::all() : Constraints::none(),
9822 context, cctx);
9823 // llvm::errs() << " solutions pre negate: " << *solutions << "\n";
9824 if (!negated) {
9825 solutions = solutions->notB(cctx);
9826 }
9827 // llvm::errs() << " solutions post negate: " << *solutions << "\n";
9828 if (!legal) {
9829 sawError = true;
9830 continue;
9831 }
9832
9833 if (solutions == Constraints::none() || solutions == Constraints::all()) {
9835 "NoSparsification", context->getDebugLoc(), context, "F: ", F,
9836 "\nL: ", *L, "\ncond: ", *cond, " negated:", negated,
9837 "\n No sparsification: not sparse solvable(nosoltn): solutions:",
9838 *solutions);
9839 sawError = true;
9840 }
9841 // llvm::errs() << " found solvable solutions " << *solutions << "\n";
9842
9843 if (forSparsification.count(L) == 0) {
9844 {
9845 IRBuilder<> PB(preheader->getTerminator());
9846 auto loopIdxPhi = PB.CreatePHI(idx->getType(), 0, "loop.idx");
9847 auto phIdxPhi = PB.CreatePHI(idx->getType(), 0, "ph.idx");
9848 forSparsification[L].first = std::make_pair(phIdxPhi, loopIdxPhi);
9849 }
9850
9851 Value *LoopCount = nullptr;
9852
9853 IRBuilder<> B(getFirstNonPHI(L->getHeader()));
9854 {
9855#if LLVM_VERSION_MAJOR >= 22
9856 SCEVExpander Exp(SE, "sparseenzyme");
9857#else
9858 SCEVExpander Exp(SE, DL, "sparseenzyme");
9859#endif
9860 auto LoopCountS = SE.getBackedgeTakenCount(L);
9861 LoopCount = B.CreateAdd(
9862 ConstantInt::get(idx->getType(), 1),
9863 Exp.expandCodeFor(LoopCountS, idx->getType(), &blk->front()));
9864 }
9865 Value *nonNegative =
9866 B.CreateICmpSGE(idx, ConstantInt::get(idx->getType(), 0));
9867 Value *belowCount = B.CreateICmpSLT(idx, LoopCount);
9868 Value *inbounds = B.CreateAnd(belowCount, nonNegative);
9869 Value *args[] = {inbounds, forSparsification[L].first.second};
9870 B.CreateCall(F.getParent()->getOrInsertFunction(
9871 "enzyme.sparse.inbounds", B.getVoidTy(),
9872 inbounds->getType(), idx->getType()),
9873 args);
9874 }
9875
9876 IRBuilder<> B(br);
9877 B.SetInsertPoint(br);
9878 auto nidx = B.CreateICmpEQ(
9879 forSparsification[L].first.first,
9880 ConstantInt::get(idx->getType(), forSparsification[L].second.size()));
9881 // TODO check direction
9882 if (!negated)
9883 nidx = B.CreateNot(nidx);
9884
9885 origConditions.emplace_back(br, cond);
9886 setBranchCondition(br, nidx);
9887 forSparsification[L].second.emplace_back(blk, solutions);
9888 }
9889
9890 if (sawError) {
9891 // Undo the rewrite of the branches that were already sparsified.
9892 for (auto &[br, cond] : origConditions)
9893 setBranchCondition(br, cond);
9895 for (auto &pair : forSparsification) {
9896 for (auto PN : {pair.second.first.first, pair.second.first.second}) {
9897 PN->replaceAllUsesWith(UndefValue::get(PN->getType()));
9898 PN->eraseFromParent();
9899 }
9900 }
9901 if (llvm::verifyFunction(F, &llvm::errs())) {
9902 llvm::errs() << F << "\n";
9903 report_fatal_error("function failed verification (6)");
9904 }
9906 return;
9907 }
9908
9909 if (forSparsification.size() == 0) {
9910 auto context = &F.getEntryBlock().front();
9911 EmitFailure("NoSparsification", context->getDebugLoc(), context, "F: ", F,
9912 "\n Found no stores for sparsification");
9914 return;
9915 }
9916
9917 for (const auto &pair : forSparsification) {
9918 auto L = pair.first;
9919 auto [PN, inductPN] = pair.second.first;
9920
9921 auto ph = L->getLoopPreheader();
9922#if LLVM_VERSION_MAJOR >= 20
9923 CodeExtractor ext(L->getBlocks(), &DT);
9924#else
9925 CodeExtractor ext(DT, *L);
9926#endif
9927 CodeExtractorAnalysisCache cache(F);
9928 SetVector<Value *> Inputs, Outputs;
9929 auto F2 = ext.extractCodeRegion(cache, Inputs, Outputs);
9930 assert(F2);
9931 F2->addFnAttr(Attribute::AlwaysInline);
9932
9933 for (auto U : F2->users())
9934 cast<Instruction>(U)->eraseFromParent();
9935
9936 ssize_t induct_idx = -1;
9937 ssize_t off_idx = -1;
9938 for (auto en : llvm::enumerate(Inputs)) {
9939 if (en.value() == inductPN)
9940 induct_idx = en.index();
9941 if (en.value() == PN)
9942 off_idx = en.index();
9943 }
9944 assert(induct_idx != -1);
9945 assert(off_idx != -1);
9946
9947 auto L2 = LI.getLoopFor(F2->getEntryBlock().getSingleSuccessor());
9948 auto new_idx = F2->getArg(induct_idx);
9949 auto L2Header = L2->getHeader();
9950 auto new_lidx = L2->getCanonicalInductionVariable();
9951
9952 auto idxty = new_idx->getType();
9953
9954 auto new_pn = F2->getArg(off_idx);
9955 // Find all sparse accumulates we weren't meant to handle
9956 {
9957 SmallVector<CallInst *, 1> toErase;
9958 // First delete any accumulates in sub loops
9959 for (auto SL : L2->getSubLoops())
9960 for (auto B : SL->getBlocks())
9961 for (auto &I : *B)
9962 if (auto CI = dyn_cast<CallInst>(&I))
9963 if (auto F = CI->getCalledFunction()) {
9964 if (F->hasFnAttribute("enzyme_sparse_accumulate")) {
9965 toErase.push_back(CI);
9966 continue;
9967 }
9968 }
9969 for (auto C : toErase)
9970 C->eraseFromParent();
9971 toErase.clear();
9972 // Next delete any accumulates not in latchany loops
9973 for (auto B : L2->getBlocks()) {
9974 bool guarded = false;
9975 if (auto P = B->getSinglePredecessor())
9976 if (auto S = B->getSingleSuccessor())
9977 if (auto BI = (isAnyBranch(P->getTerminator())
9978 ? cast<Instruction>(P->getTerminator())
9979 : nullptr))
9980 if (isConditionalBranch(BI))
9981 for (size_t i = 0; i < 2; i++)
9982 if (BI->getSuccessor(i) == B &&
9983 BI->getSuccessor(1 - i) == S) {
9984 auto val = getBranchCondition(BI);
9985 if (auto xori = dyn_cast<Instruction>(val))
9986 if (xori->getOpcode() == Instruction::Xor)
9987 val = xori->getOperand(0);
9988 if (auto cmp = dyn_cast<ICmpInst>(val))
9989 if (cmp->getOperand(0) == new_pn ||
9990 cmp->getOperand(1) == new_pn)
9991 guarded = true;
9992 }
9993 if (guarded)
9994 continue;
9995 for (auto &I : *B)
9996 if (auto CI = dyn_cast<CallInst>(&I))
9997 if (auto F = CI->getCalledFunction()) {
9998 if (F->hasFnAttribute("enzyme_sparse_accumulate")) {
9999 toErase.push_back(CI);
10000 continue;
10001 }
10002 }
10003 }
10004 for (auto C : toErase)
10005 C->eraseFromParent();
10006 toErase.clear();
10007 }
10008
10009 auto guard = L2->getLoopLatch()->getTerminator();
10010 assert(guard);
10011 IRBuilder<> G(guard);
10012 G.CreateRetVoid();
10013 guard->eraseFromParent();
10014 new_lidx->replaceAllUsesWith(new_idx);
10015 new_lidx->eraseFromParent();
10016
10017 auto phterm = ph->getTerminator();
10018 IRBuilder<> B(phterm);
10019
10020 // We extracted code, reset analyses.
10021 /*
10022 DT.reset();
10023 SE.forgetAllLoops();
10024 */
10025
10026 for (auto en : llvm::enumerate(pair.second.second)) {
10027 auto off = en.index();
10028 auto &solutions = en.value().second;
10029 ConstraintContext ctx(SE, L, Assumptions, DT);
10030#if LLVM_VERSION_MAJOR >= 22
10031 SCEVExpander Exp(SE, "sparseenzyme", /*preservelcssa*/ false);
10032#else
10033 SCEVExpander Exp(SE, DL, "sparseenzyme", /*preservelcssa*/ false);
10034#endif
10035 auto sols = solutions->allSolutions(Exp, idxty, phterm, ctx, B);
10036 SmallVector<Value *, 1> prevSols;
10037 for (auto [sol, condition] : sols) {
10038 SmallVector<Value *, 1> args(Inputs.begin(), Inputs.end());
10039 args[off_idx] = ConstantInt::get(idxty, off);
10040 args[induct_idx] = sol;
10041 for (auto sol2 : prevSols)
10042 condition = B.CreateAnd(condition, B.CreateICmpNE(sol, sol2));
10043 prevSols.push_back(sol);
10044 auto BB = B.GetInsertBlock();
10045 auto B2 = BB->splitBasicBlock(B.GetInsertPoint(), "poststore");
10046 B2->moveAfter(BB);
10047 BB->getTerminator()->eraseFromParent();
10048 B.SetInsertPoint(BB);
10049 auto callB = BasicBlock::Create(BB->getContext(), "tostore",
10050 BB->getParent(), B2);
10051 B.CreateCondBr(condition, callB, B2);
10052 B.SetInsertPoint(callB);
10053 B.CreateCall(F2, args);
10054 B.CreateBr(B2);
10055 B.SetInsertPoint(B2->getTerminator());
10056 }
10057 auto blk = en.value().first;
10058 auto term = blk->getTerminator();
10059 IRBuilder<> B2(blk);
10060 B2.CreateRetVoid();
10061 term->eraseFromParent();
10062 }
10063
10064 PN->eraseFromParent();
10065
10066 for (auto &I : *L2Header) {
10067 auto boundsCheck = dyn_cast<CallInst>(&I);
10068 if (!boundsCheck)
10069 continue;
10070 auto BF = boundsCheck->getCalledFunction();
10071 if (!BF)
10072 continue;
10073 if (BF->getName() != "enzyme.sparse.inbounds")
10074 continue;
10075
10076 auto boundsCond = boundsCheck->getArgOperand(0);
10077
10078 auto next = L2Header->splitBasicBlock(boundsCheck);
10079
10080 auto exit = BasicBlock::Create(F2->getContext(), "bounds.exit", F2,
10081 L2Header->getNextNode());
10082 {
10083 IRBuilder B(exit);
10084 B.CreateRetVoid();
10085 }
10086 L2Header->getTerminator()->eraseFromParent();
10087
10088 {
10089 IRBuilder B(L2Header);
10090 B.CreateCondBr(boundsCond, next, exit);
10091 }
10092 boundsCheck->eraseFromParent();
10093 inductPN->eraseFromParent();
10094
10095 break;
10096 }
10097 }
10098
10100}
10101
10102void replaceToDense(llvm::CallBase *CI, bool replaceAll, llvm::Function *F,
10103 const llvm::DataLayout &DL) {
10104 auto load_fn = cast<Function>(getBaseObject(CI->getArgOperand(0)));
10105 auto store_fn = cast<Function>(getBaseObject(CI->getArgOperand(1)));
10106 size_t argstart = 2;
10107 size_t num_args = CI->arg_size();
10108 SmallVector<std::pair<Instruction *, Value *>, 1> users;
10109
10110 for (auto U : CI->users()) {
10111 users.push_back(std::make_pair(cast<Instruction>(U), CI));
10112 }
10113 IntegerType *intTy = IntegerType::get(CI->getContext(), 64);
10114 auto toInt = [&](IRBuilder<> &B, llvm::Value *V) {
10115 if (auto PT = dyn_cast<PointerType>(V->getType())) {
10116 if (PT->getAddressSpace() != 0) {
10117 V = B.CreateAddrSpaceCast(V, changePointerAddrSpace(PT, 0));
10118 }
10119 return B.CreatePtrToInt(V, intTy);
10120 }
10121 auto IT = cast<IntegerType>(V->getType());
10122 if (IT == intTy)
10123 return V;
10124 return B.CreateZExtOrTrunc(V, intTy);
10125 };
10126 SmallVector<Instruction *, 1> toErase;
10127
10128 ValueToValueMapTy replacements;
10129 replacements[CI] = Constant::getNullValue(CI->getType());
10130 Instruction *remaining = nullptr;
10131 while (users.size()) {
10132 auto pair = users.back();
10133 users.pop_back();
10134 auto U = pair.first;
10135 auto val = pair.second;
10136 if (replacements.count(U))
10137 continue;
10138
10139 IRBuilder B(U);
10140 if (auto CI = dyn_cast<CastInst>(U)) {
10141 for (auto U : CI->users()) {
10142 users.push_back(std::make_pair(cast<Instruction>(U), CI));
10143 }
10144 auto rep =
10145 B.CreateCast(CI->getOpcode(), replacements[val], CI->getDestTy());
10146 if (auto I = dyn_cast<Instruction>(rep))
10147 I->setDebugLoc(CI->getDebugLoc());
10148 replacements[CI] = rep;
10149 continue;
10150 }
10151 if (auto SI = dyn_cast<SelectInst>(U)) {
10152 for (auto U : SI->users()) {
10153 users.push_back(std::make_pair(cast<Instruction>(U), SI));
10154 }
10155 auto tval = SI->getTrueValue();
10156 auto fval = SI->getFalseValue();
10157 auto rep = B.CreateSelect(
10158 SI->getCondition(),
10159 replacements.count(tval) ? (Value *)replacements[tval] : tval,
10160 replacements.count(fval) ? (Value *)replacements[fval] : fval);
10161 if (auto I = dyn_cast<Instruction>(rep))
10162 I->setDebugLoc(SI->getDebugLoc());
10163 replacements[SI] = rep;
10164 continue;
10165 }
10166 /*
10167 if (auto CI = dyn_cast<PHINode>(U)) {
10168 for (auto U : CI->users()) {
10169 users.push_back(std::make_pair(cast<Instruction>(U), CI));
10170 }
10171 continue;
10172 }
10173 */
10174 if (auto CI = dyn_cast<CallInst>(U)) {
10175 auto funcName = getFuncNameFromCall(CI);
10176 if (funcName == "julia.pointer_from_objref") {
10177 for (auto U : CI->users()) {
10178 users.push_back(std::make_pair(cast<Instruction>(U), CI));
10179 }
10180 auto *F = CI->getCalledOperand();
10181
10182 SmallVector<Value *, 1> args;
10183 for (auto &arg : CI->args())
10184 args.push_back(replacements[arg]);
10185
10186 auto FT = CI->getFunctionType();
10187
10188 auto cal = cast<CallInst>(B.CreateCall(FT, F, args));
10189 cal->setCallingConv(CI->getCallingConv());
10190 cal->setDebugLoc(CI->getDebugLoc());
10191 replacements[CI] = cal;
10192 continue;
10193 }
10194 }
10195 if (auto CI = dyn_cast<GetElementPtrInst>(U)) {
10196 for (auto U : CI->users()) {
10197 users.push_back(std::make_pair(cast<Instruction>(U), CI));
10198 }
10199 SmallVector<Value *, 1> inds;
10200 bool allconst = true;
10201 for (auto &ind : CI->indices()) {
10202 if (!isa<ConstantInt>(ind)) {
10203 allconst = false;
10204 }
10205 inds.push_back(ind);
10206 }
10207 Value *gep;
10208
10209 if (inds.size() == 1) {
10210 gep = ConstantInt::get(
10211 intTy, (DL.getTypeSizeInBits(CI->getSourceElementType()) + 7) / 8);
10212 gep = B.CreateMul(intTy == inds[0]->getType()
10213 ? inds[0]
10214 : B.CreateZExtOrTrunc(inds[0], intTy),
10215 gep, "", true, true);
10216 gep = B.CreateAdd(B.CreatePtrToInt(replacements[val], intTy), gep);
10217 gep = B.CreateIntToPtr(gep, CI->getType());
10218 } else if (!allconst) {
10219 gep = B.CreateGEP(CI->getSourceElementType(), replacements[val], inds);
10220 if (auto ge = cast<GetElementPtrInst>(gep))
10221 ge->setIsInBounds(CI->isInBounds());
10222 } else {
10223 APInt ai(64, 0);
10224 CI->accumulateConstantOffset(DL, ai);
10225 gep = B.CreateIntToPtr(ConstantInt::get(intTy, ai), CI->getType());
10226 }
10227 if (auto I = dyn_cast<Instruction>(gep))
10228 I->setDebugLoc(CI->getDebugLoc());
10229 replacements[CI] = gep;
10230 continue;
10231 }
10232 if (auto LI = dyn_cast<LoadInst>(U)) {
10233 auto diff = toInt(B, replacements[LI->getPointerOperand()]);
10234 SmallVector<Value *, 2> args;
10235 args.push_back(diff);
10236 for (size_t i = argstart; i < num_args; i++)
10237 args.push_back(CI->getArgOperand(i));
10238
10239 if (load_fn->getFunctionType()->getNumParams() != args.size()) {
10240 auto fnName = load_fn->getName();
10241 auto found_numargs = load_fn->getFunctionType()->getNumParams();
10242 auto expected_numargs = args.size();
10243 EmitFailure("IllegalSparse", CI->getDebugLoc(), CI,
10244 " incorrect number of arguments to loader function ",
10245 fnName, " expected ", expected_numargs, " found ",
10246 found_numargs, " - ", *load_fn->getFunctionType());
10247 continue;
10248 } else {
10249 bool tocontinue = false;
10250 for (size_t i = 0; i < args.size(); i++) {
10251 if (load_fn->getFunctionType()->getParamType(i) !=
10252 args[i]->getType()) {
10253 auto fnName = load_fn->getName();
10254 EmitFailure("IllegalSparse", CI->getDebugLoc(), CI,
10255 " incorrect type of argument ", i,
10256 " to loader function ", fnName, " expected ",
10257 *args[i]->getType(), " found ",
10258 load_fn->getFunctionType()->params()[i]);
10259 tocontinue = true;
10260 args[i] = UndefValue::get(args[i]->getType());
10261 }
10262 }
10263 if (tocontinue)
10264 continue;
10265 }
10266 CallInst *call = B.CreateCall(load_fn, args);
10267 call->setDebugLoc(LI->getDebugLoc());
10268 Value *tmp = call;
10269 if (tmp->getType() != LI->getType()) {
10270 if (CastInst::castIsValid(Instruction::BitCast, tmp, LI->getType()))
10271 tmp = B.CreateBitCast(tmp, LI->getType());
10272 else {
10273 auto fnName = load_fn->getName();
10274 EmitFailure("IllegalSparse", CI->getDebugLoc(), CI,
10275 " incorrect return type of loader function ", fnName,
10276 " expected ", *LI->getType(), " found ",
10277 *call->getType());
10278 tmp = UndefValue::get(LI->getType());
10279 }
10280 }
10281 LI->replaceAllUsesWith(tmp);
10282
10283 if (load_fn->hasFnAttribute(Attribute::AlwaysInline)) {
10284 InlineFunctionInfo IFI;
10285 InlineFunction(*call, IFI);
10286 }
10287 toErase.push_back(LI);
10288 continue;
10289 }
10290 if (auto SI = dyn_cast<StoreInst>(U)) {
10291 assert(SI->getValueOperand() != val);
10292 auto diff = toInt(B, replacements[SI->getPointerOperand()]);
10293 SmallVector<Value *, 2> args;
10294 args.push_back(SI->getValueOperand());
10295 auto sty = store_fn->getFunctionType()->getParamType(0);
10296 if (args[0]->getType() != store_fn->getFunctionType()->getParamType(0)) {
10297 if (CastInst::castIsValid(Instruction::BitCast, args[0], sty))
10298 args[0] = B.CreateBitCast(args[0], sty);
10299 else {
10300 auto args0ty = args[0]->getType();
10301 EmitFailure("IllegalSparse", CI->getDebugLoc(), CI,
10302 " first argument of store function must be the type of "
10303 "the store found fn arg type ",
10304 *sty, " expected ", *args0ty);
10305 args[0] = UndefValue::get(sty);
10306 }
10307 }
10308 args.push_back(diff);
10309 for (size_t i = argstart; i < num_args; i++)
10310 args.push_back(CI->getArgOperand(i));
10311
10312 if (store_fn->getFunctionType()->getNumParams() != args.size()) {
10313 auto fnName = store_fn->getName();
10314 auto found_numargs = store_fn->getFunctionType()->getNumParams();
10315 auto expected_numargs = args.size();
10316 EmitFailure("IllegalSparse", CI->getDebugLoc(), CI,
10317 " incorrect number of arguments to store function ", fnName,
10318 " expected ", expected_numargs, " found ", found_numargs,
10319 " - ", *store_fn->getFunctionType());
10320 continue;
10321 } else {
10322 bool tocontinue = false;
10323 for (size_t i = 0; i < args.size(); i++) {
10324 if (store_fn->getFunctionType()->getParamType(i) !=
10325 args[i]->getType()) {
10326 auto fnName = store_fn->getName();
10327 EmitFailure("IllegalSparse", CI->getDebugLoc(), CI,
10328 " incorrect type of argument ", i,
10329 " to storeer function ", fnName, " expected ",
10330 *args[i]->getType(), " found ",
10331 store_fn->getFunctionType()->params()[i]);
10332 tocontinue = true;
10333 args[i] = UndefValue::get(args[i]->getType());
10334 }
10335 }
10336 if (tocontinue)
10337 continue;
10338 }
10339 auto call = B.CreateCall(store_fn, args);
10340 call->setDebugLoc(SI->getDebugLoc());
10341 if (store_fn->hasFnAttribute(Attribute::AlwaysInline)) {
10342 InlineFunctionInfo IFI;
10343 InlineFunction(*call, IFI);
10344 }
10345 toErase.push_back(SI);
10346 continue;
10347 }
10348 remaining = U;
10349 }
10350 for (auto U : toErase)
10351 U->eraseFromParent();
10352
10353 if (!remaining) {
10354 CI->replaceAllUsesWith(Constant::getNullValue(CI->getType()));
10355 CI->eraseFromParent();
10356 } else if (replaceAll) {
10357 EmitFailure("IllegalSparse", remaining->getDebugLoc(), remaining,
10358 " Illegal remaining use (", *remaining, ") of todense (", *CI,
10359 ") in function ", *F);
10360 }
10361}
10362
10363bool LowerSparsification(llvm::Function *F, bool replaceAll) {
10364 auto &DL = F->getParent()->getDataLayout();
10365 bool changed = false;
10366 SmallVector<CallBase *, 1> todo;
10367 SetVector<BasicBlock *> toDenseBlocks;
10368 for (auto &BB : *F) {
10369 for (auto &I : BB) {
10370 if (auto CI = dyn_cast<CallInst>(&I)) {
10371 if (getFuncNameFromCall(CI).contains("__enzyme_todense")) {
10372 todo.push_back(CI);
10373 toDenseBlocks.insert(&BB);
10374 }
10375 }
10376 }
10377 }
10378 for (auto CI : todo) {
10379 changed = true;
10380 replaceToDense(CI, replaceAll, F, DL);
10381 }
10382 todo.clear();
10383
10384 if (changed && EnzymeAutoSparsity) {
10385 PassBuilder PB;
10386 LoopAnalysisManager LAM;
10387 FunctionAnalysisManager FAM;
10388 CGSCCAnalysisManager CGAM;
10389 ModuleAnalysisManager MAM;
10390 PB.registerModuleAnalyses(MAM);
10391 PB.registerFunctionAnalyses(FAM);
10392 PB.registerLoopAnalyses(LAM);
10393 PB.registerCGSCCAnalyses(CGAM);
10394 PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
10395
10396 SimplifyCFGPass(SimplifyCFGOptions()).run(*F, FAM);
10397 InstCombinePass().run(*F, FAM);
10398 // required to make preheaders
10399 LoopSimplifyPass().run(*F, FAM);
10400 fixSparseIndices(*F, FAM, toDenseBlocks);
10401 }
10402
10403 for (auto &BB : *F) {
10404 for (auto &I : BB) {
10405 if (auto CI = dyn_cast<CallInst>(&I)) {
10406 if (getFuncNameFromCall(CI).contains("__enzyme_post_sparse_todense")) {
10407 todo.push_back(CI);
10408 }
10409 }
10410 }
10411 }
10412 for (auto CI : todo) {
10413 changed = true;
10414 replaceToDense(CI, replaceAll, F, DL);
10415 }
10416 return changed;
10417}
static RegisterPass< ActivityAnalysisPrinter > X("print-activity-analysis", "Print Activity Analysis Results")
std::pair< PHINode *, Instruction * > InsertNewCanonicalIV(Loop *L, Type *Ty, const llvm::Twine &Name)
void RemoveRedundantIVs(Loop *L, PHINode *CanonicalIV, Instruction *Increment, MustExitScalarEvolution &SE, llvm::function_ref< void(Instruction *, Value *)> replacer, llvm::function_ref< void(Instruction *)> eraser)
llvm::cl::opt< bool > EnzymeZeroCache
static bool contains(ArrayRef< int > ar, int v)
static bool isZero(llvm::Constant *cst)
bool couldFunctionArgumentCapture(llvm::CallInst *CI, llvm::Value *val)
Is the use of value val as an argument of call CI potentially captured.
cl::opt< bool > EnzymeNoAlias("enzyme-noalias", cl::init(false), cl::Hidden, cl::desc("Force noalias of autodiff"))
cl::opt< bool > EnzymeAggressiveAA("enzyme-aggressive-aa", cl::init(false), cl::Hidden, cl::desc("Use more unstable but aggressive LLVM AA"))
static cl::opt< bool > EnzymePHIRestructure("enzyme-phi-restructure", cl::init(false), cl::Hidden, cl::desc("Whether to restructure phi's to have better unwrap behavior"))
static void lowerSparsePlaceholders(llvm::Function &F)
static bool unifyReturnBlocks(Function &F)
Route every ret of F through one new return block.
static bool reachesAvoiding(BasicBlock *From, BasicBlock *To, BasicBlock *Avoid, const AbortIgnoringPostDom &PDT)
Whether a returning path leads from the end of From back into To without passing through Avoid.
std::shared_ptr< const Constraints > getSparseConditions(bool &legal, Value *val, std::shared_ptr< const Constraints > defaultFloat, Instruction *scope, const ConstraintContext &ctx)
static void ForceRecursiveInlining(Function *NewF, size_t Limit)
Perform recursive inlinining on NewF up to the given limit.
static MergeResult MergeGCPreserveEnds(CallInst *Begin, ArrayRef< CallInst * > AllEnds, DominatorTree &DT, AbortIgnoringPostDom &PDT, bool &CFGChanged)
Give Begin a single returning llvm.julia.gc_preserve_end that post-dominates it, replacing the severa...
RecurType
@ DefinitelyRecursive
@ MaybeRecursive
@ NotRecursive
FunctionType * getFunctionTypeForClone(llvm::FunctionType *FTy, DerivativeMode mode, unsigned width, llvm::Type *additionalArg, llvm::ArrayRef< DIFFE_TYPE > constant_args, bool diffeReturnArg, bool returnTape, bool returnPrimal, bool returnShadow)
void RemoveRedundantPHI(Function *F, FunctionAnalysisManager &FAM)
void setFullWillReturn(Function *NewF)
cl::opt< bool > EnzymeSelectOpt("enzyme-select-opt", cl::init(true), cl::Hidden, cl::desc("Run Enzyme select optimization"))
void fixSparseIndices(llvm::Function &F, llvm::FunctionAnalysisManager &FAM, SetVector< BasicBlock * > &toDenseBlocks)
static bool isReturnLikeParam(const Function &F, unsigned argno)
void SplitPHIs(llvm::Function &F)
cl::opt< bool > EnzymePreopt("enzyme-preopt", cl::init(true), cl::Hidden, cl::desc("Run enzyme preprocessing optimizations"))
void ReplaceFunctionImplementation(Module &M)
bool guaranteedDataDependent(Value *z)
cl::opt< bool > EnzymeCoalese("enzyme-coalese", cl::init(false), cl::Hidden, cl::desc("Whether to coalese memory allocations"))
cl::opt< bool > EnzymeInline("enzyme-inline", cl::init(false), cl::Hidden, cl::desc("Force inlining of autodiff"))
bool cannotDependOnLoopIV(const SCEV *S, const Loop *L)
constexpr bool SparseDebug
std::optional< std::string > fixSparse_inner(Instruction *cur, llvm::Function &F, QueueType &Q, DominatorTree &DT, ScalarEvolution &SE, LoopInfo &LI, const DataLayout &DL)
bool DetectPointerArgOfFn(llvm::Function &F, SmallPtrSetImpl< Function * > &calls_todo)
DominatorOrderSet QueueType
bool directlySparse(Value *z)
void CoaleseTrivialMallocs(Function &F, DominatorTree &DT)
SmallVector< Value *, 1 > callOperands(llvm::CallBase *CB)
void RecursivelyReplaceAddressSpace(SmallVector< std::tuple< Value *, Value *, Instruction * >, 1 > &Todo, SmallVector< Instruction *, 1 > &toErase, bool legal)
Function * CreateMPIWrapper(Function *F)
void replaceToDense(llvm::CallBase *CI, bool replaceAll, llvm::Function *F, const llvm::DataLayout &DL)
cl::opt< bool > EnzymeNameInstructions("enzyme-name-instructions", cl::init(false), cl::Hidden, cl::desc("Have enzyme name all instructions"))
void simplifyExtractions(Function *NewF)
static AllocaInst * OldAllocationSize(Value *Ptr, CallInst *Loc, Function *NewF, IntegerType *T, const std::map< CallInst *, Value * > &reallocSizes)
void dump(const Constraints &c)
static void eraseSparseInbounds(llvm::Function &F)
bool DetectNoUnwindOfFn(llvm::Function &F, SmallPtrSetImpl< Function * > &calls_todo)
static bool OnlyUsedInOMP(AllocaInst *AI)
void CanonicalizeLoops(Function *F, FunctionAnalysisManager &FAM)
bool DetectReadonlyOrThrow(Module &M)
static bool IsFunctionRecursive(Function *F, std::map< const Function *, RecurType > &Results)
Return whether this function eventually calls itself.
CallInst * isSum(llvm::Value *v)
static void SimplifyMPIQueries(Function &NewF, FunctionAnalysisManager &FAM)
bool LowerSparsification(llvm::Function *F, bool replaceAll)
Lower __enzyme_todense, returning if changed.
static bool isNot(Value *a, Value *b)
Function * getProductIntrinsic(llvm::Module &M, llvm::Type *T)
cl::opt< int > EnzymePostInlineOpt("enzyme-post-inline-opt", cl::init(0), cl::Hidden, cl::desc("Force inlining of autodiff"))
cl::opt< int > EnzymeInlineCount("enzyme-inline-count", cl::init(10000), cl::Hidden, cl::desc("Limit of number of functions to inline"))
CallInst * isProduct(llvm::Value *v)
raw_ostream & operator<<(raw_ostream &os, const Constraints &c)
void SelectOptimization(Function *F)
cl::opt< bool > EnzymeAutoSparsity("enzyme-auto-sparsity", cl::init(false), cl::Hidden, cl::desc("Run Enzyme auto sparsity"))
const SCEV * evaluateAtLoopIter(const SCEV *V, ScalarEvolution &SE, const Loop *find, const SCEV *replace)
static void UpgradeAllocasToMallocs(Function *NewF, DerivativeMode mode, SmallPtrSetImpl< llvm::BasicBlock * > &Unreachable)
Convert necessary stack allocations into mallocs for use in the reverse pass.
static bool CanonicalizeGCPreserveEnds(Function &F, FunctionAnalysisManager &FAM)
A pass such as JumpThreading is free to clone a basic block holding an llvm.julia....
cl::opt< bool > EnzymeLowerGlobals("enzyme-lower-globals", cl::init(false), cl::Hidden, cl::desc("Lower globals to locals assuming the global values are not " "needed outside of this gradient"))
cl::opt< int > EnzymePostOptLevel("enzyme-post-opt-level", cl::init(0), cl::Hidden, cl::desc("Post optimization level within Enzyme differentiated function"))
static bool mayReadThroughCustomRule(Function &F, unsigned argno)
Function * getSumIntrinsic(llvm::Module &M, llvm::Type *T)
bool couldFunctionArgumentCapture(llvm::CallInst *CI, llvm::Value *val)
Is the use of value val as an argument of call CI potentially captured.
llvm::cl::opt< bool > EnzymeAlwaysInlineDiff
void RecursivelyReplaceAddressSpace(llvm::Value *AI, llvm::Value *rep, bool legal)
llvm::FunctionType * getFunctionTypeForClone(llvm::FunctionType *FTy, DerivativeMode mode, unsigned width, llvm::Type *additionalArg, llvm::ArrayRef< DIFFE_TYPE > constant_args, bool diffeReturnArg, bool returnTape, bool returnPrimal, bool returnShadow)
void ReplaceFunctionImplementation(llvm::Module &M)
static llvm::SmallPtrSet< llvm::BasicBlock *, 4 > getGuaranteedUnreachable(llvm::Function *F)
static bool isDeallocationCall(const llvm::Value *TmpOrig, llvm::TargetLibraryInfo &TLI)
static bool isAllocationFunction(const llvm::StringRef name, const llvm::TargetLibraryInfo &TLI)
Return whether a given function is a known C/C++ memory allocation function For updating below one sh...
static bool isAllocationCall(const llvm::Value *TmpOrig, llvm::TargetLibraryInfo &TLI)
static bool isReadOnly(Operation *op)
static Operation * getFunctionFromCall(CallOpInterface iface)
llvm::cl::opt< bool > EnzymePrintActivity
llvm::cl::opt< bool > EnzymePrint
StringMap< std::function< Value *(IRBuilder<> &, CallInst *, ArrayRef< Value * >, GradientUtils *)> > shadowHandlers
static bool isMemFreeLibMFunction(llvm::StringRef str, llvm::Intrinsic::ID *ID=nullptr)
llvm::SmallVector< llvm::Instruction *, 2 > PostCacheStore(llvm::StoreInst *SI, llvm::IRBuilder<> &B)
Definition Utils.cpp:517
llvm::PointerType * getDefaultAnonymousTapeType(llvm::LLVMContext &C)
Definition Utils.cpp:531
Value * simplifyLoad(Value *V, size_t valSz, size_t preOffset)
Definition Utils.cpp:3664
llvm::Value * nextPowerOfTwo(llvm::IRBuilder<> &B, llvm::Value *V)
Create function to computer nearest power of two.
Definition Utils.cpp:2415
Value * CreateAllocation(IRBuilder<> &Builder, llvm::Type *T, Value *Count, const Twine &Name, CallInst **caller, Instruction **ZeroMem, bool isDefault)
Definition Utils.cpp:714
LLVMValueRef(* CustomErrorHandler)(const char *, LLVMValueRef, ErrorType, const void *, LLVMValueRef, LLVMBuilderRef)
Definition Utils.cpp:62
llvm::SmallVector< llvm::Value *, 1 > getJuliaObjects(llvm::Value *v, llvm::IRBuilder<> &B)
Definition Utils.cpp:5347
const SCEV * evaluateAtIterationWithoutExt(const SCEVAddRecExpr *AR, const SCEV *It, ScalarEvolution &SE)
Definition Utils.cpp:2801
llvm::FastMathFlags getFast()
Get LLVM fast math flags.
Definition Utils.cpp:4026
static llvm::StringRef getFuncName(llvm::Function *called)
Definition Utils.h:1461
static bool isIntelSubscriptIntrinsic(const llvm::IntrinsicInst &II)
Definition Utils.h:1649
static bool hasCustomRuleMetadata(const T *V)
Definition Utils.h:1626
static llvm::PointerType * getPointerType(llvm::Type *T, unsigned AddressSpace=0)
Pointer into the given address space.
Definition Utils.h:1313
ReturnType
Potential return type of generated functions.
Definition Utils.h:499
@ Args
Return is a struct of all args.
static bool startsWith(llvm::StringRef string, llvm::StringRef prefix)
Definition Utils.h:847
static llvm::PointerType * getUnqual(llvm::Type *T)
Definition Utils.h:1329
void EmitFailure(llvm::StringRef RemarkName, const llvm::DiagnosticLocation &Loc, const llvm::Instruction *CodeRegion, Args &...args)
Definition Utils.h:347
static llvm::PointerType * getInt8PtrTy(llvm::LLVMContext &Context, unsigned AddressSpace=0)
Definition Utils.h:1324
static llvm::Function * getIntrinsicDeclaration(llvm::Module *M, llvm::Intrinsic::ID id, llvm::ArrayRef< llvm::Type * > Tys={})
Definition Utils.h:2541
static bool isNoCapture(const llvm::CallBase *call, size_t idx)
Definition Utils.h:2109
static void addFunctionNoCapture(llvm::Function *call, size_t idx)
Definition Utils.h:2609
static bool isDebugFunction(llvm::Function *called)
Definition Utils.h:824
static bool isPointerArithmeticInst(const llvm::Value *V, bool includephi=true, bool includebin=true)
Definition Utils.h:1660
static llvm::Value * getBaseObject(llvm::Value *V, bool offsetAllowed=true)
Definition Utils.h:1711
static llvm::MDNode * hasMetadata(const llvm::GlobalObject *O, llvm::StringRef kind)
Check if a global has metadata.
Definition Utils.h:483
static llvm::PointerType * changePointerAddrSpace(llvm::PointerType *PT, unsigned AddressSpace)
The same pointer type, in a different address space.
Definition Utils.h:1338
static llvm::SetVector< llvm::Value * > getBaseObjects(llvm::Value *V, bool offsetAllowed=true)
Definition Utils.h:1847
void EmitWarning(llvm::StringRef RemarkName, const llvm::DiagnosticLocation &Loc, const llvm::BasicBlock *BB, const Args &...args)
Definition Utils.h:252
static bool isReadOnlyOrThrow(const llvm::Function *F)
Definition Utils.h:2004
static llvm::Instruction * getFirstNonPHI(llvm::BasicBlock *B)
Definition Utils.h:2550
llvm::cl::opt< bool > EnzymeJuliaAddrLoad
static bool isConditionalBranch(const llvm::Value *V)
Definition Utils.h:65
static llvm::Value * getBranchCondition(llvm::Value *V)
The condition of a branch isConditionalBranch says yes to.
Definition Utils.h:100
static bool hasSRetRRootsOrUnionSRet(llvm::CallBase *CB)
Definition Utils.h:2918
static bool isUnconditionalBranch(const llvm::Value *V)
Definition Utils.h:75
static void addCallSiteNoCapture(llvm::CallBase *call, size_t idx)
Definition Utils.h:2599
static void resetClonedGUID(llvm::Function *F)
Definition Utils.h:91
llvm::cl::opt< bool > EnzymePrintPerf
Print additional debug info relevant to performance.
static bool isAnyBranch(const llvm::Value *V)
Definition Utils.h:57
static void setBranchCondition(llvm::Value *V, llvm::Value *Cond)
Definition Utils.h:108
static std::string convertSRetTypeToString(llvm::Type *T)
Definition Utils.h:2853
static llvm::StringRef getFuncNameFromCall(const llvm::CallBase *op)
Definition Utils.h:1470
static bool isWriteOnly(const llvm::Function *F, ssize_t arg=-1)
Definition Utils.h:2033
static bool isLocalReadOnlyOrThrow(const llvm::Function *F)
Definition Utils.h:1959
static llvm::Attribute::AttrKind PrimalParamAttrsToPreserve[]
Definition Utils.h:2486
static llvm::Attribute::AttrKind ShadowParamAttrsToPreserve[]
Definition Utils.h:2518
static void moveBeforeInst(llvm::Instruction *I, llvm::Instruction *Before)
Definition Utils.h:2571
DerivativeMode
Definition Utils.h:524
const SCEV *const node
static SetTy intersect(const SetTy &lhs, const SetTy &rhs)
bool operator==(const Constraints &rhs) const
bool operator!=(const Constraints &rhs) const
std::set< InnerTy, ConstraintComparator > SetTy
unsigned hash() const
Constraints(Type t, const SetTy &c, bool check=true)
static InnerTy make_compare(const SCEV *v, bool isEqual, const llvm::Loop *Loop, const ConstraintContext &ctx)
InnerTy notB(const ConstraintContext &ctx) const
InnerTy orB(InnerTy rhs, const ConstraintContext &ctx) const
static void insert(SetTy &set, InnerTy ty)
static InnerTy none()
const llvm::Loop *const Loop
bool operator>(const Constraints &rhs) const
bool isNone() const
static InnerTy all()
std::shared_ptr< const Constraints > InnerTy
__attribute__((noinline)) void dump() const
InnerTy andB(const InnerTy rhs, const ConstraintContext &ctx) const
enum Constraints::Type ty
bool isAll() const
const SetTy values
static void set_subtract(SetTy &set, const SetTy &rhs)
InnerTy remove(const SetTy &sub) const
SmallVector< std::pair< Value *, Value * >, 1 > allSolutions(SCEVExpander &Exp, llvm::Type *T, Instruction *IP, const ConstraintContext &ctx, IRBuilder<> &B) const
bool operator<(const Constraints &rhs) const
void remove(Instruction *I)
bool contains(Instruction *I) const
DominatorOrderSet(DominatorTree &DT, LoopInfo &LI)
Instruction * pop_back_val()
static llvm::Value * extractMeta(llvm::IRBuilder<> &Builder, llvm::Value *Agg, unsigned off, const llvm::Twine &name="")
Helper routine to extract a nested element from a struct/array. This is.
static llvm::Type * getShadowType(llvm::Type *ty, unsigned width)
llvm::Function * CloneFunctionWithReturns(DerivativeMode mode, unsigned width, llvm::Function *&F, llvm::ValueToValueMapTy &ptrInputs, llvm::ArrayRef< DIFFE_TYPE > constant_args, llvm::SmallPtrSetImpl< llvm::Value * > &constants, llvm::SmallPtrSetImpl< llvm::Value * > &nonconstant, llvm::SmallPtrSetImpl< llvm::Value * > &returnvals, bool returnTape, bool returnPrimal, bool returnShadow, const llvm::Twine &name, llvm::ValueMap< const llvm::Value *, AssertingReplacingVH > *VMapO, bool diffeReturnArg, llvm::Type *additionalArg=nullptr)
std::map< llvm::Function *, llvm::Function * > CloneOrigin
llvm::Function * preprocessForClone(llvm::Function *F, DerivativeMode mode)
llvm::ModuleAnalysisManager MAM
void optimizeIntermediate(llvm::Function *F)
llvm::LoopAnalysisManager LAM
void AlwaysInline(llvm::Function *NewF)
llvm::AAResults & getAAResultsFromFunction(llvm::Function *NewF)
std::map< std::pair< llvm::Function *, DerivativeMode >, llvm::Function * > cache
llvm::FunctionAnalysisManager FAM
void LowerAllocAddr(llvm::Function *NewF)
void ReplaceReallocs(llvm::Function *NewF, bool mem2reg=false)
Calls to realloc with an appropriate implementation.
ReadOnlyOrThrowDetector(llvm::Function &F, SmallPtrSetImpl< Function * > &calls_todo, llvm::TargetLibraryInfo &TLI, bool &local)
Class representing the underlying types of values as sequences of offsets to a ConcreteType.
Definition TypeTree.h:72
static TypeTree parse(llvm::StringRef str, llvm::LLVMContext &ctx)
Definition TypeTree.h:86
static TypeTree fromMD(llvm::MDNode *md)
Definition TypeTree.h:1516
bool operator()(std::shared_ptr< const Constraints > lhs, std::shared_ptr< const Constraints > rhs) const
ScalarEvolution & SE
DominatorTree & DT
ConstraintContext(const ConstraintContext &ctx, InnerTy lhs)
ConstraintContext(const ConstraintContext &ctx, InnerTy lhs, InnerTy rhs)
ConstraintContext(const ConstraintContext &)=delete
std::set< InnerTy, ConstraintComparator > SetTy
const Loop * loopToSolve
ConstraintContext(ScalarEvolution &SE, const Loop *loopToSolve, const SmallVectorImpl< Instruction * > &Assumptions, DominatorTree &DT)
const SmallVectorImpl< Instruction * > & Assumptions
bool contains(InnerTy x) const
std::shared_ptr< const Constraints > InnerTy
DominatorTree & DT
compare_insts(DominatorTree &DT, LoopInfo &LI)
bool operator()(Instruction *A, Instruction *B) const