Enzyme main
Loading...
Searching...
No Matches
GradientUtils.cpp
Go to the documentation of this file.
1//===- GradientUtils.cpp - Utilities for gradient interfaces --------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "Dialect/Ops.h"
14#include "Interfaces/Utils.h"
15
16#include "mlir/IR/Matchers.h"
17#include "mlir/IR/SymbolTable.h"
18#include "mlir/Interfaces/FunctionInterfaces.h"
19
20// TODO: this shouldn't depend on specific dialects except Enzyme.
21#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
22
23#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
24#include "mlir/Dialect/Func/IR/FuncOps.h"
25#include "mlir/IR/Dominance.h"
26#include "llvm/ADT/BreadthFirstIterator.h"
27
28#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
29#include "mlir/Interfaces/SideEffectInterfaces.h"
30#include "mlir/Interfaces/ViewLikeInterface.h"
31
32using namespace mlir;
33using namespace mlir::enzyme;
34
36 MEnzymeLogic &Logic, FunctionOpInterface newFunc_,
37 FunctionOpInterface oldFunc_, MTypeAnalysis &TA_, MTypeResults TR_,
38 IRMapping &invertedPointers_, const llvm::ArrayRef<bool> returnPrimals,
39 const llvm::ArrayRef<bool> returnShadows,
40 const SmallPtrSetImpl<mlir::Value> &constantvalues_,
41 const SmallPtrSetImpl<mlir::Value> &activevals_,
42 ArrayRef<DIFFE_TYPE> ReturnActivity, ArrayRef<DIFFE_TYPE> ArgDiffeTypes_,
43 IRMapping &originalToNewFn_,
44 std::map<Operation *, Operation *> &originalToNewFnOps_,
45 DerivativeMode mode, unsigned width, bool omp, llvm::StringRef postpasses,
46 bool verifyPostPasses, bool strongZero)
47 : newFunc(newFunc_), Logic(Logic), AtomicAdd(false), mode(mode),
48 oldFunc(oldFunc_), invertedPointers(invertedPointers_),
49 originalToNewFn(originalToNewFn_),
50 originalToNewFnOps(originalToNewFnOps_), blocksNotForAnalysis(),
51 activityAnalyzer(std::make_unique<enzyme::ActivityAnalyzer>(
52 blocksNotForAnalysis, readOnlyCache, constantvalues_, activevals_,
53 ReturnActivity)),
54 TA(TA_), TR(TR_), omp(omp), verifyPostPasses(verifyPostPasses),
55 postpasses(postpasses), strongZero(strongZero),
56 returnPrimals(returnPrimals), returnShadows(returnShadows), width(width),
57 ArgDiffeTypes(ArgDiffeTypes_), RetDiffeTypes(ReturnActivity) {
58 if (Logic.solver) {
59 dataflowSolver = std::make_unique<DataFlowSolver>(
60 DataFlowConfig().setInterprocedural(false));
61 dataflowActivityAnalyzer =
62 std::make_unique<enzyme::DataFlowActivityAnalyzer>(
63 *dataflowSolver, oldFunc_, ArgDiffeTypes_, ReturnActivity);
64 }
65}
66
68 const mlir::Value originst) const {
69 if (!originalToNewFn.contains(originst)) {
70 llvm::errs() << oldFunc << "\n";
71 llvm::errs() << newFunc << "\n";
72 llvm::errs() << originst << "\n";
73 llvm_unreachable("Could not get new val from original");
74 }
75 return originalToNewFn.lookupOrNull(originst);
76}
77
78SmallVector<mlir::Value, 1>
80 SmallVector<mlir::Value, 1> results;
81 for (auto op : originst) {
82 results.push_back(getNewFromOriginal(op));
83 }
84 return results;
85}
86
87Block *
89 if (!originalToNewFn.contains(originst)) {
90 llvm::errs() << oldFunc << "\n";
91 llvm::errs() << newFunc << "\n";
92 llvm::errs() << originst << "\n";
93 llvm_unreachable("Could not get new blk from original");
94 }
95 return originalToNewFn.lookupOrNull(originst);
96}
97
98Operation *
100 assert(originst);
101 auto found = originalToNewFnOps.find(originst);
102 if (found == originalToNewFnOps.end()) {
103 llvm::errs() << oldFunc << "\n";
104 llvm::errs() << newFunc << "\n";
105 for (auto &pair : originalToNewFnOps) {
106 llvm::errs() << " map[" << pair.first << "] = " << pair.second << "\n";
107 llvm::errs() << " map[" << *pair.first << "] = " << *pair.second << "\n";
108 }
109 llvm::errs() << originst << " - " << *originst << "\n";
110 llvm_unreachable("Could not get new op from original");
111 }
112 return found->second;
113}
114
116 Operation *op) {
117 IRMapping map;
118 for (auto operand : op->getOperands())
119 map.map(operand, getNewFromOriginal(operand));
120 return B.clone(*op, map);
121}
122
124 if (dataflowActivityAnalyzer)
125 return dataflowActivityAnalyzer->isInactiveOperation(op);
126 return activityAnalyzer->isConstantOperation(TR, op);
129 if (dataflowActivityAnalyzer)
130 return dataflowActivityAnalyzer->isInactiveValue(v);
131 return activityAnalyzer->isConstantValue(TR, v);
132}
133
135 OpBuilder &Builder2) {
136 // TODO
137 if (invertedPointers.contains(v))
138 return invertedPointers.lookupOrNull(v);
139
140 if (isConstantValue(v)) {
141 if (auto iface =
142 dyn_cast<AutoDiffTypeInterface>(getShadowType(v.getType()))) {
143 OpBuilder::InsertionGuard guard(Builder2);
144 if (auto op = v.getDefiningOp())
145 Builder2.setInsertionPoint(getNewFromOriginal(op));
146 else {
147 auto ba = cast<BlockArgument>(v);
148 Builder2.setInsertionPointToStart(getNewFromOriginal(ba.getOwner()));
149 }
150 Value dv = iface.createNullValue(Builder2, v.getLoc());
151 invertedPointers.map(v, dv);
152 return dv;
153 }
154 return getNewFromOriginal(v);
155 }
156 llvm::errs() << " could not invert pointer v " << v << "\n";
157 llvm_unreachable("could not invert pointer");
158}
159
161 std::function<Value(Location, Type)> hook) {
162 if (hook != nullptr)
163 gradientCreatorHook.push_back(hook);
164}
165
167 std::function<Value(Location, Type)> hook) {
168 if (hook != nullptr)
169 gradientCreatorHook.pop_back();
170}
171
172Value MDiffeGradientUtils::getNewGradient(Location loc, Type t) {
173 if (gradientCreatorHook.empty()) {
174 auto shadowty = getShadowType(t);
175 OpBuilder builder(t.getContext());
176 builder.setInsertionPointToStart(initializationBlock);
177
178 auto shadow = enzyme::InitOp::create(
179 builder, loc, enzyme::GradientType::get(t.getContext(), shadowty));
180 auto toset =
181 cast<AutoDiffTypeInterface>(shadowty).createNullValue(builder, loc);
182 enzyme::SetOp::create(builder, loc, shadow, toset);
183 return shadow;
184 } else {
185 return gradientCreatorHook.back()(loc, t);
186 }
187}
188
189mlir::Value
191 auto found = differentials.lookupOrNull(oval);
192 if (found != nullptr)
193 return found;
194
195 Value shadow = getNewGradient(oval.getLoc(), oval.getType());
196 differentials.map(oval, shadow);
197 return shadow;
198}
199
201 mlir::Value toset,
202 OpBuilder &BuilderM) {
203 assert(!isConstantValue(oval));
204 auto iface = cast<AutoDiffTypeInterface>(oval.getType());
205 if (!(mode == DerivativeMode::ForwardMode ||
207 !iface.isMutable()) {
208 auto shadow = getDifferential(oval);
209 enzyme::SetOp::create(BuilderM, oval.getLoc(), shadow, toset);
210 } else {
211 MGradientUtils::setDiffe(oval, toset, BuilderM);
212 }
213}
214
216 OpBuilder &BuilderM) {
217 assert(!isConstantValue(oval));
218 auto iface = cast<AutoDiffTypeInterface>(getShadowType(oval.getType()));
219 assert(!iface.isMutable());
220 setDiffe(oval, iface.createNullValue(BuilderM, oval.getLoc()), BuilderM);
221}
222
223mlir::Value mlir::enzyme::MDiffeGradientUtils::diffe(mlir::Value oval,
224 OpBuilder &BuilderM) {
225
226 auto shadow = getDifferential(oval);
227 return enzyme::GetOp::create(BuilderM, oval.getLoc(),
228 getShadowType(oval.getType()), shadow);
229}
230
231void mlir::enzyme::MGradientUtils::setDiffe(mlir::Value val, mlir::Value toset,
232 OpBuilder &BuilderM) {
233 /*
234 if (auto arg = dyn_cast<Argument>(val))
235 assert(arg->getParent() == oldFunc);
236 if (auto inst = dyn_cast<Instruction>(val))
237 assert(inst->getParent()->getParent() == oldFunc);
238 */
239 if (isConstantValue(val)) {
240 llvm::errs() << newFunc << "\n";
241 llvm::errs() << val << "\n";
242 }
243 assert(!isConstantValue(val));
244
245 if (mode == DerivativeMode::ForwardMode ||
247 setInvertedPointer(val, toset);
248 }
249 /*
250 Value *tostore = getDifferential(val);
251 if (toset->getType() != tostore->getType()->getPointerElementType()) {
252 llvm::errs() << "toset:" << *toset << "\n";
253 llvm::errs() << "tostore:" << *tostore << "\n";
254 }
255 assert(toset->getType() == tostore->getType()->getPointerElementType());
256 BuilderM.CreateStore(toset, tostore);
257 */
258}
259
261 assert(getShadowType(val.getType()) == toset.getType());
262 auto found = invertedPointers.lookupOrNull(val);
263 assert(found != nullptr);
264 auto placeholder = found.getDefiningOp<enzyme::PlaceholderOp>();
265 placeholder.replaceAllUsesWith(toset);
266 erase(placeholder);
267 invertedPointers.map(val, toset);
268}
269
271 // TODO also block arguments
272 // assert(TR.getFunction() == oldFunc);
273
274 // Don't create derivatives for code that results in termination
275 // if (notForAnalysis.find(&oBB) != notForAnalysis.end())
276 // continue;
277
278 // LoopContext loopContext;
279 // getContext(cast<BasicBlock>(getNewFromOriginal(&oBB)), loopContext);
280
281 oldFunc.walk([&](Block *blk) {
282 if (blk == &oldFunc.getFunctionBody().getBlocks().front())
283 return;
284 auto nblk = getNewFromOriginal(blk);
285 for (auto val : llvm::reverse(blk->getArguments())) {
286 if (isConstantValue(val))
287 continue;
288 auto i = val.getArgNumber();
289 if (mode == DerivativeMode::ForwardMode ||
291 cast<AutoDiffTypeInterface>(val.getType()).isMutable()) {
292 mlir::Value dval;
293 if (i == blk->getArguments().size() - 1)
294 dval = nblk->addArgument(getShadowType(val.getType()), val.getLoc());
295 else
296 dval =
297 nblk->insertArgument(nblk->args_begin() + i + 1,
298 getShadowType(val.getType()), val.getLoc());
299
300 invertedPointers.map(val, dval);
301 }
302 }
303 });
304
305 oldFunc.walk([&](Operation *inst) {
306 if (inst == oldFunc)
307 return;
308
309 OpBuilder BuilderZ(getNewFromOriginal(inst));
310 for (auto res : inst->getResults()) {
311 if (isConstantValue(res))
312 continue;
313
314 if (!(mode == DerivativeMode::ForwardMode ||
316 cast<AutoDiffTypeInterface>(res.getType()).isMutable()))
317 continue;
318 mlir::Type antiTy = getShadowType(res.getType());
319 auto anti = enzyme::PlaceholderOp::create(BuilderZ, res.getLoc(), antiTy);
320 invertedPointers.map(res, anti);
321 }
322 });
323}
324
325LogicalResult MGradientUtils::visitChild(Operation *op) {
327 // An op with side effects may still need to touch shadow memory even when
328 // it is constant: a store of an inactive value into active memory has to
329 // zero the shadow, or a later load reads a stale tangent. Only skip it if
330 // it is pure, or if every operand is constant and there is no shadow to
331 // write through.
332 if ((op->getBlock()->getTerminator() != op) &&
333 llvm::all_of(op->getResults(),
334 [this](Value v) { return isConstantValue(v); }) &&
335 (isPure(op) ||
336 llvm::all_of(op->getOperands(),
337 [this](Value v) { return isConstantValue(v); })) &&
339 return success();
340 }
341 // }
342 if (auto iface = dyn_cast<AutoDiffOpInterface>(op)) {
343 OpBuilder builder(op->getContext());
344 builder.setInsertionPoint(getNewFromOriginal(op));
345 return iface.createForwardModeTangent(builder, this);
346 }
347 }
348 return op->emitError() << "could not compute the adjoint for this operation "
349 << *op;
350}
351
355
357 Value base = getBaseObject(ptr);
358 auto blockArg = dyn_cast<BlockArgument>(base);
359 if (!blockArg)
360 return DIFFE_TYPE::DUP_ARG;
361 Block *owner = blockArg.getOwner();
362 if (owner != &oldFunc.getFunctionBody().front())
363 return DIFFE_TYPE::DUP_ARG;
364 unsigned idx = blockArg.getArgNumber();
365 if (idx >= ArgDiffeTypes.size())
366 return DIFFE_TYPE::DUP_ARG;
367 return ArgDiffeTypes[idx];
368}
Type getShadowType(Type type, unsigned width)
DIFFE_TYPE
Potential differentiable argument classifications.
DerivativeMode
Definition Utils.h:524
Helper class to analyze the differential activity.
SmallVector< std::function< Value(Location, Type)> > gradientCreatorHook
void registerGradientCreatorHook(std::function< Value(Location, Type)> hook)
mlir::Value getDifferential(mlir::Value origv)
void setDiffe(mlir::Value origv, mlir::Value newv, mlir::OpBuilder &builder)
void deregisterGradientCreatorHook(std::function< Value(Location, Type)> hook)
Value getNewGradient(Location loc, Type t)
mlir::Value diffe(mlir::Value origv, mlir::OpBuilder &builder)
void zeroDiffe(mlir::Value origv, mlir::OpBuilder &builder)
std::unique_ptr< DataFlowSolver > solver
Operation * cloneWithNewOperands(OpBuilder &B, Operation *op)
mlir::Type getShadowType(mlir::Type T)
FunctionOpInterface oldFunc
ArrayRef< DIFFE_TYPE > ArgDiffeTypes
LogicalResult visitChild(Operation *op)
DIFFE_TYPE getDiffeTypeOfBase(mlir::Value ptr)
The activity the caller declared for the memory this pointer views: the base's entry in ArgDiffeTypes...
void setDiffe(mlir::Value origv, mlir::Value newv, mlir::OpBuilder &builder)
mlir::Value invertPointerM(mlir::Value v, OpBuilder &Builder2)
SmallVector< mlir::Value, 1 > getNewFromOriginal(ValueRange originst) const
MGradientUtils(MEnzymeLogic &Logic, FunctionOpInterface newFunc_, FunctionOpInterface oldFunc_, MTypeAnalysis &TA_, MTypeResults TR_, IRMapping &invertedPointers_, const llvm::ArrayRef< bool > returnPrimals, const llvm::ArrayRef< bool > returnShadows, const SmallPtrSetImpl< mlir::Value > &constantvalues_, const SmallPtrSetImpl< mlir::Value > &activevals_, ArrayRef< DIFFE_TYPE > ReturnActivities, ArrayRef< DIFFE_TYPE > ArgDiffeTypes_, IRMapping &originalToNewFn_, std::map< Operation *, Operation * > &originalToNewFnOps_, DerivativeMode mode, unsigned width, bool omp, llvm::StringRef postpasses, bool verifyPostPasses, bool strongZero)
bool isConstantInstruction(mlir::Operation *v) const
void setInvertedPointer(mlir::Value origv, mlir::Value newv)
bool isConstantValue(mlir::Value v) const
static mlir::Value getBaseObject(mlir::Value v)
The base allocation a pointer-like value views: view-like ops and LLVM geps and casts peeled away,...
Value getBaseObject(Value v, bool offsetAllowed)
Definition Utils.cpp:123