42 llvm::ArrayRef<int64_t> batchSizes) {
43 auto T = dyn_cast<TensorType>(Ty);
45 return RankedTensorType::get(batchSizes, Ty);
48 SmallVector<int64_t> shape(batchSizes.begin(), batchSizes.end());
49 shape.append(T.getShape().begin(), T.getShape().end());
50 auto T2 = T.clone(shape);
55 Operation *callLike, StringRef symbolAttrName, OpBuilder &builder,
56 IRMapping &mapper, llvm::ArrayRef<int64_t> batchSizes,
57 std::map<BatchCacheKey, FunctionOpInterface> &batchedFunctionCache) {
58 auto calledFunc = SymbolTable::lookupNearestSymbolFrom<FunctionOpInterface>(
59 callLike, callLike->getAttrOfType<SymbolRefAttr>(symbolAttrName));
65 std::string fnName =
"batched_" + calledFunc.getName().str();
67 builder, calledFunc, fnName, batchSizes, batchedFunctionCache);
74 SmallVector<Value> newOperands;
75 for (
auto operand : callLike->getOperands())
76 newOperands.push_back(mapper.lookup(operand));
78 SmallVector<Type> resultTypes =
79 llvm::map_to_vector(callLike->getResultTypes(), [&](Type Ty) -> Type {
80 return applyBatchSizes(Ty, batchSizes);
83 Operation *newCallLike =
84 builder.create(callLike->getLoc(), callLike->getName().getIdentifier(),
85 newOperands, resultTypes, callLike->getAttrs());
87 newCallLike->setAttr(symbolAttrName, SymbolRefAttr::get(batchedFunc));
89 for (
auto [oldRes, newRes] :
90 llvm::zip_equal(callLike->getResults(), newCallLike->getResults())) {
91 mapper.map(oldRes, newRes);
98 func::CallOp callOp, OpBuilder &builder, IRMapping &mapper,
99 llvm::ArrayRef<int64_t> batchSizes,
100 std::map<BatchCacheKey, FunctionOpInterface> &batchedFunctionCache) {
102 batchedFunctionCache);
106 OpBuilder &builder, Block *blk, IRMapping &mapper,
107 llvm::ArrayRef<int64_t> batchSizes,
108 std::map<BatchCacheKey, FunctionOpInterface> &batchedFunctionCache,
109 bool withoutTerminator) {
110 for (
auto &src : *blk) {
111 if (
auto callOp = dyn_cast<func::CallOp>(&src)) {
112 if (succeeded(
handleCallOp(callOp, builder, mapper, batchSizes,
113 batchedFunctionCache)))
117 if (isa<enzyme::ForwardDiffOp, enzyme::AutoDiffOp>(&src)) {
119 batchedFunctionCache)))
123 if (
auto ifaceOp = dyn_cast<BatchOpInterface>(&src)) {
124 auto res = ifaceOp.createBatch(builder, mapper, batchSizes);
129 SmallVector<Value, 8> operands;
130 SmallVector<Block *, 2> successors;
133 operands.reserve(src.getNumOperands());
134 for (
auto opValue : src.getOperands())
135 operands.push_back(mapper.lookup(opValue));
137 if (withoutTerminator && src.hasTrait<OpTrait::IsTerminator>()) {
139 for (
unsigned i = 0, e = src.getNumResults(); i != e; ++i)
140 mapper.map(src.getResult(i), operands[i]);
145 successors.reserve(src.getNumSuccessors());
146 for (Block *successor : src.getSuccessors())
147 successors.push_back(mapper.lookup(successor));
149 SmallVector<Type> resultTypes(src.getResultTypes().begin(),
150 src.getResultTypes().end());
151 for (
auto &Ty : resultTypes) {
155 Operation *newOp = Operation::create(
156 src.getLoc(), src.getName(), resultTypes, operands, src.getAttrs(),
157 mlir::PropertyRef(), successors, src.getNumRegions());
160 for (
auto &&[oldReg, newReg] :
161 llvm::zip(src.getRegions(), newOp->getRegions())) {
163 batchedFunctionCache);
167 for (
unsigned i = 0, e = src.getNumResults(); i != e; ++i)
168 mapper.map(src.getResult(i), newOp->getResult(i));
170 builder.insert(newOp);
175 OpBuilder &builder, Region *src, Region *dest, IRMapping &mapper,
176 llvm::ArrayRef<int64_t> batchSizes,
177 std::map<BatchCacheKey, FunctionOpInterface> &batchedFunctionCache) {
179 for (
auto &blk : *src) {
180 auto newBlk =
new Block();
181 dest->push_back(newBlk);
183 mapper.map(&blk, newBlk);
185 for (
auto arg : blk.getArguments()) {
186 Value newArg = newBlk->addArgument(
188 mapper.map(arg, newArg);
192 for (
auto &&[blk, newBlk] : llvm::zip(*src, *dest)) {
193 IRRewriter::InsertionGuard insertGuard(builder);
194 builder.setInsertionPointToEnd(&newBlk);
195 batchCloneBlock(builder, &blk, mapper, batchSizes, batchedFunctionCache,
201 OpBuilder &builder, FunctionOpInterface F, Twine name,
202 llvm::ArrayRef<int64_t> batchSizes,
203 std::map<BatchCacheKey, FunctionOpInterface> &batchedFunctionCache) {
204 assert(!F.getFunctionBody().empty());
209 SmallVector<int64_t>(batchSizes.begin(), batchSizes.end())};
211 auto it = batchedFunctionCache.find(key);
212 if (it != batchedFunctionCache.end())
215 auto FTy = cast<FunctionType>(F.getFunctionType());
217 llvm::SmallVector<mlir::Type> RetTypes;
218 RetTypes.reserve(FTy.getNumResults());
220 for (
auto Ty : FTy.getResults()) {
224 SmallVector<mlir::Type, 4> ArgTypes;
225 ArgTypes.reserve(FTy.getNumInputs());
227 for (
auto Ty : FTy.getInputs()) {
231 FunctionType newFTy = builder.getFunctionType(ArgTypes, RetTypes);
233 auto NewF = cast<FunctionOpInterface>(F->cloneWithoutRegions());
234 SymbolTable::setSymbolName(NewF, name.str());
235 NewF.setType(newFTy);
237 Operation *parent = F->getParentWithTrait<OpTrait::SymbolTable>();
238 SymbolTable table(parent);
240 SymbolTable::setSymbolVisibility(NewF, SymbolTable::Visibility::Private);
242 batchedFunctionCache[key] = NewF;
244 auto &origReg = F.getFunctionBody();
245 auto &newReg = NewF.getFunctionBody();
249 batchedFunctionCache);
256 OpBuilder &builder, T CI, FunctionOpInterface fn,
257 std::map<BatchCacheKey, FunctionOpInterface> &batchedFunctionCache) {
259 fn, SmallVector<int64_t>(CI.getBatchShape().begin(),
260 CI.getBatchShape().end())};
263 auto it = batchedFunctionCache.find(key);
264 FunctionOpInterface newFunc;
266 if (it != batchedFunctionCache.end()) {
270 std::string newFnName =
"batched_" + fn.getName().str();
272 batchedFunctionCache);
void batchCloneBlock(OpBuilder &builder, Block *blk, IRMapping &mapper, llvm::ArrayRef< int64_t > batchSizes, std::map< BatchCacheKey, FunctionOpInterface > &batchedFunctionCache, bool withoutTerminator)
LogicalResult handleCallLikeOp(Operation *callLike, StringRef symbolAttrName, OpBuilder &builder, IRMapping &mapper, llvm::ArrayRef< int64_t > batchSizes, std::map< BatchCacheKey, FunctionOpInterface > &batchedFunctionCache)