22#include "mlir/Dialect/MemRef/IR/MemRef.h"
23#include "mlir/IR/DialectRegistry.h"
24#include "mlir/IR/Matchers.h"
25#include "mlir/Support/LogicalResult.h"
31#include "Implementations/MemRefDerivatives.inc"
33struct SubViewOpOffsetInterface
34 :
public OffsetViewInterface::ExternalModel<SubViewOpOffsetInterface,
36 bool isZeroOffset(Operation *op)
const {
37 return cast<memref::SubViewOp>(op).hasZeroOffset();
41struct ViewOpOffsetInterface
42 :
public OffsetViewInterface::ExternalModel<ViewOpOffsetInterface,
44 bool isZeroOffset(Operation *op)
const {
45 return matchPattern(cast<memref::ViewOp>(op).getByteShift(), m_Zero());
51 :
public StoreLikeInterface::ExternalModel<MemRefStoreLike,
53 Value getStoredValue(Operation *op)
const {
54 return cast<memref::StoreOp>(op).getValueToStore();
56 Value getStoredPointer(Operation *op)
const {
57 return cast<memref::StoreOp>(op).getMemRef();
61struct LoadOpInterfaceReverse
62 :
public ReverseAutoDiffOpInterface::ExternalModel<LoadOpInterfaceReverse,
64 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
66 SmallVector<Value> caches)
const {
67 auto loadOp = cast<memref::LoadOp>(op);
68 Value memref = loadOp.getMemref();
70 if (
auto iface = dyn_cast<AutoDiffTypeInterface>(loadOp.getType())) {
76 Value gradient = gutils->
diffe(loadOp, builder);
77 Value memrefGradient = gutils->
popCache(caches.front(), builder);
79 SmallVector<Value> retrievedArguments;
80 for (Value cache : ValueRange(caches).drop_front(1)) {
81 Value retrievedValue = gutils->
popCache(cache, builder);
82 retrievedArguments.push_back(retrievedValue);
86 auto loadedGradientOp =
87 memref::LoadOp::create(builder, loadOp.getLoc(), memrefGradient,
88 ArrayRef<Value>(retrievedArguments));
89 loadedGradientOp.setAlignmentAttr(loadOp.getAlignmentAttr());
90 Value addedGradient = iface.createAddOp(builder, loadOp.getLoc(),
91 loadedGradientOp, gradient);
92 auto storeGradientOp = memref::StoreOp::create(
93 builder, loadOp.getLoc(), addedGradient, memrefGradient,
94 ArrayRef<Value>(retrievedArguments));
95 storeGradientOp.setAlignmentAttr(loadOp.getAlignmentAttr());
98 builder, loadOp.getLoc(), gradient.getType(),
99 arith::AtomicRMWKind::addf, Ordering::monotonic, gradient,
100 memrefGradient, retrievedArguments, loadOp.getAlignmentAttr()));
107 SmallVector<Value> cacheValues(Operation *op,
109 auto loadOp = cast<memref::LoadOp>(op);
110 Value memref = loadOp.getMemref();
111 ValueRange indices = loadOp.getIndices();
112 if (
auto iface = dyn_cast<AutoDiffTypeInterface>(loadOp.getType())) {
116 SmallVector<Value> caches;
119 for (Value v : indices) {
126 return SmallVector<Value>();
129 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
131 auto loadOp = cast<memref::LoadOp>(op);
132 Value memref = loadOp.getMemref();
133 auto iface = dyn_cast<AutoDiffTypeInterface>(loadOp.getType());
139 return op->emitError()
140 <<
"could not compute the shadow of a load of a type without "
141 "autodiff semantics "
144 if (!iface.isMutable())
152 return op->emitError()
153 <<
"cannot load a non-constant value out of a constant memref "
157 auto shadowLoad = cast<memref::LoadOp>(builder.clone(*newLoad));
158 shadowLoad.getMemrefMutable().assign(memrefShadow);
164struct StoreOpInterfaceReverse
165 :
public ReverseAutoDiffOpInterface::ExternalModel<StoreOpInterfaceReverse,
167 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
169 SmallVector<Value> caches)
const {
170 auto storeOp = cast<memref::StoreOp>(op);
171 Value val = storeOp.getValue();
172 Value memref = storeOp.getMemref();
175 auto iface = cast<AutoDiffTypeInterface>(val.getType());
178 Value memrefGradient = gutils->
popCache(caches.front(), builder);
180 SmallVector<Value> retrievedArguments;
181 for (Value cache : ValueRange(caches).drop_front(1)) {
182 Value retrievedValue = gutils->
popCache(cache, builder);
183 retrievedArguments.push_back(retrievedValue);
186 if (!iface.isMutable()) {
188 auto loadedGradientOp =
189 memref::LoadOp::create(builder, storeOp.getLoc(), memrefGradient,
190 ArrayRef<Value>(retrievedArguments));
191 loadedGradientOp.setAlignmentAttr(storeOp.getAlignmentAttr());
192 gutils->
addToDiffe(val, loadedGradientOp, builder);
196 cast<AutoDiffTypeInterface>(gutils->
getShadowType(val.getType()))
197 .createNullValue(builder, op->getLoc());
199 auto zeroStoreOp = memref::StoreOp::create(
200 builder, storeOp.getLoc(), zero, memrefGradient,
201 ArrayRef<Value>(retrievedArguments));
202 zeroStoreOp.setAlignmentAttr(storeOp.getAlignmentAttr());
214 SmallVector<Value> cacheValues(Operation *op,
216 auto storeOp = cast<memref::StoreOp>(op);
217 Value memref = storeOp.getMemref();
218 ValueRange indices = storeOp.getIndices();
219 Value val = storeOp.getValue();
220 if (
auto iface = dyn_cast<AutoDiffTypeInterface>(val.getType())) {
223 SmallVector<Value> caches;
226 for (Value v : indices) {
233 return SmallVector<Value>();
241 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
243 auto storeOp = cast<memref::StoreOp>(op);
244 Value val = storeOp.getValue();
245 Value memref = storeOp.getMemref();
246 auto iface = dyn_cast<AutoDiffTypeInterface>(val.getType());
248 return op->emitError()
249 <<
"could not compute the shadow of a store of a type without "
250 "autodiff semantics "
257 if (!iface.isMutable())
263 : gutils->invertPointerM(val, builder);
265 auto shadowOp = cast<memref::StoreOp>(builder.clone(*newOp));
266 shadowOp.getValueMutable().assign(valShadow);
267 shadowOp.getMemrefMutable().assign(memrefShadow);
272struct SubViewOpInterfaceReverse
273 :
public ReverseAutoDiffOpInterface::ExternalModel<
274 SubViewOpInterfaceReverse, memref::SubViewOp> {
275 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
277 SmallVector<Value> caches)
const {
281 SmallVector<Value> cacheValues(Operation *op,
283 return SmallVector<Value>();
286 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
288 auto subviewOp = cast<memref::SubViewOp>(op);
291 Value shadow = memref::SubViewOp::create(
292 builder, op->getLoc(), newSubviewOp.getType(),
294 newSubviewOp.getMixedOffsets(), newSubviewOp.getMixedSizes(),
295 newSubviewOp.getMixedStrides());
302class MemRefClonableTypeInterface
303 :
public ClonableTypeInterface::ExternalModel<MemRefClonableTypeInterface,
307 mlir::Value cloneValue(mlir::Type self, OpBuilder &builder,
309 MemRefType MT = cast<MemRefType>(self);
310 SmallVector<Value> dynamicSizes;
312 for (
auto [i, s] : llvm::enumerate(MT.getShape())) {
313 if (s == ShapedType::kDynamic) {
314 Value dim = arith::ConstantIndexOp::create(builder, value.getLoc(), i);
315 dynamicSizes.push_back(
316 memref::DimOp::create(builder, value.getLoc(), value, dim));
325 memref::AllocOp::create(builder, value.getLoc(), MT, dynamicSizes);
326 memref::CopyOp::create(builder, value.getLoc(), value,
clone);
331 void copyValue(mlir::Type self, OpBuilder &builder, Value dst,
335 memref::CopyOp::create(builder, src.getLoc(), src, dst);
338 void freeClonedValue(mlir::Type self, OpBuilder &builder, Value value)
const {
339 memref::DeallocOp::create(builder, value.getLoc(), value);
342 bool implementsBatchAllocation(Type self, Value base,
343 OpFoldResult size)
const {
347 Value deriveSubElement(Type self, OpBuilder &builder, Location loc,
348 Value base, Value multiel, Value index)
const {
349 auto MT = cast<MemRefType>(base.getType());
351 SmallVector<Value> dynSizes, offsets;
353 offsets.push_back(index);
355 SmallVector<int64_t> static_offsets(MT.getRank() + 1, 0);
356 static_offsets[0] = ShapedType::kDynamic;
358 SmallVector<int64_t> static_sizes;
359 static_sizes.push_back(1);
360 static_sizes.append(MT.getShape().begin(), MT.getShape().end());
361 SmallVector<int64_t> static_strides(MT.getRank() + 1, 1);
363 for (
auto [i, sz] : llvm::enumerate(MT.getShape())) {
364 if (sz == ShapedType::kDynamic) {
365 dynSizes.push_back(memref::DimOp::create(builder, loc, base, i));
369 auto RT = memref::SubViewOp::inferRankReducedResultType(
370 MT.getShape(), cast<MemRefType>(multiel.getType()), static_offsets,
371 static_sizes, static_strides);
372 auto sv = memref::SubViewOp::create(
373 builder, loc, RT, multiel, offsets, dynSizes, ValueRange(),
374 builder.getDenseI64ArrayAttr(static_offsets),
375 builder.getDenseI64ArrayAttr(static_sizes),
376 builder.getDenseI64ArrayAttr(static_strides));
381 Value batchAllocate(Type self, OpBuilder &builder, Location loc, Value base,
382 OpFoldResult numel)
const {
383 auto MT = cast<MemRefType>(self);
385 SmallVector<int64_t> shape;
386 SmallVector<Value> dynSizes;
388 if (
auto attr = dyn_cast<Attribute>(numel)) {
389 shape.push_back(cast<IntegerAttr>(attr).getValue().getSExtValue());
391 shape.push_back(ShapedType::kDynamic);
392 dynSizes.push_back(cast<Value>(numel));
395 for (
auto [i, sz] : llvm::enumerate(MT.getShape())) {
398 if (sz == ShapedType::kDynamic) {
399 dynSizes.push_back(memref::DimOp::create(
401 arith::ConstantIndexOp::create(builder, loc, i)));
405 Value alloc = memref::AllocOp::create(
406 builder, loc, MemRefType::get(shape, MT.getElementType()), dynSizes);
412class MemRefAutoDiffTypeInterface
413 :
public AutoDiffTypeInterface::ExternalModel<MemRefAutoDiffTypeInterface,
416 mlir::Attribute createNullAttr(mlir::Type self)
const {
417 llvm_unreachable(
"Cannot create null of memref (todo polygeist null)");
419 mlir::Value createNullValue(mlir::Type self, OpBuilder &builder,
420 Location loc)
const {
423 MemRefType MT = cast<MemRefType>(self);
424 unsigned numDynamicDims = MT.getNumDynamicDims();
425 SmallVector<mlir::Value> dynamicSizes(numDynamicDims);
426 for (
unsigned i = 0; i < numDynamicDims; ++i) {
427 dynamicSizes[i] = builder.create<mlir::arith::ConstantIndexOp>(loc, 0);
429 return mlir::memref::AllocOp::create(builder, loc, MT, dynamicSizes);
432 Value createAddOp(Type self, OpBuilder &builder, Location loc, Value a,
434 llvm_unreachable(
"TODO");
438 assert(width == 1 &&
"unsupported width != 1");
442 Value createConjOp(Type self, OpBuilder &builder, Location loc,
444 llvm_unreachable(
"TODO");
447 bool isMutable(Type self)
const {
return true; }
449 LogicalResult zeroInPlace(Type self, OpBuilder &builder, Location loc,
451 auto MT = cast<MemRefType>(self);
452 if (
auto iface = dyn_cast<AutoDiffTypeInterface>(MT.getElementType())) {
453 if (!iface.isMutable()) {
454 enzyme::FillZeroOp::create(builder, loc, val);
462 bool isZero(Type self, Value val)
const {
return false; }
463 bool isZeroAttr(Type self, Attribute val)
const {
return false; }
466struct MemRefAllocOpInterface
467 :
public MultidimensionalAllocInterface::ExternalModel<
468 MemRefAllocOpInterface, memref::AllocOp> {
469 Value allocate(Operation *op, OpBuilder &rewriter, Location loc, Type newType,
470 ValueRange dynamicDims)
const {
471 return memref::AllocOp::create(rewriter, loc, cast<MemRefType>(newType),
475 void deallocate(Operation *op, OpBuilder &rewriter, Location loc,
477 memref::DeallocOp::create(rewriter, loc, val);
480 bool isDeallocation(Operation *op, Operation *user)
const {
481 return isa<memref::DeallocOp>(user);
488 DialectRegistry ®istry) {
489 registry.addExtension(+[](MLIRContext *context, memref::MemRefDialect *) {
490 registerInterfaces(context);
491 MemRefType::attachInterface<MemRefAutoDiffTypeInterface>(*context);
492 MemRefType::attachInterface<MemRefClonableTypeInterface>(*context);
494 memref::StoreOp::attachInterface<MemRefStoreLike>(*context);
495 memref::LoadOp::attachInterface<LoadOpInterfaceReverse>(*context);
496 memref::StoreOp::attachInterface<StoreOpInterfaceReverse>(*context);
497 memref::SubViewOp::attachInterface<SubViewOpInterfaceReverse>(*context);
498 memref::AllocOp::attachInterface<MemRefAllocOpInterface>(*context);
500 memref::CastOp::attachInterface<OffsetViewInterface::Model<memref::CastOp>>(
502 memref::MemorySpaceCastOp::attachInterface<
503 OffsetViewInterface::Model<memref::MemorySpaceCastOp>>(*context);
504 memref::ReshapeOp::attachInterface<
505 OffsetViewInterface::Model<memref::ReshapeOp>>(*context);
506 memref::ExpandShapeOp::attachInterface<
507 OffsetViewInterface::Model<memref::ExpandShapeOp>>(*context);
508 memref::CollapseShapeOp::attachInterface<
509 OffsetViewInterface::Model<memref::CollapseShapeOp>>(*context);
510 memref::TransposeOp::attachInterface<
511 OffsetViewInterface::Model<memref::TransposeOp>>(*context);
512 memref::SubViewOp::attachInterface<SubViewOpOffsetInterface>(*context);
513 memref::ViewOp::attachInterface<ViewOpOffsetInterface>(*context);
Type getShadowType(Type type, unsigned width)
Operation * clone(Operation *src, IRMapping &mapper, Operation::CloneOptions options, std::map< Operation *, Operation * > &opMap)
static bool isZero(llvm::Constant *cst)
static bool isMutable(Type type)
mlir::Value diffe(mlir::Value origv, mlir::OpBuilder &builder)
Value popCache(Value cache, OpBuilder &builder)
Value initAndPushCache(Value v, OpBuilder &builder)
void addToDiffe(mlir::Value oldGradient, mlir::Value addedGradient, OpBuilder &builder)
bool primalStoreElidable(mlir::Value ptr)
True when a primal store through this pointer may be omitted: declaring the base enzyme_dupnoneed is ...
mlir::Type getShadowType(mlir::Type T)
void erase(Operation *op)
mlir::Value invertPointerM(mlir::Value v, OpBuilder &Builder2)
SmallVector< mlir::Value, 1 > getNewFromOriginal(ValueRange originst) const
void setInvertedPointer(mlir::Value origv, mlir::Value newv)
bool isConstantValue(mlir::Value v) const
Value inactiveStoredValueShadow(Operation *orig, MGradientUtils &gutils, Value stored, OpBuilder &builder)
void setDerivativeFastMath(Operation *op)
Give an operation built for a derivative the fast-math flags the LLVM path gives everything it builds...
void registerMemRefDialectAutoDiffInterface(DialectRegistry ®istry)