big refactor
Validate Operations / validate-operations (push) Has been cancelled

This commit is contained in:
NiccoloN
2026-08-04 11:28:05 +02:00
parent f4a3b012cc
commit 10b6ee6c32
150 changed files with 6737 additions and 4816 deletions
File diff suppressed because it is too large Load Diff
@@ -1,49 +1,133 @@
#include "ConvGeometry.hpp"
#include <algorithm>
#include <limits>
#include "src/Accelerators/PIM/Common/IR/ShapeUtils.hpp"
#include "src/Accelerators/PIM/Compiler/PimCompilerOptions.hpp"
namespace onnx_mlir {
namespace {
static const ONNXToSpatialPlanningOptions& defaultPlanningOptions() {
static const ONNXToSpatialPlanningOptions options {
std::numeric_limits<uint64_t>::max(),
std::numeric_limits<uint64_t>::max(),
spatial::ConvLoweringStrategy::Auto,
false,
};
return options;
}
} // namespace
const ONNXToSpatialPlanningOptions& ConvLoweringState::planningOptions() const {
return options ? *options : defaultPlanningOptions();
}
bool isDepthwiseConv(int64_t group, int64_t numChannelsIn, int64_t numChannelsOut, int64_t numChannelsInPerGroup) {
return group == numChannelsIn && numChannelsInPerGroup == 1 && numChannelsOut % group == 0;
}
ConvGeometry buildConvGeometry(const ConvLoweringState& state) {
void classifyConvProblem(ConvProblem& problem) {
problem.isDepthwise = isDepthwiseConv(
problem.group, problem.numChannelsIn, problem.numChannelsOut,
problem.numChannelsInPerGroup);
problem.isGrouped = problem.group > 1;
problem.isPointwise = problem.wHeight == 1 && problem.wWidth == 1
&& problem.strideHeight == 1 && problem.strideWidth == 1
&& problem.dilationHeight == 1 && problem.dilationWidth == 1
&& problem.padHeightBegin == 0 && problem.padHeightEnd == 0
&& problem.padWidthBegin == 0 && problem.padWidthEnd == 0;
}
ConvGeometry buildConvGeometry(const ConvProblem& problem,
const spatial::SpatialTargetResources& target) {
ConvGeometry geo {
state.batchSize,
state.numChannelsIn,
state.xHeight,
state.xWidth,
state.numChannelsOut,
state.wHeight,
state.wWidth,
state.outHeight,
state.outWidth,
state.group,
state.numChannelsInPerGroup,
state.numChannelsOutPerGroup,
state.numChannelsInPerGroup * state.wHeight * state.wWidth,
state.numChannelsOutPerGroup,
state.batchSize * state.outHeight * state.outWidth,
static_cast<int64_t>(crossbarSize.getValue()),
problem.numChannelsInPerGroup * problem.wHeight * problem.wWidth,
problem.numChannelsOutPerGroup,
problem.batchSize * problem.outHeight * problem.outWidth,
static_cast<int64_t>(target.matrixShape.rows),
static_cast<int64_t>(target.matrixUnitsPerProcessor),
1,
0,
state.hasBias,
isDepthwiseConv(state.group, state.numChannelsIn, state.numChannelsOut, state.numChannelsInPerGroup),
};
geo.pack = std::max<int64_t>(1, geo.xbarSize / std::max<int64_t>(geo.k, geo.c));
geo.im2colElements = static_cast<uint64_t>(std::max<int64_t>(0, geo.p)) * static_cast<uint64_t>(std::max<int64_t>(0, geo.k));
return geo;
}
uint64_t chooseStreamChunkPositions(const ConvGeometry& geo, int64_t packFactor) {
static ConvMaterializationKind getMaterializationKind(
spatial::ConvLoweringStrategy strategy) {
switch (strategy) {
case spatial::ConvLoweringStrategy::Depthwise:
return ConvMaterializationKind::StructuredDepthwise;
case spatial::ConvLoweringStrategy::Legacy:
case spatial::ConvLoweringStrategy::PackedIm2Col:
return ConvMaterializationKind::PackedIm2Col;
case spatial::ConvLoweringStrategy::StreamedPatch:
case spatial::ConvLoweringStrategy::OutputChannelTiled:
case spatial::ConvLoweringStrategy::Tiled2D:
return ConvMaterializationKind::StreamedPatch;
case spatial::ConvLoweringStrategy::StreamedPacked:
return ConvMaterializationKind::StreamedPacked;
case spatial::ConvLoweringStrategy::InputKTiled:
return ConvMaterializationKind::InputKTiled;
case spatial::ConvLoweringStrategy::Auto:
break;
}
llvm_unreachable("auto is not a Conv materialization kind");
}
static bool fitsSingleCrossbar(const ConvGeometry& geo) {
return geo.k <= geo.xbarSize && geo.c <= geo.xbarSize;
}
static bool fitsPackedIm2Col(const ConvGeometry& geo,
const ONNXToSpatialPlanningOptions& options) {
return fitsSingleCrossbar(geo) && geo.pack >= 2
&& geo.im2colElements <= options.convIm2colMaxElements;
}
mlir::FailureOr<ConvPlan> makeConvPlan(const ConvProblem& problem,
spatial::ConvLoweringStrategy strategy,
const spatial::SpatialTargetResources& target,
const ONNXToSpatialPlanningOptions& options) {
ConvGeometry geo = buildConvGeometry(problem, target);
auto plan = [&]() { return ConvPlan {getMaterializationKind(strategy)}; };
auto ifApplicable = [&](bool applicable) -> mlir::FailureOr<ConvPlan> {
return applicable ? mlir::FailureOr<ConvPlan>(plan()) : mlir::FailureOr<ConvPlan>(mlir::failure());
};
switch (strategy) {
case spatial::ConvLoweringStrategy::Auto:
return mlir::failure();
case spatial::ConvLoweringStrategy::Legacy:
return plan();
case spatial::ConvLoweringStrategy::Depthwise:
return ifApplicable(problem.isDepthwise);
case spatial::ConvLoweringStrategy::PackedIm2Col:
return ifApplicable(fitsPackedIm2Col(geo, options));
case spatial::ConvLoweringStrategy::StreamedPatch:
return ifApplicable(fitsSingleCrossbar(geo));
case spatial::ConvLoweringStrategy::StreamedPacked:
return ifApplicable(fitsSingleCrossbar(geo) && geo.pack >= 2);
case spatial::ConvLoweringStrategy::OutputChannelTiled:
return ifApplicable(geo.k <= geo.xbarSize && geo.c > geo.xbarSize);
case spatial::ConvLoweringStrategy::InputKTiled:
return ifApplicable(geo.k > geo.xbarSize && geo.c <= geo.xbarSize);
case spatial::ConvLoweringStrategy::Tiled2D:
return ifApplicable(geo.k > geo.xbarSize && geo.c > geo.xbarSize);
}
llvm_unreachable("unknown Conv lowering strategy");
}
uint64_t chooseStreamChunkPositions(const ConvGeometry& geo,
int64_t packFactor,
const ONNXToSpatialPlanningOptions& options) {
const uint64_t patchElements = static_cast<uint64_t>(std::max<int64_t>(1, geo.k));
uint64_t chunkPositions = std::max<uint64_t>(1, pimConvIm2colMaxElements / patchElements);
uint64_t chunkPositions = std::max<uint64_t>(1, options.convIm2colMaxElements / patchElements);
chunkPositions = std::min<uint64_t>(chunkPositions, static_cast<uint64_t>(std::max<int64_t>(1, geo.p)));
chunkPositions = std::min<uint64_t>(chunkPositions, std::max<uint64_t>(1, pimConvStreamChunkPositions));
chunkPositions = std::min<uint64_t>(chunkPositions, std::max<uint64_t>(1, options.convStreamChunkPositions));
if (packFactor > 1 && chunkPositions > static_cast<uint64_t>(packFactor)) {
chunkPositions -= chunkPositions % static_cast<uint64_t>(packFactor);
@@ -52,24 +136,26 @@ uint64_t chooseStreamChunkPositions(const ConvGeometry& geo, int64_t packFactor)
return std::max<uint64_t>(1, chunkPositions);
}
RowInterval computeConvInputRowsForOutputRows(RowInterval outputRows, const ConvLoweringState& state) {
const int64_t rawBegin = outputRows.begin * state.strideHeight - state.padHeightBegin;
RowInterval computeConvInputRowsForOutputRows(RowInterval outputRows, const ConvProblem& problem) {
const int64_t rawBegin = outputRows.begin * problem.strideHeight - problem.padHeightBegin;
const int64_t rawEnd =
(outputRows.end - 1) * state.strideHeight - state.padHeightBegin + state.dilationHeight * (state.wHeight - 1) + 1;
return {std::max<int64_t>(0, rawBegin), std::min<int64_t>(state.xHeight, rawEnd)};
(outputRows.end - 1) * problem.strideHeight - problem.padHeightBegin
+ problem.dilationHeight * (problem.wHeight - 1) + 1;
return {std::max<int64_t>(0, rawBegin), std::min<int64_t>(problem.xHeight, rawEnd)};
}
ConvRowDemand buildConvRowDemand(RowInterval outputRows, const ConvLoweringState& state) {
ConvRowDemand buildConvRowDemand(RowInterval outputRows, const ConvProblem& problem) {
ConvRowDemand demand;
demand.outputRows = outputRows;
demand.neededInputRows = computeConvInputRowsForOutputRows(outputRows, state);
demand.neededInputRows = computeConvInputRowsForOutputRows(outputRows, problem);
demand.acquiredInputRows = demand.neededInputRows;
const int64_t rawBegin = outputRows.begin * state.strideHeight - state.padHeightBegin;
const int64_t rawBegin = outputRows.begin * problem.strideHeight - problem.padHeightBegin;
const int64_t rawEnd =
(outputRows.end - 1) * state.strideHeight - state.padHeightBegin + state.dilationHeight * (state.wHeight - 1) + 1;
(outputRows.end - 1) * problem.strideHeight - problem.padHeightBegin
+ problem.dilationHeight * (problem.wHeight - 1) + 1;
demand.topHaloRows = std::max<int64_t>(0, -rawBegin);
demand.bottomHaloRows = std::max<int64_t>(0, rawEnd - state.xHeight);
demand.bottomHaloRows = std::max<int64_t>(0, rawEnd - problem.xHeight);
demand.acquiredInputRows = demand.neededInputRows;
return demand;
}
@@ -3,14 +3,19 @@
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/Value.h"
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/ONNXToSpatialOptions.hpp"
#include "src/Accelerators/PIM/Dialect/Spatial/SpatialTargetResources.hpp"
#include "src/Accelerators/PIM/Dialect/Spatial/SpatialOps.hpp"
#include <cstdint>
namespace mlir {
class Operation;
} // namespace mlir
namespace onnx_mlir {
struct ConvLoweringState {
mlir::Value x;
mlir::Value w;
mlir::Value b;
struct ConvProblem {
mlir::RankedTensorType xType;
mlir::RankedTensorType wType;
mlir::RankedTensorType outType;
@@ -35,29 +40,32 @@ struct ConvLoweringState {
int64_t dilationHeight;
int64_t dilationWidth;
bool hasBias;
bool isDepthwise = false;
bool isGrouped = false;
bool isPointwise = false;
};
struct ConvLoweringState {
ConvProblem problem;
mlir::Operation* diagnosticAnchor = nullptr;
mlir::Value x;
mlir::Value w;
mlir::Value b;
const spatial::SpatialTargetResources* target = nullptr;
const ONNXToSpatialPlanningOptions* options = nullptr;
const spatial::SpatialTargetResources& targetInfo() const { return *target; }
const ONNXToSpatialPlanningOptions& planningOptions() const;
};
struct ConvGeometry {
int64_t batchSize;
int64_t numChannelsIn;
int64_t xHeight;
int64_t xWidth;
int64_t numChannelsOut;
int64_t wHeight;
int64_t wWidth;
int64_t outHeight;
int64_t outWidth;
int64_t group;
int64_t numChannelsInPerGroup;
int64_t numChannelsOutPerGroup;
int64_t k;
int64_t c;
int64_t p;
int64_t xbarSize;
int64_t matrixUnitsPerProcessor;
int64_t pack;
uint64_t im2colElements;
bool hasBias;
bool isDepthwise;
};
struct RowInterval {
@@ -73,14 +81,36 @@ struct ConvRowDemand {
int64_t bottomHaloRows = 0;
};
enum class ConvMaterializationKind : uint8_t {
StructuredDepthwise,
PackedIm2Col,
StreamedPatch,
StreamedPacked,
InputKTiled,
};
struct ConvPlan {
ConvMaterializationKind kind = ConvMaterializationKind::PackedIm2Col;
};
bool isDepthwiseConv(int64_t group, int64_t numChannelsIn, int64_t numChannelsOut, int64_t numChannelsInPerGroup);
ConvGeometry buildConvGeometry(const ConvLoweringState& state);
void classifyConvProblem(ConvProblem& problem);
uint64_t chooseStreamChunkPositions(const ConvGeometry& geo, int64_t packFactor);
ConvGeometry buildConvGeometry(const ConvProblem& problem,
const spatial::SpatialTargetResources& target);
RowInterval computeConvInputRowsForOutputRows(RowInterval outputRows, const ConvLoweringState& state);
mlir::FailureOr<ConvPlan> makeConvPlan(const ConvProblem& problem,
spatial::ConvLoweringStrategy strategy,
const spatial::SpatialTargetResources& target,
const ONNXToSpatialPlanningOptions& options);
ConvRowDemand buildConvRowDemand(RowInterval outputRows, const ConvLoweringState& state);
uint64_t chooseStreamChunkPositions(const ConvGeometry& geo,
int64_t packFactor,
const ONNXToSpatialPlanningOptions& options);
RowInterval computeConvInputRowsForOutputRows(RowInterval outputRows, const ConvProblem& problem);
ConvRowDemand buildConvRowDemand(RowInterval outputRows, const ConvProblem& problem);
} // namespace onnx_mlir
@@ -31,7 +31,7 @@ struct SiluToSpatialPlan : OpRewritePattern<ONNXMulOp> {
return failure();
auto plan = spatial::SpatSiluPlanOp::create(
rewriter, mulOp.getLoc(), mulOp.getResult().getType(), input, rewriter.getStringAttr("nchw"));
rewriter, mulOp.getLoc(), mulOp.getResult().getType(), input, spatial::getNCHWLayout(rewriter.getContext()));
rewriter.replaceOp(mulOp, plan.getResult());
rewriter.eraseOp(sigmoidOp);
return success();
@@ -48,6 +48,56 @@ static DenseElementsAttr getDenseConstantAttr(Value value) {
return nullptr;
}
struct BlueprintSplatMulToSpatial : OpConversionPattern<ONNXMulOp> {
explicit BlueprintSplatMulToSpatial(MLIRContext* ctx) : OpConversionPattern(ctx, 2) {}
LogicalResult
matchAndRewrite(ONNXMulOp op, ONNXMulOpAdaptor adaptor, ConversionPatternRewriter& rewriter) const override {
auto blueprint = op.getA().getDefiningOp<spatial::SpatBlueprintOp>();
Value scalar = adaptor.getB();
if (!blueprint) {
blueprint = op.getB().getDefiningOp<spatial::SpatBlueprintOp>();
scalar = adaptor.getA();
}
auto scalarAttr = getDenseConstantAttr(scalar);
auto resultType = dyn_cast<RankedTensorType>(op.getResult().getType());
auto storageType = blueprint ? dyn_cast<RankedTensorType>(blueprint.getInput().getType()) : RankedTensorType();
if (!blueprint || !blueprint.getFragments().empty() || !scalarAttr || !scalarAttr.isSplat() || !resultType
|| resultType != blueprint.getOutput().getType() || !storageType)
return failure();
auto mapped = mapGraphBatchFragments(
blueprint.getInput(), storageType, rewriter, op.getLoc(), [&](Value fragment, RankedTensorType fragmentType) {
auto splat = DenseElementsAttr::get(fragmentType, scalarAttr.getSplatValue<Attribute>());
Value constant = arith::ConstantOp::create(rewriter, op.getLoc(), fragmentType, splat);
return FailureOr<Value>(
spatial::SpatVMulOp::create(rewriter, op.getLoc(), fragmentType, fragment, constant).getResult());
});
if (failed(mapped))
return failure();
auto result = spatial::SpatBlueprintOp::create(rewriter,
op.getLoc(),
resultType,
*mapped,
ValueRange {},
blueprint.getLogicalLayoutAttr(),
blueprint.getPhysicalLayoutAttr(),
blueprint.getFragmentOffsetsAttr(),
blueprint.getFragmentSizesAttr(),
blueprint.getIndexMapAttr(),
blueprint.getModeAttr(),
blueprint.getFragmentOperandIndicesAttr(),
blueprint.getFragmentSourceSlotsAttr(),
blueprint.getFragmentSourceOffsetsAttr(),
blueprint.getFragmentStridesAttr(),
blueprint.getConflictPolicyAttr(),
blueprint.getCoveragePolicyAttr());
rewriter.replaceOp(op, result.getOutput());
return success();
}
};
static FailureOr<Value> materializeBroadcastedConstantTensor(Value value,
RankedTensorType resultType,
ConversionPatternRewriter& rewriter,
@@ -210,14 +260,16 @@ struct AddToSpatialCompute : OpConversionPattern<ONNXAddOp> {
classifyBiasAddPlanCandidate(adaptor.getA(), adaptor.getB(), resultType);
if (succeeded(candidate)) {
auto plan = spatial::SpatBiasAddPlanOp::create(
rewriter, op.getLoc(), resultType, candidate->data, candidate->bias, rewriter.getStringAttr("nchw"));
rewriter, op.getLoc(), resultType, candidate->data, candidate->bias,
spatial::getNCHWLayout(rewriter.getContext()));
rewriter.replaceOp(op, plan.getResult());
return success();
}
if (resultType.getRank() == 4 && adaptor.getA().getType() == resultType && adaptor.getB().getType() == resultType) {
auto plan = spatial::SpatAddPlanOp::create(
rewriter, op.getLoc(), resultType, adaptor.getA(), adaptor.getB(), rewriter.getStringAttr("nchw"));
rewriter, op.getLoc(), resultType, adaptor.getA(), adaptor.getB(),
spatial::getNCHWLayout(rewriter.getContext()));
rewriter.replaceOp(op, plan.getResult());
return success();
}
@@ -246,6 +298,7 @@ void populateElementwiseFusionPatterns(RewritePatternSet& patterns, MLIRContext*
}
void populateElementwisePatterns(RewritePatternSet& patterns, MLIRContext* ctx) {
patterns.add<BlueprintSplatMulToSpatial>(ctx);
patterns.add<AddToSpatialCompute>(ctx);
patterns.add<BinaryElementwiseToSpatialCompute<ONNXSubOp, spatial::SpatVSubOp>>(ctx);
patterns.add<BinaryElementwiseToSpatialCompute<ONNXMulOp, spatial::SpatVMulOp>>(ctx);
@@ -1,5 +1,6 @@
#include "mlir/Dialect/Affine/IR/AffineOps.h"
#include "mlir/Dialect/Arith/IR/Arith.h"
#include "mlir/Dialect/Linalg/IR/Linalg.h"
#include "mlir/Dialect/SCF/IR/SCF.h"
#include "mlir/Dialect/Tensor/IR/Tensor.h"
#include "mlir/IR/BuiltinTypes.h"
@@ -21,6 +22,9 @@
#include "src/Accelerators/PIM/Common/PimCommon.hpp"
#include "src/Accelerators/PIM/Common/Support/Diagnostics.hpp"
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/Common.hpp"
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/ContractionProblem.hpp"
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/ContractionPlanning.hpp"
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Patterns/Math/Gemm.hpp"
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/CompileTime.hpp"
#include "src/Accelerators/PIM/Dialect/Spatial/SpatialOps.hpp"
#include "src/Dialect/ONNX/ONNXOps.hpp"
@@ -31,7 +35,7 @@ namespace onnx_mlir {
namespace {
static FailureOr<Value>
materializeScaledConstantTensor(Value value, float factor, ConversionPatternRewriter& rewriter, Location loc) {
materializeScaledConstantTensor(Value value, float factor, PatternRewriter& rewriter, Location loc) {
if (factor == 1.0f)
return value;
@@ -57,7 +61,12 @@ materializeScaledConstantTensor(Value value, float factor, ConversionPatternRewr
}
static Value createGemmBatchKOffset(
Value lane, int64_t numOutRows, int64_t numKSlices, ConversionPatternRewriter& rewriter, Location loc) {
Value lane,
int64_t numOutRows,
int64_t numKSlices,
int64_t xbarSize,
PatternRewriter& rewriter,
Location loc) {
if (numKSlices == 1)
return getOrCreateIndexConstant(rewriter, rewriter.getInsertionBlock()->getParentOp(), 0);
@@ -65,7 +74,7 @@ static Value createGemmBatchKOffset(
AffineExpr d0 = getAffineDimExpr(0, context);
return createOrFoldAffineApply(rewriter,
loc,
(d0.floorDiv(numOutRows) % numKSlices) * crossbarSize.getValue(),
(d0.floorDiv(numOutRows) % numKSlices) * xbarSize,
ValueRange {lane},
rewriter.getInsertionBlock()->getParentOp());
}
@@ -74,7 +83,8 @@ static Value createGemmBatchHOffset(Value lane,
int64_t numOutRows,
int64_t numKSlices,
int64_t numOutHSlices,
ConversionPatternRewriter& rewriter,
int64_t xbarSize,
PatternRewriter& rewriter,
Location loc) {
if (numOutHSlices == 1)
return getOrCreateIndexConstant(rewriter, rewriter.getInsertionBlock()->getParentOp(), 0);
@@ -83,14 +93,14 @@ static Value createGemmBatchHOffset(Value lane,
AffineExpr d0 = getAffineDimExpr(0, context);
return createOrFoldAffineApply(rewriter,
loc,
d0.floorDiv(numOutRows * numKSlices) * crossbarSize.getValue(),
d0.floorDiv(numOutRows * numKSlices) * xbarSize,
ValueRange {lane},
rewriter.getInsertionBlock()->getParentOp());
}
static FailureOr<Value> materializePaddedConstantMatrix(Value value,
RankedTensorType resultType,
ConversionPatternRewriter& rewriter,
PatternRewriter& rewriter,
Location loc) {
auto sourceType = cast<RankedTensorType>(value.getType());
if (sourceType == resultType)
@@ -121,7 +131,7 @@ static FailureOr<Value> materializePaddedConstantMatrix(Value value,
static FailureOr<Value> materializePaddedBroadcastedConstantTensor(Value value,
RankedTensorType resultType,
int64_t unpaddedColumns,
ConversionPatternRewriter& rewriter,
PatternRewriter& rewriter,
Location loc) {
auto denseAttr = getHostConstDenseElementsAttr(value);
if (!denseAttr)
@@ -187,7 +197,7 @@ static FailureOr<Value> materializePaddedBroadcastedConstantTensor(Value value,
static FailureOr<Value> prepareBias(Value c,
RankedTensorType outType,
RankedTensorType paddedOutType,
ConversionPatternRewriter& rewriter,
PatternRewriter& rewriter,
Location loc) {
auto cType = cast<RankedTensorType>(c.getType());
if (!cType.hasStaticShape())
@@ -203,9 +213,15 @@ static FailureOr<Value> prepareBias(Value c,
}
static Value extractATile(
Value a, Value row, Value kOffset, RankedTensorType aTileType, ConversionPatternRewriter& rewriter, Location loc) {
Value a,
Value row,
Value kOffset,
RankedTensorType aTileType,
int64_t xbarSize,
PatternRewriter& rewriter,
Location loc) {
SmallVector<OpFoldResult> offsets {row, kOffset};
SmallVector<OpFoldResult> sizes {rewriter.getIndexAttr(1), rewriter.getIndexAttr(crossbarSize.getValue())};
SmallVector<OpFoldResult> sizes {rewriter.getIndexAttr(1), rewriter.getIndexAttr(xbarSize)};
SmallVector<OpFoldResult> strides {rewriter.getIndexAttr(1), rewriter.getIndexAttr(1)};
return tensor::ExtractSliceOp::create(rewriter, loc, aTileType, a, offsets, sizes, strides).getResult();
@@ -219,7 +235,8 @@ static FailureOr<spatial::SpatComputeBatch> createVmmBatch(Value a,
int64_t numOutRows,
int64_t numKSlices,
int64_t numOutHSlices,
ConversionPatternRewriter& rewriter,
int64_t xbarSize,
PatternRewriter& rewriter,
Location loc) {
const int64_t laneCount = partialPiecesType.getDimSize(0);
auto batchOp = createSpatComputeBatch(
@@ -232,21 +249,21 @@ static FailureOr<spatial::SpatComputeBatch> createVmmBatch(Value a,
[&](detail::SpatComputeBatchBodyArgs args) {
Value row =
onnx_mlir::affineModConst(rewriter, loc, args.lane, numOutRows, rewriter.getInsertionBlock()->getParentOp());
Value kOffset = createGemmBatchKOffset(args.lane, numOutRows, numKSlices, rewriter, loc);
Value hOffset = createGemmBatchHOffset(args.lane, numOutRows, numKSlices, numOutHSlices, rewriter, loc);
Value kOffset = createGemmBatchKOffset(args.lane, numOutRows, numKSlices, xbarSize, rewriter, loc);
Value hOffset = createGemmBatchHOffset(
args.lane, numOutRows, numKSlices, numOutHSlices, xbarSize, rewriter, loc);
auto aTileType =
RankedTensorType::get({1, static_cast<int64_t>(crossbarSize.getValue())}, aType.getElementType());
RankedTensorType::get({1, xbarSize}, aType.getElementType());
auto bTileType = RankedTensorType::get(
{static_cast<int64_t>(crossbarSize.getValue()), static_cast<int64_t>(crossbarSize.getValue())},
{xbarSize, xbarSize},
paddedBType.getElementType());
auto pieceType =
RankedTensorType::get({1, static_cast<int64_t>(crossbarSize.getValue())}, partialPiecesType.getElementType());
Value aTile = extractATile(args.inputs.front(), row, kOffset, aTileType, rewriter, loc);
RankedTensorType::get({1, xbarSize}, partialPiecesType.getElementType());
Value aTile = extractATile(args.inputs.front(), row, kOffset, aTileType, xbarSize, rewriter, loc);
SmallVector<OpFoldResult> bOffsets {kOffset, hOffset};
SmallVector<OpFoldResult> bSizes {rewriter.getIndexAttr(crossbarSize.getValue()),
rewriter.getIndexAttr(crossbarSize.getValue())};
SmallVector<OpFoldResult> bSizes {rewriter.getIndexAttr(xbarSize), rewriter.getIndexAttr(xbarSize)};
SmallVector<OpFoldResult> unitStrides = getUnitStrides(rewriter, 2);
Value bTile = extractStaticSliceOrIdentity(
rewriter, loc, args.weights.front(), bTileType, bOffsets, bSizes, unitStrides);
@@ -260,7 +277,7 @@ static FailureOr<spatial::SpatComputeBatch> createVmmBatch(Value a,
}
static Value extractDynamicGemmBColumn(
Value matrix, Value column, RankedTensorType vectorType, ConversionPatternRewriter& rewriter, Location loc) {
Value matrix, Value column, RankedTensorType vectorType, PatternRewriter& rewriter, Location loc) {
SmallVector<OpFoldResult> offsets {rewriter.getIndexAttr(0), column};
SmallVector<OpFoldResult> sizes {rewriter.getIndexAttr(vectorType.getDimSize(1)), rewriter.getIndexAttr(1)};
SmallVector<OpFoldResult> strides {rewriter.getIndexAttr(1), rewriter.getIndexAttr(1)};
@@ -280,7 +297,7 @@ static Value extractDynamicGemmBColumn(
}
static Value extractDynamicGemmRowVector(
Value matrix, Value row, RankedTensorType vectorType, ConversionPatternRewriter& rewriter, Location loc) {
Value matrix, Value row, RankedTensorType vectorType, PatternRewriter& rewriter, Location loc) {
SmallVector<OpFoldResult> offsets {row, rewriter.getIndexAttr(0)};
SmallVector<OpFoldResult> sizes {rewriter.getIndexAttr(1), rewriter.getIndexAttr(vectorType.getDimSize(1))};
SmallVector<OpFoldResult> strides {rewriter.getIndexAttr(1), rewriter.getIndexAttr(1)};
@@ -311,13 +328,15 @@ static FailureOr<RankedTensorType> verifyDynamicGemmBiasType(RankedTensorType cT
}
static bool hasGemmBias(Value c) {
if (!c)
return false;
Operation* definingOp = c.getDefiningOp();
return (!definingOp || !isa<ONNXNoneOp>(definingOp)) && !isZeroSplatHostConstant(c);
}
static Value createScalarTensorConstant(RankedTensorType scalarType,
float value,
ConversionPatternRewriter& rewriter,
PatternRewriter& rewriter,
Location loc) {
auto elementType = scalarType.getElementType();
auto scalarAttr = rewriter.getFloatAttr(elementType, value);
@@ -330,7 +349,7 @@ static Value createBroadcastedBiasScalar(Value bias,
Value row,
Value column,
RankedTensorType scalarType,
ConversionPatternRewriter& rewriter,
PatternRewriter& rewriter,
Location loc) {
SmallVector<OpFoldResult> unitStrides(biasType.getRank(), rewriter.getIndexAttr(1));
if (biasType.getRank() == 1) {
@@ -365,7 +384,7 @@ static FailureOr<spatial::SpatComputeBatch> createVvdmulBatch(Value a,
RankedTensorType columnPiecesType,
RankedTensorType outType,
bool transposeB,
ConversionPatternRewriter& rewriter,
PatternRewriter& rewriter,
Location loc) {
const int64_t numOutRows = outType.getDimSize(0);
const int64_t numOutCols = outType.getDimSize(1);
@@ -425,7 +444,7 @@ static FailureOr<spatial::SpatCompute> createDynamicGemmOutputCompute(Value scal
RankedTensorType outType,
float alpha,
float beta,
ConversionPatternRewriter& rewriter,
PatternRewriter& rewriter,
Location loc) {
const int64_t numOutRows = outType.getDimSize(0);
const int64_t numOutCols = outType.getDimSize(1);
@@ -510,7 +529,7 @@ static Value createPartialGroupOffset(Value hSlice,
int64_t kSlice,
int64_t numKSlices,
int64_t numOutRows,
ConversionPatternRewriter& rewriter,
PatternRewriter& rewriter,
Location loc) {
MLIRContext* context = rewriter.getContext();
AffineExpr d0 = getAffineDimExpr(0, context);
@@ -527,10 +546,12 @@ static Value extractReductionPiece(Value partialPiecesArg,
RankedTensorType pieceType,
int64_t numKSlices,
int64_t numOutRows,
ConversionPatternRewriter& rewriter,
int64_t xbarSize,
PatternRewriter& rewriter,
Location loc) {
SmallVector<OpFoldResult> unitStrides {rewriter.getIndexAttr(1), rewriter.getIndexAttr(1), rewriter.getIndexAttr(1)};
SmallVector<OpFoldResult> pieceSizes {rewriter.getIndexAttr(numOutRows), rewriter.getIndexAttr(1), rewriter.getIndexAttr(crossbarSize.getValue())};
SmallVector<OpFoldResult> pieceSizes {
rewriter.getIndexAttr(numOutRows), rewriter.getIndexAttr(1), rewriter.getIndexAttr(xbarSize)};
SmallVector<OpFoldResult> pieceOffsets {
createPartialGroupOffset(hSlice, kSlice, numKSlices, numOutRows, rewriter, loc),
rewriter.getIndexAttr(0),
@@ -545,13 +566,15 @@ static Value reducePartialPiecesForHSlice(Value partialPiecesArg,
RankedTensorType pieceType,
int64_t numKSlices,
int64_t numOutRows,
ConversionPatternRewriter& rewriter,
int64_t xbarSize,
PatternRewriter& rewriter,
Location loc) {
SmallVector<Value> activePieces;
activePieces.reserve(numKSlices);
for (int64_t kSlice = 0; kSlice < numKSlices; ++kSlice)
activePieces.push_back(
extractReductionPiece(partialPiecesArg, hSlice, kSlice, pieceType, numKSlices, numOutRows, rewriter, loc));
extractReductionPiece(
partialPiecesArg, hSlice, kSlice, pieceType, numKSlices, numOutRows, xbarSize, rewriter, loc));
while (activePieces.size() > 1) {
SmallVector<Value> nextPieces;
@@ -574,11 +597,12 @@ static FailureOr<Value> createReductionOutput(Value partialPieces,
RankedTensorType outType,
RankedTensorType paddedOutType,
int64_t numKSlices,
ConversionPatternRewriter& rewriter,
int64_t xbarSize,
PatternRewriter& rewriter,
Location loc) {
const int64_t numOutRows = outType.getDimSize(0);
const int64_t numOutHSlices = ceilIntegerDivide(outType.getDimSize(1), crossbarSize.getValue());
auto pieceType = RankedTensorType::get({numOutRows, static_cast<int64_t>(crossbarSize.getValue())},
const int64_t numOutHSlices = ceilIntegerDivide(outType.getDimSize(1), xbarSize);
auto pieceType = RankedTensorType::get({numOutRows, xbarSize},
partialPiecesType.getElementType());
if (bias && cast<RankedTensorType>(bias.getType()) != paddedOutType)
@@ -590,20 +614,20 @@ static FailureOr<Value> createReductionOutput(Value partialPieces,
SmallVector<Value> outputSlices;
outputSlices.reserve(numOutHSlices);
for (int64_t hSlice = 0; hSlice < numOutHSlices; ++hSlice) {
const int64_t columnOffset = hSlice * crossbarSize.getValue();
const int64_t columnOffset = hSlice * xbarSize;
const int64_t columns =
std::min(static_cast<int64_t>(crossbarSize.getValue()), outType.getDimSize(1) - columnOffset);
std::min(xbarSize, outType.getDimSize(1) - columnOffset);
auto outputSliceType = RankedTensorType::get({numOutRows, columns}, outType.getElementType());
auto computeOp = createSpatCompute(
rewriter, loc, TypeRange {outputSliceType}, {}, inputs, [&](ValueRange blockArgs) -> LogicalResult {
Value hSliceValue =
getOrCreateIndexConstant(rewriter, rewriter.getInsertionBlock()->getParentOp(), hSlice);
Value reduced = reducePartialPiecesForHSlice(
blockArgs[0], hSliceValue, pieceType, numKSlices, numOutRows, rewriter, loc);
blockArgs[0], hSliceValue, pieceType, numKSlices, numOutRows, xbarSize, rewriter, loc);
if (bias) {
SmallVector<OpFoldResult> biasOffsets {rewriter.getIndexAttr(0), rewriter.getIndexAttr(columnOffset)};
SmallVector<OpFoldResult> pieceSizes {rewriter.getIndexAttr(numOutRows),
rewriter.getIndexAttr(crossbarSize.getValue())};
rewriter.getIndexAttr(xbarSize)};
SmallVector<OpFoldResult> unitStrides {rewriter.getIndexAttr(1), rewriter.getIndexAttr(1)};
Value biasSlice =
tensor::ExtractSliceOp::create(rewriter, loc, pieceType, blockArgs[1], biasOffsets, pieceSizes, unitStrides)
@@ -637,79 +661,96 @@ static FailureOr<Value> createReductionOutput(Value partialPieces,
}
struct GemmToSpatialComputes : OpConversionPattern<ONNXGemmOp> {
using OpConversionPattern::OpConversionPattern;
explicit GemmToSpatialComputes(MLIRContext* ctx, const spatial::SpatialTargetResources& target)
: OpConversionPattern<ONNXGemmOp>(ctx), target(target) {}
LogicalResult matchAndRewrite(ONNXGemmOp gemmOp,
ONNXGemmOpAdaptor gemmOpAdaptor,
ConversionPatternRewriter& rewriter) const override;
const spatial::SpatialTargetResources& target;
};
} // namespace
LogicalResult GemmToSpatialComputes::matchAndRewrite(ONNXGemmOp gemmOp,
ONNXGemmOpAdaptor gemmOpAdaptor,
ConversionPatternRewriter& rewriter) const {
Location loc = gemmOp.getLoc();
Value a = gemmOpAdaptor.getA();
Value b = gemmOpAdaptor.getB();
Value c = gemmOpAdaptor.getC();
FailureOr<Value> lowerGemmToSpatial(
Operation* diagnosticAnchor,
Value a,
Value b,
Value c,
RankedTensorType outType,
bool transA,
bool transB,
float alpha,
float beta,
const spatial::SpatialTargetResources& target,
PatternRewriter& rewriter,
Location loc) {
auto aType = dyn_cast<RankedTensorType>(a.getType());
auto bType = dyn_cast<RankedTensorType>(b.getType());
auto outType = dyn_cast<RankedTensorType>(gemmOp.getY().getType());
if (!aType || !bType || !outType)
if (!diagnosticAnchor || !aType || !bType || !outType)
return failure();
if (!aType.hasStaticShape()) {
pim::emitUnsupportedStaticShapeDiagnostic(gemmOp, "Gemm input A");
pim::emitUnsupportedStaticShapeDiagnostic(diagnosticAnchor, "Gemm input A");
return failure();
}
if (!bType.hasStaticShape()) {
pim::emitUnsupportedStaticShapeDiagnostic(gemmOp, "Gemm input B");
pim::emitUnsupportedStaticShapeDiagnostic(diagnosticAnchor, "Gemm input B");
return failure();
}
if (!outType.hasStaticShape()) {
pim::emitUnsupportedStaticShapeDiagnostic(gemmOp, "Gemm result");
pim::emitUnsupportedStaticShapeDiagnostic(diagnosticAnchor, "Gemm result");
return failure();
}
if (aType.getRank() != 2) {
pim::emitUnsupportedRankDiagnostic(gemmOp, "Gemm input A", aType.getRank(), {2});
pim::emitUnsupportedRankDiagnostic(diagnosticAnchor, "Gemm input A", aType.getRank(), {2});
return failure();
}
if (bType.getRank() != 2) {
pim::emitUnsupportedRankDiagnostic(gemmOp, "Gemm input B", bType.getRank(), {2});
pim::emitUnsupportedRankDiagnostic(diagnosticAnchor, "Gemm input B", bType.getRank(), {2});
return failure();
}
if (outType.getRank() != 2) {
pim::emitUnsupportedRankDiagnostic(gemmOp, "Gemm result", outType.getRank(), {2});
pim::emitUnsupportedRankDiagnostic(diagnosticAnchor, "Gemm result", outType.getRank(), {2});
return failure();
}
if (gemmOpAdaptor.getTransA()) {
if (transA) {
auto aShape = aType.getShape();
auto transposedType = RankedTensorType::get({aShape[1], aShape[0]}, aType.getElementType(), aType.getEncoding());
a = ONNXTransposeOp::create(rewriter, loc, transposedType, a, rewriter.getI64ArrayAttr({1, 0})).getResult();
a = createLinalgTranspose(a, transposedType, {1, 0}, rewriter, loc);
aType = transposedType;
}
const int64_t numOutRows = outType.getDimSize(0);
const int64_t numOutCols = outType.getDimSize(1);
const int64_t reductionSize = aType.getDimSize(1);
const bool transposeB = gemmOpAdaptor.getTransB();
ContractionProblem problem;
problem.lhsBatchShape = {};
problem.rhsBatchShape = {};
problem.outputBatchShape = {};
problem.lhsBatch = 1;
problem.rhsBatch = 1;
problem.batch = 1;
problem.m = outType.getDimSize(0);
problem.k = aType.getDimSize(1);
problem.n = outType.getDimSize(1);
problem.lhsElementType = aType.getElementType();
problem.rhsElementType = bType.getElementType();
problem.resultElementType = outType.getElementType();
const bool transposeB = transB;
if (!isCompileTimeComputable(b)) {
ContractionPlan plan = makeContractionPlan(
problem, target, ContractionPlanKind::BatchedDynamicVVD);
bool hasC = hasGemmBias(c);
float alpha = gemmOpAdaptor.getAlpha().convertToFloat();
float beta = gemmOpAdaptor.getBeta().convertToFloat();
RankedTensorType biasType;
if (hasC) {
auto cType = dyn_cast<RankedTensorType>(c.getType());
if (!cType || !cType.hasStaticShape()) {
pim::emitUnsupportedStaticShapeDiagnostic(gemmOp, "Gemm bias");
pim::emitUnsupportedStaticShapeDiagnostic(diagnosticAnchor, "Gemm bias");
return failure();
}
auto verifiedBiasType = verifyDynamicGemmBiasType(cType, outType);
if (failed(verifiedBiasType)) {
gemmOp.emitOpError("requires Gemm bias C to be broadcastable to the output shape");
diagnosticAnchor->emitOpError("requires Gemm bias C to be broadcastable to the output shape");
return failure();
}
biasType = *verifiedBiasType;
@@ -717,19 +758,19 @@ LogicalResult GemmToSpatialComputes::matchAndRewrite(ONNXGemmOp gemmOp,
const int64_t bReductionSize = bType.getDimSize(transposeB ? 1 : 0);
const int64_t bOutputColumns = bType.getDimSize(transposeB ? 0 : 1);
if (aType.getDimSize(0) != numOutRows || bReductionSize != reductionSize || bOutputColumns != numOutCols) {
gemmOp.emitOpError("has inconsistent A, B, and output shapes");
if (aType.getDimSize(0) != problem.m || bReductionSize != problem.k || bOutputColumns != problem.n) {
diagnosticAnchor->emitOpError("has inconsistent A, B, and output shapes");
return failure();
}
const int64_t laneCount64 = numOutRows * numOutCols;
const int64_t laneCount64 = plan.laneCount;
if (laneCount64 > std::numeric_limits<int32_t>::max()) {
gemmOp.emitOpError("requires Gemm dynamic batch lane count to fit in i32");
diagnosticAnchor->emitOpError("requires Gemm dynamic batch lane count to fit in i32");
return failure();
}
auto columnType = RankedTensorType::get({numOutRows, 1}, outType.getElementType());
auto scalarPiecesType = spatial::getGraphBatchPhysicalResultType(numOutCols, columnType);
auto columnType = RankedTensorType::get({problem.m, 1}, outType.getElementType());
auto scalarPiecesType = spatial::getGraphBatchPhysicalResultType(problem.n, columnType);
auto batchOp = createVvdmulBatch(a, b, aType, bType, scalarPiecesType, outType, transposeB, rewriter, loc);
if (failed(batchOp))
return failure();
@@ -737,94 +778,128 @@ LogicalResult GemmToSpatialComputes::matchAndRewrite(ONNXGemmOp gemmOp,
batchOp->getResult(0), hasC ? c : Value(), scalarPiecesType, biasType, outType, alpha, beta, rewriter, loc);
if (failed(outputCompute))
return failure();
rewriter.replaceOp(gemmOp, outputCompute->getResults());
return success();
return outputCompute->getResult(0);
}
if (transposeB) {
auto bShape = bType.getShape();
auto transposedType = RankedTensorType::get({bShape[1], bShape[0]}, bType.getElementType(), bType.getEncoding());
b = ONNXTransposeOp::create(rewriter, loc, transposedType, b, rewriter.getI64ArrayAttr({1, 0})).getResult();
if (isCompileTimeComputable(b)) {
auto denseAttr = getHostConstDenseElementsAttr(b);
auto inputType = denseAttr ? dyn_cast<RankedTensorType>(denseAttr.getType()) : nullptr;
auto transposedAttr = inputType && inputType.hasStaticShape() && transposedType.hasStaticShape()
? transposeDenseElementsAttr(denseAttr, {1, 0})
: FailureOr<DenseElementsAttr>(failure());
if (failed(transposedAttr) || transposedAttr->getType() != transposedType) {
diagnosticAnchor->emitOpError("requires Gemm input B transpose to remain statically materializable");
return failure();
}
b = getOrCreateConstant(rewriter,
rewriter.getInsertionBlock()->getParentOp(),
*transposedAttr,
transposedType);
} else {
b = createLinalgTranspose(b, transposedType, {1, 0}, rewriter, loc);
}
bType = transposedType;
}
auto scaledB = materializeScaledConstantTensor(b, gemmOpAdaptor.getAlpha().convertToFloat(), rewriter, loc);
auto scaledB = materializeScaledConstantTensor(b, alpha, rewriter, loc);
if (failed(scaledB)) {
gemmOp.emitOpError("requires constant Gemm input B when alpha is not 1.0");
diagnosticAnchor->emitOpError("requires constant Gemm input B when alpha is not 1.0");
return failure();
}
b = *scaledB;
bType = cast<RankedTensorType>(b.getType());
if (aType.getDimSize(0) != numOutRows || bType.getDimSize(0) != reductionSize || bType.getDimSize(1) != numOutCols) {
gemmOp.emitOpError("has inconsistent A, B, and output shapes after transpose handling");
if (aType.getDimSize(0) != problem.m || bType.getDimSize(0) != problem.k || bType.getDimSize(1) != problem.n) {
diagnosticAnchor->emitOpError("has inconsistent A, B, and output shapes after transpose handling");
return failure();
}
const int64_t numKSlices = ceilIntegerDivide(reductionSize, crossbarSize.getValue());
const int64_t numOutHSlices = ceilIntegerDivide(numOutCols, crossbarSize.getValue());
const int64_t paddedReductionSize = numKSlices * static_cast<int64_t>(crossbarSize.getValue());
const int64_t paddedOutCols = numOutHSlices * static_cast<int64_t>(crossbarSize.getValue());
ContractionPlan plan = makeContractionPlan(
problem, target, ContractionPlanKind::StaticTiled);
const int64_t xbarSize = plan.tileK;
const int64_t numKSlices = plan.reductionSlices;
const int64_t numOutHSlices = plan.outputTiles;
const int64_t paddedReductionSize = numKSlices * plan.tileK;
const int64_t paddedOutCols = numOutHSlices * plan.tileN;
auto paddedBType = RankedTensorType::get({paddedReductionSize, paddedOutCols}, bType.getElementType());
auto paddedB = materializePaddedConstantMatrix(b, paddedBType, rewriter, loc);
if (failed(paddedB)) {
gemmOp.emitOpError("requires constant Gemm input B so tiled weights can be padded statically");
diagnosticAnchor->emitOpError("requires constant Gemm input B so tiled weights can be padded statically");
return failure();
}
b = *paddedB;
auto paddedAType = RankedTensorType::get({numOutRows, paddedReductionSize}, aType.getElementType());
auto paddedAType = RankedTensorType::get({problem.m, paddedReductionSize}, aType.getElementType());
a = createPaddedInputCompute(a, paddedAType, rewriter, loc);
aType = paddedAType;
Value bias;
bool hasC = hasGemmBias(c);
auto paddedOutType = RankedTensorType::get({numOutRows, paddedOutCols}, outType.getElementType());
auto paddedOutType = RankedTensorType::get({problem.m, paddedOutCols}, outType.getElementType());
if (hasC) {
auto cType = dyn_cast<RankedTensorType>(c.getType());
if (!cType || !cType.hasStaticShape()) {
pim::emitUnsupportedStaticShapeDiagnostic(gemmOp, "Gemm bias");
pim::emitUnsupportedStaticShapeDiagnostic(diagnosticAnchor, "Gemm bias");
return failure();
}
auto scaledC = materializeScaledConstantTensor(c, gemmOpAdaptor.getBeta().convertToFloat(), rewriter, loc);
auto scaledC = materializeScaledConstantTensor(c, beta, rewriter, loc);
if (failed(scaledC)) {
gemmOp.emitOpError("requires constant Gemm bias C when beta is not 1.0");
diagnosticAnchor->emitOpError("requires constant Gemm bias C when beta is not 1.0");
return failure();
}
c = *scaledC;
auto preparedBias = prepareBias(c, outType, paddedOutType, rewriter, loc);
if (failed(preparedBias)) {
gemmOp.emitOpError("requires Gemm bias C to be broadcastable to the output shape");
diagnosticAnchor->emitOpError("requires Gemm bias C to be broadcastable to the output shape");
return failure();
}
bias = *preparedBias;
}
const int64_t laneCount64 = numOutHSlices * numKSlices * numOutRows;
const int64_t laneCount64 = plan.laneCount;
if (laneCount64 > std::numeric_limits<int32_t>::max()) {
gemmOp.emitOpError("requires Gemm tiled batch lane count to fit in i32");
diagnosticAnchor->emitOpError("requires Gemm tiled batch lane count to fit in i32");
return failure();
}
auto partialPiecesType = spatial::getGraphBatchPhysicalResultType(
laneCount64, RankedTensorType::get({1, static_cast<int64_t>(crossbarSize.getValue())}, outType.getElementType()));
laneCount64, RankedTensorType::get({1, xbarSize}, outType.getElementType()));
auto batchOp =
createVmmBatch(a, b, aType, paddedBType, partialPiecesType, numOutRows, numKSlices, numOutHSlices, rewriter, loc);
createVmmBatch(
a, b, aType, paddedBType, partialPiecesType, problem.m, numKSlices, numOutHSlices, xbarSize, rewriter, loc);
if (failed(batchOp))
return failure();
auto reductionOutput = createReductionOutput(
batchOp->getResult(0), bias, partialPiecesType, outType, paddedOutType, numKSlices, rewriter, loc);
batchOp->getResult(0), bias, partialPiecesType, outType, paddedOutType, numKSlices, xbarSize, rewriter, loc);
if (failed(reductionOutput))
return failure();
rewriter.replaceOp(gemmOp, *reductionOutput);
return *reductionOutput;
}
LogicalResult GemmToSpatialComputes::matchAndRewrite(ONNXGemmOp gemmOp,
ONNXGemmOpAdaptor gemmOpAdaptor,
ConversionPatternRewriter& rewriter) const {
FailureOr<Value> result = lowerGemmToSpatial(
gemmOp.getOperation(), gemmOpAdaptor.getA(), gemmOpAdaptor.getB(), gemmOpAdaptor.getC(),
cast<RankedTensorType>(gemmOp.getY().getType()), gemmOpAdaptor.getTransA(),
gemmOpAdaptor.getTransB(), gemmOpAdaptor.getAlpha().convertToFloat(),
gemmOpAdaptor.getBeta().convertToFloat(), target, rewriter, gemmOp.getLoc());
if (failed(result))
return failure();
rewriter.replaceOp(gemmOp, *result);
return success();
}
void populateGemmPatterns(RewritePatternSet& patterns, MLIRContext* ctx) {
patterns.insert<GemmToSpatialComputes>(ctx);
void populateGemmPatterns(RewritePatternSet& patterns,
MLIRContext* ctx,
const spatial::SpatialTargetResources& target) {
patterns.insert<GemmToSpatialComputes>(ctx, target);
}
} // namespace onnx_mlir
@@ -0,0 +1,27 @@
#pragma once
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/Location.h"
#include "mlir/IR/Value.h"
#include "mlir/IR/PatternMatch.h"
namespace onnx_mlir {
namespace spatial {
struct SpatialTargetResources;
}
mlir::FailureOr<mlir::Value> lowerGemmToSpatial(
mlir::Operation* diagnosticAnchor,
mlir::Value a,
mlir::Value b,
mlir::Value c,
mlir::RankedTensorType outputType,
bool transA,
bool transB,
float alpha,
float beta,
const spatial::SpatialTargetResources& target,
mlir::PatternRewriter& rewriter,
mlir::Location loc);
} // namespace onnx_mlir
File diff suppressed because it is too large Load Diff
@@ -280,12 +280,12 @@ static FailureOr<Value> buildReduceMeanKeepdimsBlueprint(
SmallVector<int64_t> fragmentStrides(fragmentOffsets.size(), 1);
return spatial::SpatBlueprintOp::create(
rewriter, loc, keepdimsType, batchValue, ValueRange {},
rewriter.getStringAttr("nchw"),
rewriter.getStringAttr("fragmented"),
spatial::getNCHWLayout(rewriter.getContext()),
spatial::getFragmentedLayout(rewriter.getContext()),
rewriter.getDenseI64ArrayAttr(fragmentOffsets),
rewriter.getDenseI64ArrayAttr(fragmentSizes),
rewriter.getStringAttr("reduce_mean_keepdims_fragments"),
rewriter.getStringAttr("fragment_assembly"),
spatial::getFragmentAssemblyMode(rewriter.getContext()),
rewriter.getDenseI64ArrayAttr(operandIndices),
rewriter.getDenseI64ArrayAttr(sourceSlots),
rewriter.getDenseI64ArrayAttr(sourceOffsets),