updat ops validations
This commit is contained in:
@@ -9,6 +9,53 @@ using namespace mlir;
|
||||
|
||||
namespace onnx_mlir {
|
||||
|
||||
FailureOr<Value> createFragmentAssemblyBlueprint(Value physicalBatch,
|
||||
RankedTensorType logicalType,
|
||||
ArrayRef<FragmentAssemblyEntry> entries,
|
||||
StringRef physicalLayout,
|
||||
StringRef indexMap,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
auto physicalType = dyn_cast<RankedTensorType>(physicalBatch.getType());
|
||||
if (!physicalType || !physicalType.hasStaticShape() || !logicalType || !logicalType.hasStaticShape()
|
||||
|| physicalType.getRank() != logicalType.getRank() + 1 || entries.empty())
|
||||
return emitError(loc, "invalid static physical batch for fragment assembly"), failure();
|
||||
|
||||
const int64_t rank = logicalType.getRank();
|
||||
const int64_t laneCount = physicalType.getDimSize(0);
|
||||
if (laneCount <= 0)
|
||||
return emitError(loc, "fragment assembly requires at least one physical source slot"), failure();
|
||||
const int64_t fragmentElements = physicalType.getNumElements() / laneCount;
|
||||
SmallVector<int64_t> operandIndices(entries.size(), 0), sourceSlots, sourceOffsets, offsets, sizes,
|
||||
strides(entries.size() * rank, 1);
|
||||
for (const FragmentAssemblyEntry& entry : entries) {
|
||||
if (entry.sourceSlot < 0 || entry.sourceSlot >= laneCount || entry.sourceOffset < 0
|
||||
|| entry.destinationOffsets.size() != static_cast<size_t>(rank)
|
||||
|| entry.sizes.size() != static_cast<size_t>(rank))
|
||||
return emitError(loc, "invalid fragment assembly entry"), failure();
|
||||
int64_t entryElements = 1;
|
||||
for (int64_t dim = 0; dim < rank; ++dim) {
|
||||
if (entry.destinationOffsets[dim] < 0 || entry.sizes[dim] <= 0
|
||||
|| entry.destinationOffsets[dim] + entry.sizes[dim] > logicalType.getDimSize(dim))
|
||||
return emitError(loc, "fragment assembly entry exceeds the logical tensor"), failure();
|
||||
entryElements *= entry.sizes[dim];
|
||||
}
|
||||
if (entry.sourceOffset + entryElements > fragmentElements)
|
||||
return emitError(loc, "fragment assembly entry exceeds its physical source slot"), failure();
|
||||
sourceSlots.push_back(entry.sourceSlot);
|
||||
sourceOffsets.push_back(entry.sourceOffset);
|
||||
llvm::append_range(offsets, entry.destinationOffsets);
|
||||
llvm::append_range(sizes, entry.sizes);
|
||||
}
|
||||
return spatial::SpatBlueprintOp::create(rewriter, loc, logicalType, physicalBatch, ValueRange {},
|
||||
rewriter.getStringAttr("nchw"), rewriter.getStringAttr(physicalLayout),
|
||||
rewriter.getDenseI64ArrayAttr(offsets), rewriter.getDenseI64ArrayAttr(sizes),
|
||||
rewriter.getStringAttr(indexMap), rewriter.getStringAttr("fragment_assembly"),
|
||||
rewriter.getDenseI64ArrayAttr(operandIndices), rewriter.getDenseI64ArrayAttr(sourceSlots),
|
||||
rewriter.getDenseI64ArrayAttr(sourceOffsets), rewriter.getDenseI64ArrayAttr(strides),
|
||||
rewriter.getStringAttr("disjoint"), rewriter.getStringAttr("complete")).getOutput();
|
||||
}
|
||||
|
||||
Value sumTensors(ArrayRef<Value> tensors, PatternRewriter& rewriter) {
|
||||
if (tensors.size() == 1)
|
||||
return tensors[0];
|
||||
|
||||
@@ -19,6 +19,13 @@
|
||||
|
||||
namespace onnx_mlir {
|
||||
|
||||
struct FragmentAssemblyEntry {
|
||||
int64_t sourceSlot;
|
||||
int64_t sourceOffset;
|
||||
llvm::SmallVector<int64_t, 4> destinationOffsets;
|
||||
llvm::SmallVector<int64_t, 4> sizes;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline mlir::ValueRange getBlockArgs(mlir::Block* block) { return mlir::ValueRange(block->getArguments()); }
|
||||
@@ -407,4 +414,12 @@ mlir::Value materializeOrComputeUnary(mlir::Value input,
|
||||
|
||||
mlir::Value sumTensors(mlir::ArrayRef<mlir::Value> tensors, mlir::PatternRewriter& rewriter);
|
||||
|
||||
mlir::FailureOr<mlir::Value> createFragmentAssemblyBlueprint(mlir::Value physicalBatch,
|
||||
mlir::RankedTensorType logicalType,
|
||||
llvm::ArrayRef<FragmentAssemblyEntry> entries,
|
||||
llvm::StringRef physicalLayout,
|
||||
llvm::StringRef indexMap,
|
||||
mlir::PatternRewriter& rewriter,
|
||||
mlir::Location loc);
|
||||
|
||||
} // namespace onnx_mlir
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "src/Accelerators/PIM/Common/IR/ConstantUtils.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/ComputeRegionBuilder.hpp"
|
||||
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/RowStripLayoutUtils.hpp"
|
||||
#include "src/Accelerators/PIM/Dialect/Spatial/SpatialOps.hpp"
|
||||
#include "src/Dialect/ONNX/ONNXOps.hpp"
|
||||
@@ -12,6 +13,22 @@ using namespace mlir;
|
||||
|
||||
namespace onnx_mlir {
|
||||
|
||||
FailureOr<RowStripPhysicalValue> describeRowStripPhysicalValue(Value storage, RankedTensorType logicalType) {
|
||||
auto storageType = dyn_cast<RankedTensorType>(storage.getType());
|
||||
if (!storageType || !storageType.hasStaticShape() || !logicalType || !logicalType.hasStaticShape()
|
||||
|| storageType.getRank() != 5 || logicalType.getRank() != 4 || logicalType.getDimSize(0) != 1
|
||||
|| storageType.getElementType() != logicalType.getElementType()
|
||||
|| storageType.getDimSize(1) != 1 || storageType.getDimSize(2) <= 0
|
||||
|| storageType.getDimSize(3) != 1 || storageType.getDimSize(4) != logicalType.getDimSize(3))
|
||||
return failure();
|
||||
const int64_t tilesPerRow = ceilIntegerDivide(logicalType.getDimSize(1), storageType.getDimSize(2));
|
||||
if (storageType.getDimSize(0) != logicalType.getDimSize(2) * tilesPerRow)
|
||||
return failure();
|
||||
return RowStripPhysicalValue {storage, logicalType,
|
||||
RankedTensorType::get(storageType.getShape().drop_front(), storageType.getElementType(), storageType.getEncoding()),
|
||||
tilesPerRow};
|
||||
}
|
||||
|
||||
RankedTensorType getRowStripFragmentType(RankedTensorType logicalType) {
|
||||
return RankedTensorType::get({logicalType.getDimSize(0), logicalType.getDimSize(1), 1, logicalType.getDimSize(3)},
|
||||
logicalType.getElementType(),
|
||||
@@ -123,42 +140,39 @@ FailureOr<Value> createRowStripStorageFromRows(Value rows,
|
||||
return batchOp->getResult(0);
|
||||
}
|
||||
|
||||
FailureOr<Value>
|
||||
createRowStripAssemblyBlueprint(Value storage, RankedTensorType logicalType, PatternRewriter& rewriter, Location loc) {
|
||||
auto storageType = dyn_cast<RankedTensorType>(storage.getType());
|
||||
if (!storageType || storageType != getRowStripStorageType(logicalType))
|
||||
return failure();
|
||||
|
||||
auto [offsets, sizes] = buildRowStripMetadata(logicalType);
|
||||
int64_t height = logicalType.getDimSize(2);
|
||||
SmallVector<int64_t> operandIndices(height, 0), sourceSlots, sourceOffsets(height, 0), strides(height * 4, 1);
|
||||
for (int64_t row = 0; row < height; ++row)
|
||||
sourceSlots.push_back(row);
|
||||
return spatial::SpatBlueprintOp::create(rewriter, loc, logicalType, storage, ValueRange {},
|
||||
rewriter.getStringAttr("nchw"), rewriter.getStringAttr("nchw_row_strip"),
|
||||
rewriter.getDenseI64ArrayAttr(offsets), rewriter.getDenseI64ArrayAttr(sizes),
|
||||
rewriter.getStringAttr("nchw_row_strip_fragments"), rewriter.getStringAttr("fragment_assembly"),
|
||||
rewriter.getDenseI64ArrayAttr(operandIndices), rewriter.getDenseI64ArrayAttr(sourceSlots),
|
||||
rewriter.getDenseI64ArrayAttr(sourceOffsets), rewriter.getDenseI64ArrayAttr(strides),
|
||||
rewriter.getStringAttr("disjoint"), rewriter.getStringAttr("complete")).getOutput();
|
||||
FailureOr<Value> createRowStripAssemblyBlueprint(const RowStripPhysicalValue& value,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
SmallVector<FragmentAssemblyEntry> entries;
|
||||
const int64_t tileChannels = value.fragmentType.getDimSize(1);
|
||||
for (int64_t row = 0; row < value.logicalType.getDimSize(2); ++row)
|
||||
for (int64_t tile = 0; tile < value.tilesPerRow; ++tile) {
|
||||
const int64_t channelOffset = tile * tileChannels;
|
||||
entries.push_back({row * value.tilesPerRow + tile, 0, {0, channelOffset, row, 0},
|
||||
{1, std::min(tileChannels, value.logicalType.getDimSize(1) - channelOffset), 1,
|
||||
value.logicalType.getDimSize(3)}});
|
||||
}
|
||||
return createFragmentAssemblyBlueprint(value.storage, value.logicalType, entries, "nchw_row_strip",
|
||||
kRowStripIndexMap, rewriter, loc);
|
||||
}
|
||||
|
||||
FailureOr<Value>
|
||||
applyRowStripRelu(Value storage, RankedTensorType logicalType, PatternRewriter& rewriter, Location loc) {
|
||||
auto fragmentType = getRowStripFragmentType(logicalType);
|
||||
auto storageType = getRowStripStorageType(logicalType);
|
||||
FailureOr<Value> applyRowStripRelu(const RowStripPhysicalValue& value, PatternRewriter& rewriter, Location loc) {
|
||||
auto storageType = cast<RankedTensorType>(value.storage.getType());
|
||||
const int64_t laneCount = storageType.getDimSize(0);
|
||||
auto batchOp = createSpatComputeBatch(rewriter,
|
||||
loc,
|
||||
TypeRange {storageType},
|
||||
logicalType.getDimSize(2),
|
||||
laneCount,
|
||||
{},
|
||||
ValueRange {storage},
|
||||
ValueRange {value.storage},
|
||||
[&](detail::SpatComputeBatchBodyArgs args) {
|
||||
Value fragment =
|
||||
extractRowStripFragment(args.inputs.front(), logicalType, args.lane, rewriter, loc);
|
||||
fragment = spatial::SpatReluOp::create(rewriter, loc, fragmentType, fragment).getResult();
|
||||
insertRowStripFragment(
|
||||
fragment, args.outputs.front(), logicalType, args.lane, rewriter, loc);
|
||||
FailureOr<Value> fragment = extractGraphBatchPhysicalFragment(
|
||||
rewriter, loc, args.inputs.front(), args.lane, value.fragmentType);
|
||||
if (failed(fragment)) return failure();
|
||||
Value relu = spatial::SpatReluOp::create(
|
||||
rewriter, loc, value.fragmentType, *fragment).getResult();
|
||||
publishGraphBatchPhysicalFragment(
|
||||
rewriter, loc, relu, args.outputs.front(), args.lane);
|
||||
return success();
|
||||
});
|
||||
if (failed(batchOp))
|
||||
@@ -166,41 +180,47 @@ applyRowStripRelu(Value storage, RankedTensorType logicalType, PatternRewriter&
|
||||
return batchOp->getResult(0);
|
||||
}
|
||||
|
||||
FailureOr<Value>
|
||||
applyRowStripBiasAdd(Value storage, RankedTensorType logicalType, Value bias, PatternRewriter& rewriter, Location loc) {
|
||||
FailureOr<Value> applyRowStripBiasAdd(const RowStripPhysicalValue& value,
|
||||
Value bias,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
DenseElementsAttr denseAttr;
|
||||
if (!isSupportedBiasAddValue(bias, logicalType, &denseAttr))
|
||||
if (!isSupportedBiasAddValue(bias, value.logicalType, &denseAttr))
|
||||
return failure();
|
||||
auto fragmentType = getRowStripFragmentType(logicalType);
|
||||
auto storageType = getRowStripStorageType(logicalType);
|
||||
FailureOr<SmallVector<Attribute>> channelValues = getBiasChannelValues(denseAttr, value.logicalType);
|
||||
if (failed(channelValues)) return failure();
|
||||
auto storageType = cast<RankedTensorType>(value.storage.getType());
|
||||
auto biasStorageType = spatial::getGraphBatchPhysicalResultType(value.tilesPerRow, value.fragmentType);
|
||||
SmallVector<Attribute> biasValues(
|
||||
biasStorageType.getNumElements(), cast<Attribute>(rewriter.getZeroAttr(value.fragmentType.getElementType())));
|
||||
const int64_t tileChannels = value.fragmentType.getDimSize(1);
|
||||
const int64_t width = value.fragmentType.getDimSize(3);
|
||||
for (int64_t channel = 0; channel < value.logicalType.getDimSize(1); ++channel)
|
||||
for (int64_t w = 0; w < width; ++w)
|
||||
biasValues[((channel / tileChannels) * tileChannels + channel % tileChannels) * width + w] =
|
||||
(*channelValues)[channel];
|
||||
Value biasStorage = getOrCreateConstant(rewriter, rewriter.getInsertionBlock()->getParentOp(),
|
||||
DenseElementsAttr::get(biasStorageType, biasValues), biasStorageType);
|
||||
const int64_t laneCount = storageType.getDimSize(0);
|
||||
auto batchOp = createSpatComputeBatch(rewriter,
|
||||
loc,
|
||||
TypeRange {storageType},
|
||||
logicalType.getDimSize(2),
|
||||
laneCount,
|
||||
{},
|
||||
ValueRange {storage},
|
||||
ValueRange {value.storage, biasStorage},
|
||||
[&](detail::SpatComputeBatchBodyArgs args) {
|
||||
Value fragment =
|
||||
extractRowStripFragment(args.inputs.front(), logicalType, args.lane, rewriter, loc);
|
||||
Value constant;
|
||||
if (denseAttr.isSplat()) {
|
||||
constant = getOrCreateConstant(
|
||||
rewriter,
|
||||
rewriter.getInsertionBlock()->getParentOp(),
|
||||
DenseElementsAttr::get(fragmentType, denseAttr.getSplatValue<Attribute>()),
|
||||
fragmentType);
|
||||
}
|
||||
else {
|
||||
FailureOr<Value> perChannel =
|
||||
createPerChannelConstantFragment(denseAttr, fragmentType, rewriter);
|
||||
if (failed(perChannel))
|
||||
return failure();
|
||||
constant = *perChannel;
|
||||
}
|
||||
fragment =
|
||||
spatial::SpatVAddOp::create(rewriter, loc, fragmentType, fragment, constant).getResult();
|
||||
insertRowStripFragment(
|
||||
fragment, args.outputs.front(), logicalType, args.lane, rewriter, loc);
|
||||
Operation* anchorOp = rewriter.getInsertionBlock()->getParentOp();
|
||||
FailureOr<Value> fragment = extractGraphBatchPhysicalFragment(
|
||||
rewriter, loc, args.inputs[0], args.lane, value.fragmentType);
|
||||
Value tile = affineModConst(rewriter, loc, args.lane,
|
||||
value.tilesPerRow, anchorOp);
|
||||
FailureOr<Value> constant = extractGraphBatchPhysicalFragment(
|
||||
rewriter, loc, args.inputs[1], tile, value.fragmentType);
|
||||
if (failed(fragment) || failed(constant)) return failure();
|
||||
Value added = spatial::SpatVAddOp::create(
|
||||
rewriter, loc, value.fragmentType, *fragment, *constant).getResult();
|
||||
publishGraphBatchPhysicalFragment(
|
||||
rewriter, loc, added, args.outputs.front(), args.lane);
|
||||
return success();
|
||||
});
|
||||
if (failed(batchOp))
|
||||
|
||||
@@ -11,10 +11,13 @@ inline constexpr llvm::StringLiteral kRowStripIndexMap = "nchw_row_strip_fragmen
|
||||
struct RowStripPhysicalValue {
|
||||
mlir::Value storage;
|
||||
mlir::RankedTensorType logicalType;
|
||||
llvm::SmallVector<int64_t, 16> fragmentOffsets;
|
||||
llvm::SmallVector<int64_t, 16> fragmentSizes;
|
||||
mlir::RankedTensorType fragmentType;
|
||||
int64_t tilesPerRow;
|
||||
};
|
||||
|
||||
mlir::FailureOr<RowStripPhysicalValue> describeRowStripPhysicalValue(mlir::Value storage,
|
||||
mlir::RankedTensorType logicalType);
|
||||
|
||||
std::pair<llvm::SmallVector<int64_t>, llvm::SmallVector<int64_t>>
|
||||
buildRowStripMetadata(mlir::RankedTensorType type);
|
||||
|
||||
@@ -50,18 +53,15 @@ mlir::FailureOr<mlir::Value> createRowStripStorageFromRows(mlir::Value rows,
|
||||
mlir::PatternRewriter& rewriter,
|
||||
mlir::Location loc);
|
||||
|
||||
mlir::FailureOr<mlir::Value> createRowStripAssemblyBlueprint(mlir::Value storage,
|
||||
mlir::RankedTensorType logicalType,
|
||||
mlir::FailureOr<mlir::Value> createRowStripAssemblyBlueprint(const RowStripPhysicalValue& value,
|
||||
mlir::PatternRewriter& rewriter,
|
||||
mlir::Location loc);
|
||||
|
||||
mlir::FailureOr<mlir::Value> applyRowStripRelu(mlir::Value storage,
|
||||
mlir::RankedTensorType logicalType,
|
||||
mlir::FailureOr<mlir::Value> applyRowStripRelu(const RowStripPhysicalValue& value,
|
||||
mlir::PatternRewriter& rewriter,
|
||||
mlir::Location loc);
|
||||
|
||||
mlir::FailureOr<mlir::Value> applyRowStripBiasAdd(mlir::Value storage,
|
||||
mlir::RankedTensorType logicalType,
|
||||
mlir::FailureOr<mlir::Value> applyRowStripBiasAdd(const RowStripPhysicalValue& value,
|
||||
mlir::Value bias,
|
||||
mlir::PatternRewriter& rewriter,
|
||||
mlir::Location loc);
|
||||
|
||||
@@ -45,38 +45,30 @@ static FailureOr<RowStripPhysicalValue> buildRowStripValue(spatial::SpatBlueprin
|
||||
auto logicalType = dyn_cast<RankedTensorType>(blueprint.getOutput().getType());
|
||||
if (!logicalType)
|
||||
return blueprint.emitOpError("requires ranked logical output type"), failure();
|
||||
RowStripPhysicalValue value;
|
||||
value.storage = storage;
|
||||
value.logicalType = logicalType;
|
||||
value.fragmentOffsets.append(blueprint.getFragmentOffsets().begin(), blueprint.getFragmentOffsets().end());
|
||||
value.fragmentSizes.append(blueprint.getFragmentSizes().begin(), blueprint.getFragmentSizes().end());
|
||||
if (blueprint.getIndexMap() != kRowStripIndexMap)
|
||||
return blueprint.emitOpError("requires the canonical row-strip index map"), failure();
|
||||
auto storageType = dyn_cast<RankedTensorType>(storage.getType());
|
||||
if (!storageType || storageType != getRowStripStorageType(logicalType))
|
||||
FailureOr<RowStripPhysicalValue> value = describeRowStripPhysicalValue(storage, logicalType);
|
||||
if (failed(value))
|
||||
return blueprint.emitOpError("requires physical row-strip fragment storage"), failure();
|
||||
return value;
|
||||
return *value;
|
||||
}
|
||||
|
||||
static FailureOr<Value>
|
||||
lowerRowStripRelu(const RowStripPhysicalValue& input, spatial::SpatReluPlanOp planOp, PatternRewriter& rewriter) {
|
||||
return applyRowStripRelu(input.storage, input.logicalType, rewriter, planOp.getLoc());
|
||||
return applyRowStripRelu(input, rewriter, planOp.getLoc());
|
||||
}
|
||||
|
||||
static FailureOr<Value> lowerRowStripBiasAdd(const RowStripPhysicalValue& input,
|
||||
spatial::SpatBiasAddPlanOp planOp,
|
||||
PatternRewriter& rewriter) {
|
||||
return applyRowStripBiasAdd(input.storage, input.logicalType, planOp.getBias(), rewriter, planOp.getLoc());
|
||||
return applyRowStripBiasAdd(input, planOp.getBias(), rewriter, planOp.getLoc());
|
||||
}
|
||||
|
||||
static FailureOr<Value>
|
||||
materializeRowStripToDense(const RowStripPhysicalValue& rowStripValue, Location loc, PatternRewriter& rewriter) {
|
||||
if (rowStripValue.logicalType.getRank() != 4 || !rowStripValue.logicalType.hasStaticShape())
|
||||
return failure();
|
||||
auto [expectedOffsets, expectedSizes] = buildRowStripMetadata(rowStripValue.logicalType);
|
||||
if (!llvm::equal(rowStripValue.fragmentOffsets, expectedOffsets) || !llvm::equal(rowStripValue.fragmentSizes, expectedSizes))
|
||||
return failure();
|
||||
return createRowStripAssemblyBlueprint(rowStripValue.storage, rowStripValue.logicalType, rewriter, loc);
|
||||
return createRowStripAssemblyBlueprint(rowStripValue, rewriter, loc);
|
||||
}
|
||||
|
||||
static FailureOr<Value> lowerDenseBatchBiasAdd(Value input, Value bias, RankedTensorType resultType,
|
||||
@@ -168,9 +160,12 @@ struct LowerSpatialPlansPass final : PassWrapper<LowerSpatialPlansPass, Operatio
|
||||
});
|
||||
if (rowStripBlueprint != planOp.getResult().getUsers().end()) {
|
||||
rewriter.setInsertionPoint(planOp);
|
||||
std::optional<Value> physicalInput;
|
||||
if (succeeded(rowStripInput))
|
||||
physicalInput = rowStripInput->storage;
|
||||
FailureOr<Value> lowered = lowerSelectedConv2DPlan(
|
||||
planOp,
|
||||
succeeded(rowStripInput) ? std::optional<Value> {rowStripInput->storage} : std::nullopt,
|
||||
physicalInput,
|
||||
/*emitRowStripLayout=*/true,
|
||||
rewriter);
|
||||
if (failed(lowered)) {
|
||||
@@ -255,8 +250,23 @@ struct LowerSpatialPlansPass final : PassWrapper<LowerSpatialPlansPass, Operatio
|
||||
|
||||
FailureOr<RowStripPhysicalValue> input = getRowStripValue(rowStripValues, planOp.getInput());
|
||||
rewriter.setInsertionPoint(planOp);
|
||||
std::optional<Value> physicalInput;
|
||||
if (succeeded(input)) {
|
||||
if (input->tilesPerRow == 1) {
|
||||
physicalInput = input->storage;
|
||||
}
|
||||
else {
|
||||
FailureOr<Value> denseInput = materializeRowStripToDense(*input, planOp.getLoc(), rewriter);
|
||||
if (failed(denseInput)) {
|
||||
planOp.emitOpError("failed to materialize tiled row-strip input for MaxPool");
|
||||
signalPassFailure();
|
||||
return;
|
||||
}
|
||||
planOp.getInputMutable().assign(*denseInput);
|
||||
}
|
||||
}
|
||||
FailureOr<Value> lowered = lowerSelectedMaxPool2DPlan(
|
||||
planOp, succeeded(input) ? std::optional<Value> {input->storage} : std::nullopt, rewriter);
|
||||
planOp, physicalInput, rewriter);
|
||||
if (failed(lowered)) {
|
||||
planOp.emitOpError("failed to lower selected row-strip Spatial MaxPool plan");
|
||||
signalPassFailure();
|
||||
|
||||
@@ -2326,54 +2326,25 @@ static Value maybeUnpackChunkRows(Value gemmRows,
|
||||
return unpackCompute.getResult(0);
|
||||
}
|
||||
|
||||
static Value createChunkedConvRows(const ConvLoweringState& state,
|
||||
const PreparedConvInput& preparedInput,
|
||||
Value weightMatrix,
|
||||
Value biasMatrix,
|
||||
DenseElementsAttr wDenseAttr,
|
||||
DenseElementsAttr biasDenseAttr,
|
||||
int64_t forcedPackFactor,
|
||||
uint64_t chunkPositions,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
SmallVector<Value> chunkRows;
|
||||
static 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;
|
||||
for (int64_t chunkStart = 0; chunkStart < totalPatches; chunkStart += static_cast<int64_t>(chunkPositions)) {
|
||||
const int64_t chunkNumPatches = std::min<int64_t>(static_cast<int64_t>(chunkPositions), totalPatches - chunkStart);
|
||||
ConvGemmPlan chunkPlan = buildConvGemmPlan(state,
|
||||
static_cast<bool>(wDenseAttr),
|
||||
!state.hasBias || static_cast<bool>(biasDenseAttr),
|
||||
chunkStart,
|
||||
chunkNumPatches,
|
||||
forcedPackFactor);
|
||||
Value chunkInputRows = createIm2colRows(state, preparedInput, chunkPlan, rewriter, loc);
|
||||
Value chunkB = buildPackedWeights(wDenseAttr, weightMatrix, state, chunkPlan, rewriter, loc);
|
||||
Value gemmBias = createZeroGemmBias(chunkPlan.gemmOutputRowsType, rewriter);
|
||||
if (state.hasBias)
|
||||
gemmBias = state.b;
|
||||
Value chunkC = buildPackedBias(gemmBias, biasMatrix, biasDenseAttr, state, chunkPlan, rewriter, loc);
|
||||
Value chunkGemmRows = ONNXGemmOp::create(rewriter,
|
||||
loc,
|
||||
chunkPlan.gemmOutputRowsType,
|
||||
chunkInputRows,
|
||||
chunkB,
|
||||
chunkC,
|
||||
APFloat(1.0f),
|
||||
APFloat(1.0f),
|
||||
/*transA=*/0,
|
||||
/*transB=*/0)
|
||||
.getY();
|
||||
chunkRows.push_back(maybeUnpackChunkRows(chunkGemmRows, chunkPlan, rewriter, loc));
|
||||
}
|
||||
|
||||
if (chunkRows.size() == 1)
|
||||
return chunkRows.front();
|
||||
|
||||
auto rowType = RankedTensorType::get({totalPatches, state.numChannelsOut}, state.outType.getElementType());
|
||||
auto collectRows = createSpatCompute(rewriter, loc, TypeRange {rowType}, {}, chunkRows, [&](ValueRange rows) {
|
||||
spatial::SpatYieldOp::create(rewriter, loc, createSpatConcat(rewriter, loc, /*axis=*/0, rows));
|
||||
});
|
||||
return collectRows.getResult(0);
|
||||
ConvGemmPlan plan = buildConvGemmPlan(state, static_cast<bool>(wDenseAttr),
|
||||
!state.hasBias || static_cast<bool>(biasDenseAttr), 0, totalPatches, forcedPackFactor);
|
||||
Value inputRows = createIm2colRows(state, preparedInput, plan, rewriter, loc);
|
||||
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, 0).getY();
|
||||
return maybeUnpackChunkRows(gemmRows, plan, rewriter, loc);
|
||||
}
|
||||
|
||||
static Value rewritePackedIm2ColConv(const ConvLoweringState& state,
|
||||
@@ -2444,16 +2415,13 @@ 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, rewriter, loc);
|
||||
ConvGeometry geo = buildConvGeometry(state);
|
||||
uint64_t chunkPositions = chooseStreamChunkPositions(geo, forcedPackFactor);
|
||||
Value collectedRows = createChunkedConvRows(state,
|
||||
Value collectedRows = createStreamedConvRows(state,
|
||||
preparedInput,
|
||||
weightMatrix,
|
||||
biasMatrix,
|
||||
wDenseAttr,
|
||||
biasDenseAttr,
|
||||
forcedPackFactor,
|
||||
chunkPositions,
|
||||
rewriter,
|
||||
loc);
|
||||
auto gemmOutType = cast<RankedTensorType>(collectedRows.getType());
|
||||
@@ -2524,6 +2492,21 @@ static bool canConsumeNchwRowStripFragments(const ConvLoweringState& state, Stri
|
||||
failureReason = "dilation_not_one";
|
||||
return false;
|
||||
}
|
||||
ConvGeometry geometry = buildConvGeometry(state);
|
||||
const bool pointwise = state.xHeight == 1 && state.xWidth == 1 && state.outHeight == 1 && state.outWidth == 1
|
||||
&& state.wHeight == 1 && state.wWidth == 1 && state.padHeightBegin == 0
|
||||
&& state.padHeightEnd == 0 && state.padWidthBegin == 0 && state.padWidthEnd == 0;
|
||||
if (pointwise) {
|
||||
if (!getHostConstDenseElementsAttr(state.w)) {
|
||||
failureReason = "non_constant_weight";
|
||||
return false;
|
||||
}
|
||||
if (state.hasBias && !isSupportedBiasAddValue(state.b, state.outType)) {
|
||||
failureReason = "unsupported_bias";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (state.wHeight != 3 || state.wWidth != 3) {
|
||||
failureReason = "kernel_not_3x3";
|
||||
return false;
|
||||
@@ -2544,7 +2527,6 @@ static bool canConsumeNchwRowStripFragments(const ConvLoweringState& state, Stri
|
||||
failureReason = "unsupported_bias";
|
||||
return false;
|
||||
}
|
||||
ConvGeometry geometry = buildConvGeometry(state);
|
||||
if (geometry.c > geometry.xbarSize) {
|
||||
failureReason = "output_channels_exceed_crossbar";
|
||||
return false;
|
||||
@@ -2575,6 +2557,25 @@ static FailureOr<Value> createPaddedBiasRowConstant(const ConvLoweringState& sta
|
||||
return getOrCreateConstant(rewriter, rewriter.getInsertionBlock()->getParentOp(), biasAttr, biasType);
|
||||
}
|
||||
|
||||
static FailureOr<Value> createPaddedBiasTileConstant(const ConvLoweringState& state,
|
||||
int64_t tileChannels,
|
||||
PatternRewriter& rewriter) {
|
||||
DenseElementsAttr denseAttr;
|
||||
if (!isSupportedBiasAddValue(state.b, state.outType, &denseAttr))
|
||||
return failure();
|
||||
FailureOr<SmallVector<Attribute>> channelValues = getBiasChannelValues(denseAttr, state.outType);
|
||||
if (failed(channelValues))
|
||||
return failure();
|
||||
const int64_t tileCount = ceilIntegerDivide(state.numChannelsOut, tileChannels);
|
||||
auto tileType = RankedTensorType::get({tileCount, 1, tileChannels}, state.outType.getElementType());
|
||||
SmallVector<Attribute> values(
|
||||
tileType.getNumElements(), cast<Attribute>(rewriter.getZeroAttr(tileType.getElementType())));
|
||||
for (int64_t channel = 0; channel < state.numChannelsOut; ++channel)
|
||||
values[channel] = (*channelValues)[channel];
|
||||
return getOrCreateConstant(rewriter, rewriter.getInsertionBlock()->getParentOp(),
|
||||
DenseElementsAttr::get(tileType, values), tileType);
|
||||
}
|
||||
|
||||
static Value createHorizontallyPaddedRowStripFragment(Value fragment,
|
||||
const ConvLoweringState& state,
|
||||
PatternRewriter& rewriter,
|
||||
@@ -2732,8 +2733,11 @@ static FailureOr<Value> createConvInputWindow(Value input,
|
||||
? extractDenseConvWindowRow(input, sourceRowTable, state, outputHeight, kernelRow, rewriter, loc)
|
||||
: extractProjectedRowStripWindowRow(
|
||||
input, sourceRowTable, state, outputHeight, kernelRow, rewriter, loc);
|
||||
Value mask = extractProjectedRowStripWindowMask(*maskTable, state, outputHeight, kernelRow, rewriter, loc);
|
||||
Value semanticRow = spatial::SpatVMulOp::create(rewriter, loc, fragmentType, sourceRow, mask).getResult();
|
||||
Value semanticRow = sourceRow;
|
||||
if (state.padHeightBegin != 0 || state.padHeightEnd != 0) {
|
||||
Value mask = extractProjectedRowStripWindowMask(*maskTable, state, outputHeight, kernelRow, rewriter, loc);
|
||||
semanticRow = spatial::SpatVMulOp::create(rewriter, loc, fragmentType, sourceRow, mask).getResult();
|
||||
}
|
||||
Value paddedRow = createHorizontallyPaddedRowStripFragment(semanticRow, state, rewriter, loc);
|
||||
window = tensor::InsertSliceOp::create(rewriter,
|
||||
loc,
|
||||
@@ -2906,12 +2910,6 @@ static FailureOr<Value> createPaddedConvOutputRow(Value patchRow,
|
||||
.getResult();
|
||||
}
|
||||
|
||||
static bool rowStripOutputFitsOneCore(const ConvGeometry& geometry) {
|
||||
const int64_t inputTileCount = ceilIntegerDivide(geometry.k, geometry.xbarSize);
|
||||
const int64_t outputTileCount = ceilIntegerDivide(geometry.c, geometry.xbarSize);
|
||||
return inputTileCount * outputTileCount <= static_cast<int64_t>(crossbarCountInCore.getValue());
|
||||
}
|
||||
|
||||
static bool rowStripOutputTileFitsOneCore(const ConvGeometry& geometry) {
|
||||
return ceilIntegerDivide(geometry.k, geometry.xbarSize)
|
||||
<= static_cast<int64_t>(crossbarCountInCore.getValue());
|
||||
@@ -2928,38 +2926,40 @@ static FailureOr<Value> createOutputChannelTiledRowStripConvOutput(const ConvLow
|
||||
const int64_t patchSize = state.numChannelsIn * state.wHeight * state.wWidth;
|
||||
auto elementType = state.outType.getElementType();
|
||||
auto paddedPatchRowType = RankedTensorType::get({1, paddedK}, elementType);
|
||||
auto paddedRowType = RankedTensorType::get({1, xbarDim}, elementType);
|
||||
auto tilePixelType = RankedTensorType::get({1, xbarDim, 1, 1}, elementType);
|
||||
auto tileFragmentType = RankedTensorType::get({1, xbarDim, 1, state.outWidth}, elementType);
|
||||
auto tileWeightsType = RankedTensorType::get({paddedK, xbarDim}, state.wType.getElementType());
|
||||
SmallVector<Value> outputTiles;
|
||||
outputTiles.reserve(outputTileCount);
|
||||
|
||||
for (int64_t outputTile = 0; outputTile < outputTileCount; ++outputTile) {
|
||||
const int64_t channelOffset = outputTile * xbarDim;
|
||||
const int64_t tileChannels = std::min(xbarDim, state.numChannelsOut - channelOffset);
|
||||
auto tileRowType = RankedTensorType::get({1, tileChannels}, elementType);
|
||||
auto tilePixelType = RankedTensorType::get({1, tileChannels, 1, 1}, elementType);
|
||||
auto tileFragmentType = RankedTensorType::get({1, tileChannels, 1, state.outWidth}, elementType);
|
||||
auto tileStorageType = spatial::getGraphBatchPhysicalResultType(state.outHeight, tileFragmentType);
|
||||
SmallVector<OpFoldResult> weightOffsets {
|
||||
rewriter.getIndexAttr(outputTile), rewriter.getIndexAttr(0), rewriter.getIndexAttr(0)};
|
||||
SmallVector<OpFoldResult> weightSizes {
|
||||
rewriter.getIndexAttr(1), rewriter.getIndexAttr(paddedK), rewriter.getIndexAttr(xbarDim)};
|
||||
Value tileWeights = extractStaticSliceOrIdentity(
|
||||
rewriter, loc, paddedWeights, tileWeightsType, weightOffsets, weightSizes, getUnitStrides(rewriter, 3));
|
||||
|
||||
auto tileBatch = createSpatComputeBatch(
|
||||
rewriter,
|
||||
loc,
|
||||
TypeRange {tileStorageType},
|
||||
state.outHeight,
|
||||
ValueRange {tileWeights},
|
||||
ValueRange {state.x},
|
||||
[&](detail::SpatComputeBatchBodyArgs args) {
|
||||
const int64_t laneCount = state.outHeight * outputTileCount;
|
||||
auto tileStorageType = spatial::getGraphBatchPhysicalResultType(laneCount, tileFragmentType);
|
||||
FailureOr<Value> paddedBias = failure();
|
||||
if (state.hasBias)
|
||||
paddedBias = createPaddedBiasTileConstant(state, xbarDim, rewriter);
|
||||
if (state.hasBias && failed(paddedBias))
|
||||
return failure();
|
||||
auto tileBatch = createSpatComputeBatch(
|
||||
rewriter, loc, TypeRange {tileStorageType}, laneCount, ValueRange {paddedWeights},
|
||||
state.hasBias ? ValueRange {state.x, *paddedBias} : ValueRange {state.x},
|
||||
[&](detail::SpatComputeBatchBodyArgs args) {
|
||||
Operation* anchorOp = rewriter.getInsertionBlock()->getParentOp();
|
||||
Value c0 = getOrCreateIndexConstant(rewriter, anchorOp, 0);
|
||||
Value c1 = getOrCreateIndexConstant(rewriter, anchorOp, 1);
|
||||
Value cOutWidth = getOrCreateIndexConstant(rewriter, anchorOp, state.outWidth);
|
||||
Value outputRow = affineFloorDivConst(rewriter, loc, args.lane, outputTileCount, anchorOp);
|
||||
Value outputTile = affineModConst(rewriter, loc, args.lane, outputTileCount, anchorOp);
|
||||
SmallVector<OpFoldResult> weightOffsets {
|
||||
outputTile, rewriter.getIndexAttr(0), rewriter.getIndexAttr(0)};
|
||||
SmallVector<OpFoldResult> weightSizes {
|
||||
rewriter.getIndexAttr(1), rewriter.getIndexAttr(paddedK), rewriter.getIndexAttr(xbarDim)};
|
||||
Value tileWeights = tensor::ExtractSliceOp::create(
|
||||
rewriter, loc, tileWeightsType, args.weights.front(), weightOffsets, weightSizes, getUnitStrides(rewriter, 3));
|
||||
FailureOr<Value> biasTile = failure();
|
||||
if (state.hasBias)
|
||||
biasTile = extractGraphBatchPhysicalFragment(rewriter, loc, args.inputs[1], outputTile, paddedRowType);
|
||||
if (state.hasBias && failed(biasTile))
|
||||
return failure();
|
||||
FailureOr<Value> inputWindow =
|
||||
createConvInputWindow(args.inputs.front(), state, args.lane, rewriter, loc);
|
||||
createConvInputWindow(args.inputs.front(), state, outputRow, rewriter, loc);
|
||||
if (failed(inputWindow))
|
||||
return failure();
|
||||
Value fragmentInit = tensor::EmptyOp::create(rewriter, loc, tileFragmentType.getShape(), elementType);
|
||||
@@ -2984,28 +2984,18 @@ static FailureOr<Value> createOutputChannelTiledRowStripConvOutput(const ConvLow
|
||||
paddedPatchRow = createZeroPaddedTensor(
|
||||
paddedPatchRow, paddedPatchRowType, {0, 0}, {0, paddedK - patchSize}, rewriter, widthLoc);
|
||||
FailureOr<Value> paddedOutputRow = createPaddedConvOutputTile(
|
||||
paddedPatchRow, args.weights.front(), numKSlices, xbarDim, rewriter, widthLoc);
|
||||
paddedPatchRow, tileWeights, numKSlices, xbarDim, rewriter, widthLoc);
|
||||
if (failed(paddedOutputRow))
|
||||
return failure();
|
||||
Value outputRow = *paddedOutputRow;
|
||||
if (tileChannels != xbarDim) {
|
||||
SmallVector<OpFoldResult> rowOffsets {rewriter.getIndexAttr(0), rewriter.getIndexAttr(0)};
|
||||
SmallVector<OpFoldResult> rowSizes {
|
||||
rewriter.getIndexAttr(1), rewriter.getIndexAttr(tileChannels)};
|
||||
outputRow = tensor::ExtractSliceOp::create(rewriter,
|
||||
widthLoc,
|
||||
tileRowType,
|
||||
outputRow,
|
||||
rowOffsets,
|
||||
rowSizes,
|
||||
getUnitStrides(rewriter, 2));
|
||||
}
|
||||
if (state.hasBias)
|
||||
paddedOutputRow = spatial::SpatVAddOp::create(
|
||||
rewriter, widthLoc, paddedRowType, *paddedOutputRow, *biasTile).getResult();
|
||||
Value outputPixel = tensor::ExpandShapeOp::create(
|
||||
rewriter, widthLoc, tilePixelType, outputRow, SmallVector<ReassociationIndices> {{0}, {1, 2, 3}});
|
||||
rewriter, widthLoc, tilePixelType, *paddedOutputRow, SmallVector<ReassociationIndices> {{0}, {1, 2, 3}});
|
||||
SmallVector<OpFoldResult> rowOffsets {
|
||||
rewriter.getIndexAttr(0), rewriter.getIndexAttr(0), rewriter.getIndexAttr(0), widthIndex};
|
||||
SmallVector<OpFoldResult> rowSizes {rewriter.getIndexAttr(1),
|
||||
rewriter.getIndexAttr(tileChannels),
|
||||
rewriter.getIndexAttr(xbarDim),
|
||||
rewriter.getIndexAttr(1),
|
||||
rewriter.getIndexAttr(1)};
|
||||
Value nextFragment = tensor::InsertSliceOp::create(rewriter,
|
||||
@@ -3024,58 +3014,9 @@ static FailureOr<Value> createOutputChannelTiledRowStripConvOutput(const ConvLow
|
||||
rewriter, loc, widthLoop->results.front(), args.outputs.front(), args.lane);
|
||||
return success();
|
||||
});
|
||||
if (failed(tileBatch))
|
||||
return failure();
|
||||
outputTiles.push_back(tileBatch->getResult(0));
|
||||
}
|
||||
|
||||
auto fragmentType = getRowStripFragmentType(state.outType);
|
||||
auto outputStorageType = getRowStripStorageType(state.outType);
|
||||
auto assemblyBatch = createSpatComputeBatch(rewriter,
|
||||
loc,
|
||||
TypeRange {outputStorageType},
|
||||
state.outHeight,
|
||||
{},
|
||||
ValueRange(outputTiles),
|
||||
[&](detail::SpatComputeBatchBodyArgs args) {
|
||||
Value fragment = tensor::EmptyOp::create(
|
||||
rewriter, loc, fragmentType.getShape(), elementType);
|
||||
for (int64_t outputTile = 0; outputTile < outputTileCount; ++outputTile) {
|
||||
const int64_t channelOffset = outputTile * xbarDim;
|
||||
const int64_t tileChannels =
|
||||
std::min(xbarDim, state.numChannelsOut - channelOffset);
|
||||
auto tileFragmentType = RankedTensorType::get(
|
||||
{1, tileChannels, 1, state.outWidth}, elementType);
|
||||
FailureOr<Value> tileFragment = extractGraphBatchPhysicalFragment(
|
||||
rewriter, loc, args.inputs[outputTile], args.lane, tileFragmentType);
|
||||
if (failed(tileFragment))
|
||||
return failure();
|
||||
SmallVector<OpFoldResult> offsets {rewriter.getIndexAttr(0),
|
||||
rewriter.getIndexAttr(channelOffset),
|
||||
rewriter.getIndexAttr(0),
|
||||
rewriter.getIndexAttr(0)};
|
||||
SmallVector<OpFoldResult> sizes {rewriter.getIndexAttr(1),
|
||||
rewriter.getIndexAttr(tileChannels),
|
||||
rewriter.getIndexAttr(1),
|
||||
rewriter.getIndexAttr(state.outWidth)};
|
||||
fragment = tensor::InsertSliceOp::create(rewriter,
|
||||
loc,
|
||||
*tileFragment,
|
||||
fragment,
|
||||
offsets,
|
||||
sizes,
|
||||
getUnitStrides(rewriter, 4));
|
||||
}
|
||||
insertRowStripFragment(
|
||||
fragment, args.outputs.front(), state.outType, args.lane, rewriter, loc);
|
||||
return success();
|
||||
});
|
||||
if (failed(assemblyBatch))
|
||||
if (failed(tileBatch))
|
||||
return failure();
|
||||
Value output = assemblyBatch->getResult(0);
|
||||
if (state.hasBias)
|
||||
return applyRowStripBiasAdd(output, state.outType, state.b, rewriter, loc);
|
||||
return output;
|
||||
return tileBatch->getResult(0);
|
||||
}
|
||||
|
||||
static FailureOr<Value>
|
||||
@@ -3104,7 +3045,7 @@ createRowStripConvOutputFromDenseInput(const ConvLoweringState& state, PatternRe
|
||||
weightDenseAttr, state, paddedK, xbarDim, rewriter)
|
||||
: standard::createPaddedOutputChannelTiledWeightConstant(
|
||||
weightDenseAttr, state, paddedK, xbarDim, rewriter);
|
||||
if (!rowStripOutputFitsOneCore(geometry))
|
||||
if (state.numChannelsOut > xbarDim)
|
||||
return createOutputChannelTiledRowStripConvOutput(
|
||||
state, paddedWeights, paddedK, numKSlices, xbarDim, rewriter, loc);
|
||||
|
||||
@@ -3279,11 +3220,106 @@ static FailureOr<Value> createConvOutputFromNchwRowStripFragments(Value rowStrip
|
||||
return batchOp->getResult(0);
|
||||
}
|
||||
|
||||
static FailureOr<Value> createPointwiseOutputFromRowStripFragments(Value rowStripStorage,
|
||||
const ConvLoweringState& state,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
FailureOr<RowStripPhysicalValue> input = describeRowStripPhysicalValue(rowStripStorage, state.xType);
|
||||
if (failed(input)) return failure();
|
||||
ConvGeometry geometry = buildConvGeometry(state);
|
||||
const int64_t xbarDim = geometry.xbarSize;
|
||||
const int64_t inputFragmentChannels = input->fragmentType.getDimSize(1);
|
||||
if (inputFragmentChannels % xbarDim != 0 || state.numChannelsIn % xbarDim != 0)
|
||||
return failure();
|
||||
auto weightDenseAttr = getHostConstDenseElementsAttr(state.w);
|
||||
if (!weightDenseAttr) return failure();
|
||||
|
||||
const int64_t outputTileCount = ceilIntegerDivide(state.numChannelsOut, xbarDim);
|
||||
const int64_t numKSlices = state.numChannelsIn / xbarDim;
|
||||
auto elementType = state.outType.getElementType();
|
||||
auto paddedRowType = RankedTensorType::get({1, xbarDim}, elementType);
|
||||
auto inputRowType = RankedTensorType::get({1, inputFragmentChannels}, elementType);
|
||||
auto weightTileType = RankedTensorType::get({state.numChannelsIn, xbarDim}, state.wType.getElementType());
|
||||
auto weightSliceType = RankedTensorType::get({xbarDim, xbarDim}, state.wType.getElementType());
|
||||
auto outputFragmentType = RankedTensorType::get({1, xbarDim, 1, 1}, elementType);
|
||||
auto outputStorageType = spatial::getGraphBatchPhysicalResultType(outputTileCount, outputFragmentType);
|
||||
Value paddedWeights = standard::createPaddedOutputChannelTiledWeightConstant(
|
||||
weightDenseAttr, state, state.numChannelsIn, xbarDim, rewriter);
|
||||
FailureOr<Value> paddedBias = failure();
|
||||
if (state.hasBias) paddedBias = createPaddedBiasTileConstant(state, xbarDim, rewriter);
|
||||
if (state.hasBias && failed(paddedBias)) return failure();
|
||||
|
||||
auto batch = createSpatComputeBatch(rewriter, loc, TypeRange {outputStorageType}, outputTileCount,
|
||||
ValueRange {paddedWeights},
|
||||
state.hasBias ? ValueRange {rowStripStorage, *paddedBias} : ValueRange {rowStripStorage},
|
||||
[&](detail::SpatComputeBatchBodyArgs args) {
|
||||
Operation* anchorOp = rewriter.getInsertionBlock()->getParentOp();
|
||||
Value c0 = getOrCreateIndexConstant(rewriter, anchorOp, 0);
|
||||
Value c1 = getOrCreateIndexConstant(rewriter, anchorOp, 1);
|
||||
Value cNumKSlices = getOrCreateIndexConstant(rewriter, anchorOp, numKSlices);
|
||||
SmallVector<OpFoldResult> weightOffsets {args.lane, rewriter.getIndexAttr(0), rewriter.getIndexAttr(0)};
|
||||
SmallVector<OpFoldResult> weightSizes {rewriter.getIndexAttr(1),
|
||||
rewriter.getIndexAttr(state.numChannelsIn), rewriter.getIndexAttr(xbarDim)};
|
||||
Value weightTile = tensor::ExtractSliceOp::create(
|
||||
rewriter, loc, weightTileType, args.weights.front(), weightOffsets, weightSizes, getUnitStrides(rewriter, 3));
|
||||
auto createPiece = [&](Value kSlice, Location pieceLoc) -> FailureOr<Value> {
|
||||
Value channelOffset = affineMulConst(rewriter, pieceLoc, kSlice, xbarDim, anchorOp);
|
||||
Value sourceSlot = affineFloorDivConst(
|
||||
rewriter, pieceLoc, channelOffset, inputFragmentChannels, anchorOp);
|
||||
Value sourceOffset = affineModConst(
|
||||
rewriter, pieceLoc, channelOffset, inputFragmentChannels, anchorOp);
|
||||
FailureOr<Value> fragment = extractGraphBatchPhysicalFragment(
|
||||
rewriter, pieceLoc, args.inputs.front(), sourceSlot, input->fragmentType);
|
||||
if (failed(fragment)) return failure();
|
||||
Value inputRow = tensor::CollapseShapeOp::create(rewriter, pieceLoc, inputRowType, *fragment,
|
||||
SmallVector<ReassociationIndices> {{0}, {1, 2, 3}});
|
||||
Value inputSlice = tensor::ExtractSliceOp::create(rewriter, pieceLoc, paddedRowType, inputRow,
|
||||
SmallVector<OpFoldResult> {rewriter.getIndexAttr(0), sourceOffset},
|
||||
SmallVector<OpFoldResult> {rewriter.getIndexAttr(1), rewriter.getIndexAttr(xbarDim)},
|
||||
getUnitStrides(rewriter, 2));
|
||||
Value weightSlice = tensor::ExtractSliceOp::create(rewriter, pieceLoc, weightSliceType, weightTile,
|
||||
SmallVector<OpFoldResult> {channelOffset, rewriter.getIndexAttr(0)},
|
||||
SmallVector<OpFoldResult> {rewriter.getIndexAttr(xbarDim), rewriter.getIndexAttr(xbarDim)},
|
||||
getUnitStrides(rewriter, 2));
|
||||
return spatial::SpatVMMOp::create(rewriter, pieceLoc, paddedRowType, weightSlice, inputSlice).getResult();
|
||||
};
|
||||
FailureOr<Value> result = createPiece(c0, loc);
|
||||
if (failed(result)) return failure();
|
||||
if (numKSlices > 1) {
|
||||
auto reduction = buildNormalizedScfFor(rewriter, loc, c1, cNumKSlices, c1, ValueRange {*result},
|
||||
[&](OpBuilder&, Location reduceLoc, Value kSlice, ValueRange iterArgs,
|
||||
SmallVectorImpl<Value>& yielded) {
|
||||
FailureOr<Value> piece = createPiece(kSlice, reduceLoc);
|
||||
if (failed(piece)) return failure();
|
||||
yielded.push_back(spatial::SpatVAddOp::create(
|
||||
rewriter, reduceLoc, paddedRowType, iterArgs.front(), *piece).getResult());
|
||||
return success();
|
||||
});
|
||||
if (failed(reduction)) return failure();
|
||||
result = reduction->results.front();
|
||||
}
|
||||
if (state.hasBias) {
|
||||
FailureOr<Value> bias = extractGraphBatchPhysicalFragment(
|
||||
rewriter, loc, args.inputs[1], args.lane, paddedRowType);
|
||||
if (failed(bias)) return failure();
|
||||
result = spatial::SpatVAddOp::create(rewriter, loc, paddedRowType, *result, *bias).getResult();
|
||||
}
|
||||
Value fragment = tensor::ExpandShapeOp::create(rewriter, loc, outputFragmentType, *result,
|
||||
SmallVector<ReassociationIndices> {{0}, {1, 2, 3}});
|
||||
publishGraphBatchPhysicalFragment(rewriter, loc, fragment, args.outputs.front(), args.lane);
|
||||
return success();
|
||||
});
|
||||
if (failed(batch)) return failure();
|
||||
return batch->getResult(0);
|
||||
}
|
||||
|
||||
static FailureOr<Value> createConvOutputFromRowStripInput(const ConvLoweringState& state,
|
||||
[[maybe_unused]] const ConvLoweringDecision& decision,
|
||||
Value rowStripInput,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
if (state.xHeight == 1 && state.xWidth == 1 && state.wHeight == 1 && state.wWidth == 1)
|
||||
return createPointwiseOutputFromRowStripFragments(rowStripInput, state, rewriter, loc);
|
||||
return createConvOutputFromNchwRowStripFragments(rowStripInput, state, rewriter, loc);
|
||||
}
|
||||
|
||||
@@ -4187,22 +4223,9 @@ lowerSelectedConv2DPlan(spatial::SpatConv2DPlanOp planOp,
|
||||
}
|
||||
if (failed(canLowerConvPlanToRowStrip(planOp)))
|
||||
return planOp.emitOpError("selected row-strip layout is not supported for this Conv plan"), failure();
|
||||
ConvLoweringState rowState = *state;
|
||||
const bool applyBiasAfterStorage = rowState.hasBias;
|
||||
Value originalBias = rowState.b;
|
||||
if (applyBiasAfterStorage) {
|
||||
rowState.b = Value();
|
||||
rowState.hasBias = false;
|
||||
}
|
||||
|
||||
FailureOr<Value> rowStripStorage = createRowStripConvOutputFromDenseInput(rowState, rewriter, planOp.getLoc());
|
||||
FailureOr<Value> rowStripStorage = createRowStripConvOutputFromDenseInput(*state, rewriter, planOp.getLoc());
|
||||
if (failed(rowStripStorage))
|
||||
return planOp.emitOpError("failed to build row-strip fragment storage for the selected Conv plan"), failure();
|
||||
if (applyBiasAfterStorage) {
|
||||
rowStripStorage = applyRowStripBiasAdd(*rowStripStorage, state->outType, originalBias, rewriter, planOp.getLoc());
|
||||
if (failed(rowStripStorage))
|
||||
return planOp.emitOpError("failed to apply row-strip Conv bias per fragment"), failure();
|
||||
}
|
||||
return *rowStripStorage;
|
||||
}
|
||||
|
||||
|
||||
+32
-4
@@ -77,9 +77,16 @@ static LogicalResult eraseOldGraph(func::FuncOp funcOp,
|
||||
rewriter.eraseOp(blueprint);
|
||||
continue;
|
||||
}
|
||||
if (!op->use_empty())
|
||||
return op->emitOpError(
|
||||
"phase 2 cannot erase an old graph compute with live results");
|
||||
if (!op->use_empty()) {
|
||||
for (OpResult result : op->getResults()) {
|
||||
if (!result.use_empty()) {
|
||||
Operation *user = result.use_begin()->getOwner();
|
||||
return op->emitOpError()
|
||||
<< "phase 2 cannot erase old graph result "
|
||||
<< result.getResultNumber() << " used by " << user->getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
rewriter.eraseOp(op);
|
||||
}
|
||||
return success();
|
||||
@@ -100,6 +107,25 @@ static LogicalResult eraseDeferredSourceSelectors(
|
||||
return success();
|
||||
}
|
||||
|
||||
static void eraseUnusedIdentityDeferredCommunications(
|
||||
func::FuncOp funcOp, IRRewriter &rewriter) {
|
||||
SmallVector<SpatDeferredCommunicationOp> unused;
|
||||
funcOp.walk([&](SpatDeferredCommunicationOp deferred) {
|
||||
if (!deferred.getOutput().use_empty() || !deferred.getBody().hasOneBlock())
|
||||
return;
|
||||
Block &body = deferred.getBody().front();
|
||||
auto yield = dyn_cast<SpatYieldOp>(body.getTerminator());
|
||||
auto argument = yield && yield.getOutputs().size() == 1
|
||||
? dyn_cast<BlockArgument>(yield.getOutputs().front())
|
||||
: BlockArgument();
|
||||
if (argument && argument.getOwner() == &body
|
||||
&& argument.getArgNumber() < deferred.getSources().size())
|
||||
unused.push_back(deferred);
|
||||
});
|
||||
for (SpatDeferredCommunicationOp deferred : llvm::reverse(unused))
|
||||
rewriter.eraseOp(deferred);
|
||||
}
|
||||
|
||||
static LogicalResult verifyDominance(func::FuncOp funcOp) {
|
||||
DominanceInfo dominance(funcOp);
|
||||
WalkResult result = funcOp.walk([&](Operation *op) {
|
||||
@@ -119,6 +145,9 @@ static LogicalResult verifyDominance(func::FuncOp funcOp) {
|
||||
LogicalResult realizeDeferredCommunication(
|
||||
func::FuncOp funcOp,
|
||||
const ScheduledComputeMaterializationResult &materialization) {
|
||||
IRRewriter rewriter(funcOp.getContext());
|
||||
eraseUnusedIdentityDeferredCommunications(funcOp, rewriter);
|
||||
|
||||
auto transfers = buildDeferredTransferPlan(funcOp, materialization);
|
||||
if (failed(transfers))
|
||||
return funcOp.emitOpError(
|
||||
@@ -134,7 +163,6 @@ LogicalResult realizeDeferredCommunication(
|
||||
return funcOp.emitOpError(
|
||||
"phase 2 failed to build sparse boundary programs");
|
||||
|
||||
IRRewriter rewriter(funcOp.getContext());
|
||||
if (failed(retargetDeferredPublications(funcOp, *transfers))
|
||||
|| failed(replaceFinalGraphPublications(funcOp, *transfers)))
|
||||
return failure();
|
||||
|
||||
Reference in New Issue
Block a user