Enzyme main
Loading...
Searching...
No Matches
CoreDialectsAutoDiffImplementations.h
Go to the documentation of this file.
1//===- CoreDialectsAutoDiffImplementation.h - Impl registrations -* C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file contains context registration facilities for external model
10// implementations of the automatic differentiation interface for upstream MLIR
11// dialects.
12//
13//===----------------------------------------------------------------------===//
14
15#ifndef ENZYMEMLIR_CORE_IMPL_H_
16#define ENZYMEMLIR_CORE_IMPL_H_
17
19#include "mlir/Interfaces/FunctionInterfaces.h"
20#include "mlir/Support/LogicalResult.h"
21
22#include "llvm/ADT/DenseSet.h"
23
24namespace mlir {
25class DialectRegistry;
26class Operation;
27class OpBuilder;
28class RegionSuccessor;
29
30namespace enzyme {
31class MGradientUtils;
32class MGradientUtilsReverse;
33
34namespace detail {
35// Non-template implementation of
36// AutoDiffUsingControlFlow::createForwardModeTangent.
37
38LogicalResult controlFlowForwardHandler(Operation *op, OpBuilder &builder,
39 MGradientUtils *gutils);
40
41LogicalResult controlFlowForwardHandler(
42 Operation *op, OpBuilder &builder, MGradientUtils *gutils,
43 const llvm::SmallDenseSet<unsigned> &operandPositionsToShadow,
44 const llvm::SmallDenseSet<unsigned> &resultPositionsToShadow);
45
46// Implements forward-mode differentiation of branching operations.
47// Assumes that successive shadows are legal
48void branchingForwardHandler(Operation *op, OpBuilder &builder,
49 MGradientUtils *gutils);
50
51// Implements forward-mode differentiation of region-terminator operations.
52// Assumes that successive shadows are legal
53void regionTerminatorForwardHandler(Operation *op, OpBuilder &builder,
54 MGradientUtils *gutils);
55
56// Implements reverse-mode differentiation of return operations.
57void returnReverseHandler(Operation *op, OpBuilder &builder,
58 MGradientUtilsReverse *gutils);
59
60// Implements forward-mode differentiation of read-only (including read-none)
61// operations which do not perform computation
62LogicalResult memoryIdentityForwardHandler(Operation *op, OpBuilder &builder,
63 MGradientUtils *gutils,
64 ArrayRef<int> storedVals);
65
66// Implements shadow initialization differentiation of allocation
67LogicalResult allocationForwardHandler(Operation *op, OpBuilder &builder,
68 MGradientUtils *gutils, bool zero);
69
70// Implements the forward autodiff interface for operations whose derivatives
71// are can be inferred by analyzing their control flow and differentiating the
72// nested operations.
73template <typename OpTy>
75 : public AutoDiffOpInterface::ExternalModel<AutoDiffUsingControlFlow<OpTy>,
76 OpTy> {
77public:
78 LogicalResult createForwardModeTangent(Operation *op, OpBuilder &builder,
79 MGradientUtils *gutils) const {
80 return controlFlowForwardHandler(op, builder, gutils);
81 }
82};
83
84// Implements the forward autodiff interface for operations whose derivatives
85// are can be inferred by analyzing their branching properties.
86template <typename OpTy>
88 : public AutoDiffOpInterface::ExternalModel<AutoDiffUsingBranch<OpTy>,
89 OpTy> {
90public:
91 LogicalResult createForwardModeTangent(Operation *op, OpBuilder &builder,
92 MGradientUtils *gutils) const {
93 branchingForwardHandler(op, builder, gutils);
94 return success();
95 }
96};
97
98// Implements the forward autodiff interface for operations whose derivatives
99// are can be inferred by analyzing their region terminator properties.
100template <typename OpTy>
102 : public AutoDiffOpInterface::ExternalModel<
103 AutoDiffUsingRegionTerminator<OpTy>, OpTy> {
104public:
105 LogicalResult createForwardModeTangent(Operation *op, OpBuilder &builder,
106 MGradientUtils *gutils) const {
107 regionTerminatorForwardHandler(op, builder, gutils);
108 return success();
109 }
110};
111
112template <typename OpTy>
114 : public ReverseAutoDiffOpInterface::ExternalModel<
115 NoopRevAutoDiffInterface<OpTy>, OpTy> {
116public:
117 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
118 MGradientUtilsReverse *gutils,
119 SmallVector<Value> caches) const {
120 return success();
121 }
122
123 SmallVector<Value> cacheValues(Operation *op,
124 MGradientUtilsReverse *gutils) const {
125 return SmallVector<Value>();
126 }
127
128 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
129 MGradientUtilsReverse *gutils) const {
130 return success();
131 }
132};
133
134template <typename OpTy>
136 : public ReverseAutoDiffOpInterface::ExternalModel<
137 ReturnRevAutoDiffInterface<OpTy>, OpTy> {
138public:
139 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
140 MGradientUtilsReverse *gutils,
141 SmallVector<Value> caches) const {
142 returnReverseHandler(op, builder, gutils);
143 return success();
144 }
145
146 SmallVector<Value> cacheValues(Operation *op,
147 MGradientUtilsReverse *gutils) const {
148 return SmallVector<Value>();
149 }
150
151 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
152 MGradientUtilsReverse *gutils) const {
153 return success();
154 }
155};
156
157// Implements the forward autodiff interface for operations which are
158// read only and identity like (aka not computing sin of mem read).
159template <typename OpTy, int... storedvals>
161 : public AutoDiffOpInterface::ExternalModel<
162 AutoDiffUsingMemoryIdentity<OpTy, storedvals...>, OpTy> {
163public:
164 LogicalResult createForwardModeTangent(Operation *op, OpBuilder &builder,
165 MGradientUtils *gutils) const {
166
168 op, builder, gutils, std::initializer_list<int>{storedvals...});
169 }
170};
171
172// Implements the forward autodiff interface for operations which are
173// allocation like
174template <typename OpTy>
175class AutoDiffUsingAllocationFwd : public AutoDiffOpInterface::ExternalModel<
176 AutoDiffUsingAllocationFwd<OpTy>, OpTy> {
177public:
178 LogicalResult createForwardModeTangent(Operation *op, OpBuilder &builder,
179 MGradientUtils *gutils) const {
180
181 return allocationForwardHandler(op, builder, gutils, /*zero*/ false);
182 }
183};
184
185// Implements the reverse autodiff interface for operations which are
186// allocation like
187template <typename OpTy>
189 : public ReverseAutoDiffOpInterface::ExternalModel<
190 AutoDiffUsingAllocationRev<OpTy>, OpTy> {
191public:
192 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
193 MGradientUtilsReverse *gutils,
194 SmallVector<Value> caches) const {
195 return success();
196 }
197
198 SmallVector<Value> cacheValues(Operation *op,
199 MGradientUtilsReverse *gutils) const {
200 return SmallVector<Value>();
201 }
202
203 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
204 MGradientUtilsReverse *gutils) const {
205 (void)allocationForwardHandler(op, builder, (MGradientUtils *)gutils,
206 /*zero*/ true);
207 return success();
208 }
209};
210
211// Differentiating a call is the same work whichever dialect spelled it:
212// find the callee, ask Logic for its derivative, and call that. What differs
213// is how the call names its callee and how a call to the derivative is
214// written, and both are already asked through interfaces.
215
216LogicalResult callForwardHandler(Operation *orig, OpBuilder &builder,
217 MGradientUtils *gutils);
218
219LogicalResult callReverseHandler(Operation *orig, OpBuilder &builder,
220 MGradientUtilsReverse *gutils,
221 SmallVector<Value> caches);
222
223SmallVector<Value> callCacheValues(Operation *orig,
224 MGradientUtilsReverse *gutils);
225
226} // namespace detail
227
228template <typename OpTy>
230 : public AutoDiffOpInterface::ExternalModel<AutoDiffCallFwd<OpTy>, OpTy> {
231public:
232 LogicalResult createForwardModeTangent(Operation *orig, OpBuilder &builder,
233 MGradientUtils *gutils) const {
234 return detail::callForwardHandler(orig, builder, gutils);
235 }
236};
237
238template <typename OpTy>
240 : public ReverseAutoDiffOpInterface::ExternalModel<AutoDiffCallRev<OpTy>,
241 OpTy> {
242public:
243 LogicalResult createReverseModeAdjoint(Operation *orig, OpBuilder &builder,
244 MGradientUtilsReverse *gutils,
245 SmallVector<Value> caches) const {
246 return detail::callReverseHandler(orig, builder, gutils, caches);
247 }
248
249 SmallVector<Value> cacheValues(Operation *orig,
250 MGradientUtilsReverse *gutils) const {
251 return detail::callCacheValues(orig, gutils);
252 }
253
254 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
255 MGradientUtilsReverse *gutils) const {
256 return success();
257 }
258};
259
260// Registers the generic call handlers for the given op; the tablegen-emitted
261// registerInterfaces calls this for each `def : CallOp<...>`.
262template <typename OpTy>
263void registerAutoDiffUsingCallInterface(MLIRContext &context) {
264 OpTy::template attachInterface<AutoDiffCallFwd<OpTy>>(context);
265 OpTy::template attachInterface<AutoDiffCallRev<OpTy>>(context);
266}
267
268// Registers AutoDiffUsingControlFlow for the given op.
269template <typename OpTy>
271 OpTy::template attachInterface<detail::AutoDiffUsingControlFlow<OpTy>>(
272 context);
273}
274// Registers AutoDiffUsingBranch for the given op.
275template <typename OpTy>
276void registerAutoDiffUsingBranchInterface(MLIRContext &context) {
277 OpTy::template attachInterface<detail::AutoDiffUsingBranch<OpTy>>(context);
278 OpTy::template attachInterface<detail::NoopRevAutoDiffInterface<OpTy>>(
279 context);
280}
281// Registers AutoDiffUsingRegionTerminator for the given op.
282template <typename OpTy>
284 OpTy::template attachInterface<detail::AutoDiffUsingRegionTerminator<OpTy>>(
285 context);
286 OpTy::template attachInterface<detail::NoopRevAutoDiffInterface<OpTy>>(
287 context);
288}
289// Registers registerAutoDiffUsingReturnInterface for the given op.
290template <typename OpTy>
291void registerAutoDiffUsingReturnInterface(MLIRContext &context) {
292 OpTy::template attachInterface<detail::AutoDiffUsingRegionTerminator<OpTy>>(
293 context);
294 OpTy::template attachInterface<detail::ReturnRevAutoDiffInterface<OpTy>>(
295 context);
296}
297// Registers AutoDiffUsingMemoryIdentity for the given op.
298template <typename OpTy, int... storedvals>
300 OpTy::template attachInterface<
301 detail::AutoDiffUsingMemoryIdentity<OpTy, storedvals...>>(context);
302}
303// Registers AutoDiffUsingAllocation for the given op.
304template <typename OpTy>
306 OpTy::template attachInterface<detail::AutoDiffUsingAllocationFwd<OpTy>>(
307 context);
308 OpTy::template attachInterface<detail::AutoDiffUsingAllocationRev<OpTy>>(
309 context);
310}
311
312// Interface registration hooks for individual upstream dialects.
313void registerAffineDialectAutoDiffInterface(DialectRegistry &registry);
314void registerArithDialectAutoDiffInterface(DialectRegistry &registry);
315void registerBuiltinDialectAutoDiffInterface(DialectRegistry &registry);
316void registerLLVMDialectAutoDiffInterface(DialectRegistry &registry);
317void registerLLVMExtDialectAutoDiffInterface(DialectRegistry &registry);
318void registerNVVMDialectAutoDiffInterface(DialectRegistry &registry);
319void registerMemRefDialectAutoDiffInterface(DialectRegistry &registry);
320void registerComplexDialectAutoDiffInterface(DialectRegistry &registry);
321void registerSCFDialectAutoDiffInterface(DialectRegistry &registry);
322void registerCFDialectAutoDiffInterface(DialectRegistry &registry);
323void registerLinalgDialectAutoDiffInterface(DialectRegistry &registry);
324void registerMathDialectAutoDiffInterface(DialectRegistry &registry);
325void registerFuncDialectAutoDiffInterface(DialectRegistry &registry);
326void registerTensorDialectAutoDiffInterface(DialectRegistry &registry);
327void registerGPUDialectAutoDiffInterface(DialectRegistry &registry);
328void registerEnzymeDialectAutoDiffInterface(DialectRegistry &registry);
329
330void registerCoreDialectAutodiffInterfaces(DialectRegistry &registry);
331
332mlir::TypedAttr getConstantAttr(mlir::Type type, llvm::StringRef value);
333
334bool allResultsActive(Operation *op, const MGradientUtils *gutils);
335
336/// Give an operation built for a derivative the fast-math flags the LLVM path
337/// gives everything it builds (Utils.cpp's getFast()). A gradient is only as
338/// fast as the arithmetic it is allowed to reassociate, and one built without
339/// the flags its primal was compiled with leaves work on the table: much of
340/// what the reverse pass computes is a product with a seed, and only under
341/// fast-math does a zero seed fold away the expression it feeds.
342/// An op with no fast-math flags to give is left alone.
343void setDerivativeFastMath(Operation *op);
344
345inline Value setDerivativeFastMath(Value v) {
346 if (v)
347 setDerivativeFastMath(v.getDefiningOp());
348 return v;
349}
350
351template <typename OpTy>
352std::enable_if_t<std::is_base_of<OpState, OpTy>::value, OpTy>
354 setDerivativeFastMath(op.getOperation());
355 return op;
356}
357} // namespace enzyme
358} // namespace mlir
359
360#endif
LogicalResult createForwardModeTangent(Operation *orig, OpBuilder &builder, MGradientUtils *gutils) const
LogicalResult createReverseModeAdjoint(Operation *orig, OpBuilder &builder, MGradientUtilsReverse *gutils, SmallVector< Value > caches) const
SmallVector< Value > cacheValues(Operation *orig, MGradientUtilsReverse *gutils) const
LogicalResult createShadowValues(Operation *op, OpBuilder &builder, MGradientUtilsReverse *gutils) const
LogicalResult createForwardModeTangent(Operation *op, OpBuilder &builder, MGradientUtils *gutils) const
SmallVector< Value > cacheValues(Operation *op, MGradientUtilsReverse *gutils) const
LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder, MGradientUtilsReverse *gutils, SmallVector< Value > caches) const
LogicalResult createShadowValues(Operation *op, OpBuilder &builder, MGradientUtilsReverse *gutils) const
LogicalResult createForwardModeTangent(Operation *op, OpBuilder &builder, MGradientUtils *gutils) const
LogicalResult createForwardModeTangent(Operation *op, OpBuilder &builder, MGradientUtils *gutils) const
LogicalResult createForwardModeTangent(Operation *op, OpBuilder &builder, MGradientUtils *gutils) const
LogicalResult createForwardModeTangent(Operation *op, OpBuilder &builder, MGradientUtils *gutils) const
SmallVector< Value > cacheValues(Operation *op, MGradientUtilsReverse *gutils) const
LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder, MGradientUtilsReverse *gutils, SmallVector< Value > caches) const
LogicalResult createShadowValues(Operation *op, OpBuilder &builder, MGradientUtilsReverse *gutils) const
SmallVector< Value > cacheValues(Operation *op, MGradientUtilsReverse *gutils) const
LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder, MGradientUtilsReverse *gutils, SmallVector< Value > caches) const
LogicalResult createShadowValues(Operation *op, OpBuilder &builder, MGradientUtilsReverse *gutils) const
LogicalResult controlFlowForwardHandler(Operation *op, OpBuilder &builder, MGradientUtils *gutils)
LogicalResult memoryIdentityForwardHandler(Operation *op, OpBuilder &builder, MGradientUtils *gutils, ArrayRef< int > storedVals)
void returnReverseHandler(Operation *op, OpBuilder &builder, MGradientUtilsReverse *gutils)
void regionTerminatorForwardHandler(Operation *op, OpBuilder &builder, MGradientUtils *gutils)
SmallVector< Value > callCacheValues(Operation *orig, MGradientUtilsReverse *gutils)
void branchingForwardHandler(Operation *op, OpBuilder &builder, MGradientUtils *gutils)
LogicalResult callReverseHandler(Operation *orig, OpBuilder &builder, MGradientUtilsReverse *gutils, SmallVector< Value > caches)
LogicalResult allocationForwardHandler(Operation *op, OpBuilder &builder, MGradientUtils *gutils, bool zero)
LogicalResult callForwardHandler(Operation *orig, OpBuilder &builder, MGradientUtils *gutils)
void registerGPUDialectAutoDiffInterface(DialectRegistry &registry)
void registerComplexDialectAutoDiffInterface(DialectRegistry &registry)
void registerArithDialectAutoDiffInterface(DialectRegistry &registry)
bool allResultsActive(Operation *op, const MGradientUtils *gutils)
void registerAutoDiffUsingAllocationInterface(MLIRContext &context)
void registerAutoDiffUsingRegionTerminatorInterface(MLIRContext &context)
void registerLinalgDialectAutoDiffInterface(DialectRegistry &registry)
void registerLLVMDialectAutoDiffInterface(DialectRegistry &registry)
void registerAutoDiffUsingBranchInterface(MLIRContext &context)
void registerMathDialectAutoDiffInterface(DialectRegistry &registry)
void registerEnzymeDialectAutoDiffInterface(DialectRegistry &registry)
void registerBuiltinDialectAutoDiffInterface(DialectRegistry &registry)
void setDerivativeFastMath(Operation *op)
Give an operation built for a derivative the fast-math flags the LLVM path gives everything it builds...
void registerFuncDialectAutoDiffInterface(DialectRegistry &registry)
mlir::TypedAttr getConstantAttr(mlir::Type type, llvm::StringRef value)
void registerCoreDialectAutodiffInterfaces(DialectRegistry &registry)
void registerSCFDialectAutoDiffInterface(DialectRegistry &registry)
void registerMemRefDialectAutoDiffInterface(DialectRegistry &registry)
void registerCFDialectAutoDiffInterface(DialectRegistry &registry)
void registerAutoDiffUsingCallInterface(MLIRContext &context)
void registerAutoDiffUsingMemoryIdentityInterface(MLIRContext &context)
void registerAutoDiffUsingReturnInterface(MLIRContext &context)
void registerLLVMExtDialectAutoDiffInterface(DialectRegistry &registry)
void registerAffineDialectAutoDiffInterface(DialectRegistry &registry)
void registerAutoDiffUsingControlFlowInterface(MLIRContext &context)
void registerNVVMDialectAutoDiffInterface(DialectRegistry &registry)
void registerTensorDialectAutoDiffInterface(DialectRegistry &registry)