Enzyme main
Loading...
Searching...
No Matches
EnzymeLogic.cpp
Go to the documentation of this file.
1#include "Dialect/Ops.h"
7#include "mlir/IR/Matchers.h"
8#include "mlir/Interfaces/FunctionInterfaces.h"
9
10// TODO: this shouldn't depend on specific dialects except Enzyme.
11#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
12
13#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
14#include "mlir/Dialect/Func/IR/FuncOps.h"
15#include "mlir/IR/Dominance.h"
16#include "mlir/Pass/PassManager.h"
17#include "mlir/Pass/PassRegistry.h"
18
19#include "llvm/ADT/BreadthFirstIterator.h"
20
21#include "EnzymeLogic.h"
22#include "GradientUtils.h"
23
24using namespace mlir;
25using namespace mlir::enzyme;
26
28 : solver(dataflowActivity ? new DataFlowSolver(
29 DataFlowConfig().setInterprocedural(false))
30 : nullptr) {}
31
32void createTerminator(MGradientUtils *gutils, mlir::Block *oBB,
33 const ArrayRef<bool> returnPrimals,
34 const ArrayRef<bool> returnShadows) {
35 auto inst = oBB->getTerminator();
36
37 mlir::Block *nBB = gutils->getNewFromOriginal(inst->getBlock());
38 assert(nBB);
39 auto newInst = nBB->getTerminator();
40
41 OpBuilder nBuilder(inst);
42 nBuilder.setInsertionPointToEnd(nBB);
43
44 if (auto binst = dyn_cast<BranchOpInterface>(inst)) {
46 return;
47 }
48
49 // In forward mode we only need to update the return value
51 return;
52
53 SmallVector<mlir::Value, 2> retargs;
54
55 for (auto &&[ret, returnPrimal, returnShadow] :
56 llvm::zip(inst->getOperands(), returnPrimals, returnShadows)) {
57 if (returnPrimal) {
58 retargs.push_back(gutils->getNewFromOriginal(ret));
59 }
60 if (returnShadow) {
61 if (!gutils->isConstantValue(ret)) {
62 retargs.push_back(gutils->invertPointerM(ret, nBuilder));
63 } else {
64 Type retTy = cast<AutoDiffTypeInterface>(ret.getType()).getShadowType();
65 auto toret = cast<AutoDiffTypeInterface>(retTy).createNullValue(
66 nBuilder, ret.getLoc());
67 retargs.push_back(toret);
68 }
69 }
70 }
71
72 if (auto fnIface =
73 dyn_cast<AutoDiffFunctionInterface>(gutils->newFunc.getOperation())) {
74 nBuilder.setInsertionPointToEnd(nBB);
75 fnIface.createReturn(nBuilder, newInst->getLoc(), retargs);
76 } else {
77 nBB->push_back(
78 newInst->create(newInst->getLoc(), newInst->getName(), TypeRange(),
79 retargs, newInst->getAttrs(), mlir::PropertyRef(),
80 newInst->getSuccessors(), newInst->getNumRegions()));
81 }
82 gutils->erase(newInst);
83 return;
84}
85
86//===----------------------------------------------------------------------===//
87//===----------------------------------------------------------------------===//
88
90 FunctionOpInterface fn, std::vector<DIFFE_TYPE> RetActivity,
91 std::vector<DIFFE_TYPE> ArgActivity, MTypeAnalysis &TA,
92 std::vector<bool> returnPrimals, DerivativeMode mode, bool freeMemory,
93 size_t width, mlir::Type addedType, MFnTypeInfo type_args,
94 std::vector<bool> overwritten_args, void *augmented, bool omp,
95 llvm::StringRef postpasses, bool verifyPostPasses, bool strongZero) {
96 if (fn.getFunctionBody().empty()) {
97 fn.emitError() << "cannot differentiate a function without a body: "
98 << fn.getNameAttr() << "\n";
99 return nullptr;
100 }
101 assert(fn.getFunctionBody().front().getNumArguments() == ArgActivity.size());
102 assert(fn.getFunctionBody().front().getNumArguments() ==
103 overwritten_args.size());
104
105 MForwardCacheKey tup = {
106 fn, RetActivity, ArgActivity,
107 // std::map<Argument *, bool>(_uncacheable_args.begin(),
108 // _uncacheable_args.end()),
109 returnPrimals, mode, static_cast<unsigned>(width), addedType, type_args,
110 omp, strongZero};
111
112 if (ForwardCachedFunctions.find(tup) != ForwardCachedFunctions.end()) {
113 return ForwardCachedFunctions.find(tup)->second;
114 }
115 std::vector<bool> returnShadows;
116 for (auto act : RetActivity) {
117 returnShadows.push_back(act != DIFFE_TYPE::CONSTANT);
118 }
119 SmallVector<bool> returnPrimalsP(returnPrimals.begin(), returnPrimals.end());
120 SmallVector<bool> returnShadowsP(returnShadows.begin(), returnShadows.end());
122 *this, mode, width, fn, TA, type_args, returnPrimalsP, returnShadowsP,
123 RetActivity, ArgActivity, addedType,
124 /*omp*/ false, postpasses, verifyPostPasses, strongZero);
125 ForwardCachedFunctions[tup] = gutils->newFunc;
126
128 ForwardCachedFunctions, tup, gutils->newFunc);
129
130 // gutils->FreeMemory = freeMemory;
131
132 const SmallPtrSet<mlir::Block *, 4> guaranteedUnreachable;
133 // = getGuaranteedUnreachable(gutils->oldFunc);
134
135 // gutils->forceActiveDetection();
136 gutils->forceAugmentedReturns();
137 /*
138
139 // TODO populate with actual unnecessaryInstructions once the dependency
140 // cycle with activity analysis is removed
141 SmallPtrSet<const Instruction *, 4> unnecessaryInstructionsTmp;
142 for (auto BB : guaranteedUnreachable) {
143 for (auto &I : *BB)
144 unnecessaryInstructionsTmp.insert(&I);
145 }
146 if (mode == DerivativeMode::ForwardModeSplit)
147 gutils->computeGuaranteedFrees();
148
149 SmallPtrSet<const Value *, 4> unnecessaryValues;
150 SmallPtrSet<const Instruction *, 4> unnecessaryInstructions;
151 calculateUnusedValuesInFunction(
152 *gutils->oldFunc, unnecessaryValues, unnecessaryInstructions,
153 returnUsed, mode, gutils, TLI, constant_args, guaranteedUnreachable);
154 gutils->unnecessaryValuesP = &unnecessaryValues;
155
156 SmallPtrSet<const Instruction *, 4> unnecessaryStores;
157 calculateUnusedStoresInFunction(*gutils->oldFunc, unnecessaryStores,
158 unnecessaryInstructions, gutils, TLI);
159 */
160
161 bool valid = true;
162 for (Block &oBB : gutils->oldFunc.getFunctionBody().getBlocks()) {
163 // Don't create derivatives for code that results in termination
164 if (guaranteedUnreachable.find(&oBB) != guaranteedUnreachable.end()) {
165 auto newBB = gutils->getNewFromOriginal(&oBB);
166
167 for (auto &I : make_early_inc_range(reverse(oBB))) {
168 gutils->eraseIfUnused(&I, /*erase*/ true, /*check*/ false);
169 }
170
171 OpBuilder builder(gutils->oldFunc.getContext());
172 builder.setInsertionPointToEnd(newBB);
173 LLVM::UnreachableOp::create(builder, gutils->oldFunc.getLoc());
174 continue;
175 }
176
177 assert(oBB.getTerminator());
178
179 auto first = oBB.begin();
180 auto last = oBB.empty() ? oBB.end() : std::prev(oBB.end());
181 for (auto it = first; it != last; ++it) {
182 // TODO: propagate errors.
183 auto res = gutils->visitChild(&*it);
184 valid &= res.succeeded();
185 }
186
187 createTerminator(gutils, &oBB, returnPrimalsP, returnShadowsP);
188 }
190 // if (mode == DerivativeMode::ForwardModeSplit && augmenteddata)
191 // restoreCache(gutils, augmenteddata->tapeIndices, guaranteedUnreachable);
192
193 // gutils->eraseFictiousPHIs();
195 // mlir::Block *entry = &gutils->newFunc.getFunctionBody().front();
196
197 // cleanupInversionAllocs(gutils, entry);
198 // clearFunctionAttributes(gutils->newFunc);
199
200 /*
201 if (llvm::verifyFunction(*gutils->newFunc, &llvm::errs())) {
202 llvm::errs() << *gutils->oldFunc << "\n";
203 llvm::errs() << *gutils->newFunc << "\n";
204 report_fatal_error("function failed verification (4)");
205 }
206 */
207
208 auto nf = gutils->newFunc;
209 delete gutils;
211 if (!valid)
212 return nullptr;
214 if (postpasses != "") {
215 mlir::PassManager pm(nf->getContext());
216 pm.enableVerifier(verifyPostPasses);
217 std::string error_message;
218 // llvm::raw_string_ostream error_stream(error_message);
219 mlir::LogicalResult result = mlir::parsePassPipeline(postpasses, pm);
220 if (mlir::failed(result)) {
221 return nullptr;
224 if (!mlir::succeeded(pm.run(nf))) {
225 return nullptr;
227 }
228
229 return nf;
230}
void createTerminator(DiffeGradientUtils *gutils, BasicBlock *oBB, DIFFE_TYPE retType, bool returnPrimal, bool returnShadow)
static std::map< K, V >::iterator insert_or_assign2(std::map< K, V > &map, K key, V val)
Insert into a map.
Definition Utils.h:990
DerivativeMode
Definition Utils.h:524
static MDiffeGradientUtils * CreateFromClone(MEnzymeLogic &Logic, DerivativeMode mode, unsigned width, FunctionOpInterface todiff, MTypeAnalysis &TA, MFnTypeInfo &oldTypeInfo, const llvm::ArrayRef< bool > returnPrimals, const llvm::ArrayRef< bool > returnShadows, ArrayRef< DIFFE_TYPE > RetActivity, ArrayRef< DIFFE_TYPE > ArgActivity, mlir::Type additionalArg, bool omp, llvm::StringRef postpasses, bool verifyPostPasses, bool strongZero)
FunctionOpInterface CreateForwardDiff(FunctionOpInterface fn, std::vector< DIFFE_TYPE > retType, std::vector< DIFFE_TYPE > constants, MTypeAnalysis &TA, std::vector< bool > returnPrimals, DerivativeMode mode, bool freeMemory, size_t width, mlir::Type addedType, MFnTypeInfo type_args, std::vector< bool > overwritten_args, void *augmented, bool omp, llvm::StringRef postpasses, bool verifyPostPasses, bool strongZero)
MEnzymeLogic(bool dataflowActivity)
void erase(Operation *op)
mlir::Value invertPointerM(mlir::Value v, OpBuilder &Builder2)
SmallVector< mlir::Value, 1 > getNewFromOriginal(ValueRange originst) const
FunctionOpInterface newFunc
bool isConstantValue(mlir::Value v) const
void branchingForwardHandler(Operation *op, OpBuilder &builder, MGradientUtils *gutils)
bool isFunctionReturnLike(Operation *op)