second temp commit: i will soft-reset and recommit after next changes
Validate Operations / validate-operations (push) Has been cancelled

This commit is contained in:
NiccoloN
2026-08-03 11:07:28 +02:00
parent 893e90feac
commit 942a9faa4f
62 changed files with 3657 additions and 1891 deletions
@@ -14,10 +14,11 @@
#include "src/Accelerators/PIM/Common/IR/LoopUtils.hpp"
#include "src/Accelerators/PIM/Common/IR/TensorSliceUtils.hpp"
#include "src/Accelerators/PIM/Common/Support/Diagnostics.hpp"
#include "src/Accelerators/PIM/Compiler/PimCompilerOptions.hpp"
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/BiasAddUtils.hpp"
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/Common.hpp"
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/MatrixProductLowering.hpp"
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/RowStripLayoutUtils.hpp"
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Patterns/Math/Gemm.hpp"
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/CompileTime.hpp"
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/PlanLowering.hpp"
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Patterns/Math/ConvGeometry.hpp"
@@ -30,11 +31,14 @@ namespace onnx_mlir {
namespace {
struct ConvToGemm : OpConversionPattern<ONNXConvOp> {
using OpConversionPattern::OpConversionPattern;
explicit ConvToGemm(MLIRContext* ctx, const spatial::SpatialTargetInfo& target)
: OpConversionPattern<ONNXConvOp>(ctx), target(target) {}
LogicalResult matchAndRewrite(ONNXConvOp convOp,
ONNXConvOpAdaptor convOpAdaptor,
ConversionPatternRewriter& rewriter) const override;
const spatial::SpatialTargetInfo& target;
};
struct PreparedConvInput {
@@ -43,45 +47,21 @@ struct PreparedConvInput {
};
static Value createZeroGemmBias(RankedTensorType gemmResultType, PatternRewriter& rewriter);
static StringRef stringifyConvLoweringStrategy(PimConvLoweringType strategy) {
static StringRef stringifyConvLoweringStrategy(spatial::ConvLoweringStrategy strategy) {
switch (strategy) {
case PimConvLoweringAuto: return "auto";
case PimConvLoweringLegacy: return "legacy";
case PimConvLoweringDepthwise: return "depthwise";
case PimConvLoweringPackedIm2Col: return "packed-im2col";
case PimConvLoweringStreamedPatch: return "streamed-patch";
case PimConvLoweringStreamedPacked: return "streamed-packed";
case PimConvLoweringOutputChannelTiled: return "output-channel-tiled";
case PimConvLoweringInputKTiled: return "input-k-tiled";
case PimConvLoweringTiled2D: return "tiled-2d";
case spatial::ConvLoweringStrategy::Auto: return "auto";
case spatial::ConvLoweringStrategy::Legacy: return "legacy";
case spatial::ConvLoweringStrategy::Depthwise: return "depthwise";
case spatial::ConvLoweringStrategy::PackedIm2Col: return "packed-im2col";
case spatial::ConvLoweringStrategy::StreamedPatch: return "streamed-patch";
case spatial::ConvLoweringStrategy::StreamedPacked: return "streamed-packed";
case spatial::ConvLoweringStrategy::OutputChannelTiled: return "output-channel-tiled";
case spatial::ConvLoweringStrategy::InputKTiled: return "input-k-tiled";
case spatial::ConvLoweringStrategy::Tiled2D: return "tiled-2d";
}
llvm_unreachable("unknown conv lowering strategy");
}
static PimConvLoweringType chooseConvLoweringStrategy(const ConvGeometry& geo,
PimConvLoweringType requested) {
if (requested != PimConvLoweringAuto)
return requested;
// Transform-based convolution is intentionally not selected for this ISA:
// it would require explicit transform sequences and staging traffic on top of
// the same crossbar MVM primitive, which is not attractive here.
if (geo.isDepthwise)
return PimConvLoweringDepthwise;
if (geo.k <= geo.xbarSize && geo.c <= geo.xbarSize && geo.pack >= 2 && geo.im2colElements <= pimConvIm2colMaxElements)
return PimConvLoweringPackedIm2Col;
if (geo.k <= geo.xbarSize && geo.c <= geo.xbarSize && geo.pack >= 2 && geo.im2colElements > pimConvIm2colMaxElements)
return PimConvLoweringStreamedPacked;
if (geo.k <= geo.xbarSize && geo.c <= geo.xbarSize)
return PimConvLoweringStreamedPatch;
if (geo.k <= geo.xbarSize && geo.c > geo.xbarSize)
return PimConvLoweringOutputChannelTiled;
if (geo.k > geo.xbarSize && geo.c <= geo.xbarSize)
return PimConvLoweringLegacy;
return PimConvLoweringTiled2D;
}
static Value expandBiasIfNeeded(Value bias, PatternRewriter& rewriter, Location loc) {
auto biasType = cast<RankedTensorType>(bias.getType());
if (biasType.getRank() != 1)
@@ -194,7 +174,11 @@ static Value createCollectedConvOutput(ValueRange gemmRows,
int64_t packFactor,
PatternRewriter& rewriter,
Location loc);
static FailureOr<ConvLoweringState> analyzeConvLoweringState(ONNXConvOp convOp, Value x, Value w, Value b);
static FailureOr<ConvLoweringState> analyzeConvLoweringState(ONNXConvOp convOp,
Value x,
Value w,
Value b,
const spatial::SpatialTargetInfo& target);
namespace depthwise {
@@ -215,10 +199,10 @@ static std::optional<Tiling> computeTiling(int64_t batchSize,
int64_t wHeight,
int64_t wWidth,
int64_t outHeight,
int64_t outWidth) {
int64_t outWidth,
int64_t xbarDim) {
const int64_t kernelElements = wHeight * wWidth;
const int64_t outputMultiplier = numChannelsOut / numChannelsIn;
const int64_t xbarDim = static_cast<int64_t>(crossbarSize.getValue());
if (kernelElements <= 0 || outputMultiplier <= 0 || kernelElements > xbarDim || outputMultiplier > xbarDim)
return std::nullopt;
@@ -249,8 +233,9 @@ static Value buildPackedWeights(DenseElementsAttr wDenseAttr,
const Tiling& tiling,
PatternRewriter& rewriter,
Location loc,
int64_t xbarDim,
int64_t paddedInputRows = -1) {
const int64_t paddedOutputChannels = static_cast<int64_t>(crossbarSize.getValue());
const int64_t paddedOutputChannels = xbarDim;
const int64_t packedInputRows = paddedInputRows > 0 ? paddedInputRows : tiling.tileInputRows;
auto packedWeightType = RankedTensorType::get(
{tiling.numChannelTiles, packedInputRows, paddedOutputChannels}, wType.getElementType());
@@ -396,7 +381,7 @@ static Value createWeightTile(Value packedWeights,
PatternRewriter& rewriter,
Location loc) {
SmallVector<OpFoldResult> offsets {channelTileIndex, rewriter.getIndexAttr(0), rewriter.getIndexAttr(0)};
const int64_t paddedOutputChannels = static_cast<int64_t>(crossbarSize.getValue());
const int64_t paddedOutputChannels = packedWeightType.getDimSize(2);
SmallVector<OpFoldResult> sizes {rewriter.getIndexAttr(1),
rewriter.getIndexAttr(tiling.tileInputRows),
rewriter.getIndexAttr(paddedOutputChannels)};
@@ -528,7 +513,8 @@ static bool canUseStructuredRewrite(const ConvLoweringState& state) {
state.wHeight,
state.wWidth,
state.outHeight,
state.outWidth);
state.outWidth,
state.targetInfo().matrixShape.rows);
if (!tiling)
return false;
@@ -559,7 +545,8 @@ rewriteConv(Operation* convOp, const ConvLoweringState& state, PatternRewriter&
state.wType.getDimSize(2),
state.wType.getDimSize(3),
state.outType.getDimSize(2),
state.outType.getDimSize(3));
state.outType.getDimSize(3),
state.targetInfo().matrixShape.rows);
if (!tiling) {
convOp->emitOpError("failed to derive a structured depthwise tiling that fits Spatial weighted VMM lowering");
return failure();
@@ -579,9 +566,10 @@ rewriteConv(Operation* convOp, const ConvLoweringState& state, PatternRewriter&
paddedInputType.getDimSize(3),
paddedInputType.getDimSize(1)},
paddedInputType.getElementType());
Value channelLastInput = ONNXTransposeOp::create(
rewriter, loc, channelLastInputType, paddedInput, rewriter.getI64ArrayAttr({0, 2, 3, 1}));
Value packedWeights = buildPackedWeights(wDenseAttr, state.wType, *tiling, rewriter, loc);
Value channelLastInput = createLinalgTranspose(
paddedInput, channelLastInputType, {0, 2, 3, 1}, rewriter, loc);
Value packedWeights = buildPackedWeights(
wDenseAttr, state.wType, *tiling, rewriter, loc, state.targetInfo().matrixShape.rows);
Value expandedBias;
SmallVector<Value> batchInputs {channelLastInput};
@@ -600,7 +588,7 @@ rewriteConv(Operation* convOp, const ConvLoweringState& state, PatternRewriter&
RankedTensorType::get({tiling->totalPatches, state.outType.getDimSize(1)}, state.outType.getElementType());
auto rowTileType = RankedTensorType::get({1, tiling->tileOutputChannels}, state.outType.getElementType());
auto paddedRowTileType = RankedTensorType::get(
{1, static_cast<int64_t>(crossbarSize.getValue())}, state.outType.getElementType());
{1, static_cast<int64_t>(state.targetInfo().matrixShape.rows)}, state.outType.getElementType());
auto piecesType = spatial::getGraphBatchPhysicalResultType(
tiling->totalPatches * tiling->numChannelTiles, rowTileType);
auto inputTileType =
@@ -826,8 +814,7 @@ static Value createWeightMatrix(
});
if (!transpose)
return flattened;
return ONNXTransposeOp::create(rewriter, loc, plan.wTransType, flattened, rewriter.getI64ArrayAttr({1, 0}))
.getResult();
return createLinalgTranspose(flattened, plan.wTransType, {1, 0}, rewriter, loc);
};
if (isCompileTimeComputable(weights))
@@ -963,17 +950,17 @@ static FailureOr<Value> rewriteInputKTiledConv(const ConvLoweringState& state,
PatternRewriter& rewriter,
Location loc) {
PreparedConvInput preparedInput = prepareInputForIm2Col(state, rewriter, loc);
ConvGeometry geo = buildConvGeometry(state);
ConvGeometry geo = buildConvGeometry(state, state.targetInfo());
const int64_t xbarDim = geo.xbarSize;
const int64_t numKSlices = ceilIntegerDivide(geo.k, xbarDim);
const int64_t paddedK = numKSlices * xbarDim;
const uint64_t maxLanesPerBatch =
std::max<uint64_t>(1,
static_cast<uint64_t>(crossbarCountInCore.getValue())
static_cast<uint64_t>(state.targetInfo().matrixUnitsPerProcessor)
/ static_cast<uint64_t>(std::max<int64_t>(1, numKSlices * 4)));
const uint64_t rowChunkWidth = std::max<uint64_t>(
1,
std::min<uint64_t>({chooseStreamChunkPositions(geo, /*packFactor=*/1),
std::min<uint64_t>({chooseStreamChunkPositions(geo, /*packFactor=*/1, state.targetInfo()),
maxLanesPerBatch,
static_cast<uint64_t>(state.outWidth)}));
const auto elementType = state.outType.getElementType();
@@ -1227,7 +1214,7 @@ buildConvGemmPlan(const ConvLoweringState& state,
const int64_t wMaxDim = std::max(plan.patchSize, state.numChannelsOut);
plan.maxParallelPixels = forcedPackFactor
? *forcedPackFactor
: std::max<int64_t>(1, static_cast<int64_t>(crossbarSize.getValue()) / wMaxDim);
: std::max<int64_t>(1, static_cast<int64_t>(state.targetInfo().matrixShape.rows) / wMaxDim);
plan.effectiveMaxParallelPixels =
(canPackWeightsAsConstants && canPackBiasAsConstants) ? plan.maxParallelPixels : 1;
plan.packedNumRows = ceilIntegerDivide(plan.chunkNumPatches, plan.effectiveMaxParallelPixels);
@@ -1251,7 +1238,8 @@ static Value createIm2colRows(const ConvLoweringState& state,
const ConvGemmPlan& plan,
PatternRewriter& rewriter,
Location loc) {
if (plan.gemmInputRowsType.getDimSize(1) > crossbarSize.getValue()) {
if (plan.gemmInputRowsType.getDimSize(1)
> static_cast<int64_t>(state.targetInfo().matrixShape.rows)) {
assert(plan.effectiveMaxParallelPixels == 1 && "multi-crossbar im2col rows cannot pack pixels");
auto compute = createSpatCompute<1>(
rewriter, loc, TypeRange {plan.gemmInputRowsType}, {}, preparedInput.value, [&](Value input) {
@@ -1419,15 +1407,15 @@ static Value maybeUnpackChunkRows(Value gemmRows,
return unpackCompute.getResult(0);
}
static Value createStreamedConvRows(const ConvLoweringState& state,
const PreparedConvInput& preparedInput,
Value weightMatrix,
Value biasMatrix,
DenseElementsAttr wDenseAttr,
DenseElementsAttr biasDenseAttr,
int64_t forcedPackFactor,
PatternRewriter& rewriter,
Location loc) {
static FailureOr<Value> createStreamedConvRows(const ConvLoweringState& state,
const PreparedConvInput& preparedInput,
Value weightMatrix,
Value biasMatrix,
DenseElementsAttr wDenseAttr,
DenseElementsAttr biasDenseAttr,
int64_t forcedPackFactor,
PatternRewriter& rewriter,
Location loc) {
const int64_t totalPatches = state.batchSize * state.outHeight * state.outWidth;
ConvGemmPlan plan = buildConvGemmPlan(state, static_cast<bool>(wDenseAttr),
!state.hasBias || static_cast<bool>(biasDenseAttr), 0, totalPatches, forcedPackFactor);
@@ -1435,14 +1423,18 @@ static Value createStreamedConvRows(const ConvLoweringState& state,
Value packedWeights = buildPackedWeights(wDenseAttr, weightMatrix, state, plan, rewriter, loc);
Value gemmBias = state.hasBias ? state.b : createZeroGemmBias(plan.gemmOutputRowsType, rewriter);
Value packedBias = buildPackedBias(gemmBias, biasMatrix, biasDenseAttr, state, plan, rewriter, loc);
Value gemmRows = ONNXGemmOp::create(rewriter, loc, plan.gemmOutputRowsType, inputRows,
packedWeights, packedBias, APFloat(1.0f), APFloat(1.0f), 0, !wDenseAttr).getY();
return maybeUnpackChunkRows(gemmRows, plan, rewriter, loc);
FailureOr<Value> gemmRows = lowerGemmToSpatial(
state.diagnosticAnchor, inputRows, packedWeights, packedBias,
plan.gemmOutputRowsType, /*transA=*/false, /*transB=*/!wDenseAttr,
/*alpha=*/1.0f, /*beta=*/1.0f, state.targetInfo(), rewriter, loc);
if (failed(gemmRows))
return failure();
return maybeUnpackChunkRows(*gemmRows, plan, rewriter, loc);
}
static Value rewritePackedIm2ColConv(const ConvLoweringState& state,
PatternRewriter& rewriter,
Location loc) {
static FailureOr<Value> rewritePackedIm2ColConv(const ConvLoweringState& state,
PatternRewriter& rewriter,
Location loc) {
auto wDenseAttr = getHostConstDenseElementsAttr(state.w);
PreparedConvInput preparedInput = prepareInputForIm2Col(state, rewriter, loc);
Value biasMatrix;
@@ -1466,19 +1458,14 @@ static Value rewritePackedIm2ColConv(const ConvLoweringState& state,
gemmBias = state.b;
Value gemmC = buildPackedBias(gemmBias, biasMatrix, biasDenseAttr, state, plan, rewriter, loc);
Value gemmRows = ONNXGemmOp::create(rewriter,
loc,
plan.gemmOutputRowsType,
gemmInputRows,
gemmB,
gemmC,
APFloat(1.0f),
APFloat(1.0f),
/*transA=*/0,
/*transB=*/!wDenseAttr)
.getY();
FailureOr<Value> gemmRows = lowerGemmToSpatial(
state.diagnosticAnchor, gemmInputRows, gemmB, gemmC,
plan.gemmOutputRowsType, /*transA=*/false, /*transB=*/!wDenseAttr,
/*alpha=*/1.0f, /*beta=*/1.0f, state.targetInfo(), rewriter, loc);
if (failed(gemmRows))
return failure();
return createCollectedConvOutput(ValueRange {gemmRows},
return createCollectedConvOutput(ValueRange {*gemmRows},
state.outType,
plan.gemmOutType,
plan.nhwcType,
@@ -1490,10 +1477,10 @@ static Value rewritePackedIm2ColConv(const ConvLoweringState& state,
loc);
}
static Value rewriteStreamedConv(const ConvLoweringState& state,
PatternRewriter& rewriter,
Location loc,
int64_t forcedPackFactor) {
static FailureOr<Value> rewriteStreamedConv(const ConvLoweringState& state,
PatternRewriter& rewriter,
Location loc,
int64_t forcedPackFactor) {
auto wDenseAttr = getHostConstDenseElementsAttr(state.w);
PreparedConvInput preparedInput = prepareInputForIm2Col(state, rewriter, loc);
Value biasMatrix;
@@ -1506,20 +1493,22 @@ static Value rewriteStreamedConv(const ConvLoweringState& state,
ConvGemmPlan seedPlan = buildConvGemmPlan(
state, static_cast<bool>(wDenseAttr), !state.hasBias || static_cast<bool>(biasDenseAttr), 0, 1, forcedPackFactor);
Value weightMatrix = createWeightMatrix(state.w, seedPlan, static_cast<bool>(wDenseAttr), rewriter, loc);
Value collectedRows = createStreamedConvRows(state,
preparedInput,
weightMatrix,
biasMatrix,
wDenseAttr,
biasDenseAttr,
forcedPackFactor,
rewriter,
loc);
auto gemmOutType = cast<RankedTensorType>(collectedRows.getType());
FailureOr<Value> collectedRows = createStreamedConvRows(state,
preparedInput,
weightMatrix,
biasMatrix,
wDenseAttr,
biasDenseAttr,
forcedPackFactor,
rewriter,
loc);
if (failed(collectedRows))
return failure();
auto gemmOutType = cast<RankedTensorType>(collectedRows->getType());
auto nhwcType = RankedTensorType::get({state.batchSize, state.outHeight, state.outWidth, state.numChannelsOut},
state.outType.getElementType());
return createCollectedConvOutput(
ValueRange {collectedRows}, state.outType, gemmOutType, nhwcType, state.outType, gemmOutType.getDimSize(0),
ValueRange {*collectedRows}, state.outType, gemmOutType, nhwcType, state.outType, gemmOutType.getDimSize(0),
state.numChannelsOut, /*packFactor=*/1, rewriter, loc);
}
@@ -1534,12 +1523,12 @@ static Value createZeroGemmBias(RankedTensorType gemmResultType, PatternRewriter
static bool rowStripOutputTileFitsOneCore(const ConvGeometry& geometry) {
return ceilIntegerDivide(geometry.k, geometry.xbarSize)
* ceilIntegerDivide(geometry.c, geometry.xbarSize)
<= static_cast<int64_t>(crossbarCountInCore.getValue());
<= geometry.matrixUnitsPerProcessor;
}
static bool rowStripOutputChannelTileFitsOneCore(const ConvGeometry& geometry) {
return ceilIntegerDivide(geometry.k, geometry.xbarSize)
<= static_cast<int64_t>(crossbarCountInCore.getValue());
<= geometry.matrixUnitsPerProcessor;
}
static int64_t chooseRowStripPixelPackFactor(const ConvLoweringState& state, int64_t xbarDim) {
@@ -1581,7 +1570,7 @@ static bool canConsumePixelMajorRowStripFragments(const ConvLoweringState& state
failureReason = "non_constant_weight";
return false;
}
if (!rowStripOutputChannelTileFitsOneCore(buildConvGeometry(state))) {
if (!rowStripOutputChannelTileFitsOneCore(buildConvGeometry(state, state.targetInfo()))) {
failureReason = "output_channel_tile_does_not_fit_one_core";
return false;
}
@@ -1790,8 +1779,7 @@ static Value extractDenseConvWindowRow(Value denseInput,
rewriter.getIndexAttr(state.xWidth)};
Value nchw = tensor::ExtractSliceOp::create(
rewriter, loc, nchwType, denseInput, offsets, sizes, getUnitStrides(rewriter, 4));
return ONNXTransposeOp::create(
rewriter, loc, fragmentType, nchw, rewriter.getI64ArrayAttr({0, 2, 3, 1}));
return createLinalgTranspose(nchw, fragmentType, {0, 2, 3, 1}, rewriter, loc);
}
static Value createRowStripWindowMaskTable(const ConvLoweringState& state, PatternRewriter& rewriter) {
@@ -2429,7 +2417,7 @@ static FailureOr<Value> createOutputChannelTiledRowStripConvOutput(const ConvLow
static FailureOr<Value>
createRowStripConvOutputFromDenseInput(const ConvLoweringState& state, PatternRewriter& rewriter, Location loc) {
ConvGeometry geometry = buildConvGeometry(state);
ConvGeometry geometry = buildConvGeometry(state, state.targetInfo());
if (state.group != 1 || state.batchSize != 1 || !rowStripOutputChannelTileFitsOneCore(geometry))
return failure();
@@ -2481,7 +2469,7 @@ static FailureOr<Value> createConvOutputFromPixelMajorRowStripFragments(Value ro
if (!canConsumePixelMajorRowStripFragments(state, failureReason))
return failure();
ConvGeometry geometry = buildConvGeometry(state);
ConvGeometry geometry = buildConvGeometry(state, state.targetInfo());
const int64_t xbarDim = geometry.xbarSize;
const int64_t basePatchSize = state.numChannelsIn * state.wHeight * state.wWidth;
const int64_t baseNumKSlices = ceilIntegerDivide(basePatchSize, xbarDim);
@@ -2521,7 +2509,7 @@ static FailureOr<Value> createPointwiseOutputFromRowStripFragments(Value rowStri
Location loc) {
FailureOr<RowStripPhysicalValue> input = describeRowStripPhysicalValue(rowStripStorage, state.xType);
if (failed(input)) return failure();
ConvGeometry geometry = buildConvGeometry(state);
ConvGeometry geometry = buildConvGeometry(state, state.targetInfo());
const int64_t xbarDim = geometry.xbarSize;
const int64_t inputFragmentChannels = input->fragmentType.getDimSize(3);
if (inputFragmentChannels % xbarDim != 0 || state.numChannelsIn % xbarDim != 0)
@@ -2621,8 +2609,10 @@ static bool canConsumeDepthwiseRowStrip(const ConvLoweringState& state) {
state.wHeight,
state.wWidth,
state.outHeight,
state.outWidth);
return tiling && tiling->numChannelTiles <= static_cast<int64_t>(crossbarCountInCore.getValue());
state.outWidth,
state.targetInfo().matrixShape.rows);
return tiling && tiling->numChannelTiles
<= static_cast<int64_t>(state.targetInfo().matrixUnitsPerProcessor);
}
static Value insertDepthwiseInputSegment(Value inputWindow,
@@ -2726,16 +2716,23 @@ static FailureOr<Value> createDepthwiseOutputFromRowStripFragments(Value rowStri
state.wHeight,
state.wWidth,
state.outHeight,
state.outWidth);
state.outWidth,
state.targetInfo().matrixShape.rows);
auto weight = getHostConstDenseElementsAttr(state.w);
if (!tiling || !weight)
return failure();
Value packedWeights = depthwise::buildPackedWeights(
weight, state.wType, *tiling, rewriter, loc, static_cast<int64_t>(crossbarSize.getValue()));
weight,
state.wType,
*tiling,
rewriter,
loc,
static_cast<int64_t>(state.targetInfo().matrixShape.rows),
static_cast<int64_t>(state.targetInfo().matrixShape.rows));
Value bias = state.hasBias ? expandBiasIfNeeded(state.b, rewriter, loc) : Value();
auto paddedOutputType = RankedTensorType::get(
{1, static_cast<int64_t>(crossbarSize.getValue())}, state.outType.getElementType());
{1, static_cast<int64_t>(state.targetInfo().matrixShape.rows)}, state.outType.getElementType());
auto outputTileType = RankedTensorType::get(
{1, tiling->tileOutputChannels}, state.outType.getElementType());
auto outputPixelType = RankedTensorType::get(
@@ -2759,7 +2756,7 @@ static FailureOr<Value> createDepthwiseOutputFromRowStripFragments(Value rowStri
Value c0 = getOrCreateIndexConstant(rewriter, anchor, 0);
Value c1 = getOrCreateIndexConstant(rewriter, anchor, 1);
Value cOutWidth = getOrCreateIndexConstant(rewriter, anchor, state.outWidth);
const int64_t xbarDim = static_cast<int64_t>(crossbarSize.getValue());
const int64_t xbarDim = static_cast<int64_t>(state.targetInfo().matrixShape.rows);
auto paddedInputScratchType = RankedTensorType::get(
{tiling->numChannelTiles, 1, 1, xbarDim}, state.xType.getElementType(), state.xType.getEncoding());
auto tileScratchType = RankedTensorType::get(
@@ -2773,7 +2770,8 @@ static FailureOr<Value> createDepthwiseOutputFromRowStripFragments(Value rowStri
SmallVector<Value> biasTiles;
SmallVector<Value> tileIndices;
SmallVector<OpFoldResult> weightTileSizes {
rewriter.getIndexAttr(1), rewriter.getIndexAttr(xbarDim), rewriter.getIndexAttr(xbarDim)};
rewriter.getIndexAttr(1), rewriter.getIndexAttr(xbarDim),
rewriter.getIndexAttr(xbarDim)};
for (int64_t tile = 0; tile < tiling->numChannelTiles; ++tile) {
Value tileIndex = getOrCreateIndexConstant(rewriter, anchor, tile);
tileIndices.push_back(tileIndex);
@@ -2861,10 +2859,10 @@ static FailureOr<Value> createDepthwiseOutputFromRowStripFragments(Value rowStri
static FailureOr<Value> createConvOutputFromRowStripInput(const ConvLoweringState& state,
Value rowStripInput,
PimConvLoweringType strategy,
spatial::ConvLoweringStrategy strategy,
PatternRewriter& rewriter,
Location loc) {
if (strategy == PimConvLoweringDepthwise)
if (strategy == spatial::ConvLoweringStrategy::Depthwise)
return createDepthwiseOutputFromRowStripFragments(rowStripInput, state, rewriter, loc);
if (state.xHeight == 1 && state.xWidth == 1 && state.wHeight == 1 && state.wWidth == 1)
return createPointwiseOutputFromRowStripFragments(rowStripInput, state, rewriter, loc);
@@ -2905,17 +2903,23 @@ static Value createCollectedConvOutput(ValueRange gemmRows,
{0, 1, 2},
{3}
});
Value nchwOut = ONNXTransposeOp::create(rewriter, loc, outType, nhwcOut, rewriter.getI64ArrayAttr({0, 3, 1, 2}));
Value nchwOut = createLinalgTranspose(nhwcOut, outType, {0, 3, 1, 2}, rewriter, loc);
spatial::SpatYieldOp::create(rewriter, loc, nchwOut);
});
return collectComputeOp.getResult(0);
}
static FailureOr<ConvLoweringState> analyzeConvLoweringState(ONNXConvOp convOp, Value x, Value w, Value b) {
static FailureOr<ConvLoweringState> analyzeConvLoweringState(ONNXConvOp convOp,
Value x,
Value w,
Value b,
const spatial::SpatialTargetInfo& target) {
ConvLoweringState state;
state.diagnosticAnchor = convOp.getOperation();
state.x = x;
state.w = w;
state.b = b;
state.target = &target;
state.xType = cast<RankedTensorType>(state.x.getType());
state.wType = cast<RankedTensorType>(state.w.getType());
state.outType = cast<RankedTensorType>(convOp.getY().getType());
@@ -3019,6 +3023,7 @@ static FailureOr<ConvLoweringState> analyzeConvLoweringState(ONNXConvOp convOp,
state.padWidthBegin = getI64Attr(*padsAttr, 1);
state.padHeightEnd = getI64Attr(*padsAttr, 2);
state.padWidthEnd = getI64Attr(*padsAttr, 3);
classifyConvProblem(state);
return state;
}
@@ -3043,6 +3048,7 @@ static FailureOr<ConvLoweringState> analyzeConvLoweringState(ONNXConvOp convOp,
state.padWidthEnd = totalPadW / 2;
state.padWidthBegin = totalPadW - state.padWidthEnd;
}
classifyConvProblem(state);
return state;
}
@@ -3051,18 +3057,25 @@ static FailureOr<ConvLoweringState> analyzeConvLoweringState(ONNXConvOp convOp,
return failure();
}
classifyConvProblem(state);
return state;
}
static FailureOr<ConvLoweringState> analyzeConvLoweringState(ONNXConvOp convOp, ONNXConvOpAdaptor convOpAdaptor) {
return analyzeConvLoweringState(convOp, convOpAdaptor.getX(), convOpAdaptor.getW(), convOpAdaptor.getB());
static FailureOr<ConvLoweringState> analyzeConvLoweringState(ONNXConvOp convOp,
ONNXConvOpAdaptor convOpAdaptor,
const spatial::SpatialTargetInfo& target) {
return analyzeConvLoweringState(
convOp, convOpAdaptor.getX(), convOpAdaptor.getW(), convOpAdaptor.getB(), target);
}
static FailureOr<ConvLoweringState> analyzeConvLoweringState(spatial::SpatConv2DPlanOp planOp) {
static FailureOr<ConvLoweringState> analyzeConvLoweringState(
spatial::SpatConv2DPlanOp planOp, const spatial::SpatialTargetInfo& target) {
ConvLoweringState state;
state.diagnosticAnchor = planOp.getOperation();
state.x = planOp.getInput();
state.w = planOp.getWeight();
state.b = planOp.getBias() ? planOp.getBias() : Value();
state.target = &target;
state.xType = dyn_cast<RankedTensorType>(state.x.getType());
state.wType = dyn_cast<RankedTensorType>(state.w.getType());
state.outType = dyn_cast<RankedTensorType>(planOp.getOutput().getType());
@@ -3111,79 +3124,56 @@ static FailureOr<ConvLoweringState> analyzeConvLoweringState(spatial::SpatConv2D
state.strideWidth = strides[1];
state.dilationHeight = dilations[0];
state.dilationWidth = dilations[1];
classifyConvProblem(state);
return state;
}
static FailureOr<PimConvLoweringType> resolveRequestedConvLoweringStrategy(Operation* op) {
if (!useExperimentalConvImpl)
return pimConvLowering.getValue();
static FailureOr<spatial::ConvLoweringStrategy>
resolveRequestedConvLoweringStrategy(Operation* op, const spatial::SpatialTargetInfo& target) {
if (!target.useExperimentalConvImplementation)
return target.convLoweringStrategy;
if (pimConvLowering != PimConvLoweringAuto && pimConvLowering != PimConvLoweringPackedIm2Col) {
if (target.convLoweringStrategy != spatial::ConvLoweringStrategy::Auto
&& target.convLoweringStrategy != spatial::ConvLoweringStrategy::PackedIm2Col) {
op->emitOpError() << "--use-experimental-conv-impl conflicts with --pim-conv-lowering="
<< stringifyConvLoweringStrategy(pimConvLowering);
<< stringifyConvLoweringStrategy(target.convLoweringStrategy);
return failure();
}
return PimConvLoweringPackedIm2Col;
return spatial::ConvLoweringStrategy::PackedIm2Col;
}
static LogicalResult verifyForcedConvLoweringStrategy(Operation* op,
const ConvGeometry& geo,
PimConvLoweringType strategy) {
switch (strategy) {
case PimConvLoweringAuto:
case PimConvLoweringLegacy:
return success();
case PimConvLoweringDepthwise:
if (geo.isDepthwise)
return success();
return op->emitOpError("forced depthwise Conv lowering requires a depthwise convolution");
case PimConvLoweringPackedIm2Col:
if (geo.k <= geo.xbarSize && geo.c <= geo.xbarSize && geo.pack >= 2 && geo.im2colElements <= pimConvIm2colMaxElements)
return success();
return op->emitOpError("forced packed-im2col Conv lowering requires K/C to fit, pack >= 2, and im2col within budget");
case PimConvLoweringStreamedPatch:
if (geo.k <= geo.xbarSize && geo.c <= geo.xbarSize)
return success();
return op->emitOpError("forced streamed-patch Conv lowering requires K and C to each fit one crossbar");
case PimConvLoweringStreamedPacked:
if (geo.k <= geo.xbarSize && geo.c <= geo.xbarSize && geo.pack >= 2)
return success();
return op->emitOpError("forced streamed-packed Conv lowering requires K/C to fit and pack >= 2");
case PimConvLoweringOutputChannelTiled:
if (geo.k <= geo.xbarSize && geo.c > geo.xbarSize)
return success();
return op->emitOpError("forced output-channel-tiled Conv lowering requires K <= X and C > X");
case PimConvLoweringInputKTiled:
if (geo.k > geo.xbarSize && geo.c <= geo.xbarSize)
return success();
return op->emitOpError("forced input-k-tiled Conv lowering requires K > X and C <= X");
case PimConvLoweringTiled2D:
if (geo.k > geo.xbarSize && geo.c > geo.xbarSize)
return success();
return op->emitOpError("forced tiled-2d Conv lowering requires K > X and C > X");
}
llvm_unreachable("unknown conv lowering strategy");
}
static FailureOr<PimConvLoweringType> selectConvLoweringStrategy(Operation* op,
const ConvLoweringState& state) {
FailureOr<PimConvLoweringType> requested = resolveRequestedConvLoweringStrategy(op);
static FailureOr<ConvPlan> selectConvLoweringPlan(
Operation* op, const ConvLoweringState& state) {
FailureOr<spatial::ConvLoweringStrategy> requested =
resolveRequestedConvLoweringStrategy(op, state.targetInfo());
if (failed(requested))
return failure();
ConvGeometry geometry = buildConvGeometry(state);
PimConvLoweringType strategy = chooseConvLoweringStrategy(geometry, *requested);
if (strategy == PimConvLoweringDepthwise && !depthwise::canUseStructuredRewrite(state)
&& *requested == PimConvLoweringAuto)
strategy = PimConvLoweringLegacy;
if (failed(verifyForcedConvLoweringStrategy(op, geometry, strategy)))
if (*requested == spatial::ConvLoweringStrategy::Auto) {
for (const ConvPlan& candidate : buildConvPlanCandidates(state, state.targetInfo())) {
if (candidate.strategy == spatial::ConvLoweringStrategy::Depthwise
&& !depthwise::canUseStructuredRewrite(state)) {
continue;
}
return candidate;
}
op->emitOpError("has no applicable Conv lowering candidate for the injected Spatial target");
return failure();
return strategy;
}
FailureOr<ConvPlan> candidate = makeConvPlan(state, *requested, state.targetInfo());
if (failed(candidate)) {
op->emitOpError() << "forced Conv lowering `"
<< stringifyConvLoweringStrategy(*requested)
<< "` is not applicable to this Conv problem";
return failure();
}
return *candidate;
}
static FailureOr<Value> lowerDenseSelectedConvPlan(Operation* op,
const ConvLoweringState& state,
PimConvLoweringType strategy,
spatial::ConvLoweringStrategy strategy,
PatternRewriter& rewriter,
Location loc);
@@ -3196,18 +3186,18 @@ static ConvLoweringState makeGroupedConvLoweringState(const ConvLoweringState& p
static FailureOr<Value> buildConvValueForStrategy(Operation* op,
Location loc,
const ConvLoweringState& state,
PimConvLoweringType strategy,
spatial::ConvLoweringStrategy strategy,
PatternRewriter& rewriter);
static FailureOr<Value> buildGroupedConvValue(Operation* op,
Location loc,
const ConvLoweringState& state,
PimConvLoweringType strategy,
spatial::ConvLoweringStrategy strategy,
PatternRewriter& rewriter);
static FailureOr<Value> lowerGroupedSelectedConvPlan(Operation* op,
const ConvLoweringState& state,
PimConvLoweringType strategy,
spatial::ConvLoweringStrategy strategy,
PatternRewriter& rewriter,
Location loc) {
return buildGroupedConvValue(op, loc, state, strategy, rewriter);
@@ -3215,7 +3205,7 @@ static FailureOr<Value> lowerGroupedSelectedConvPlan(Operation* op,
static FailureOr<Value> lowerDenseSelectedConvPlan(Operation* op,
const ConvLoweringState& state,
PimConvLoweringType strategy,
spatial::ConvLoweringStrategy strategy,
PatternRewriter& rewriter,
Location loc) {
return buildConvValueForStrategy(op, loc, state, strategy, rewriter);
@@ -3224,29 +3214,29 @@ static FailureOr<Value> lowerDenseSelectedConvPlan(Operation* op,
static FailureOr<Value> buildConvValueForStrategy(Operation* op,
Location loc,
const ConvLoweringState& state,
PimConvLoweringType strategy,
spatial::ConvLoweringStrategy strategy,
PatternRewriter& rewriter) {
const ConvGeometry geo = buildConvGeometry(state);
const ConvGeometry geo = buildConvGeometry(state, state.targetInfo());
switch (strategy) {
case PimConvLoweringDepthwise: {
case spatial::ConvLoweringStrategy::Depthwise: {
return depthwise::rewriteConv(op, state, rewriter, loc);
}
case PimConvLoweringLegacy:
case PimConvLoweringPackedIm2Col: {
case spatial::ConvLoweringStrategy::Legacy:
case spatial::ConvLoweringStrategy::PackedIm2Col: {
return standard::rewritePackedIm2ColConv(state, rewriter, loc);
}
case PimConvLoweringStreamedPatch:
case PimConvLoweringOutputChannelTiled:
case PimConvLoweringTiled2D: {
case spatial::ConvLoweringStrategy::StreamedPatch:
case spatial::ConvLoweringStrategy::OutputChannelTiled:
case spatial::ConvLoweringStrategy::Tiled2D: {
return standard::rewriteStreamedConv(state, rewriter, loc, /*forcedPackFactor=*/1);
}
case PimConvLoweringInputKTiled: {
case spatial::ConvLoweringStrategy::InputKTiled: {
return standard::rewriteInputKTiledConv(state, rewriter, loc);
}
case PimConvLoweringStreamedPacked: {
case spatial::ConvLoweringStrategy::StreamedPacked: {
return standard::rewriteStreamedConv(state, rewriter, loc, geo.pack);
}
case PimConvLoweringAuto:
case spatial::ConvLoweringStrategy::Auto:
break;
}
op->emitOpError("unexpected auto strategy at Conv lowering dispatch");
@@ -3282,13 +3272,14 @@ static ConvLoweringState makeGroupedConvLoweringState(
state.numChannelsInPerGroup = state.numChannelsIn;
state.numChannelsOutPerGroup = state.numChannelsOut;
state.hasBias = static_cast<bool>(groupB);
classifyConvProblem(state);
return state;
}
static FailureOr<Value> buildGroupedConvValue(Operation* op,
Location loc,
const ConvLoweringState& state,
PimConvLoweringType strategy,
spatial::ConvLoweringStrategy strategy,
PatternRewriter& rewriter) {
SmallVector<Value> xSlices = sliceTensor(state.x, /*axis=*/1, state.numChannelsInPerGroup, rewriter, loc);
SmallVector<Value> wSlices = sliceTensor(state.w, /*axis=*/0, state.numChannelsOutPerGroup, rewriter, loc);
@@ -3344,7 +3335,7 @@ static FailureOr<Value> buildGroupedConvValue(Operation* op,
LogicalResult ConvToGemm::matchAndRewrite(ONNXConvOp convOp,
ONNXConvOpAdaptor convOpAdaptor,
ConversionPatternRewriter& rewriter) const {
FailureOr<ConvLoweringState> state = analyzeConvLoweringState(convOp, convOpAdaptor);
FailureOr<ConvLoweringState> state = analyzeConvLoweringState(convOp, convOpAdaptor, target);
if (failed(state))
return failure();
SmallVector<int64_t> pads {
@@ -3362,15 +3353,20 @@ LogicalResult ConvToGemm::matchAndRewrite(ONNXConvOp convOp,
rewriter.getDenseI64ArrayAttr(strides),
rewriter.getDenseI64ArrayAttr(dilations),
rewriter.getI64IntegerAttr(state->group),
rewriter.getStringAttr("nchw"));
spatial::getNCHWLayout(rewriter.getContext()));
rewriter.replaceOp(convOp, convPlan.getResult());
return success();
}
void populateConvPatterns(RewritePatternSet& patterns, MLIRContext* ctx) { patterns.insert<ConvToGemm>(ctx); }
void populateConvPatterns(RewritePatternSet& patterns,
MLIRContext* ctx,
const spatial::SpatialTargetInfo& target) {
patterns.insert<ConvToGemm>(ctx, target);
}
LogicalResult canLowerConvPlanToRowStrip(spatial::SpatConv2DPlanOp planOp) {
FailureOr<ConvLoweringState> state = analyzeConvLoweringState(planOp);
LogicalResult canLowerConvPlanToRowStrip(spatial::SpatConv2DPlanOp planOp,
const spatial::SpatialTargetInfo& target) {
FailureOr<ConvLoweringState> state = analyzeConvLoweringState(planOp, target);
if (failed(state))
return failure();
@@ -3383,38 +3379,41 @@ LogicalResult canLowerConvPlanToRowStrip(spatial::SpatConv2DPlanOp planOp) {
if (state->hasBias && !isSupportedBiasAddValue(state->b, state->outType))
return failure();
ConvGeometry geometry = buildConvGeometry(*state);
ConvGeometry geometry = buildConvGeometry(*state, state->targetInfo());
if (!rowStripOutputChannelTileFitsOneCore(geometry))
return failure();
FailureOr<PimConvLoweringType> strategy = selectConvLoweringStrategy(planOp.getOperation(), *state);
if (failed(strategy))
FailureOr<ConvPlan> plan =
selectConvLoweringPlan(planOp.getOperation(), *state);
if (failed(plan))
return failure();
switch (*strategy) {
case PimConvLoweringLegacy:
case PimConvLoweringDepthwise:
case PimConvLoweringPackedIm2Col:
case PimConvLoweringStreamedPatch:
case PimConvLoweringOutputChannelTiled:
case PimConvLoweringTiled2D:
case PimConvLoweringStreamedPacked:
switch (plan->strategy) {
case spatial::ConvLoweringStrategy::Legacy:
case spatial::ConvLoweringStrategy::Depthwise:
case spatial::ConvLoweringStrategy::PackedIm2Col:
case spatial::ConvLoweringStrategy::StreamedPatch:
case spatial::ConvLoweringStrategy::OutputChannelTiled:
case spatial::ConvLoweringStrategy::Tiled2D:
case spatial::ConvLoweringStrategy::StreamedPacked:
return success();
case PimConvLoweringAuto:
case PimConvLoweringInputKTiled:
case spatial::ConvLoweringStrategy::Auto:
case spatial::ConvLoweringStrategy::InputKTiled:
return failure();
}
llvm_unreachable("unknown conv lowering strategy");
}
LogicalResult canConsumeAndProduceRowStrip(spatial::SpatConv2DPlanOp planOp) {
FailureOr<ConvLoweringState> state = analyzeConvLoweringState(planOp);
LogicalResult canConsumeAndProduceRowStrip(spatial::SpatConv2DPlanOp planOp,
const spatial::SpatialTargetInfo& target) {
FailureOr<ConvLoweringState> state = analyzeConvLoweringState(planOp, target);
if (failed(state))
return failure();
FailureOr<PimConvLoweringType> strategy = selectConvLoweringStrategy(planOp.getOperation(), *state);
if (failed(strategy))
FailureOr<ConvPlan> plan =
selectConvLoweringPlan(planOp.getOperation(), *state);
if (failed(plan))
return failure();
if (*strategy == PimConvLoweringDepthwise)
if (plan->strategy == spatial::ConvLoweringStrategy::Depthwise)
return canConsumeDepthwiseRowStrip(*state) ? success() : failure();
StringRef failureReason;
return canConsumePixelMajorRowStripFragments(*state, failureReason) ? success() : failure();
@@ -3424,23 +3423,25 @@ FailureOr<Value>
lowerSelectedConv2DPlan(spatial::SpatConv2DPlanOp planOp,
std::optional<Value> rowStripInput,
bool emitRowStripLayout,
const spatial::SpatialTargetInfo& target,
PatternRewriter& rewriter) {
FailureOr<ConvLoweringState> state = analyzeConvLoweringState(planOp);
FailureOr<ConvLoweringState> state = analyzeConvLoweringState(planOp, target);
if (failed(state))
return failure();
FailureOr<PimConvLoweringType> strategy = selectConvLoweringStrategy(planOp.getOperation(), *state);
if (failed(strategy))
FailureOr<ConvPlan> plan =
selectConvLoweringPlan(planOp.getOperation(), *state);
if (failed(plan))
return failure();
if (emitRowStripLayout) {
if (rowStripInput) {
if (failed(canConsumeAndProduceRowStrip(planOp)))
if (failed(canConsumeAndProduceRowStrip(planOp, target)))
return planOp.emitOpError("selected row-strip input/output layout is not supported for this Conv plan"), failure();
return createConvOutputFromRowStripInput(
*state, *rowStripInput, *strategy, rewriter, planOp.getLoc());
*state, *rowStripInput, plan->strategy, rewriter, planOp.getLoc());
}
if (failed(canLowerConvPlanToRowStrip(planOp)))
if (failed(canLowerConvPlanToRowStrip(planOp, target)))
return planOp.emitOpError("selected row-strip layout is not supported for this Conv plan"), failure();
FailureOr<Value> rowStripStorage = createRowStripConvOutputFromDenseInput(*state, rewriter, planOp.getLoc());
if (failed(rowStripStorage))
@@ -3448,11 +3449,11 @@ lowerSelectedConv2DPlan(spatial::SpatConv2DPlanOp planOp,
return *rowStripStorage;
}
if (*strategy == PimConvLoweringDepthwise)
return lowerDenseSelectedConvPlan(planOp.getOperation(), *state, *strategy, rewriter, planOp.getLoc());
if (plan->strategy == spatial::ConvLoweringStrategy::Depthwise)
return lowerDenseSelectedConvPlan(planOp.getOperation(), *state, plan->strategy, rewriter, planOp.getLoc());
if (state->group != 1)
return lowerGroupedSelectedConvPlan(planOp.getOperation(), *state, *strategy, rewriter, planOp.getLoc());
return lowerDenseSelectedConvPlan(planOp.getOperation(), *state, *strategy, rewriter, planOp.getLoc());
return lowerGroupedSelectedConvPlan(planOp.getOperation(), *state, plan->strategy, rewriter, planOp.getLoc());
return lowerDenseSelectedConvPlan(planOp.getOperation(), *state, plan->strategy, rewriter, planOp.getLoc());
}
} // namespace onnx_mlir