@@ -26,7 +26,6 @@ limitations under the License.
2626#include " mlir/IR/OpDefinition.h"
2727#include " mlir/IR/Operation.h"
2828#include " mlir/IR/PatternMatch.h"
29- #include " mlir/IR/SymbolTable.h"
3029#include " mlir/IR/Value.h"
3130#include " mlir/IR/Visitors.h"
3231#include " mlir/Interfaces/SideEffectInterfaces.h"
@@ -49,7 +48,6 @@ namespace sdy {
4948
5049namespace {
5150
52- using func::CallOp;
5351using func::FuncOp;
5452
5553void cloneShardingGroupUsers (OpResult opResult, IRMapping& mapping,
@@ -67,12 +65,14 @@ void cloneShardingGroupUsers(OpResult opResult, IRMapping& mapping,
6765// given op is either:
6866// - A broadcast, reshape or slice op.
6967// - An elementwise op.
70- // - A call to a func that all operations are constant preserving.
68+ // - A named computation all operations are constant preserving.
7169// Assumes the op is not constant or iota.
7270bool isConstantPreserving (
73- Operation* op, const llvm::SmallDenseSet<StringRef>& nonConstFuncOps) {
74- if (CallOp callOp = dyn_cast<CallOp>(op)) {
75- return !nonConstFuncOps.contains (callOp.getCallee ());
71+ Operation* op,
72+ const llvm::SmallDenseSet<StringRef>& nonConstantNamedComputationOps) {
73+ if (auto namedComputationOp = dyn_cast<NamedComputationOp>(op)) {
74+ return !nonConstantNamedComputationOps.contains (
75+ namedComputationOp.getName ());
7676 }
7777 if (!isPure (op)) {
7878 return false ;
@@ -93,11 +93,11 @@ bool isConstantPreserving(
9393// constants, that is, exist in `constantOps`.
9494bool isConstantExpression (
9595 Operation* op, const llvm::SetVector<Operation*>& constantOps,
96- const llvm::SmallDenseSet<StringRef>& nonConstFuncOps ) {
96+ const llvm::SmallDenseSet<StringRef>& nonConstantNamedComputationOps ) {
9797 if (isa<ConstantOp, stablehlo::IotaOp>(op)) {
9898 return true ;
9999 }
100- return isConstantPreserving (op, nonConstFuncOps ) &&
100+ return isConstantPreserving (op, nonConstantNamedComputationOps ) &&
101101 llvm::all_of (op->getOperands (), [&](Value operand) {
102102 return operand.getDefiningOp () &&
103103 constantOps.contains (operand.getDefiningOp ());
@@ -117,26 +117,20 @@ bool isScalarExpansion(Operation* op) {
117117// Recursively clones all operands of the given op, that are not already mapped
118118// in `mapping`, and finally clones the op itself. We do not clone scalars as
119119// they do not get sharded.
120- void cloneSubComputation (OpResult opResult, IRMapping& mapping,
121- SymbolTable& symbolTable) {
120+ void cloneSubComputation (OpResult opResult, IRMapping& mapping) {
122121 if (isScalar (opResult) || mapping.lookupOrNull (opResult)) {
123122 return ;
124123 }
125124 Operation* op = opResult.getOwner ();
126125 for (Value operand : op->getOperands ()) {
127126 if (auto defOpResult = dyn_cast<OpResult>(operand)) {
128- cloneSubComputation (defOpResult, mapping, symbolTable );
127+ cloneSubComputation (defOpResult, mapping);
129128 }
130129 }
131130
132131 // This will insert the cloned op right before the original op.
133132 OpBuilder builder (op);
134- Operation* clonedOp = builder.clone (*op, mapping);
135- if (CallOp callOp = dyn_cast<CallOp>(clonedOp)) {
136- FuncOp funcOp = symbolTable.lookup <FuncOp>(callOp.getCallee ());
137- callOp.setCallee (
138- symbolTable.insert (cloneFuncRecursively (funcOp, symbolTable)));
139- }
133+ builder.clone (*op, mapping);
140134 cloneShardingGroupUsers (opResult, mapping, builder);
141135}
142136
@@ -145,19 +139,18 @@ void cloneSubComputation(OpResult opResult, IRMapping& mapping,
145139// sharded.
146140//
147141// Returns the cloned op result.
148- Value cloneSubComputation (OpResult opResult, SymbolTable& symbolTable ) {
142+ Value cloneSubComputation (OpResult opResult) {
149143 if (isScalar (opResult)) {
150144 return opResult;
151145 }
152146 IRMapping mapping;
153- cloneSubComputation (opResult, mapping, symbolTable );
147+ cloneSubComputation (opResult, mapping);
154148 return mapping.lookup (opResult);
155149}
156150
157151void cloneSubComputationOnOperands (
158152 Operation* op, const llvm::SetVector<Operation*>& constantOps,
159- const llvm::SetVector<Operation*>& scalarExpansionOps,
160- SymbolTable& symbolTable) {
153+ const llvm::SetVector<Operation*>& scalarExpansionOps) {
161154 for (OpOperand& operand : op->getOpOperands ()) {
162155 if (auto defOpResult = dyn_cast<OpResult>(operand.get ());
163156 defOpResult && (constantOps.contains (defOpResult.getOwner ()) ||
@@ -167,38 +160,38 @@ void cloneSubComputationOnOperands(
167160 // `defOpResult`, and replace the `operand` with the cloned defining
168161 // op. The cloned constant sub-computation has only one user `op`,
169162 // so that it is isolated from the rest of the computation.
170- operand.set (cloneSubComputation (defOpResult, symbolTable ));
163+ operand.set (cloneSubComputation (defOpResult));
171164 }
172165 }
173166}
174167
175- void processOp (Operation* op, FuncOp funcOp,
176- llvm::SetVector<Operation*>& constantOps,
168+ void processOp (Operation* op, llvm::SetVector<Operation*>& constantOps,
177169 llvm::SetVector<Operation*>& scalarExpansionOps,
178- llvm::SmallDenseSet<StringRef>& nonConstFuncOps,
179- SymbolTable& symbolTable) {
180- if (isa<FuncOp, ShardingGroupOp>(op)) {
170+ llvm::SmallDenseSet<StringRef>& nonConstantNamedComputationOps) {
171+ if (isa<ShardingGroupOp>(op)) {
181172 return ;
182173 }
183- if (isConstantExpression (op, constantOps, nonConstFuncOps )) {
174+ if (isConstantExpression (op, constantOps, nonConstantNamedComputationOps )) {
184175 constantOps.insert (op);
185176 return ;
186177 }
187178 // NOTE: There are cases that op is an constant expression but may not pass
188179 // the following check such as constant and iota ops. That is fine because if
189180 // the op is a constant expression it is a stronger condition than being just
190- // constant preserving and it does not make the `funcOp` non-const, and at
191- // this point, it is guaranteed that the op is not constant expression.
192- if (!isConstantPreserving (op, nonConstFuncOps) &&
181+ // constant preserving and it does not make the parent named computation
182+ // non-const, and at this point, it is guaranteed that the op is not constant
183+ // expression.
184+ if (!isConstantPreserving (op, nonConstantNamedComputationOps) &&
193185 !op->hasTrait <OpTrait::IsTerminator>()) {
194- nonConstFuncOps.insert (funcOp.getName ());
186+ if (auto namedCompuationOp = op->getParentOfType <NamedComputationOp>()) {
187+ nonConstantNamedComputationOps.insert (namedCompuationOp.getName ());
188+ }
195189 }
196190 if (isScalarExpansion (op)) {
197191 scalarExpansionOps.insert (op);
198192 return ;
199193 }
200- cloneSubComputationOnOperands (op, constantOps, scalarExpansionOps,
201- symbolTable);
194+ cloneSubComputationOnOperands (op, constantOps, scalarExpansionOps);
202195}
203196
204197// Converts stablehlo::ConstantOp to sdy::ConstantOp.
@@ -247,14 +240,21 @@ struct ConstantOrScalarSplitterPass
247240 }
248241 }
249242
250- void walkOnRegion (FuncOp funcOp,
251- llvm::SmallDenseSet<StringRef>& nonConstFuncOps,
252- SymbolTable& symbolTable) {
243+ // Assumes that the `NamedComputationOp` of the region are already walked, and
244+ // skips walking on them.
245+ void walkOnRegion (
246+ mlir::Region& region,
247+ llvm::SmallDenseSet<StringRef>& nonConstantNamedComputationOps) {
253248 llvm::SetVector<Operation*> constantOps;
254249 llvm::SetVector<Operation*> scalarExpansionOps;
255- funcOp.walk <WalkOrder::PreOrder>([&](Operation* op) {
256- processOp (op, funcOp, constantOps, scalarExpansionOps, nonConstFuncOps,
257- symbolTable);
250+ region.walk <WalkOrder::PreOrder>([&](Operation* op) {
251+ processOp (op, constantOps, scalarExpansionOps,
252+ nonConstantNamedComputationOps);
253+ // Skip walking on the `NamedComputationOp`.
254+ if (isa<NamedComputationOp>(op)) {
255+ return WalkResult::skip ();
256+ }
257+ return WalkResult::advance ();
258258 });
259259 // Since for every op in `constantOps` that has a use that isn't in
260260 // `constantOps`, we replaced the use with a clone of the entire
@@ -267,7 +267,6 @@ struct ConstantOrScalarSplitterPass
267267
268268 void runOnOperation () final {
269269 ModuleOp moduleOp = getOperation ();
270- SymbolTable symbolTable (moduleOp);
271270
272271 // We first convert any `stablehlo::ConstantOp` to an `sdy::ConstantOp`, so
273272 // that constants won't be deduped via folding.
@@ -276,13 +275,17 @@ struct ConstantOrScalarSplitterPass
276275 }
277276
278277 // Then we split constant sub-computations for each non-constant user.
279- llvm::SmallDenseSet<StringRef> nonConstFuncOps;
280- FuncOp mainFuncOp = walkCallsOrDie (moduleOp, [&](CallOp callOp) {
281- FuncOp funcOp = symbolTable.lookup <FuncOp>(callOp.getCallee ());
282- walkOnRegion (funcOp, nonConstFuncOps, symbolTable);
283- return WalkResult::advance ();
278+ llvm::SmallDenseSet<StringRef> nonConstantNamedComputationOps;
279+ // Iterate on a post-order of NamedComputationOp blocks.
280+ moduleOp.walk ([&](NamedComputationOp namedComputationOp) {
281+ walkOnRegion (namedComputationOp.getBody (),
282+ nonConstantNamedComputationOps);
283+ });
284+ // Iterate order does not matter. Funcs do not call each other. The calls
285+ // are inlined to NamedComputationOps.
286+ moduleOp.walk ([&](FuncOp funcOp) {
287+ walkOnRegion (funcOp.getBody (), nonConstantNamedComputationOps);
284288 });
285- walkOnRegion (mainFuncOp, nonConstFuncOps, symbolTable);
286289 }
287290
288291 private:
0 commit comments