22#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
23#include "mlir/IR/DialectRegistry.h"
24#include "mlir/IR/Matchers.h"
25#include "mlir/Interfaces/DataLayoutInterfaces.h"
26#include "mlir/Support/LogicalResult.h"
33static mlir::Value allocaExtent(mlir::OpBuilder &builder,
34 mlir::LLVM::AllocaOp alloca);
35#include "Implementations/LLVMDerivatives.inc"
37struct BitcastOpViewInterface
38 :
public ViewLikeOpInterface::ExternalModel<BitcastOpViewInterface,
40 Value getViewSource(Operation *op)
const {
41 return cast<LLVM::BitcastOp>(op).getArg();
45struct GEPOpOffsetInterface
46 :
public OffsetViewInterface::ExternalModel<GEPOpOffsetInterface,
48 bool isZeroOffset(Operation *op)
const {
49 for (
auto index : cast<LLVM::GEPOp>(op).getIndices()) {
50 if (
auto attr = dyn_cast<IntegerAttr>(index)) {
51 if (!attr.getValue().isZero())
53 }
else if (!matchPattern(cast<Value>(index), m_Zero())) {
63 :
public StoreLikeInterface::ExternalModel<LLVMStoreLike, LLVM::StoreOp> {
64 Value getStoredValue(Operation *op)
const {
65 return cast<LLVM::StoreOp>(op).getValue();
67 Value getStoredPointer(Operation *op)
const {
68 return cast<LLVM::StoreOp>(op).getAddr();
72struct InlineAsmActivityInterface
73 :
public ActivityOpInterface::ExternalModel<InlineAsmActivityInterface,
75 bool isInactive(Operation *op)
const {
76 auto asmOp = cast<LLVM::InlineAsmOp>(op);
77 auto str = asmOp.getAsmString();
78 return str.contains(
"cpuid") ||
str.contains(
"exit");
80 bool isArgInactive(Operation *op,
size_t)
const {
return isInactive(op); }
83struct SelectActivityInterface
84 :
public ActivityOpInterface::ExternalModel<SelectActivityInterface,
86 bool isInactive(Operation *op)
const {
return false; }
87 bool isArgInactive(Operation *op,
size_t idx)
const {
94class ArrayTypeInterface
95 :
public AutoDiffTypeInterface::ExternalModel<ArrayTypeInterface,
96 LLVM::LLVMArrayType> {
98 mlir::Attribute createNullAttr(mlir::Type self)
const {
99 llvm::errs() <<
" unsupported: createNullAttribute of LLVMArrayType\n";
103 mlir::Value createNullValue(mlir::Type self, OpBuilder &builder,
104 Location loc)
const {
105 auto arrayTy = cast<LLVM::LLVMArrayType>(self);
106 Type elemTy = arrayTy.getElementType();
107 auto elemIface = dyn_cast<AutoDiffTypeInterface>(elemTy);
109 return LLVM::ZeroOp::create(builder, loc, arrayTy);
111 Value nullElem = elemIface.createNullValue(builder, loc);
112 Value result = LLVM::PoisonOp::create(builder, loc, arrayTy);
113 for (uint64_t i = 0; i < arrayTy.getNumElements(); ++i) {
114 result = LLVM::InsertValueOp::create(builder, loc, result, nullElem, i);
119 Value createAddOp(Type self, OpBuilder &builder, Location loc, Value a,
121 auto arrayTy = cast<LLVM::LLVMArrayType>(self);
122 Type elemTy = arrayTy.getElementType();
123 Value result = LLVM::PoisonOp::create(builder, loc, arrayTy);
124 auto elemIface = dyn_cast<AutoDiffTypeInterface>(elemTy);
125 for (uint64_t i = 0; i < arrayTy.getNumElements(); ++i) {
126 Value aElem = LLVM::ExtractValueOp::create(builder, loc, a, i);
127 Value bElem = LLVM::ExtractValueOp::create(builder, loc, b, i);
130 sum = elemIface.createAddOp(builder, loc, aElem, bElem);
134 result = LLVM::InsertValueOp::create(builder, loc, result, sum, i);
139 Value createConjOp(Type self, OpBuilder &builder, Location loc,
141 llvm_unreachable(
"TODO");
145 assert(width == 1 &&
"unsupported width != 1");
149 bool isMutable(Type self)
const {
return false; }
151 LogicalResult zeroInPlace(Type self, OpBuilder &builder, Location loc,
156 bool isZero(Type self, Value val)
const {
return false; }
157 bool isZeroAttr(Type self, Attribute attr)
const {
return false; }
159 int64_t getApproxSize(Type self)
const {
160 auto arrayType = cast<LLVM::LLVMArrayType>(self);
162 dyn_cast<AutoDiffTypeInterface>(arrayType.getElementType())) {
163 int64_t elSize = elemType.getApproxSize();
164 if (elSize != INT64_MAX)
165 return arrayType.getNumElements() * elSize;
171class PointerTypeInterface
172 :
public AutoDiffTypeInterface::ExternalModel<PointerTypeInterface,
173 LLVM::LLVMPointerType> {
175 mlir::Attribute createNullAttr(mlir::Type self)
const {
176 llvm::errs() <<
" unsupported: createNullAttribute of pointertype\n";
180 mlir::Value createNullValue(mlir::Type self, OpBuilder &builder,
181 Location loc)
const {
182 return LLVM::ZeroOp::create(builder, loc, self);
185 Value createAddOp(Type self, OpBuilder &builder, Location loc, Value a,
187 llvm_unreachable(
"TODO");
190 Value createConjOp(Type self, OpBuilder &builder, Location loc,
192 llvm_unreachable(
"TODO");
196 assert(width == 1 &&
"unsupported width != 1");
200 bool isMutable(Type self)
const {
return true; }
202 LogicalResult zeroInPlace(Type self, OpBuilder &builder, Location loc,
204 if (
auto allocaOp = val.getDefiningOp<LLVM::AllocaOp>()) {
205 Value size = allocaExtent(builder, allocaOp);
209 LLVM::ConstantOp::create(builder, loc, builder.getI8IntegerAttr(0));
210 LLVM::MemsetOp::create(builder, loc, val, zero, size,
217 bool isZero(Type self, Value val)
const {
return false; }
218 bool isZeroAttr(Type self, Attribute attr)
const {
return false; }
226template <
typename OpTy>
227struct LifetimeForwardInterface
228 :
public AutoDiffOpInterface::ExternalModel<LifetimeForwardInterface<OpTy>,
230 LogicalResult createForwardModeTangent(Operation *op, OpBuilder &builder,
232 auto ltOp = cast<OpTy>(op);
239 auto shadowOp = cast<OpTy>(builder.clone(*newOp));
240 shadowOp.getPtrMutable().assign(shadow);
252template <
typename OpTy>
253struct NoTangentForwardInterface
254 :
public AutoDiffOpInterface::ExternalModel<NoTangentForwardInterface<OpTy>,
256 LogicalResult createForwardModeTangent(Operation *op, OpBuilder &builder,
266template <
typename OpTy>
267struct NoAdjointReverseInterface
268 :
public ReverseAutoDiffOpInterface::ExternalModel<
269 NoAdjointReverseInterface<OpTy>, OpTy> {
270 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
272 SmallVector<Value> caches)
const {
276 SmallVector<Value> cacheValues(Operation *op,
278 return SmallVector<Value>();
281 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
294struct MemsetForwardInterface
295 :
public AutoDiffOpInterface::ExternalModel<MemsetForwardInterface,
297 LogicalResult createForwardModeTangent(Operation *op, OpBuilder &builder,
299 auto memset = cast<LLVM::MemsetOp>(op);
308 return op->emitError()
309 <<
"could not compute the tangent of a memset whose value is "
315 Value zero = LLVM::ConstantOp::create(builder, op->getLoc(),
316 newOp.getVal().getType(),
317 builder.getI8IntegerAttr(0));
318 auto shadowOp = cast<LLVM::MemsetOp>(builder.clone(*newOp));
319 shadowOp.getDstMutable().assign(shadow);
320 shadowOp.getValMutable().assign(zero);
325 gutils->
erase(newOp);
330struct GEPOpInterfaceReverse
331 :
public ReverseAutoDiffOpInterface::ExternalModel<GEPOpInterfaceReverse,
333 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
335 SmallVector<Value> caches)
const {
339 SmallVector<Value> cacheValues(Operation *op,
344 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
346 auto gep = cast<LLVM::GEPOp>(op);
348 auto base = gep.getBase();
351 auto shadowGep = cast<LLVM::GEPOp>(builder.clone(*newGep));
352 shadowGep.getBaseMutable().assign(baseShadow);
364struct AddrSpaceCastOpInterfaceReverse
365 :
public ReverseAutoDiffOpInterface::ExternalModel<
366 AddrSpaceCastOpInterfaceReverse, LLVM::AddrSpaceCastOp> {
367 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
369 SmallVector<Value> caches)
const {
373 SmallVector<Value> cacheValues(Operation *op,
378 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
380 auto asCast = cast<LLVM::AddrSpaceCastOp>(op);
381 auto arg = asCast.getArg();
386 auto shadowCast = cast<LLVM::AddrSpaceCastOp>(builder.clone(*newCast));
387 shadowCast.getArgMutable().assign(gutils->
invertPointerM(arg, builder));
393struct LoadOpInterfaceReverse
394 :
public ReverseAutoDiffOpInterface::ExternalModel<LoadOpInterfaceReverse,
396 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
398 SmallVector<Value> caches)
const {
399 auto loadOp = cast<LLVM::LoadOp>(op);
400 Value addr = loadOp.getAddr();
402 if (
auto iface = dyn_cast<AutoDiffTypeInterface>(loadOp.getType())) {
407 !iface.isMutable()) {
408 Value gradient = gutils->
diffe(loadOp, builder);
409 Value addrGradient = gutils->
popCache(caches.front(), builder);
412 Value loadedGradient = LLVM::LoadOp::create(
413 builder, loadOp.getLoc(), iface, addrGradient,
414 loadOp.getAlignment().value_or(0));
415 Value addedGradient = iface.createAddOp(builder, loadOp.getLoc(),
416 loadedGradient, gradient);
418 LLVM::StoreOp::create(builder, loadOp.getLoc(), addedGradient,
420 loadOp.getAlignment().value_or(0));
422 LLVM::AtomicRMWOp::create(builder, loadOp.getLoc(),
423 LLVM::AtomicBinOp::fadd, addrGradient,
424 gradient, LLVM::AtomicOrdering::monotonic);
432 SmallVector<Value> cacheValues(Operation *op,
434 auto loadOp = cast<LLVM::LoadOp>(op);
435 auto addr = loadOp.getAddr();
436 if (!(isa<AutoDiffTypeInterface>(loadOp.getType()) &&
444 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
446 auto loadOp = cast<LLVM::LoadOp>(op);
447 Value addr = loadOp.getAddr();
448 auto iface = dyn_cast<AutoDiffTypeInterface>(loadOp.getType());
454 return op->emitError()
455 <<
"could not compute the shadow of a load of a type without "
456 "autodiff semantics "
459 if (!iface.isMutable())
467 return op->emitError()
468 <<
"cannot load a non-constant value out of a constant address "
472 auto shadowLoad = cast<LLVM::LoadOp>(builder.clone(*newLoad));
473 shadowLoad.getAddrMutable().assign(addrShadow);
489static Type getUnderlyingAllocaType(Value ptr) {
491 return alloca.getElemType();
495static bool typeContainsFloat(Type type) {
496 if (isa<FloatType>(type))
498 if (
auto structType = dyn_cast<LLVM::LLVMStructType>(type))
499 return llvm::any_of(structType.getBody(), typeContainsFloat);
500 if (
auto arrayType = dyn_cast<LLVM::LLVMArrayType>(type))
501 return typeContainsFloat(arrayType.getElementType());
502 if (
auto vecType = dyn_cast<VectorType>(type))
503 return typeContainsFloat(vecType.getElementType());
513struct MemcpyForwardInterface
514 :
public AutoDiffOpInterface::ExternalModel<MemcpyForwardInterface,
516 LogicalResult createForwardModeTangent(Operation *op, OpBuilder &builder,
518 auto memcpy = cast<LLVM::MemcpyOp>(op);
521 Value dstShadow = gutils->
invertPointerM(memcpy.getDst(), builder);
527 Type dstType = getUnderlyingAllocaType(memcpy.getDst());
529 return op->emitError()
530 <<
"could not compute the tangent of a memcpy from an "
531 "undifferentiated source without knowing the copied type "
533 if (typeContainsFloat(dstType)) {
538 <<
"assuming the bytes copied from an undifferentiated source "
539 "are floating point and zeroing their tangent "
542 LLVM::ConstantOp::create(builder, op->getLoc(), builder.getI8Type(),
543 builder.getI8IntegerAttr(0));
544 LLVM::MemsetOp::create(builder, op->getLoc(), dstShadow, zero,
545 newOp.getLen(), newOp.getIsVolatile());
553 op, *gutils, memcpy.getSrc(), builder)
554 : gutils->invertPointerM(memcpy.getSrc(), builder);
555 auto shadowOp = cast<LLVM::MemcpyOp>(builder.clone(*newOp));
556 shadowOp.getDstMutable().assign(dstShadow);
557 shadowOp.getSrcMutable().assign(srcShadow);
562 gutils->
erase(newOp);
567struct StoreOpInterfaceReverse
568 :
public ReverseAutoDiffOpInterface::ExternalModel<StoreOpInterfaceReverse,
570 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
572 SmallVector<Value> caches)
const {
573 auto storeOp = cast<LLVM::StoreOp>(op);
574 Value val = storeOp.getValue();
575 Value addr = storeOp.getAddr();
577 auto iface = cast<AutoDiffTypeInterface>(val.getType());
580 Value addrGradient = gutils->
popCache(caches.front(), builder);
582 if (!iface.isMutable()) {
584 Value loadedGradient = LLVM::LoadOp::create(
585 builder, storeOp.getLoc(), val.getType(), addrGradient,
586 storeOp.getAlignment().value_or(0));
587 gutils->
addToDiffe(val, loadedGradient, builder);
591 cast<AutoDiffTypeInterface>(gutils->
getShadowType(val.getType()))
592 .createNullValue(builder, op->getLoc());
594 LLVM::StoreOp::create(builder, storeOp.getLoc(), zero, addrGradient,
595 storeOp.getAlignment().value_or(0));
607 SmallVector<Value> cacheValues(Operation *op,
609 auto storeOp = cast<LLVM::StoreOp>(op);
610 auto addr = storeOp.getAddr();
624 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
626 auto storeOp = cast<LLVM::StoreOp>(op);
627 Value val = storeOp.getValue();
628 Value addr = storeOp.getAddr();
629 auto iface = dyn_cast<AutoDiffTypeInterface>(val.getType());
631 return op->emitError()
632 <<
"could not compute the shadow of a store of a type without "
633 "autodiff semantics "
640 if (!iface.isMutable())
646 : gutils->invertPointerM(val, builder);
648 auto shadowOp = cast<LLVM::StoreOp>(builder.clone(*newOp));
649 shadowOp.getValueMutable().assign(valShadow);
650 shadowOp.getAddrMutable().assign(addrShadow);
655struct ExtractValueOpInterfaceReverse
656 :
public ReverseAutoDiffOpInterface::ExternalModel<
657 ExtractValueOpInterfaceReverse, LLVM::ExtractValueOp> {
658 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
660 SmallVector<Value> caches)
const {
661 auto evOp = cast<LLVM::ExtractValueOp>(op);
662 Value container = evOp.getContainer();
664 auto containerIface = dyn_cast<AutoDiffTypeInterface>(container.getType());
669 Value gradient = gutils->
diffe(evOp, builder);
674 Value zero = containerIface.createNullValue(builder, op->getLoc());
675 Value grad = LLVM::InsertValueOp::create(builder, op->getLoc(), zero,
676 gradient, evOp.getPosition());
684 SmallVector<Value> cacheValues(Operation *op,
689 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
695struct InsertValueOpInterfaceReverse
696 :
public ReverseAutoDiffOpInterface::ExternalModel<
697 InsertValueOpInterfaceReverse, LLVM::InsertValueOp> {
698 LogicalResult createReverseModeAdjoint(Operation *op, OpBuilder &builder,
700 SmallVector<Value> caches)
const {
701 auto ivOp = cast<LLVM::InsertValueOp>(op);
702 Value container = ivOp.getContainer();
703 Value value = ivOp.getValue();
705 auto resultIface = dyn_cast<AutoDiffTypeInterface>(ivOp.getType());
710 Value gradient = gutils->
diffe(ivOp, builder);
715 auto valIface = dyn_cast<AutoDiffTypeInterface>(value.getType());
717 Value valGrad = LLVM::ExtractValueOp::create(
718 builder, op->getLoc(), gradient, ivOp.getPosition());
727 ? valIface.createNullValue(builder, op->getLoc())
728 : LLVM::ZeroOp::create(builder, op->getLoc(), value.getType());
729 Value containerGrad = LLVM::InsertValueOp::create(
730 builder, op->getLoc(), gradient, zeroVal, ivOp.getPosition());
731 gutils->
addToDiffe(container, containerGrad, builder);
738 SmallVector<Value> cacheValues(Operation *op,
743 LogicalResult createShadowValues(Operation *op, OpBuilder &builder,
762static Value normalizeToI64(OpBuilder &builder, Location loc, Value size);
767static Value allocaExtent(OpBuilder &builder, LLVM::AllocaOp alloca) {
768 auto dl = DataLayout::closest(alloca);
769 uint64_t elemBytes = dl.getTypeSize(alloca.getElemType());
770 auto i64Ty = builder.getIntegerType(64);
771 Value bytes = LLVM::ConstantOp::create(builder, alloca.getLoc(), i64Ty,
772 builder.getI64IntegerAttr(elemBytes));
773 Value count = normalizeToI64(builder, alloca.getLoc(), alloca.getArraySize());
776 Value size = LLVM::MulOp::create(builder, alloca.getLoc(), bytes, count);
783static PtrExtent findPtrExtent(Value ptr, OpBuilder *builder =
nullptr) {
784 if (
auto allocOp = ptr.getDefiningOp<llvm_ext::AllocOp>())
785 return {allocOp.getSize(), ptr};
787 for (
auto user : ptr.getUsers())
788 if (
auto psh = dyn_cast<llvm_ext::PtrSizeHintOp>(user))
789 return {psh.getSize(), ptr};
793 for (
auto user : ptr.getUsers()) {
794 auto cast = dyn_cast<LLVM::AddrSpaceCastOp>(user);
797 for (
auto castUser : cast.getRes().getUsers())
798 if (
auto psh = dyn_cast<llvm_ext::PtrSizeHintOp>(castUser))
799 return {psh.getSize(), cast.getRes()};
805 if (
auto alloca = ptr.getDefiningOp<LLVM::AllocaOp>())
806 if (Value size = allocaExtent(*builder, alloca))
809 return {
nullptr, ptr};
814static Value castLikeSpaceOf(OpBuilder &builder, Value ptr, Value inSpaceOf) {
815 if (ptr.getType() == inSpaceOf.getType())
817 return LLVM::AddrSpaceCastOp::create(builder, ptr.getLoc(),
818 inSpaceOf.getType(), ptr);
821static Value normalizeToI64(OpBuilder &builder, Location loc, Value val) {
822 auto i64Ty = builder.getI64Type();
823 if (val.getType() == i64Ty)
826 if (val.getType().isIndex())
827 return arith::IndexCastOp::create(builder, loc, i64Ty, val);
829 auto srcTy = dyn_cast<IntegerType>(val.getType());
831 llvm::errs() <<
"ptr size hint is not an integer: " << val <<
"\n";
835 if (srcTy.getWidth() < 64)
836 return LLVM::ZExtOp::create(builder, loc, i64Ty, val);
837 return LLVM::TruncOp::create(builder, loc, i64Ty, val);
840struct PointerClonableTypeInterface
841 :
public ClonableTypeInterface::ExternalModel<PointerClonableTypeInterface,
842 LLVM::LLVMPointerType> {
843 mlir::Value cloneValue(Type self, OpBuilder &builder, Value value)
const {
844 PtrExtent extent = findPtrExtent(value, &builder);
846 llvm::errs() <<
"cannot find size of ptr: " << value <<
"\n";
850 Value size = normalizeToI64(builder, value.getLoc(), extent.size);
860 Value
clone = llvm_ext::AllocOp::create(builder, value.getLoc(),
861 extent.ptr.getType(), size);
862 llvm_ext::MemcpyOp::create(builder, value.getLoc(),
clone, extent.ptr,
868 void copyValue(Type self, OpBuilder &builder, Value dst, Value src)
const {
872 PtrExtent extent = findPtrExtent(src);
874 extent = findPtrExtent(dst);
876 llvm::errs() <<
"cannot find size of ptr to copy: " << src <<
"\n";
880 Value size = normalizeToI64(builder, src.getLoc(), extent.size);
884 Value dstInSpace = castLikeSpaceOf(builder, dst, extent.ptr);
885 Value srcInSpace = castLikeSpaceOf(builder, src, extent.ptr);
886 llvm_ext::MemcpyOp::create(builder, src.getLoc(), dstInSpace, srcInSpace,
890 void freeClonedValue(Type self, OpBuilder &builder, Value value)
const {
891 llvm_ext::FreeOp::create(builder, value.getLoc(), value);
894 bool implementsBatchAllocation(Type self, Value base,
895 OpFoldResult size)
const {
899 Value deriveSubElement(Type self, OpBuilder &builder, Location loc,
900 Value base, Value multiel, Value index)
const {
901 PtrExtent extent = findPtrExtent(base);
902 Value size = normalizeToI64(builder, loc, extent.size);
903 index = normalizeToI64(builder, loc, index);
905 Value offset = arith::MulIOp::create(builder, loc, size, index);
907 LLVM::GEPOp::create(builder, loc, multiel.getType(),
908 builder.getI8Type(), multiel, ValueRange{offset});
913 Value batchAllocate(Type self, OpBuilder &builder, Location loc, Value base,
914 OpFoldResult size)
const {
915 PtrExtent extent = findPtrExtent(base);
917 Value numel = dyn_cast<Value>(size);
918 if (
auto attr = dyn_cast<Attribute>(size)) {
919 numel = arith::ConstantOp::materialize(builder, attr,
920 builder.getI64Type(), loc);
923 Value baseSize = normalizeToI64(builder, loc, extent.size);
924 Value totalSize = arith::MulIOp::create(builder, loc, numel, baseSize);
926 Value ptr = llvm_ext::AllocOp::create(builder, loc, extent.ptr.getType(),
933class StructTypeInterface
934 :
public AutoDiffTypeInterface::ExternalModel<StructTypeInterface,
935 LLVM::LLVMStructType> {
937 mlir::Attribute createNullAttr(mlir::Type self)
const {
938 llvm::errs() <<
" unsupported: createNullAttribute of LLVMStructType\n";
942 mlir::Value createNullValue(mlir::Type self, OpBuilder &builder,
943 Location loc)
const {
944 auto structTy = cast<LLVM::LLVMStructType>(self);
945 Value result = LLVM::PoisonOp::create(builder, loc, structTy);
946 for (
auto &&[i, elemTy] : llvm::enumerate(structTy.getBody())) {
947 auto elemIface = dyn_cast<AutoDiffTypeInterface>(elemTy);
949 Value zero = LLVM::ZeroOp::create(builder, loc, elemTy);
950 result = LLVM::InsertValueOp::create(builder, loc, result, zero, i);
953 Value nullElem = elemIface.createNullValue(builder, loc);
954 result = LLVM::InsertValueOp::create(builder, loc, result, nullElem, i);
959 Value createAddOp(Type self, OpBuilder &builder, Location loc, Value a,
961 auto structTy = cast<LLVM::LLVMStructType>(self);
962 Value result = LLVM::PoisonOp::create(builder, loc, structTy);
963 for (
auto &&[i, elemTy] : llvm::enumerate(structTy.getBody())) {
964 Value aElem = LLVM::ExtractValueOp::create(builder, loc, a, i);
965 Value bElem = LLVM::ExtractValueOp::create(builder, loc, b, i);
966 auto elemIface = dyn_cast<AutoDiffTypeInterface>(elemTy);
969 sum = elemIface.createAddOp(builder, loc, aElem, bElem);
973 result = LLVM::InsertValueOp::create(builder, loc, result, sum, i);
978 Value createConjOp(Type self, OpBuilder &builder, Location loc,
980 llvm_unreachable(
"TODO");
984 assert(width == 1 &&
"unsupported width != 1");
988 bool isMutable(Type self)
const {
return false; }
990 LogicalResult zeroInPlace(Type self, OpBuilder &builder, Location loc,
995 bool isZero(Type self, Value val)
const {
return false; }
996 bool isZeroAttr(Type self, Attribute attr)
const {
return false; }
999static Value packIntoStruct(ValueRange values, OpBuilder &builder,
1001 SmallVector<Type> resultTypes =
1002 llvm::map_to_vector(values, [](Value v) {
return v.getType(); });
1004 LLVM::LLVMStructType::getLiteral(builder.getContext(), resultTypes);
1005 Value result = LLVM::PoisonOp::create(builder, loc, structType);
1006 for (
auto &&[i, v] : llvm::enumerate(values))
1007 result = LLVM::InsertValueOp::create(builder, loc, result, v, i);
1012class AutoDiffLLVMFuncOpFunctionInterface
1013 :
public AutoDiffFunctionInterface::ExternalModel<
1014 AutoDiffLLVMFuncOpFunctionInterface, LLVM::LLVMFuncOp> {
1028 void detachFromPrimalDefinition(Operation *self)
const {
1029 auto fn = cast<LLVM::LLVMFuncOp>(self);
1030 SymbolRefAttr comdat = fn.getComdatAttr();
1037 SymbolTable::lookupNearestSymbolFrom<LLVM::ComdatSelectorOp>(self,
1041 auto table = dyn_cast<LLVM::ComdatOp>(selector->getParentOp());
1045 StringRef name = fn.getSymName();
1046 SymbolTable selectors(table);
1047 if (!selectors.lookup(name)) {
1048 OpBuilder builder(table.getContext());
1049 builder.setInsertionPointToEnd(&table.getBody().back());
1050 LLVM::ComdatSelectorOp::create(builder, selector.getLoc(), name,
1051 selector.getComdat(),
1055 SymbolRefAttr::get(table.getSymNameAttr(),
1056 {FlatSymbolRefAttr::get(fn.getContext(), name)}));
1059 void transformResultTypes(Operation *self,
1060 SmallVectorImpl<Type> &resultTypes)
const {
1061 auto fn = cast<mlir::FunctionOpInterface>(self);
1062 auto FTy = fn.getFunctionType();
1063 if (resultTypes.empty()) {
1066 resultTypes.push_back(LLVM::LLVMVoidType::get(FTy.getContext()));
1067 }
else if (resultTypes.size() > 1) {
1069 LLVM::LLVMStructType::getLiteral(FTy.getContext(), resultTypes);
1070 resultTypes.clear();
1071 resultTypes.push_back(structType);
1075 Operation *createCall(Operation *self, OpBuilder &builder, Location loc,
1076 ValueRange args)
const {
1077 return LLVM::CallOp::create(builder, loc, cast<LLVM::LLVMFuncOp>(self),
1081 Operation *createReturn(Operation *self, OpBuilder &builder, Location loc,
1082 ValueRange retargs)
const {
1083 if (retargs.size() > 1) {
1084 Value packedReturns = packIntoStruct(retargs, builder, loc);
1085 return LLVM::ReturnOp::create(builder, loc, packedReturns);
1088 return LLVM::ReturnOp::create(builder, loc, retargs);
1095 DialectRegistry ®istry) {
1096 registry.addExtension(+[](MLIRContext *context, LLVM::LLVMDialect *) {
1097 registerInterfaces(context);
1098 LLVM::LLVMPointerType::attachInterface<PointerTypeInterface>(*context);
1099 LLVM::LLVMPointerType::attachInterface<PointerClonableTypeInterface>(
1101 LLVM::LLVMStructType::attachInterface<StructTypeInterface>(*context);
1102 LLVM::LLVMArrayType::attachInterface<ArrayTypeInterface>(*context);
1104 LLVM::LifetimeStartOp::attachInterface<
1105 LifetimeForwardInterface<LLVM::LifetimeStartOp>>(*context);
1106 LLVM::LifetimeEndOp::attachInterface<
1107 LifetimeForwardInterface<LLVM::LifetimeEndOp>>(*context);
1108 LLVM::DbgValueOp::attachInterface<
1109 NoTangentForwardInterface<LLVM::DbgValueOp>>(*context);
1110 LLVM::DbgDeclareOp::attachInterface<
1111 NoTangentForwardInterface<LLVM::DbgDeclareOp>>(*context);
1112 LLVM::DbgLabelOp::attachInterface<
1113 NoTangentForwardInterface<LLVM::DbgLabelOp>>(*context);
1114 LLVM::DbgValueOp::attachInterface<
1115 NoAdjointReverseInterface<LLVM::DbgValueOp>>(*context);
1116 LLVM::DbgDeclareOp::attachInterface<
1117 NoAdjointReverseInterface<LLVM::DbgDeclareOp>>(*context);
1118 LLVM::DbgLabelOp::attachInterface<
1119 NoAdjointReverseInterface<LLVM::DbgLabelOp>>(*context);
1120 LLVM::MemsetOp::attachInterface<MemsetForwardInterface>(*context);
1121 LLVM::MemcpyOp::attachInterface<MemcpyForwardInterface>(*context);
1122 LLVM::SelectOp::attachInterface<SelectActivityInterface>(*context);
1123 LLVM::StoreOp::attachInterface<LLVMStoreLike>(*context);
1124 LLVM::LoadOp::attachInterface<LoadOpInterfaceReverse>(*context);
1125 LLVM::StoreOp::attachInterface<StoreOpInterfaceReverse>(*context);
1126 LLVM::GEPOp::attachInterface<GEPOpInterfaceReverse>(*context);
1127 LLVM::GEPOp::attachInterface<GEPOpOffsetInterface>(*context);
1128 LLVM::BitcastOp::attachInterface<BitcastOpViewInterface>(*context);
1129 LLVM::BitcastOp::attachInterface<
1130 OffsetViewInterface::Model<LLVM::BitcastOp>>(*context);
1131 LLVM::AddrSpaceCastOp::attachInterface<AddrSpaceCastOpInterfaceReverse>(
1133 LLVM::AddrSpaceCastOp::attachInterface<
1134 OffsetViewInterface::Model<LLVM::AddrSpaceCastOp>>(*context);
1135 LLVM::ExtractValueOp::attachInterface<ExtractValueOpInterfaceReverse>(
1137 LLVM::InsertValueOp::attachInterface<InsertValueOpInterfaceReverse>(
1139 LLVM::UnreachableOp::template attachInterface<
1141 LLVM::LLVMFuncOp::attachInterface<AutoDiffLLVMFuncOpFunctionInterface>(
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 std::string str(AugmentedStruct c)
static bool isMutable(Type type)
mlir::Value diffe(mlir::Value origv, mlir::OpBuilder &builder)
void zeroDiffe(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 getBaseObject(Value v, bool offsetAllowed)
Value inactiveStoredValueShadow(Operation *orig, MGradientUtils &gutils, Value stored, OpBuilder &builder)
void registerLLVMDialectAutoDiffInterface(DialectRegistry ®istry)