19#include "mlir/Dialect/Func/IR/FuncOps.h"
20#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
21#include "mlir/Dialect/Math/IR/Math.h"
22#include "mlir/IR/Builders.h"
23#include "mlir/Interfaces/FunctionInterfaces.h"
24#include "mlir/Pass/PassManager.h"
26#define DEBUG_TYPE "enzyme"
30using namespace enzyme;
34#define GEN_PASS_DEF_DIFFERENTIATEPASS
35#include "Passes/Passes.h.inc"
40struct DifferentiatePass
41 :
public enzyme::impl::DifferentiatePassBase<DifferentiatePass> {
42 using DifferentiatePassBase::DifferentiatePassBase;
44 void runOnOperation()
override;
46 void getDependentDialects(DialectRegistry ®istry)
const override {
47 mlir::OpPassManager pm;
48 mlir::LogicalResult result = mlir::parsePassPipeline(postpasses, pm);
49 if (!mlir::failed(result)) {
50 pm.getDependentDialects(registry);
60 registry.insert<mlir::arith::ArithDialect, mlir::complex::ComplexDialect,
61 mlir::cf::ControlFlowDialect, mlir::tensor::TensorDialect,
62 mlir::memref::MemRefDialect, mlir::math::MathDialect,
63 mlir::enzyme::EnzymeDialect, mlir::LLVM::LLVMDialect,
64 mlir::enzyme::llvm_ext::LLVMExtDialect>();
67 static std::vector<DIFFE_TYPE> mode_from_fn(FunctionOpInterface fn,
69 std::vector<DIFFE_TYPE> retTypes;
70 for (
auto ty : fn.getResultTypes()) {
71 if (isa<IntegerType>(ty)) {
86 SymbolTableCollection &symbolTable, T CI) {
87 std::vector<DIFFE_TYPE> constants;
88 SmallVector<mlir::Value, 2> args;
91 auto activityAttr = CI.getActivity();
93 for (
unsigned i = 0; i < CI.getInputs().size(); ++i) {
94 mlir::Value res = CI.getInputs()[i];
96 auto mop = activityAttr[truei];
97 auto iattr = cast<mlir::enzyme::ActivityAttr>(mop);
100 switch (iattr.getValue()) {
101 case mlir::enzyme::Activity::enzyme_active:
104 case mlir::enzyme::Activity::enzyme_dup:
107 case mlir::enzyme::Activity::enzyme_const:
110 case mlir::enzyme::Activity::enzyme_dupnoneed:
113 case mlir::enzyme::Activity::enzyme_activenoneed:
115 assert(0 &&
"unsupported arg activenoneed");
117 case mlir::enzyme::Activity::enzyme_constnoneed:
119 assert(0 &&
"unsupported arg constnoneed");
123 constants.push_back(ty);
127 res = CI.getInputs()[i];
134 auto *symbolOp = symbolTable.lookupNearestSymbolFrom(CI, CI.getFnAttr());
135 auto fn = cast<FunctionOpInterface>(symbolOp);
138 std::vector<DIFFE_TYPE> retType;
140 std::vector<bool> returnPrimals;
141 for (
auto act : CI.getRetActivity()) {
142 auto iattr = cast<mlir::enzyme::ActivityAttr>(act);
143 auto val = iattr.getValue();
145 bool primalNeeded =
true;
147 case mlir::enzyme::Activity::enzyme_active:
150 case mlir::enzyme::Activity::enzyme_dup:
153 case mlir::enzyme::Activity::enzyme_const:
156 case mlir::enzyme::Activity::enzyme_dupnoneed:
158 primalNeeded =
false;
160 case mlir::enzyme::Activity::enzyme_activenoneed:
162 primalNeeded =
false;
164 case mlir::enzyme::Activity::enzyme_constnoneed:
166 primalNeeded =
false;
169 retType.push_back(ty);
170 returnPrimals.push_back(primalNeeded);
175 bool freeMemory =
true;
177 size_t width = CI.getWidth();
179 std::vector<bool> overwritten_args;
180 for (
auto &a : fn.getFunctionBody().getArguments()) {
182 overwritten_args.push_back(
187 fn, retType, constants, TA, returnPrimals, mode, freeMemory, width,
188 nullptr, type_args, overwritten_args,
189 nullptr, omp, postpasses, verifyPostPasses,
194 OpBuilder builder(CI);
198 auto iface = dyn_cast<AutoDiffFunctionInterface>(newFunc.getOperation());
200 newFunc.getOperation()->emitError()
201 <<
"this function operation does not implement "
202 "AutoDiffFunctionInterface";
205 Operation *dCI = iface.createCall(builder, CI.getLoc(), args);
206 if (dCI->getNumResults() != CI.getNumResults()) {
207 CI.emitError() <<
"Incorrect number of results for enzyme operation: "
208 << *CI <<
" expected " << *dCI;
211 CI.replaceAllUsesWith(dCI);
216 template <
typename T>
217 LogicalResult HandleAutoDiffReverse(
MEnzymeLogic &Logic,
218 SymbolTableCollection &symbolTable,
221 auto *symbolOp = symbolTable.lookupNearestSymbolFrom(CI, CI.getFnAttr());
222 auto fn = cast<FunctionOpInterface>(symbolOp);
224 if (CI.getActivity().size() != fn.getNumArguments()) {
225 llvm::errs() <<
"Incorrect number of argument activities on autodiff op"
226 <<
"CI: " << CI <<
", expected " << fn.getNumArguments()
227 <<
" found " << CI.getActivity().size() <<
"\n";
230 if (CI.getRetActivity().size() != fn.getNumResults()) {
231 llvm::errs() <<
"Incorrect number of result activities on autodiff op"
232 <<
"CI: " << CI <<
", expected " << fn.getNumResults()
233 <<
" found " << CI.getRetActivity().size() <<
"\n";
237 std::vector<DIFFE_TYPE> arg_activities;
238 SmallVector<mlir::Value, 2> args;
242 for (
auto act : CI.getActivity()) {
243 if (call_idx >= CI.getInputs().size()) {
244 llvm::errs() <<
"Too few arguments to autodiff op"
245 <<
"CI: " << CI <<
"\n";
248 mlir::Value res = CI.getInputs()[call_idx];
251 auto iattr = cast<mlir::enzyme::ActivityAttr>(act);
252 auto val = iattr.getValue();
255 case mlir::enzyme::Activity::enzyme_active:
258 case mlir::enzyme::Activity::enzyme_dup:
261 case mlir::enzyme::Activity::enzyme_const:
264 case mlir::enzyme::Activity::enzyme_dupnoneed:
267 case mlir::enzyme::Activity::enzyme_activenoneed:
269 assert(0 &&
"unsupported arg activenoneed");
271 case mlir::enzyme::Activity::enzyme_constnoneed:
273 assert(0 &&
"unsupported arg constnoneed");
276 arg_activities.push_back(ty);
279 if (call_idx >= CI.getInputs().size()) {
280 llvm::errs() <<
"Too few arguments to autodiff op"
281 <<
"CI: " << CI <<
"\n";
284 res = CI.getInputs()[call_idx];
293 std::vector<DIFFE_TYPE> retType;
294 std::vector<bool> returnPrimals;
295 std::vector<bool> returnShadows;
298 for (
auto act : CI.getRetActivity()) {
299 auto iattr = cast<mlir::enzyme::ActivityAttr>(act);
300 auto val = iattr.getValue();
302 bool primalNeeded =
true;
304 case mlir::enzyme::Activity::enzyme_active:
307 case mlir::enzyme::Activity::enzyme_dup:
310 case mlir::enzyme::Activity::enzyme_const:
313 case mlir::enzyme::Activity::enzyme_dupnoneed:
315 primalNeeded =
false;
317 case mlir::enzyme::Activity::enzyme_activenoneed:
319 primalNeeded =
false;
321 case mlir::enzyme::Activity::enzyme_constnoneed:
323 primalNeeded =
false;
326 retType.push_back(ty);
327 returnPrimals.push_back(primalNeeded);
328 returnShadows.push_back(
false);
330 if (call_idx >= CI.getInputs().size()) {
331 llvm::errs() <<
"Too few arguments to autodiff op"
332 <<
"CI: " << CI <<
"\n";
335 mlir::Value res = CI.getInputs()[call_idx];
343 bool freeMemory =
true;
344 size_t width = CI.getWidth();
346 std::vector<bool> overwritten_args;
347 for (
auto &a : fn.getFunctionBody().getArguments()) {
349 overwritten_args.push_back(
354 fn, retType, arg_activities, TA, returnPrimals, returnShadows, mode,
355 freeMemory, CI.getAtomicAdd(), width,
356 nullptr, type_args, overwritten_args,
357 nullptr, omp, postpasses, verifyPostPasses,
358 CI.getStrongZero(), markReadonly);
362 OpBuilder builder(CI);
364 dyn_cast<AutoDiffFunctionInterface>(newFunc.getOperation())) {
365 auto dCI = iface.createCall(builder, CI.getLoc(), args);
366 CI.replaceAllUsesWith(dCI);
368 newFunc.getOperation()->emitError()
369 <<
"this function operation does not implement "
370 "AutoDiffFunctionInterface";
377 void lowerEnzymeCalls(
MEnzymeLogic &Logic, SymbolTableCollection &symbolTable,
378 FunctionOpInterface op) {
380 SmallVector<Operation *> toLower;
381 op->walk([&](enzyme::ForwardDiffOp dop) {
383 symbolTable.lookupNearestSymbolFrom(dop, dop.getFnAttr());
384 auto callableOp = cast<FunctionOpInterface>(symbolOp);
386 lowerEnzymeCalls(Logic, symbolTable, callableOp);
387 toLower.push_back(dop);
390 for (
auto T : toLower) {
391 if (
auto F = dyn_cast<enzyme::ForwardDiffOp>(T)) {
392 auto res = HandleAutoDiff(Logic, symbolTable, F);
393 if (!res.succeeded()) {
398 llvm_unreachable(
"Illegal type");
404 SmallVector<Operation *> toLower;
405 op->walk([&](enzyme::AutoDiffOp dop) {
407 symbolTable.lookupNearestSymbolFrom(dop, dop.getFnAttr());
408 auto callableOp = cast<FunctionOpInterface>(symbolOp);
410 lowerEnzymeCalls(Logic, symbolTable, callableOp);
411 toLower.push_back(dop);
414 for (
auto T : toLower) {
415 if (
auto F = dyn_cast<enzyme::AutoDiffOp>(T)) {
416 auto res = HandleAutoDiffReverse(Logic, symbolTable, F);
417 if (!res.succeeded()) {
422 llvm_unreachable(
"Illegal type");
431void DifferentiatePass::runOnOperation() {
433 SymbolTableCollection symbolTable;
434 symbolTable.getSymbolTable(getOperation());
435 getOperation()->walk([&](FunctionOpInterface op) {
436 lowerEnzymeCalls(Logic, symbolTable, op);
438 getOperation()->walk([&](FunctionOpInterface op) {
removeSummaries(op); });
DIFFE_TYPE
Potential differentiable argument classifications.
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)
FunctionOpInterface CreateReverseDiff(FunctionOpInterface fn, std::vector< DIFFE_TYPE > retType, std::vector< DIFFE_TYPE > constants, MTypeAnalysis &TA, std::vector< bool > returnPrimals, std::vector< bool > returnShadows, DerivativeMode mode, bool freeMemory, bool atomicAdd, 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, bool markReadonly)
MFnTypeInfo getAnalyzedTypeInfo(FunctionOpInterface op) const
void removeSummaries(Operation *op)