[Mlir-commits] [mlir] [mlir][linalg] Add constant folder for linalg.elementwise ops (PR #203608)

Federico Bruzzone llvmlistbot at llvm.org
Mon Jun 15 07:04:34 PDT 2026


================
@@ -300,10 +300,95 @@ struct FoldConstantTranspose : public FoldConstantBase<FoldConstantTranspose> {
 
   ControlFusionFn controlFn;
 };
+
+/// Folds linalg.elementwise ops with all-constant inputs by interpreting the
+/// region body. Each op in the body is folded using its own fold()
+/// implementation, enabling constant propagation through any elementwise kind
+/// (unary, binary, ternary) without explicit per-kind handling.
+struct FoldConstantElementwise
+    : public FoldConstantBase<FoldConstantElementwise> {
+
+  using FoldConstantBase::FoldConstantBase;
+
+  bool matchIndexingMaps(LinalgOp linalgOp) const {
+    return isa<ElementwiseOp>(linalgOp.getOperation());
+  }
+
+  RegionComputationFn getRegionComputeFn(LinalgOp linalgOp) const {
+    Block &body = linalgOp->getRegion(0).front();
+
+    auto yieldOp = dyn_cast<linalg::YieldOp>(body.getTerminator());
+    if (!yieldOp || yieldOp.getNumOperands() != 1)
+      return nullptr;
+
+    return [&body](const APIntOrFloatArray &inputs) -> APIntOrFloat {
+      // Map Value -> folded constant Attribute.
+      DenseMap<Value, Attribute> valueMap;
+
+      // Seed block arguments with input constant attributes.
+      bool isFloat = !inputs.apFloats.empty();
+      unsigned numInputs =
+          isFloat ? inputs.apFloats.size() : inputs.apInts.size();
+      for (unsigned i = 0; i < numInputs; ++i) {
+        Value blockArg = body.getArgument(i);
+        Type argType = blockArg.getType();
+        if (isFloat)
+          valueMap[blockArg] = FloatAttr::get(argType, inputs.apFloats[i]);
+        else
+          valueMap[blockArg] = IntegerAttr::get(argType, inputs.apInts[i]);
+      }
+
+      // Walk body ops (excluding terminator) and fold each one.
+      for (Operation &op : body.without_terminator()) {
+        SmallVector<Attribute> operandAttrs;
+        for (Value operand : op.getOperands()) {
+          auto it = valueMap.find(operand);
+          if (it == valueMap.end())
+            return APIntOrFloat{std::nullopt, std::nullopt};
+          operandAttrs.push_back(it->second);
+        }
+
+        SmallVector<OpFoldResult> foldResults;
+        if (failed(op.fold(operandAttrs, foldResults)) || foldResults.empty())
+          return APIntOrFloat{std::nullopt, std::nullopt};
+
+        for (auto [result, foldResult] :
----------------
FedericoBruzzone wrote:

`llvm::zip` silently stops at the shorter range. If `op.fold` ever returns fewer `foldResults` than `op.getNumResults()`, some results stay unmapped and we'd return nullopt downstream. Can we use `llvm::zip_equal` maybe?

https://github.com/llvm/llvm-project/pull/203608


More information about the Mlir-commits mailing list