updat ops validations
This commit is contained in:
@@ -32,6 +32,18 @@ schedule semantics, scheduling granularity, and available parallelism.
|
|||||||
An optimization is acceptable only when its static runtime proxies are equal or
|
An optimization is acceptable only when its static runtime proxies are equal or
|
||||||
better.
|
better.
|
||||||
|
|
||||||
|
## Asymptotic cost
|
||||||
|
|
||||||
|
For each new or materially changed compiler algorithm, identify the relevant
|
||||||
|
input size and target linear or sublinear time and space. Avoid repeated full-IR
|
||||||
|
walks, nested scans, and per-operation recomputation when indexing, caching, or
|
||||||
|
a single traversal can express the same behavior.
|
||||||
|
|
||||||
|
When linear-or-better complexity is not possible, use the lowest justified
|
||||||
|
complexity and report the actual time and space Big-O, the input variable, and
|
||||||
|
why a lower bound is not practical. Include that cost in the final report; do
|
||||||
|
not hide a superlinear path behind small current test sizes.
|
||||||
|
|
||||||
## Forbidden optimization trades
|
## Forbidden optimization trades
|
||||||
|
|
||||||
Do not:
|
Do not:
|
||||||
|
|||||||
@@ -9,6 +9,53 @@ using namespace mlir;
|
|||||||
|
|
||||||
namespace onnx_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) {
|
Value sumTensors(ArrayRef<Value> tensors, PatternRewriter& rewriter) {
|
||||||
if (tensors.size() == 1)
|
if (tensors.size() == 1)
|
||||||
return tensors[0];
|
return tensors[0];
|
||||||
|
|||||||
@@ -19,6 +19,13 @@
|
|||||||
|
|
||||||
namespace onnx_mlir {
|
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 {
|
namespace detail {
|
||||||
|
|
||||||
inline mlir::ValueRange getBlockArgs(mlir::Block* block) { return mlir::ValueRange(block->getArguments()); }
|
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::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
|
} // namespace onnx_mlir
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "src/Accelerators/PIM/Common/IR/ConstantUtils.hpp"
|
#include "src/Accelerators/PIM/Common/IR/ConstantUtils.hpp"
|
||||||
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/BiasAddUtils.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/Common.hpp"
|
||||||
|
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/ComputeRegionBuilder.hpp"
|
||||||
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/RowStripLayoutUtils.hpp"
|
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/RowStripLayoutUtils.hpp"
|
||||||
#include "src/Accelerators/PIM/Dialect/Spatial/SpatialOps.hpp"
|
#include "src/Accelerators/PIM/Dialect/Spatial/SpatialOps.hpp"
|
||||||
#include "src/Dialect/ONNX/ONNXOps.hpp"
|
#include "src/Dialect/ONNX/ONNXOps.hpp"
|
||||||
@@ -12,6 +13,22 @@ using namespace mlir;
|
|||||||
|
|
||||||
namespace onnx_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) {
|
RankedTensorType getRowStripFragmentType(RankedTensorType logicalType) {
|
||||||
return RankedTensorType::get({logicalType.getDimSize(0), logicalType.getDimSize(1), 1, logicalType.getDimSize(3)},
|
return RankedTensorType::get({logicalType.getDimSize(0), logicalType.getDimSize(1), 1, logicalType.getDimSize(3)},
|
||||||
logicalType.getElementType(),
|
logicalType.getElementType(),
|
||||||
@@ -123,42 +140,39 @@ FailureOr<Value> createRowStripStorageFromRows(Value rows,
|
|||||||
return batchOp->getResult(0);
|
return batchOp->getResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
FailureOr<Value>
|
FailureOr<Value> createRowStripAssemblyBlueprint(const RowStripPhysicalValue& value,
|
||||||
createRowStripAssemblyBlueprint(Value storage, RankedTensorType logicalType, PatternRewriter& rewriter, Location loc) {
|
PatternRewriter& rewriter,
|
||||||
auto storageType = dyn_cast<RankedTensorType>(storage.getType());
|
Location loc) {
|
||||||
if (!storageType || storageType != getRowStripStorageType(logicalType))
|
SmallVector<FragmentAssemblyEntry> entries;
|
||||||
return failure();
|
const int64_t tileChannels = value.fragmentType.getDimSize(1);
|
||||||
|
for (int64_t row = 0; row < value.logicalType.getDimSize(2); ++row)
|
||||||
auto [offsets, sizes] = buildRowStripMetadata(logicalType);
|
for (int64_t tile = 0; tile < value.tilesPerRow; ++tile) {
|
||||||
int64_t height = logicalType.getDimSize(2);
|
const int64_t channelOffset = tile * tileChannels;
|
||||||
SmallVector<int64_t> operandIndices(height, 0), sourceSlots, sourceOffsets(height, 0), strides(height * 4, 1);
|
entries.push_back({row * value.tilesPerRow + tile, 0, {0, channelOffset, row, 0},
|
||||||
for (int64_t row = 0; row < height; ++row)
|
{1, std::min(tileChannels, value.logicalType.getDimSize(1) - channelOffset), 1,
|
||||||
sourceSlots.push_back(row);
|
value.logicalType.getDimSize(3)}});
|
||||||
return spatial::SpatBlueprintOp::create(rewriter, loc, logicalType, storage, ValueRange {},
|
}
|
||||||
rewriter.getStringAttr("nchw"), rewriter.getStringAttr("nchw_row_strip"),
|
return createFragmentAssemblyBlueprint(value.storage, value.logicalType, entries, "nchw_row_strip",
|
||||||
rewriter.getDenseI64ArrayAttr(offsets), rewriter.getDenseI64ArrayAttr(sizes),
|
kRowStripIndexMap, rewriter, loc);
|
||||||
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>
|
FailureOr<Value> applyRowStripRelu(const RowStripPhysicalValue& value, PatternRewriter& rewriter, Location loc) {
|
||||||
applyRowStripRelu(Value storage, RankedTensorType logicalType, PatternRewriter& rewriter, Location loc) {
|
auto storageType = cast<RankedTensorType>(value.storage.getType());
|
||||||
auto fragmentType = getRowStripFragmentType(logicalType);
|
const int64_t laneCount = storageType.getDimSize(0);
|
||||||
auto storageType = getRowStripStorageType(logicalType);
|
|
||||||
auto batchOp = createSpatComputeBatch(rewriter,
|
auto batchOp = createSpatComputeBatch(rewriter,
|
||||||
loc,
|
loc,
|
||||||
TypeRange {storageType},
|
TypeRange {storageType},
|
||||||
logicalType.getDimSize(2),
|
laneCount,
|
||||||
{},
|
{},
|
||||||
ValueRange {storage},
|
ValueRange {value.storage},
|
||||||
[&](detail::SpatComputeBatchBodyArgs args) {
|
[&](detail::SpatComputeBatchBodyArgs args) {
|
||||||
Value fragment =
|
FailureOr<Value> fragment = extractGraphBatchPhysicalFragment(
|
||||||
extractRowStripFragment(args.inputs.front(), logicalType, args.lane, rewriter, loc);
|
rewriter, loc, args.inputs.front(), args.lane, value.fragmentType);
|
||||||
fragment = spatial::SpatReluOp::create(rewriter, loc, fragmentType, fragment).getResult();
|
if (failed(fragment)) return failure();
|
||||||
insertRowStripFragment(
|
Value relu = spatial::SpatReluOp::create(
|
||||||
fragment, args.outputs.front(), logicalType, args.lane, rewriter, loc);
|
rewriter, loc, value.fragmentType, *fragment).getResult();
|
||||||
|
publishGraphBatchPhysicalFragment(
|
||||||
|
rewriter, loc, relu, args.outputs.front(), args.lane);
|
||||||
return success();
|
return success();
|
||||||
});
|
});
|
||||||
if (failed(batchOp))
|
if (failed(batchOp))
|
||||||
@@ -166,41 +180,47 @@ applyRowStripRelu(Value storage, RankedTensorType logicalType, PatternRewriter&
|
|||||||
return batchOp->getResult(0);
|
return batchOp->getResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
FailureOr<Value>
|
FailureOr<Value> applyRowStripBiasAdd(const RowStripPhysicalValue& value,
|
||||||
applyRowStripBiasAdd(Value storage, RankedTensorType logicalType, Value bias, PatternRewriter& rewriter, Location loc) {
|
Value bias,
|
||||||
|
PatternRewriter& rewriter,
|
||||||
|
Location loc) {
|
||||||
DenseElementsAttr denseAttr;
|
DenseElementsAttr denseAttr;
|
||||||
if (!isSupportedBiasAddValue(bias, logicalType, &denseAttr))
|
if (!isSupportedBiasAddValue(bias, value.logicalType, &denseAttr))
|
||||||
return failure();
|
return failure();
|
||||||
auto fragmentType = getRowStripFragmentType(logicalType);
|
FailureOr<SmallVector<Attribute>> channelValues = getBiasChannelValues(denseAttr, value.logicalType);
|
||||||
auto storageType = getRowStripStorageType(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,
|
auto batchOp = createSpatComputeBatch(rewriter,
|
||||||
loc,
|
loc,
|
||||||
TypeRange {storageType},
|
TypeRange {storageType},
|
||||||
logicalType.getDimSize(2),
|
laneCount,
|
||||||
{},
|
{},
|
||||||
ValueRange {storage},
|
ValueRange {value.storage, biasStorage},
|
||||||
[&](detail::SpatComputeBatchBodyArgs args) {
|
[&](detail::SpatComputeBatchBodyArgs args) {
|
||||||
Value fragment =
|
Operation* anchorOp = rewriter.getInsertionBlock()->getParentOp();
|
||||||
extractRowStripFragment(args.inputs.front(), logicalType, args.lane, rewriter, loc);
|
FailureOr<Value> fragment = extractGraphBatchPhysicalFragment(
|
||||||
Value constant;
|
rewriter, loc, args.inputs[0], args.lane, value.fragmentType);
|
||||||
if (denseAttr.isSplat()) {
|
Value tile = affineModConst(rewriter, loc, args.lane,
|
||||||
constant = getOrCreateConstant(
|
value.tilesPerRow, anchorOp);
|
||||||
rewriter,
|
FailureOr<Value> constant = extractGraphBatchPhysicalFragment(
|
||||||
rewriter.getInsertionBlock()->getParentOp(),
|
rewriter, loc, args.inputs[1], tile, value.fragmentType);
|
||||||
DenseElementsAttr::get(fragmentType, denseAttr.getSplatValue<Attribute>()),
|
if (failed(fragment) || failed(constant)) return failure();
|
||||||
fragmentType);
|
Value added = spatial::SpatVAddOp::create(
|
||||||
}
|
rewriter, loc, value.fragmentType, *fragment, *constant).getResult();
|
||||||
else {
|
publishGraphBatchPhysicalFragment(
|
||||||
FailureOr<Value> perChannel =
|
rewriter, loc, added, args.outputs.front(), args.lane);
|
||||||
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);
|
|
||||||
return success();
|
return success();
|
||||||
});
|
});
|
||||||
if (failed(batchOp))
|
if (failed(batchOp))
|
||||||
|
|||||||
@@ -11,10 +11,13 @@ inline constexpr llvm::StringLiteral kRowStripIndexMap = "nchw_row_strip_fragmen
|
|||||||
struct RowStripPhysicalValue {
|
struct RowStripPhysicalValue {
|
||||||
mlir::Value storage;
|
mlir::Value storage;
|
||||||
mlir::RankedTensorType logicalType;
|
mlir::RankedTensorType logicalType;
|
||||||
llvm::SmallVector<int64_t, 16> fragmentOffsets;
|
mlir::RankedTensorType fragmentType;
|
||||||
llvm::SmallVector<int64_t, 16> fragmentSizes;
|
int64_t tilesPerRow;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mlir::FailureOr<RowStripPhysicalValue> describeRowStripPhysicalValue(mlir::Value storage,
|
||||||
|
mlir::RankedTensorType logicalType);
|
||||||
|
|
||||||
std::pair<llvm::SmallVector<int64_t>, llvm::SmallVector<int64_t>>
|
std::pair<llvm::SmallVector<int64_t>, llvm::SmallVector<int64_t>>
|
||||||
buildRowStripMetadata(mlir::RankedTensorType type);
|
buildRowStripMetadata(mlir::RankedTensorType type);
|
||||||
|
|
||||||
@@ -50,18 +53,15 @@ mlir::FailureOr<mlir::Value> createRowStripStorageFromRows(mlir::Value rows,
|
|||||||
mlir::PatternRewriter& rewriter,
|
mlir::PatternRewriter& rewriter,
|
||||||
mlir::Location loc);
|
mlir::Location loc);
|
||||||
|
|
||||||
mlir::FailureOr<mlir::Value> createRowStripAssemblyBlueprint(mlir::Value storage,
|
mlir::FailureOr<mlir::Value> createRowStripAssemblyBlueprint(const RowStripPhysicalValue& value,
|
||||||
mlir::RankedTensorType logicalType,
|
|
||||||
mlir::PatternRewriter& rewriter,
|
mlir::PatternRewriter& rewriter,
|
||||||
mlir::Location loc);
|
mlir::Location loc);
|
||||||
|
|
||||||
mlir::FailureOr<mlir::Value> applyRowStripRelu(mlir::Value storage,
|
mlir::FailureOr<mlir::Value> applyRowStripRelu(const RowStripPhysicalValue& value,
|
||||||
mlir::RankedTensorType logicalType,
|
|
||||||
mlir::PatternRewriter& rewriter,
|
mlir::PatternRewriter& rewriter,
|
||||||
mlir::Location loc);
|
mlir::Location loc);
|
||||||
|
|
||||||
mlir::FailureOr<mlir::Value> applyRowStripBiasAdd(mlir::Value storage,
|
mlir::FailureOr<mlir::Value> applyRowStripBiasAdd(const RowStripPhysicalValue& value,
|
||||||
mlir::RankedTensorType logicalType,
|
|
||||||
mlir::Value bias,
|
mlir::Value bias,
|
||||||
mlir::PatternRewriter& rewriter,
|
mlir::PatternRewriter& rewriter,
|
||||||
mlir::Location loc);
|
mlir::Location loc);
|
||||||
|
|||||||
@@ -45,38 +45,30 @@ static FailureOr<RowStripPhysicalValue> buildRowStripValue(spatial::SpatBlueprin
|
|||||||
auto logicalType = dyn_cast<RankedTensorType>(blueprint.getOutput().getType());
|
auto logicalType = dyn_cast<RankedTensorType>(blueprint.getOutput().getType());
|
||||||
if (!logicalType)
|
if (!logicalType)
|
||||||
return blueprint.emitOpError("requires ranked logical output type"), failure();
|
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)
|
if (blueprint.getIndexMap() != kRowStripIndexMap)
|
||||||
return blueprint.emitOpError("requires the canonical row-strip index map"), failure();
|
return blueprint.emitOpError("requires the canonical row-strip index map"), failure();
|
||||||
auto storageType = dyn_cast<RankedTensorType>(storage.getType());
|
FailureOr<RowStripPhysicalValue> value = describeRowStripPhysicalValue(storage, logicalType);
|
||||||
if (!storageType || storageType != getRowStripStorageType(logicalType))
|
if (failed(value))
|
||||||
return blueprint.emitOpError("requires physical row-strip fragment storage"), failure();
|
return blueprint.emitOpError("requires physical row-strip fragment storage"), failure();
|
||||||
return value;
|
return *value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static FailureOr<Value>
|
static FailureOr<Value>
|
||||||
lowerRowStripRelu(const RowStripPhysicalValue& input, spatial::SpatReluPlanOp planOp, PatternRewriter& rewriter) {
|
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,
|
static FailureOr<Value> lowerRowStripBiasAdd(const RowStripPhysicalValue& input,
|
||||||
spatial::SpatBiasAddPlanOp planOp,
|
spatial::SpatBiasAddPlanOp planOp,
|
||||||
PatternRewriter& rewriter) {
|
PatternRewriter& rewriter) {
|
||||||
return applyRowStripBiasAdd(input.storage, input.logicalType, planOp.getBias(), rewriter, planOp.getLoc());
|
return applyRowStripBiasAdd(input, planOp.getBias(), rewriter, planOp.getLoc());
|
||||||
}
|
}
|
||||||
|
|
||||||
static FailureOr<Value>
|
static FailureOr<Value>
|
||||||
materializeRowStripToDense(const RowStripPhysicalValue& rowStripValue, Location loc, PatternRewriter& rewriter) {
|
materializeRowStripToDense(const RowStripPhysicalValue& rowStripValue, Location loc, PatternRewriter& rewriter) {
|
||||||
if (rowStripValue.logicalType.getRank() != 4 || !rowStripValue.logicalType.hasStaticShape())
|
if (rowStripValue.logicalType.getRank() != 4 || !rowStripValue.logicalType.hasStaticShape())
|
||||||
return failure();
|
return failure();
|
||||||
auto [expectedOffsets, expectedSizes] = buildRowStripMetadata(rowStripValue.logicalType);
|
return createRowStripAssemblyBlueprint(rowStripValue, rewriter, loc);
|
||||||
if (!llvm::equal(rowStripValue.fragmentOffsets, expectedOffsets) || !llvm::equal(rowStripValue.fragmentSizes, expectedSizes))
|
|
||||||
return failure();
|
|
||||||
return createRowStripAssemblyBlueprint(rowStripValue.storage, rowStripValue.logicalType, rewriter, loc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static FailureOr<Value> lowerDenseBatchBiasAdd(Value input, Value bias, RankedTensorType resultType,
|
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()) {
|
if (rowStripBlueprint != planOp.getResult().getUsers().end()) {
|
||||||
rewriter.setInsertionPoint(planOp);
|
rewriter.setInsertionPoint(planOp);
|
||||||
|
std::optional<Value> physicalInput;
|
||||||
|
if (succeeded(rowStripInput))
|
||||||
|
physicalInput = rowStripInput->storage;
|
||||||
FailureOr<Value> lowered = lowerSelectedConv2DPlan(
|
FailureOr<Value> lowered = lowerSelectedConv2DPlan(
|
||||||
planOp,
|
planOp,
|
||||||
succeeded(rowStripInput) ? std::optional<Value> {rowStripInput->storage} : std::nullopt,
|
physicalInput,
|
||||||
/*emitRowStripLayout=*/true,
|
/*emitRowStripLayout=*/true,
|
||||||
rewriter);
|
rewriter);
|
||||||
if (failed(lowered)) {
|
if (failed(lowered)) {
|
||||||
@@ -255,8 +250,23 @@ struct LowerSpatialPlansPass final : PassWrapper<LowerSpatialPlansPass, Operatio
|
|||||||
|
|
||||||
FailureOr<RowStripPhysicalValue> input = getRowStripValue(rowStripValues, planOp.getInput());
|
FailureOr<RowStripPhysicalValue> input = getRowStripValue(rowStripValues, planOp.getInput());
|
||||||
rewriter.setInsertionPoint(planOp);
|
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(
|
FailureOr<Value> lowered = lowerSelectedMaxPool2DPlan(
|
||||||
planOp, succeeded(input) ? std::optional<Value> {input->storage} : std::nullopt, rewriter);
|
planOp, physicalInput, rewriter);
|
||||||
if (failed(lowered)) {
|
if (failed(lowered)) {
|
||||||
planOp.emitOpError("failed to lower selected row-strip Spatial MaxPool plan");
|
planOp.emitOpError("failed to lower selected row-strip Spatial MaxPool plan");
|
||||||
signalPassFailure();
|
signalPassFailure();
|
||||||
|
|||||||
@@ -2326,54 +2326,25 @@ static Value maybeUnpackChunkRows(Value gemmRows,
|
|||||||
return unpackCompute.getResult(0);
|
return unpackCompute.getResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Value createChunkedConvRows(const ConvLoweringState& state,
|
static Value createStreamedConvRows(const ConvLoweringState& state,
|
||||||
const PreparedConvInput& preparedInput,
|
const PreparedConvInput& preparedInput,
|
||||||
Value weightMatrix,
|
Value weightMatrix,
|
||||||
Value biasMatrix,
|
Value biasMatrix,
|
||||||
DenseElementsAttr wDenseAttr,
|
DenseElementsAttr wDenseAttr,
|
||||||
DenseElementsAttr biasDenseAttr,
|
DenseElementsAttr biasDenseAttr,
|
||||||
int64_t forcedPackFactor,
|
int64_t forcedPackFactor,
|
||||||
uint64_t chunkPositions,
|
PatternRewriter& rewriter,
|
||||||
PatternRewriter& rewriter,
|
Location loc) {
|
||||||
Location loc) {
|
|
||||||
SmallVector<Value> chunkRows;
|
|
||||||
const int64_t totalPatches = state.batchSize * state.outHeight * state.outWidth;
|
const int64_t totalPatches = state.batchSize * state.outHeight * state.outWidth;
|
||||||
for (int64_t chunkStart = 0; chunkStart < totalPatches; chunkStart += static_cast<int64_t>(chunkPositions)) {
|
ConvGemmPlan plan = buildConvGemmPlan(state, static_cast<bool>(wDenseAttr),
|
||||||
const int64_t chunkNumPatches = std::min<int64_t>(static_cast<int64_t>(chunkPositions), totalPatches - chunkStart);
|
!state.hasBias || static_cast<bool>(biasDenseAttr), 0, totalPatches, forcedPackFactor);
|
||||||
ConvGemmPlan chunkPlan = buildConvGemmPlan(state,
|
Value inputRows = createIm2colRows(state, preparedInput, plan, rewriter, loc);
|
||||||
static_cast<bool>(wDenseAttr),
|
Value packedWeights = buildPackedWeights(wDenseAttr, weightMatrix, state, plan, rewriter, loc);
|
||||||
!state.hasBias || static_cast<bool>(biasDenseAttr),
|
Value gemmBias = state.hasBias ? state.b : createZeroGemmBias(plan.gemmOutputRowsType, rewriter);
|
||||||
chunkStart,
|
Value packedBias = buildPackedBias(gemmBias, biasMatrix, biasDenseAttr, state, plan, rewriter, loc);
|
||||||
chunkNumPatches,
|
Value gemmRows = ONNXGemmOp::create(rewriter, loc, plan.gemmOutputRowsType, inputRows,
|
||||||
forcedPackFactor);
|
packedWeights, packedBias, APFloat(1.0f), APFloat(1.0f), 0, 0).getY();
|
||||||
Value chunkInputRows = createIm2colRows(state, preparedInput, chunkPlan, rewriter, loc);
|
return maybeUnpackChunkRows(gemmRows, plan, 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Value rewritePackedIm2ColConv(const ConvLoweringState& state,
|
static Value rewritePackedIm2ColConv(const ConvLoweringState& state,
|
||||||
@@ -2444,16 +2415,13 @@ static Value rewriteStreamedConv(const ConvLoweringState& state,
|
|||||||
ConvGemmPlan seedPlan = buildConvGemmPlan(
|
ConvGemmPlan seedPlan = buildConvGemmPlan(
|
||||||
state, static_cast<bool>(wDenseAttr), !state.hasBias || static_cast<bool>(biasDenseAttr), 0, 1, forcedPackFactor);
|
state, static_cast<bool>(wDenseAttr), !state.hasBias || static_cast<bool>(biasDenseAttr), 0, 1, forcedPackFactor);
|
||||||
Value weightMatrix = createWeightMatrix(state.w, seedPlan, rewriter, loc);
|
Value weightMatrix = createWeightMatrix(state.w, seedPlan, rewriter, loc);
|
||||||
ConvGeometry geo = buildConvGeometry(state);
|
Value collectedRows = createStreamedConvRows(state,
|
||||||
uint64_t chunkPositions = chooseStreamChunkPositions(geo, forcedPackFactor);
|
|
||||||
Value collectedRows = createChunkedConvRows(state,
|
|
||||||
preparedInput,
|
preparedInput,
|
||||||
weightMatrix,
|
weightMatrix,
|
||||||
biasMatrix,
|
biasMatrix,
|
||||||
wDenseAttr,
|
wDenseAttr,
|
||||||
biasDenseAttr,
|
biasDenseAttr,
|
||||||
forcedPackFactor,
|
forcedPackFactor,
|
||||||
chunkPositions,
|
|
||||||
rewriter,
|
rewriter,
|
||||||
loc);
|
loc);
|
||||||
auto gemmOutType = cast<RankedTensorType>(collectedRows.getType());
|
auto gemmOutType = cast<RankedTensorType>(collectedRows.getType());
|
||||||
@@ -2524,6 +2492,21 @@ static bool canConsumeNchwRowStripFragments(const ConvLoweringState& state, Stri
|
|||||||
failureReason = "dilation_not_one";
|
failureReason = "dilation_not_one";
|
||||||
return false;
|
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) {
|
if (state.wHeight != 3 || state.wWidth != 3) {
|
||||||
failureReason = "kernel_not_3x3";
|
failureReason = "kernel_not_3x3";
|
||||||
return false;
|
return false;
|
||||||
@@ -2544,7 +2527,6 @@ static bool canConsumeNchwRowStripFragments(const ConvLoweringState& state, Stri
|
|||||||
failureReason = "unsupported_bias";
|
failureReason = "unsupported_bias";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ConvGeometry geometry = buildConvGeometry(state);
|
|
||||||
if (geometry.c > geometry.xbarSize) {
|
if (geometry.c > geometry.xbarSize) {
|
||||||
failureReason = "output_channels_exceed_crossbar";
|
failureReason = "output_channels_exceed_crossbar";
|
||||||
return false;
|
return false;
|
||||||
@@ -2575,6 +2557,25 @@ static FailureOr<Value> createPaddedBiasRowConstant(const ConvLoweringState& sta
|
|||||||
return getOrCreateConstant(rewriter, rewriter.getInsertionBlock()->getParentOp(), biasAttr, biasType);
|
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,
|
static Value createHorizontallyPaddedRowStripFragment(Value fragment,
|
||||||
const ConvLoweringState& state,
|
const ConvLoweringState& state,
|
||||||
PatternRewriter& rewriter,
|
PatternRewriter& rewriter,
|
||||||
@@ -2732,8 +2733,11 @@ static FailureOr<Value> createConvInputWindow(Value input,
|
|||||||
? extractDenseConvWindowRow(input, sourceRowTable, state, outputHeight, kernelRow, rewriter, loc)
|
? extractDenseConvWindowRow(input, sourceRowTable, state, outputHeight, kernelRow, rewriter, loc)
|
||||||
: extractProjectedRowStripWindowRow(
|
: extractProjectedRowStripWindowRow(
|
||||||
input, sourceRowTable, state, outputHeight, kernelRow, rewriter, loc);
|
input, sourceRowTable, state, outputHeight, kernelRow, rewriter, loc);
|
||||||
Value mask = extractProjectedRowStripWindowMask(*maskTable, state, outputHeight, kernelRow, rewriter, loc);
|
Value semanticRow = sourceRow;
|
||||||
Value semanticRow = spatial::SpatVMulOp::create(rewriter, loc, fragmentType, sourceRow, mask).getResult();
|
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);
|
Value paddedRow = createHorizontallyPaddedRowStripFragment(semanticRow, state, rewriter, loc);
|
||||||
window = tensor::InsertSliceOp::create(rewriter,
|
window = tensor::InsertSliceOp::create(rewriter,
|
||||||
loc,
|
loc,
|
||||||
@@ -2906,12 +2910,6 @@ static FailureOr<Value> createPaddedConvOutputRow(Value patchRow,
|
|||||||
.getResult();
|
.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) {
|
static bool rowStripOutputTileFitsOneCore(const ConvGeometry& geometry) {
|
||||||
return ceilIntegerDivide(geometry.k, geometry.xbarSize)
|
return ceilIntegerDivide(geometry.k, geometry.xbarSize)
|
||||||
<= static_cast<int64_t>(crossbarCountInCore.getValue());
|
<= 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;
|
const int64_t patchSize = state.numChannelsIn * state.wHeight * state.wWidth;
|
||||||
auto elementType = state.outType.getElementType();
|
auto elementType = state.outType.getElementType();
|
||||||
auto paddedPatchRowType = RankedTensorType::get({1, paddedK}, elementType);
|
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());
|
auto tileWeightsType = RankedTensorType::get({paddedK, xbarDim}, state.wType.getElementType());
|
||||||
SmallVector<Value> outputTiles;
|
const int64_t laneCount = state.outHeight * outputTileCount;
|
||||||
outputTiles.reserve(outputTileCount);
|
auto tileStorageType = spatial::getGraphBatchPhysicalResultType(laneCount, tileFragmentType);
|
||||||
|
FailureOr<Value> paddedBias = failure();
|
||||||
for (int64_t outputTile = 0; outputTile < outputTileCount; ++outputTile) {
|
if (state.hasBias)
|
||||||
const int64_t channelOffset = outputTile * xbarDim;
|
paddedBias = createPaddedBiasTileConstant(state, xbarDim, rewriter);
|
||||||
const int64_t tileChannels = std::min(xbarDim, state.numChannelsOut - channelOffset);
|
if (state.hasBias && failed(paddedBias))
|
||||||
auto tileRowType = RankedTensorType::get({1, tileChannels}, elementType);
|
return failure();
|
||||||
auto tilePixelType = RankedTensorType::get({1, tileChannels, 1, 1}, elementType);
|
auto tileBatch = createSpatComputeBatch(
|
||||||
auto tileFragmentType = RankedTensorType::get({1, tileChannels, 1, state.outWidth}, elementType);
|
rewriter, loc, TypeRange {tileStorageType}, laneCount, ValueRange {paddedWeights},
|
||||||
auto tileStorageType = spatial::getGraphBatchPhysicalResultType(state.outHeight, tileFragmentType);
|
state.hasBias ? ValueRange {state.x, *paddedBias} : ValueRange {state.x},
|
||||||
SmallVector<OpFoldResult> weightOffsets {
|
[&](detail::SpatComputeBatchBodyArgs args) {
|
||||||
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) {
|
|
||||||
Operation* anchorOp = rewriter.getInsertionBlock()->getParentOp();
|
Operation* anchorOp = rewriter.getInsertionBlock()->getParentOp();
|
||||||
Value c0 = getOrCreateIndexConstant(rewriter, anchorOp, 0);
|
Value c0 = getOrCreateIndexConstant(rewriter, anchorOp, 0);
|
||||||
Value c1 = getOrCreateIndexConstant(rewriter, anchorOp, 1);
|
Value c1 = getOrCreateIndexConstant(rewriter, anchorOp, 1);
|
||||||
Value cOutWidth = getOrCreateIndexConstant(rewriter, anchorOp, state.outWidth);
|
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 =
|
FailureOr<Value> inputWindow =
|
||||||
createConvInputWindow(args.inputs.front(), state, args.lane, rewriter, loc);
|
createConvInputWindow(args.inputs.front(), state, outputRow, rewriter, loc);
|
||||||
if (failed(inputWindow))
|
if (failed(inputWindow))
|
||||||
return failure();
|
return failure();
|
||||||
Value fragmentInit = tensor::EmptyOp::create(rewriter, loc, tileFragmentType.getShape(), elementType);
|
Value fragmentInit = tensor::EmptyOp::create(rewriter, loc, tileFragmentType.getShape(), elementType);
|
||||||
@@ -2984,28 +2984,18 @@ static FailureOr<Value> createOutputChannelTiledRowStripConvOutput(const ConvLow
|
|||||||
paddedPatchRow = createZeroPaddedTensor(
|
paddedPatchRow = createZeroPaddedTensor(
|
||||||
paddedPatchRow, paddedPatchRowType, {0, 0}, {0, paddedK - patchSize}, rewriter, widthLoc);
|
paddedPatchRow, paddedPatchRowType, {0, 0}, {0, paddedK - patchSize}, rewriter, widthLoc);
|
||||||
FailureOr<Value> paddedOutputRow = createPaddedConvOutputTile(
|
FailureOr<Value> paddedOutputRow = createPaddedConvOutputTile(
|
||||||
paddedPatchRow, args.weights.front(), numKSlices, xbarDim, rewriter, widthLoc);
|
paddedPatchRow, tileWeights, numKSlices, xbarDim, rewriter, widthLoc);
|
||||||
if (failed(paddedOutputRow))
|
if (failed(paddedOutputRow))
|
||||||
return failure();
|
return failure();
|
||||||
Value outputRow = *paddedOutputRow;
|
if (state.hasBias)
|
||||||
if (tileChannels != xbarDim) {
|
paddedOutputRow = spatial::SpatVAddOp::create(
|
||||||
SmallVector<OpFoldResult> rowOffsets {rewriter.getIndexAttr(0), rewriter.getIndexAttr(0)};
|
rewriter, widthLoc, paddedRowType, *paddedOutputRow, *biasTile).getResult();
|
||||||
SmallVector<OpFoldResult> rowSizes {
|
|
||||||
rewriter.getIndexAttr(1), rewriter.getIndexAttr(tileChannels)};
|
|
||||||
outputRow = tensor::ExtractSliceOp::create(rewriter,
|
|
||||||
widthLoc,
|
|
||||||
tileRowType,
|
|
||||||
outputRow,
|
|
||||||
rowOffsets,
|
|
||||||
rowSizes,
|
|
||||||
getUnitStrides(rewriter, 2));
|
|
||||||
}
|
|
||||||
Value outputPixel = tensor::ExpandShapeOp::create(
|
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 {
|
SmallVector<OpFoldResult> rowOffsets {
|
||||||
rewriter.getIndexAttr(0), rewriter.getIndexAttr(0), rewriter.getIndexAttr(0), widthIndex};
|
rewriter.getIndexAttr(0), rewriter.getIndexAttr(0), rewriter.getIndexAttr(0), widthIndex};
|
||||||
SmallVector<OpFoldResult> rowSizes {rewriter.getIndexAttr(1),
|
SmallVector<OpFoldResult> rowSizes {rewriter.getIndexAttr(1),
|
||||||
rewriter.getIndexAttr(tileChannels),
|
rewriter.getIndexAttr(xbarDim),
|
||||||
rewriter.getIndexAttr(1),
|
rewriter.getIndexAttr(1),
|
||||||
rewriter.getIndexAttr(1)};
|
rewriter.getIndexAttr(1)};
|
||||||
Value nextFragment = tensor::InsertSliceOp::create(rewriter,
|
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);
|
rewriter, loc, widthLoop->results.front(), args.outputs.front(), args.lane);
|
||||||
return success();
|
return success();
|
||||||
});
|
});
|
||||||
if (failed(tileBatch))
|
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))
|
|
||||||
return failure();
|
return failure();
|
||||||
Value output = assemblyBatch->getResult(0);
|
return tileBatch->getResult(0);
|
||||||
if (state.hasBias)
|
|
||||||
return applyRowStripBiasAdd(output, state.outType, state.b, rewriter, loc);
|
|
||||||
return output;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static FailureOr<Value>
|
static FailureOr<Value>
|
||||||
@@ -3104,7 +3045,7 @@ createRowStripConvOutputFromDenseInput(const ConvLoweringState& state, PatternRe
|
|||||||
weightDenseAttr, state, paddedK, xbarDim, rewriter)
|
weightDenseAttr, state, paddedK, xbarDim, rewriter)
|
||||||
: standard::createPaddedOutputChannelTiledWeightConstant(
|
: standard::createPaddedOutputChannelTiledWeightConstant(
|
||||||
weightDenseAttr, state, paddedK, xbarDim, rewriter);
|
weightDenseAttr, state, paddedK, xbarDim, rewriter);
|
||||||
if (!rowStripOutputFitsOneCore(geometry))
|
if (state.numChannelsOut > xbarDim)
|
||||||
return createOutputChannelTiledRowStripConvOutput(
|
return createOutputChannelTiledRowStripConvOutput(
|
||||||
state, paddedWeights, paddedK, numKSlices, xbarDim, rewriter, loc);
|
state, paddedWeights, paddedK, numKSlices, xbarDim, rewriter, loc);
|
||||||
|
|
||||||
@@ -3279,11 +3220,106 @@ static FailureOr<Value> createConvOutputFromNchwRowStripFragments(Value rowStrip
|
|||||||
return batchOp->getResult(0);
|
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,
|
static FailureOr<Value> createConvOutputFromRowStripInput(const ConvLoweringState& state,
|
||||||
[[maybe_unused]] const ConvLoweringDecision& decision,
|
[[maybe_unused]] const ConvLoweringDecision& decision,
|
||||||
Value rowStripInput,
|
Value rowStripInput,
|
||||||
PatternRewriter& rewriter,
|
PatternRewriter& rewriter,
|
||||||
Location loc) {
|
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);
|
return createConvOutputFromNchwRowStripFragments(rowStripInput, state, rewriter, loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4187,22 +4223,9 @@ lowerSelectedConv2DPlan(spatial::SpatConv2DPlanOp planOp,
|
|||||||
}
|
}
|
||||||
if (failed(canLowerConvPlanToRowStrip(planOp)))
|
if (failed(canLowerConvPlanToRowStrip(planOp)))
|
||||||
return planOp.emitOpError("selected row-strip layout is not supported for this Conv plan"), failure();
|
return planOp.emitOpError("selected row-strip layout is not supported for this Conv plan"), failure();
|
||||||
ConvLoweringState rowState = *state;
|
FailureOr<Value> rowStripStorage = createRowStripConvOutputFromDenseInput(*state, rewriter, planOp.getLoc());
|
||||||
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());
|
|
||||||
if (failed(rowStripStorage))
|
if (failed(rowStripStorage))
|
||||||
return planOp.emitOpError("failed to build row-strip fragment storage for the selected Conv plan"), failure();
|
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;
|
return *rowStripStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+32
-4
@@ -77,9 +77,16 @@ static LogicalResult eraseOldGraph(func::FuncOp funcOp,
|
|||||||
rewriter.eraseOp(blueprint);
|
rewriter.eraseOp(blueprint);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!op->use_empty())
|
if (!op->use_empty()) {
|
||||||
return op->emitOpError(
|
for (OpResult result : op->getResults()) {
|
||||||
"phase 2 cannot erase an old graph compute with live results");
|
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);
|
rewriter.eraseOp(op);
|
||||||
}
|
}
|
||||||
return success();
|
return success();
|
||||||
@@ -100,6 +107,25 @@ static LogicalResult eraseDeferredSourceSelectors(
|
|||||||
return success();
|
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) {
|
static LogicalResult verifyDominance(func::FuncOp funcOp) {
|
||||||
DominanceInfo dominance(funcOp);
|
DominanceInfo dominance(funcOp);
|
||||||
WalkResult result = funcOp.walk([&](Operation *op) {
|
WalkResult result = funcOp.walk([&](Operation *op) {
|
||||||
@@ -119,6 +145,9 @@ static LogicalResult verifyDominance(func::FuncOp funcOp) {
|
|||||||
LogicalResult realizeDeferredCommunication(
|
LogicalResult realizeDeferredCommunication(
|
||||||
func::FuncOp funcOp,
|
func::FuncOp funcOp,
|
||||||
const ScheduledComputeMaterializationResult &materialization) {
|
const ScheduledComputeMaterializationResult &materialization) {
|
||||||
|
IRRewriter rewriter(funcOp.getContext());
|
||||||
|
eraseUnusedIdentityDeferredCommunications(funcOp, rewriter);
|
||||||
|
|
||||||
auto transfers = buildDeferredTransferPlan(funcOp, materialization);
|
auto transfers = buildDeferredTransferPlan(funcOp, materialization);
|
||||||
if (failed(transfers))
|
if (failed(transfers))
|
||||||
return funcOp.emitOpError(
|
return funcOp.emitOpError(
|
||||||
@@ -134,7 +163,6 @@ LogicalResult realizeDeferredCommunication(
|
|||||||
return funcOp.emitOpError(
|
return funcOp.emitOpError(
|
||||||
"phase 2 failed to build sparse boundary programs");
|
"phase 2 failed to build sparse boundary programs");
|
||||||
|
|
||||||
IRRewriter rewriter(funcOp.getContext());
|
|
||||||
if (failed(retargetDeferredPublications(funcOp, *transfers))
|
if (failed(retargetDeferredPublications(funcOp, *transfers))
|
||||||
|| failed(replaceFinalGraphPublications(funcOp, *transfers)))
|
|| failed(replaceFinalGraphPublications(funcOp, *transfers)))
|
||||||
return failure();
|
return failure();
|
||||||
|
|||||||
+270
-136
@@ -1,174 +1,308 @@
|
|||||||
# Validation Operations
|
# Operation Validation Suite
|
||||||
|
|
||||||
ONNX test models used by `validate.py` to verify the Raptor compiler + PIM simulator pipeline.
|
This directory contains the ONNX models used by `validation/validate.py` to
|
||||||
|
validate individual operations through compilation, PIM simulation, and
|
||||||
|
comparison with the ONNX-MLIR reference runtime.
|
||||||
|
|
||||||
Generated tests can be regenerated with:
|
## Naming
|
||||||
|
|
||||||
```
|
Every model uses the same path convention:
|
||||||
python3 validation/operations/gen_tests.py
|
|
||||||
|
```text
|
||||||
|
<category>/<case>/<category>_<case>.onnx
|
||||||
```
|
```
|
||||||
|
|
||||||
## Conv
|
The `<category>/<case>` pair is the operation ID printed by the validator.
|
||||||
|
Use lowercase `snake_case` for both components. Keep the generator function,
|
||||||
|
graph name, directory, and filename based on the same operation ID when adding
|
||||||
|
a test.
|
||||||
|
|
||||||
| Test | Directory | Input | Output | Kernel | Stride | Padding | Bias | Notes |
|
## Generate and validate
|
||||||
|------------------|-------------------------|-----------|-----------|--------|--------|------------|------|------------------------------------|
|
|
||||||
| Simple | `conv/simple` | [1,3,3,3] | [1,1,2,2] | 2x2 | 1 | none | no | Basic conv, hand-crafted |
|
|
||||||
| With constant | `conv/with_constant` | [1,3,3,3] | [1,1,3,3] | 2x2 | 1 | SAME_UPPER | yes | Hand-crafted, constant weight+bias |
|
|
||||||
| Batch 2 | `conv/batch_2` | [2,3,3,3] | [2,1,3,3] | 2x2 | 1 | SAME_UPPER | yes | Batched input |
|
|
||||||
| Kernel 3x3 | `conv/kernel_3x3` | [1,1,5,5] | [1,1,3,3] | 3x3 | 1 | none | no | Larger kernel |
|
|
||||||
| Stride 2 | `conv/stride_2` | [1,1,6,6] | [1,1,2,2] | 3x3 | 2 | none | no | Strided convolution |
|
|
||||||
| Multi channel | `conv/multi_channel` | [1,3,5,5] | [1,4,3,3] | 3x3 | 1 | none | no | 3 in channels, 4 out channels |
|
|
||||||
| Pointwise 1x1 | `conv/pointwise_1x1` | [1,8,4,4] | [1,4,4,4] | 1x1 | 1 | none | no | Channel mixing |
|
|
||||||
| SAME padding 3x3 | `conv/same_padding_3x3` | [1,1,5,5] | [1,1,5,5] | 3x3 | 1 | SAME_UPPER | no | Spatial dims preserved |
|
|
||||||
| Explicit padding | `conv/explicit_padding` | [1,1,4,4] | [1,1,4,4] | 3x3 | 1 | [1,1,1,1] | no | Symmetric explicit pads |
|
|
||||||
| With bias 3x3 | `conv/with_bias_3x3` | [1,3,5,5] | [1,2,3,3] | 3x3 | 1 | none | yes | Multi-channel with bias |
|
|
||||||
| Large spatial | `conv/large_spatial` | [1,1,8,8] | [1,1,6,6] | 3x3 | 1 | none | no | Larger spatial input |
|
|
||||||
| Grouped two groups | `conv/grouped_two_groups` | [1,4,4,4] | [1,4,4,4] | 1x1 | 1 | none | yes | group=2 channel partitioning |
|
|
||||||
| Depthwise grouped | `conv/depthwise_grouped` | [1,3,4,4] | [1,3,2,2] | 3x3 | 1 | none | no | group=3, one input channel per group |
|
|
||||||
| Dynamic | `conv/dynamic` | [1,1,4,4] | [1,1,2,2] | 3x3 | 1 | none | no | Runtime input and weight |
|
|
||||||
|
|
||||||
## Gemm
|
Regenerate all generated models from the repository root:
|
||||||
|
|
||||||
| Test | Directory | A (input) | B/W tensor | Output | transB | alpha | beta | Bias | Notes |
|
```bash
|
||||||
|---------------|-------------------------|-----------|------------|----------|--------|-------|------|-------|------------------------------|
|
.venv/bin/python validation/operations/gen_tests.py
|
||||||
| Simple | `gemm/simple` | [10,132] | [132,132] | [10,132] | no | 1 | 1 | no | Square weights |
|
```
|
||||||
| Non-square | `gemm/non_square` | [4,128] | [128,64] | [4,64] | no | 1 | 1 | no | K != N |
|
|
||||||
| With bias | `gemm/with_bias` | [4,128] | [128,128] | [4,128] | no | 1 | 1 | [128] | Bias vector |
|
|
||||||
| transB | `gemm/transB` | [4,128] | [64,128] | [4,64] | yes | 1 | 1 | no | Transposed weight |
|
|
||||||
| Alpha/beta | `gemm/alpha_beta` | [4,64] | [64,64] | [4,64] | no | 0.5 | 0.25 | [64] | Scaled matmul + bias |
|
|
||||||
| Small | `gemm/small` | [2,8] | [8,4] | [2,4] | no | 1 | 1 | no | Tiny matrices |
|
|
||||||
| Large | `gemm/large` | [8,256] | [256,128] | [8,128] | no | 1 | 1 | no | Larger matrices |
|
|
||||||
| transB + bias | `gemm/transB_with_bias` | [4,128] | [64,128] | [4,64] | yes | 1 | 1 | [64] | Combined |
|
|
||||||
| Dynamic | `gemm/dynamic` | [2,8] | [8,4] | [2,4] | no | 1 | 1 | no | Runtime matrix operands |
|
|
||||||
| Dynamic transB | `gemm/dynamic_transB` | [2,8] | [4,8] | [2,4] | yes | 1 | 1 | no | Runtime transpose handling |
|
|
||||||
| Dynamic bias | `gemm/dynamic_bias` | [2,8] | [8,4] | [2,4] | no | 1 | 1 | [4] | Runtime bias broadcast |
|
|
||||||
| Dynamic alpha | `gemm/dynamic_alpha` | [2,8] | [8,4] | [2,4] | no | 0.5 | 1 | no | Runtime alpha scaling |
|
|
||||||
| Dynamic beta | `gemm/dynamic_beta` | [2,8] | [8,4] | [2,4] | no | 1 | 2 | [4] | Runtime beta scaling |
|
|
||||||
| Dynamic bias + scale | `gemm/dynamic_bias_alpha_beta` | [2,8] | [8,4] | [2,4] | no | 0.5 | 2 | [4] | Runtime operands and bias |
|
|
||||||
|
|
||||||
## MatMul
|
Run the complete suite with deadlock detection:
|
||||||
|
|
||||||
| Test | Directory | A input | B tensor | Output | Notes |
|
```bash
|
||||||
|---------------------|----------------------------------|----------|----------|---------|-------------------------------------------------|
|
.venv/bin/python validation/validate.py \
|
||||||
| Basic | `matmul/basic` | [2,3] | [3,4] | [2,4] | Direct 2D MatMul rewrite path |
|
--raptor-path build_release/Release/bin/onnx-mlir \
|
||||||
| Left constant | `matmul/left_constant` | [2,3] | [3,4] | [2,4] | Constant LHS transpose rewrite path |
|
--onnx-include-dir onnx-mlir/include \
|
||||||
| Dynamic | `matmul/dynamic` | [2,3] | [3,4] | [2,4] | Runtime matrix operands |
|
--operations-dir validation/operations \
|
||||||
| Batched 3D | `matmul/batched_3d` | [2,2,3] | [2,3,4] | [2,2,4] | Matching-batch direct batched lowering |
|
--crossbar-count 64 \
|
||||||
| Batched 3D dynamic | `matmul/batched_3d_dynamic` | [2,2,3] | [2,3,4] | [2,2,4] | Batched runtime operands |
|
--crossbar-size 128 \
|
||||||
| Batched left const | `matmul/batched_left_constant` | [2,2,3] | [2,3,4] | [2,2,4] | Batched constant-LHS transpose path |
|
--core-count 144 \
|
||||||
| Batched RHS broadcast | `matmul/batched_rhs_broadcast` | [2,2,3] | [3,4] | [2,2,4] | Rank-2 RHS broadcast across batch |
|
--raptor-extra-arg=--pim-detect-communication-deadlock \
|
||||||
| Batched LHS broadcast | `matmul/batched_lhs_broadcast` | [2,3] | [2,3,4] | [2,2,4] | Rank-2 LHS broadcast across batched RHS |
|
--raptor-extra-arg=--pim-export-spatial-dataflow=none
|
||||||
|
```
|
||||||
|
|
||||||
## Gemv
|
Use `--compile-only` for compiler and deadlock checks, then `--run-only` to
|
||||||
|
reuse those artifacts for reference execution, simulation, and comparison.
|
||||||
|
The validator prints the complete operation results table before its summary.
|
||||||
|
|
||||||
| Test | Directory | Input | W (weight) | Output | Bias | Notes |
|
## Complete inventory
|
||||||
|---------------------|------------------------------------|----------|------------|---------|---------|----------------------------|
|
|
||||||
| Simple | `gemv/simple` | [1,132] | [132,132] | [1,132] | no | Single-sample matmul |
|
|
||||||
| Constant | `gemv/constant` | _(none)_ | [132,132] | [1,132] | no | All inputs constant |
|
|
||||||
| Homogeneous const | `gemv/with_homogeneous_constant` | [1,132] | [132,132] | [1,132] | [1,132] | Bias matches output shape |
|
|
||||||
| Heterogeneous const | `gemv/with_heterogeneous_constant` | [1,132] | [132,132] | [1,132] | [1,132] | Different constant pattern |
|
|
||||||
| Scalar const | `gemv/with_scalar_constant` | [1,132] | [132,132] | [1,132] | [1,1] | Scalar bias, broadcast |
|
|
||||||
|
|
||||||
## Pool
|
The suite contains 164 models. Tensor shapes, attributes, and constants are
|
||||||
|
defined in `gen_tests.py` and in the checked-in ONNX models.
|
||||||
|
|
||||||
| Test | Directory | Input | Output | Kernel | Stride | Padding | Notes |
|
### Add (5)
|
||||||
|----------------------------|---------------------------------|-----------|-----------|------------------------|--------|------------|----------------------------------|
|
|
||||||
| Max basic | `pool/max_basic` | [1,1,4,4] | [1,1,3,3] | 2x2 | 1 | none | Basic max pooling |
|
|
||||||
| Max stride 2 multi-channel | `pool/max_stride2_multichannel` | [1,5,6,6] | [1,5,3,3] | 2x2 | 2 | none | Channel-preserving max pool |
|
|
||||||
| Max SAME_UPPER | `pool/max_same_upper` | [1,1,5,5] | [1,1,3,3] | 3x3 | 2 | SAME_UPPER | Deprecated auto_pad path |
|
|
||||||
| Avg basic | `pool/avg_basic` | [1,3,4,4] | [1,3,3,3] | 2x2 | 1 | none | Basic average pooling |
|
|
||||||
| Avg explicit padding | `pool/avg_explicit_padding` | [1,2,4,4] | [1,2,2,2] | 3x3 | 2 | [1,1,1,1] | `count_include_pad=0` |
|
|
||||||
| Avg include pad | `pool/avg_include_pad` | [1,2,4,4] | [1,2,2,2] | 3x3 | 2 | [1,1,1,1] | `count_include_pad=1` |
|
|
||||||
| Max after Conv | `pool/max_after_conv` | [1,3,6,6] | [1,4,2,2] | Conv 3x3 then Pool 2x2 | 2 | none | Regression for `pool(conv(...))` |
|
|
||||||
|
|
||||||
## ReduceMean
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `after_gemm` | Gemm followed by Add with a broadcast bias vector. |
|
||||||
|
| `basic` | Elementwise Add on two inputs with identical shapes. |
|
||||||
|
| `broadcast_row` | Elementwise Add with row-vector broadcasting. |
|
||||||
|
| `channel_broadcast_1024` | NCHW per-channel broadcasting over 1024 channels. |
|
||||||
|
| `leading_dimension_broadcast` | Trailing-dimension broadcasting across leading dimensions. |
|
||||||
|
|
||||||
| Test | Directory | Input | Output | Axes | Keepdims | Notes |
|
### Concat (3)
|
||||||
|------------|--------------------------|-----------|-----------|-------|----------|-------------------------------------------------|
|
|
||||||
| Basic | `reduce_mean/basic` | [4,8] | [4,1] | [1] | 1 | Reduce feature dimension, preserving rank |
|
|
||||||
| Keepdims 0 | `reduce_mean/keepdims_0` | [4,8] | [4] | [1] | 0 | Reduce feature dimension, dropping reduced axis |
|
|
||||||
| 4D spatial | `reduce_mean/4d_spatial` | [1,3,4,4] | [1,3,1,1] | [2,3] | 1 | Reduce H and W on NCHW input |
|
|
||||||
| After Conv | `reduce_mean/after_conv` | [1,3,5,5] | [1,2,1,1] | [2,3] | 1 | Conv 3x3 + bias, then spatial ReduceMean |
|
|
||||||
|
|
||||||
## Relu
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `channel_axis` | Concatenates two runtime NCHW tensors along the channel axis. |
|
||||||
|
| `negative_axis` | Concatenates tensors using a negative axis. |
|
||||||
|
| `three_inputs_channel_axis` | Concatenates three runtime NCHW tensors along the channel axis. |
|
||||||
|
|
||||||
| Test | Directory | Input | Output | Notes |
|
### Conv (31)
|
||||||
|------------|-------------------|-----------|-----------|----------------------------|
|
|
||||||
| Basic | `relu/basic` | [4,8] | [4,8] | Standalone 2D Relu |
|
|
||||||
| 4D | `relu/4d` | [2,3,4,4] | [2,3,4,4] | Standalone NCHW Relu |
|
|
||||||
| After Conv | `relu/after_conv` | [1,3,5,5] | [1,2,3,3] | Conv 3x3 + bias, then Relu |
|
|
||||||
| After Gemm | `relu/after_gemm` | [4,64] | [4,32] | Gemm + bias, then Relu |
|
|
||||||
|
|
||||||
## Sigmoid
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `batch_2` | Batched Conv with SAME_UPPER padding and bias. |
|
||||||
|
| `batch_4_pointwise` | Pointwise Conv with batch size four. |
|
||||||
|
| `depthwise_1024_channels` | Depthwise pointwise Conv with 1024 groups. |
|
||||||
|
| `depthwise_grouped` | Depthwise-style grouped Conv with one input channel per group. |
|
||||||
|
| `dilated_3x3` | Conv with a dilated 3x3 kernel. |
|
||||||
|
| `dynamic` | Conv with runtime input and weight tensors. |
|
||||||
|
| `explicit_padding` | 3x3 Conv with symmetric explicit padding. |
|
||||||
|
| `grouped_many_groups` | Pointwise Conv with many groups and high channel counts. |
|
||||||
|
| `grouped_two_groups` | Two-group pointwise Conv with bias. |
|
||||||
|
| `huge_pointwise_1024` | Pointwise Conv with 1024 input and output channels. |
|
||||||
|
| `huge_pointwise_1024_dynamic` | The 1024-channel pointwise Conv with runtime weights. |
|
||||||
|
| `kernel_3x3` | Basic 3x3 Conv without padding. |
|
||||||
|
| `kernel_equals_input_spatial` | Conv whose kernel covers the full spatial input. |
|
||||||
|
| `large_input_channels_1x1` | Pointwise Conv with 1024 input channels and modest output width. |
|
||||||
|
| `large_output_channels_1x1` | Pointwise Conv with modest input width and 1024 output channels. |
|
||||||
|
| `large_spatial` | 3x3 Conv on a larger spatial input. |
|
||||||
|
| `multi_channel` | 3x3 Conv with multiple input and output channels. |
|
||||||
|
| `non_square_kernel_1x3` | Conv with a non-square 1x3 kernel. |
|
||||||
|
| `non_square_kernel_3x1` | Conv with a non-square 3x1 kernel. |
|
||||||
|
| `non_uniform_stride` | Conv with different height and width strides. |
|
||||||
|
| `pointwise_1x1` | Basic pointwise channel-mixing Conv. |
|
||||||
|
| `pointwise_tiled_chain` | Relu and chained pointwise Convs with a tiled intermediate. |
|
||||||
|
| `real_asymmetric_padding` | Conv with asymmetric explicit padding. |
|
||||||
|
| `relu_conv_store` | Conv, Relu, and a second Conv to validate an intermediate stored result. |
|
||||||
|
| `same_lower_3x3` | 3x3 Conv with SAME_LOWER padding. |
|
||||||
|
| `same_padding_3x3` | 3x3 Conv with SAME_UPPER padding. |
|
||||||
|
| `simple` | Hand-authored basic 2x2 Conv. |
|
||||||
|
| `stride_2` | 3x3 Conv with stride two. |
|
||||||
|
| `with_bias_3x3` | Multi-channel 3x3 Conv with bias. |
|
||||||
|
| `with_constant` | Hand-authored SAME_UPPER Conv with constant weight and bias. |
|
||||||
|
| `without_kernel_shape_attr` | Conv whose kernel shape is inferred from its weight tensor. |
|
||||||
|
|
||||||
| Test | Directory | Input | Output | Notes |
|
### Div (6)
|
||||||
|------------|----------------------|-----------|-----------|---------------------------|
|
|
||||||
| Basic | `sigmoid/basic` | [4,8] | [4,8] | Standalone 2D Sigmoid |
|
|
||||||
| 4D | `sigmoid/4d` | [2,3,4,4] | [2,3,4,4] | Standalone NCHW Sigmoid |
|
|
||||||
| After Gemm | `sigmoid/after_gemm` | [4,64] | [4,32] | Gemm + bias, then Sigmoid |
|
|
||||||
|
|
||||||
## Softmax
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `after_gemm` | Gemm followed by Div with a broadcast divisor vector. |
|
||||||
|
| `basic` | Elementwise Div by a same-shape constant tensor. |
|
||||||
|
| `channel_broadcast_1024` | Div with NCHW per-channel broadcasting over 1024 channels. |
|
||||||
|
| `leading_dimension_broadcast` | Div with trailing-dimension broadcasting. |
|
||||||
|
| `runtime_scalar_rhs` | Div of a runtime tensor by a scalar initializer. |
|
||||||
|
| `scalar_constant` | Div with scalar broadcasting on a 2D tensor. |
|
||||||
|
|
||||||
| Test | Directory | Input | Output | Axis | Notes |
|
### Gather (5)
|
||||||
|--------------|--------------------------|-------------|-------------|------|---------------------------------|
|
|
||||||
| Basic | `softmax/basic` | [3,5] | [3,5] | 1 | Row-wise softmax over features |
|
|
||||||
| 3D last axis | `softmax/3d_last_axis` | [2,3,4] | [2,3,4] | 2 | Last-dimension normalization |
|
|
||||||
| Channel axis | `softmax/channel_axis` | [1,3,2,2] | [1,3,2,2] | 1 | NCHW channel-wise softmax |
|
|
||||||
|
|
||||||
## Resize
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `3d_input_axis1` | Gathers along axis one of a 3D input. |
|
||||||
|
| `axis0_matrix_indices` | Gathers rows using a 2D indices tensor. |
|
||||||
|
| `axis1` | Gathers selected columns from a 2D tensor. |
|
||||||
|
| `negative_axis` | Gathers using a negative axis. |
|
||||||
|
| `negative_indices` | Gathers with negative indices along axis zero. |
|
||||||
|
|
||||||
| Test | Directory | Input | Output | Mode | Notes |
|
### Gemm (21)
|
||||||
|---------------------|-------------------------|-----------|-----------|---------|-----------------------------------------|
|
|
||||||
| Nearest 2x | `resize/nearest_2x` | [1,1,2,3] | [1,1,4,6] | nearest | NCHW upsampling with scales [1,1,2,2] |
|
|
||||||
| Non-uniform scales | `resize/non_uniform` | [1,1,2,3] | [1,1,6,6] | nearest | Different height/width scaling factors |
|
|
||||||
| Explicit sizes | `resize/with_sizes` | [1,1,2,3] | [1,1,3,5] | nearest | Sizes input used instead of scales |
|
|
||||||
|
|
||||||
## Split
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `alpha_beta` | Applies non-default alpha and beta scaling with bias. |
|
||||||
|
| `bias_rank2_broadcast` | Broadcasts a rank-2 bias across output rows. |
|
||||||
|
| `dynamic` | Uses both matrix operands at runtime. |
|
||||||
|
| `dynamic_alpha` | Uses runtime operands with non-default alpha scaling. |
|
||||||
|
| `dynamic_beta` | Uses runtime operands and bias with non-default beta scaling. |
|
||||||
|
| `dynamic_bias` | Uses runtime matrix operands and runtime bias. |
|
||||||
|
| `dynamic_bias_alpha_beta` | Combines runtime operands and bias with alpha and beta scaling. |
|
||||||
|
| `dynamic_transB` | Transposes a runtime right-hand matrix. |
|
||||||
|
| `huge_1024` | Uses 1024-wide inner and output dimensions. |
|
||||||
|
| `large` | Exercises larger rectangular matrices. |
|
||||||
|
| `large_k_small_n` | Uses a large reduction dimension and narrow output. |
|
||||||
|
| `non_square` | Uses different reduction and output widths. |
|
||||||
|
| `scalar_bias` | Broadcasts a scalar bias to the full output. |
|
||||||
|
| `simple` | Basic Gemm with square weights. |
|
||||||
|
| `small` | Tiny Gemm for fast focused validation. |
|
||||||
|
| `small_k_large_n` | Uses a modest reduction dimension and wide output. |
|
||||||
|
| `transA` | Transposes the left-hand matrix. |
|
||||||
|
| `transA_transB` | Transposes both matrix operands. |
|
||||||
|
| `transB` | Transposes the right-hand weight matrix. |
|
||||||
|
| `transB_with_bias` | Combines a transposed weight matrix with bias. |
|
||||||
|
| `with_bias` | Basic matrix product with vector bias. |
|
||||||
|
|
||||||
| Test | Directory | Input | Outputs | Axis | Notes |
|
### Gemv (5)
|
||||||
|-----------------|---------------------------|-------|----------------------|------|-------------------------------------|
|
|
||||||
| Basic | `split/basic` | [2,6] | [2,2], [2,4] | 1 | Two-way split with explicit sizes |
|
|
||||||
| Equal three-way | `split/equal_three_way` | [2,6] | [2,2], [2,2], [2,2] | 1 | Optional split input omitted |
|
|
||||||
|
|
||||||
## Gather
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `constant` | Vector-matrix product with all inputs constant. |
|
||||||
|
| `simple` | Basic single-row vector-matrix product. |
|
||||||
|
| `with_heterogeneous_constant` | Adds a non-uniform constant bias pattern. |
|
||||||
|
| `with_homogeneous_constant` | Adds a constant bias matching the output shape. |
|
||||||
|
| `with_scalar_constant` | Adds a scalar broadcast bias. |
|
||||||
|
|
||||||
| Test | Directory | Input | Indices | Output | Axis | Notes |
|
### MatMul (11)
|
||||||
|----------------------|--------------------------------|-------|---------|----------|------|--------------------------------|
|
|
||||||
| Axis 1 | `gather/axis1` | [3,4] | [2] | [3,2] | 1 | Select two columns |
|
|
||||||
| Axis 0 matrix indices| `gather/axis0_matrix_indices` | [4,3] | [2,2] | [2,2,3] | 0 | Gather rows with 2D indices |
|
|
||||||
|
|
||||||
## Concat
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `basic` | Direct 2D MatMul with constant right-hand matrix. |
|
||||||
|
| `batched_3d` | Batched 3D MatMul with matching batch dimensions. |
|
||||||
|
| `batched_3d_dynamic` | Batched 3D MatMul with both operands at runtime. |
|
||||||
|
| `batched_left_constant` | Batched 3D MatMul with constant left-hand matrix. |
|
||||||
|
| `batched_lhs_broadcast` | Broadcasts a 2D left-hand matrix across a batched right-hand tensor. |
|
||||||
|
| `batched_rhs_broadcast` | Broadcasts a 2D right-hand matrix across a batched left-hand tensor. |
|
||||||
|
| `dynamic` | Direct 2D MatMul with both operands at runtime. |
|
||||||
|
| `huge_1024` | Uses 1024-wide inner and output dimensions. |
|
||||||
|
| `left_constant` | Direct 2D MatMul with constant left-hand matrix. |
|
||||||
|
| `matrix_vector` | Matrix-vector multiplication producing a 1D output. |
|
||||||
|
| `vector_matrix` | Vector-matrix multiplication producing a 1D output. |
|
||||||
|
|
||||||
| Test | Directory | Input(s) | Output | Axis | Notes |
|
### Mul (5)
|
||||||
|--------------|-----------------------|---------------------------|-----------|------|-----------------------------|
|
|
||||||
| Channel axis | `concat/channel_axis` | A:[1,1,2,2], B:[1,2,2,2] | [1,3,2,2] | 1 | Runtime NCHW channel concat |
|
|
||||||
|
|
||||||
## Reshape
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `after_conv` | Conv followed by per-channel scaling. |
|
||||||
|
| `basic` | Elementwise Mul on two inputs with identical shapes. |
|
||||||
|
| `channel_broadcast_1024` | Mul with NCHW per-channel broadcasting over 1024 channels. |
|
||||||
|
| `leading_dimension_broadcast` | Mul with trailing-dimension broadcasting. |
|
||||||
|
| `scalar_constant` | Mul with scalar broadcasting. |
|
||||||
|
|
||||||
| Test | Directory | Input | Output | Notes |
|
### Pool (15)
|
||||||
|-----------|---------------------|-------|--------|----------------------------------------------|
|
|
||||||
| Same rank | `reshape/same_rank` | [2,3] | [3,2] | Runtime tensor with static shape initializer |
|
|
||||||
|
|
||||||
## Add
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `avg_basic` | AveragePool with a 2x2 kernel and unit stride. |
|
||||||
|
| `avg_ceil_mode` | AveragePool with ceil mode enabled. |
|
||||||
|
| `avg_explicit_padding` | Explicitly padded AveragePool excluding pad from the divisor. |
|
||||||
|
| `avg_include_pad` | Explicitly padded AveragePool including pad in the divisor. |
|
||||||
|
| `avg_large_channels` | AveragePool with a large channel count and small spatial extent. |
|
||||||
|
| `avg_non_uniform_stride` | AveragePool with different height and width strides. |
|
||||||
|
| `avg_real_asymmetric_padding` | AveragePool with asymmetric explicit padding. |
|
||||||
|
| `max_after_conv` | Conv followed by MaxPool. |
|
||||||
|
| `max_basic` | MaxPool with a 2x2 kernel and unit stride. |
|
||||||
|
| `max_ceil_mode` | MaxPool with ceil mode enabled. |
|
||||||
|
| `max_global_style_kernel_equals_input` | MaxPool whose kernel covers the full spatial input. |
|
||||||
|
| `max_non_square_kernel` | MaxPool with a non-square kernel. |
|
||||||
|
| `max_real_asymmetric_padding` | MaxPool with asymmetric explicit padding. |
|
||||||
|
| `max_same_upper` | MaxPool with SAME_UPPER padding. |
|
||||||
|
| `max_stride2_multichannel` | Multi-channel MaxPool with stride two. |
|
||||||
|
|
||||||
| Test | Directory | Input(s) | Output | Notes |
|
### ReduceMean (17)
|
||||||
|---------------|---------------------|------------------|--------|---------------------------------------------|
|
|
||||||
| Basic | `add/basic` | A:[4,8], B:[4,8] | [4,8] | Elementwise add, same-shape inputs |
|
|
||||||
| Broadcast row | `add/broadcast_row` | A:[4,8], B:[8] | [4,8] | Row-vector broadcasting via initializer |
|
|
||||||
| After Gemm | `add/after_gemm` | A:[4,64], D:[32] | [4,32] | Gemm + bias, then Add with broadcast vector |
|
|
||||||
|
|
||||||
## Mul
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `4d_spatial` | Reduces height and width of an NCHW tensor while preserving rank. |
|
||||||
|
| `4d_spatial_keepdims_0` | Reduces NCHW height and width while dropping those axes. |
|
||||||
|
| `after_conv` | Conv followed by a spatial ReduceMean. |
|
||||||
|
| `all_axes_keepdims_0` | Reduces all axes to a scalar. |
|
||||||
|
| `all_axes_keepdims_1` | Reduces all axes while preserving rank. |
|
||||||
|
| `basic` | Reduces a feature dimension while preserving rank. |
|
||||||
|
| `channel_axis_nchw` | Reduces the channel axis of an NCHW tensor. |
|
||||||
|
| `keepdims_0` | Reduces a feature dimension and drops that axis. |
|
||||||
|
| `large_dimension_1024` | Reduces a dimension of length 1024. |
|
||||||
|
| `legacy_axes_1_2_keepdims_1` | Opset-18 reduction over multiple positive axes. |
|
||||||
|
| `legacy_axis1_keepdims_0` | Opset-18 reduction over one axis while dropping it. |
|
||||||
|
| `legacy_axis1_keepdims_1` | Opset-18 reduction over one axis while preserving rank. |
|
||||||
|
| `legacy_empty_axes_noop` | Opset-18 empty-axes no-op followed by Relu. |
|
||||||
|
| `legacy_nchw_spatial` | Opset-18 spatial reduction on NCHW input. |
|
||||||
|
| `legacy_negative_axis` | Opset-18 reduction using a negative axis. |
|
||||||
|
| `legacy_reduce_all_keepdims_1` | Opset-18 all-axis reduction with the axes input omitted. |
|
||||||
|
| `negative_axis` | ReduceMean using a negative axis. |
|
||||||
|
|
||||||
| Test | Directory | Input(s) | Output | Notes |
|
### Relu (4)
|
||||||
|-----------------|-----------------------|--------------------------|-----------|-------------------------------------------|
|
|
||||||
| Basic | `mul/basic` | A:[4,8], B:[4,8] | [4,8] | Elementwise multiply, same-shape inputs |
|
|
||||||
| Scalar constant | `mul/scalar_constant` | X:[4,8], S:[1] | [4,8] | Scalar broadcasting via initializer |
|
|
||||||
| After Conv | `mul/after_conv` | X:[1,3,5,5], S:[1,2,1,1] | [1,2,3,3] | Conv 3x3 + bias, then per-channel scaling |
|
|
||||||
|
|
||||||
## Div
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `4d` | Standalone Relu on an NCHW tensor. |
|
||||||
|
| `after_conv` | Conv followed by Relu. |
|
||||||
|
| `after_gemm` | Gemm followed by Relu. |
|
||||||
|
| `basic` | Standalone Relu on a 2D tensor. |
|
||||||
|
|
||||||
| Test | Directory | Input(s) | Output | Notes |
|
### Reshape (4)
|
||||||
|-----------------|-----------------------|------------------|--------|------------------------------------------------------|
|
|
||||||
| Basic | `div/basic` | X:[4,8], D:[4,8] | [4,8] | Elementwise divide by same-shape constant tensor |
|
| Case | Description |
|
||||||
| Scalar constant | `div/scalar_constant` | X:[4,8], S:[1] | [4,8] | Scalar broadcasting via initializer |
|
|---|---|
|
||||||
| After Gemm | `div/after_gemm` | A:[4,64], D:[32] | [4,32] | Gemm + bias, then Div with positive broadcast vector |
|
| `4d_to_2d_flatten` | Flattens a 4D tensor into a 2D view. |
|
||||||
|
| `infer_dim_minus_one` | Uses `-1` to infer one output dimension. |
|
||||||
|
| `same_rank` | Changes shape without changing rank. |
|
||||||
|
| `zero_copies_input_dim` | Uses `0` to copy an input dimension. |
|
||||||
|
|
||||||
|
### Resize (6)
|
||||||
|
|
||||||
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `height_only` | Nearest-neighbor resize of only the height dimension. |
|
||||||
|
| `nearest_2x` | Nearest-neighbor upsampling by a factor of two. |
|
||||||
|
| `nearest_downsample` | Nearest-neighbor downsampling. |
|
||||||
|
| `non_uniform` | Nearest-neighbor resize with different spatial scales. |
|
||||||
|
| `width_only` | Nearest-neighbor resize of only the width dimension. |
|
||||||
|
| `with_sizes` | Resize using explicit output sizes instead of scales. |
|
||||||
|
|
||||||
|
### Sigmoid (3)
|
||||||
|
|
||||||
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `4d` | Standalone Sigmoid on an NCHW tensor. |
|
||||||
|
| `after_gemm` | Gemm followed by Sigmoid. |
|
||||||
|
| `basic` | Standalone Sigmoid on a 2D tensor. |
|
||||||
|
|
||||||
|
### Slice (8)
|
||||||
|
|
||||||
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `2d_basic` | Slices a 2D tensor with explicit axes and unit steps. |
|
||||||
|
| `after_conv` | Conv followed by a spatial crop. |
|
||||||
|
| `default_axes` | Omits axes and steps to use positional defaults. |
|
||||||
|
| `large_channel_1024` | Slices a channel range from a 1024-channel tensor. |
|
||||||
|
| `nchw_spatial_crop` | Crops the spatial axes of an NCHW tensor. |
|
||||||
|
| `negative_axis` | Slices using a negative axis. |
|
||||||
|
| `negative_indices` | Slices using negative indices. |
|
||||||
|
| `step2` | Slices using a positive step greater than one. |
|
||||||
|
|
||||||
|
### Softmax (5)
|
||||||
|
|
||||||
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `3d_last_axis` | Softmax over the last axis of a 3D tensor. |
|
||||||
|
| `basic` | Softmax over the last dimension of a 2D tensor. |
|
||||||
|
| `channel_axis` | Softmax over the channel axis of an NCHW tensor. |
|
||||||
|
| `large_dimension_1024` | Softmax over a last dimension of length 1024. |
|
||||||
|
| `negative_axis` | Softmax using a negative axis. |
|
||||||
|
|
||||||
|
### Split (4)
|
||||||
|
|
||||||
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `basic` | Splits a 2D tensor into two explicit output sizes. |
|
||||||
|
| `equal_three_way` | Splits a 2D tensor evenly into three outputs. |
|
||||||
|
| `negative_axis` | Splits using a negative axis. |
|
||||||
|
| `uneven_channel_axis_4d` | Splits an NCHW channel axis into uneven outputs. |
|
||||||
|
|
||||||
|
### Sub (6)
|
||||||
|
|
||||||
|
| Case | Description |
|
||||||
|
|---|---|
|
||||||
|
| `after_gemm` | Gemm followed by Sub with a broadcast constant vector. |
|
||||||
|
| `basic` | Elementwise Sub on two runtime inputs with identical shapes. |
|
||||||
|
| `broadcast_row` | Sub with a broadcast row-vector right-hand constant. |
|
||||||
|
| `channel_broadcast_1024` | Sub with NCHW per-channel broadcasting over 1024 channels. |
|
||||||
|
| `constant_lhs_broadcast` | Sub with a broadcast constant left-hand operand. |
|
||||||
|
| `leading_dimension_broadcast` | Sub with trailing-dimension broadcasting. |
|
||||||
|
|||||||
Binary file not shown.
@@ -80,7 +80,7 @@ def conv_1x1():
|
|||||||
kernel_shape=[1, 1], strides=[1, 1], pads=[0, 0, 0, 0])
|
kernel_shape=[1, 1], strides=[1, 1], pads=[0, 0, 0, 0])
|
||||||
graph = helper.make_graph([node], "conv_1x1", [X], [Y], initializer=[W])
|
graph = helper.make_graph([node], "conv_1x1", [X], [Y], initializer=[W])
|
||||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||||
save_model(model, "conv/pointwise_1x1", "conv_1x1.onnx")
|
save_model(model, "conv/pointwise_1x1", "conv_pointwise_1x1.onnx")
|
||||||
|
|
||||||
|
|
||||||
def conv_same_padding_3x3():
|
def conv_same_padding_3x3():
|
||||||
@@ -218,6 +218,33 @@ def conv_huge_pointwise_1024_dynamic():
|
|||||||
save_model(model, "conv/huge_pointwise_1024_dynamic", "conv_huge_pointwise_1024_dynamic.onnx")
|
save_model(model, "conv/huge_pointwise_1024_dynamic", "conv_huge_pointwise_1024_dynamic.onnx")
|
||||||
|
|
||||||
|
|
||||||
|
def conv_pointwise_tiled_chain():
|
||||||
|
"""Chained pointwise Convs with a tiled intermediate."""
|
||||||
|
X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [1, 1024, 1, 1])
|
||||||
|
Y = helper.make_tensor_value_info("Y", TensorProto.FLOAT, [1, 256, 1, 1])
|
||||||
|
rng = np.random.default_rng(79)
|
||||||
|
W1 = numpy_helper.from_array(
|
||||||
|
rng.uniform(-1, 1, (1024, 1024, 1, 1)).astype(np.float32), name="W1")
|
||||||
|
B1 = numpy_helper.from_array(
|
||||||
|
rng.uniform(-1, 1, 1024).astype(np.float32), name="B1")
|
||||||
|
W2 = numpy_helper.from_array(
|
||||||
|
rng.uniform(-1, 1, (256, 1024, 1, 1)).astype(np.float32), name="W2")
|
||||||
|
B2 = numpy_helper.from_array(
|
||||||
|
rng.uniform(-1, 1, 256).astype(np.float32), name="B2")
|
||||||
|
nodes = [
|
||||||
|
helper.make_node("Relu", ["X"], ["X_relu"]),
|
||||||
|
helper.make_node("Conv", ["X_relu", "W1", "B1"], ["hidden"],
|
||||||
|
kernel_shape=[1, 1], strides=[1, 1], pads=[0, 0, 0, 0]),
|
||||||
|
helper.make_node("Relu", ["hidden"], ["hidden_relu"]),
|
||||||
|
helper.make_node("Conv", ["hidden_relu", "W2", "B2"], ["Y"],
|
||||||
|
kernel_shape=[1, 1], strides=[1, 1], pads=[0, 0, 0, 0]),
|
||||||
|
]
|
||||||
|
graph = helper.make_graph(
|
||||||
|
nodes, "conv_pointwise_tiled_chain", [X], [Y], initializer=[W1, B1, W2, B2])
|
||||||
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||||
|
save_model(model, "conv/pointwise_tiled_chain", "conv_pointwise_tiled_chain.onnx")
|
||||||
|
|
||||||
|
|
||||||
def conv_large_output_channels_1x1():
|
def conv_large_output_channels_1x1():
|
||||||
"""1x1 Conv with modest inputs and very large output channel count."""
|
"""1x1 Conv with modest inputs and very large output channel count."""
|
||||||
X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [1, 64, 1, 1])
|
X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [1, 64, 1, 1])
|
||||||
@@ -790,7 +817,7 @@ def maxpool_basic():
|
|||||||
node = helper.make_node("MaxPool", ["X"], ["Y"], kernel_shape=[2, 2], strides=[1, 1], pads=[0, 0, 0, 0])
|
node = helper.make_node("MaxPool", ["X"], ["Y"], kernel_shape=[2, 2], strides=[1, 1], pads=[0, 0, 0, 0])
|
||||||
graph = helper.make_graph([node], "maxpool_basic", [X], [Y])
|
graph = helper.make_graph([node], "maxpool_basic", [X], [Y])
|
||||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||||
save_model(model, "pool/max_basic", "maxpool_basic.onnx")
|
save_model(model, "pool/max_basic", "pool_max_basic.onnx")
|
||||||
|
|
||||||
|
|
||||||
def maxpool_stride2_multichannel():
|
def maxpool_stride2_multichannel():
|
||||||
@@ -800,7 +827,7 @@ def maxpool_stride2_multichannel():
|
|||||||
node = helper.make_node("MaxPool", ["X"], ["Y"], kernel_shape=[2, 2], strides=[2, 2], pads=[0, 0, 0, 0])
|
node = helper.make_node("MaxPool", ["X"], ["Y"], kernel_shape=[2, 2], strides=[2, 2], pads=[0, 0, 0, 0])
|
||||||
graph = helper.make_graph([node], "maxpool_stride2_multichannel", [X], [Y])
|
graph = helper.make_graph([node], "maxpool_stride2_multichannel", [X], [Y])
|
||||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||||
save_model(model, "pool/max_stride2_multichannel", "maxpool_stride2_multichannel.onnx")
|
save_model(model, "pool/max_stride2_multichannel", "pool_max_stride2_multichannel.onnx")
|
||||||
|
|
||||||
|
|
||||||
def maxpool_same_upper():
|
def maxpool_same_upper():
|
||||||
@@ -810,7 +837,7 @@ def maxpool_same_upper():
|
|||||||
node = helper.make_node("MaxPool", ["X"], ["Y"], kernel_shape=[3, 3], strides=[2, 2], auto_pad="SAME_UPPER")
|
node = helper.make_node("MaxPool", ["X"], ["Y"], kernel_shape=[3, 3], strides=[2, 2], auto_pad="SAME_UPPER")
|
||||||
graph = helper.make_graph([node], "maxpool_same_upper", [X], [Y])
|
graph = helper.make_graph([node], "maxpool_same_upper", [X], [Y])
|
||||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||||
save_model(model, "pool/max_same_upper", "maxpool_same_upper.onnx")
|
save_model(model, "pool/max_same_upper", "pool_max_same_upper.onnx")
|
||||||
|
|
||||||
|
|
||||||
def avgpool_basic():
|
def avgpool_basic():
|
||||||
@@ -820,7 +847,7 @@ def avgpool_basic():
|
|||||||
node = helper.make_node("AveragePool", ["X"], ["Y"], kernel_shape=[2, 2], strides=[1, 1], pads=[0, 0, 0, 0])
|
node = helper.make_node("AveragePool", ["X"], ["Y"], kernel_shape=[2, 2], strides=[1, 1], pads=[0, 0, 0, 0])
|
||||||
graph = helper.make_graph([node], "avgpool_basic", [X], [Y])
|
graph = helper.make_graph([node], "avgpool_basic", [X], [Y])
|
||||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||||
save_model(model, "pool/avg_basic", "avgpool_basic.onnx")
|
save_model(model, "pool/avg_basic", "pool_avg_basic.onnx")
|
||||||
|
|
||||||
|
|
||||||
def avgpool_explicit_padding():
|
def avgpool_explicit_padding():
|
||||||
@@ -831,7 +858,7 @@ def avgpool_explicit_padding():
|
|||||||
kernel_shape=[3, 3], strides=[2, 2], pads=[1, 1, 1, 1], count_include_pad=0)
|
kernel_shape=[3, 3], strides=[2, 2], pads=[1, 1, 1, 1], count_include_pad=0)
|
||||||
graph = helper.make_graph([node], "avgpool_explicit_padding", [X], [Y])
|
graph = helper.make_graph([node], "avgpool_explicit_padding", [X], [Y])
|
||||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||||
save_model(model, "pool/avg_explicit_padding", "avgpool_explicit_padding.onnx")
|
save_model(model, "pool/avg_explicit_padding", "pool_avg_explicit_padding.onnx")
|
||||||
|
|
||||||
|
|
||||||
def avgpool_include_pad():
|
def avgpool_include_pad():
|
||||||
@@ -842,7 +869,7 @@ def avgpool_include_pad():
|
|||||||
kernel_shape=[3, 3], strides=[2, 2], pads=[1, 1, 1, 1], count_include_pad=1)
|
kernel_shape=[3, 3], strides=[2, 2], pads=[1, 1, 1, 1], count_include_pad=1)
|
||||||
graph = helper.make_graph([node], "avgpool_include_pad", [X], [Y])
|
graph = helper.make_graph([node], "avgpool_include_pad", [X], [Y])
|
||||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||||
save_model(model, "pool/avg_include_pad", "avgpool_include_pad.onnx")
|
save_model(model, "pool/avg_include_pad", "pool_avg_include_pad.onnx")
|
||||||
|
|
||||||
|
|
||||||
def maxpool_after_conv():
|
def maxpool_after_conv():
|
||||||
@@ -855,7 +882,7 @@ def maxpool_after_conv():
|
|||||||
pool = helper.make_node("MaxPool", ["C"], ["Y"], kernel_shape=[2, 2], strides=[2, 2], pads=[0, 0, 0, 0])
|
pool = helper.make_node("MaxPool", ["C"], ["Y"], kernel_shape=[2, 2], strides=[2, 2], pads=[0, 0, 0, 0])
|
||||||
graph = helper.make_graph([conv, pool], "maxpool_after_conv", [X], [Y], initializer=[W])
|
graph = helper.make_graph([conv, pool], "maxpool_after_conv", [X], [Y], initializer=[W])
|
||||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||||
save_model(model, "pool/max_after_conv", "maxpool_after_conv.onnx")
|
save_model(model, "pool/max_after_conv", "pool_max_after_conv.onnx")
|
||||||
|
|
||||||
|
|
||||||
def maxpool_ceil_mode():
|
def maxpool_ceil_mode():
|
||||||
@@ -866,7 +893,7 @@ def maxpool_ceil_mode():
|
|||||||
kernel_shape=[2, 2], strides=[2, 2], pads=[0, 0, 0, 0], ceil_mode=1)
|
kernel_shape=[2, 2], strides=[2, 2], pads=[0, 0, 0, 0], ceil_mode=1)
|
||||||
graph = helper.make_graph([node], "maxpool_ceil_mode", [X], [Y])
|
graph = helper.make_graph([node], "maxpool_ceil_mode", [X], [Y])
|
||||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||||
save_model(model, "pool/max_ceil_mode", "maxpool_ceil_mode.onnx")
|
save_model(model, "pool/max_ceil_mode", "pool_max_ceil_mode.onnx")
|
||||||
|
|
||||||
|
|
||||||
def avgpool_ceil_mode():
|
def avgpool_ceil_mode():
|
||||||
@@ -877,7 +904,7 @@ def avgpool_ceil_mode():
|
|||||||
kernel_shape=[2, 2], strides=[2, 2], pads=[0, 0, 0, 0], ceil_mode=1)
|
kernel_shape=[2, 2], strides=[2, 2], pads=[0, 0, 0, 0], ceil_mode=1)
|
||||||
graph = helper.make_graph([node], "avgpool_ceil_mode", [X], [Y])
|
graph = helper.make_graph([node], "avgpool_ceil_mode", [X], [Y])
|
||||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||||
save_model(model, "pool/avg_ceil_mode", "avgpool_ceil_mode.onnx")
|
save_model(model, "pool/avg_ceil_mode", "pool_avg_ceil_mode.onnx")
|
||||||
|
|
||||||
|
|
||||||
def maxpool_real_asymmetric_padding():
|
def maxpool_real_asymmetric_padding():
|
||||||
@@ -888,7 +915,7 @@ def maxpool_real_asymmetric_padding():
|
|||||||
kernel_shape=[3, 3], strides=[1, 2], pads=[0, 1, 2, 1])
|
kernel_shape=[3, 3], strides=[1, 2], pads=[0, 1, 2, 1])
|
||||||
graph = helper.make_graph([node], "maxpool_real_asymmetric_padding", [X], [Y])
|
graph = helper.make_graph([node], "maxpool_real_asymmetric_padding", [X], [Y])
|
||||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||||
save_model(model, "pool/max_real_asymmetric_padding", "maxpool_real_asymmetric_padding.onnx")
|
save_model(model, "pool/max_real_asymmetric_padding", "pool_max_real_asymmetric_padding.onnx")
|
||||||
|
|
||||||
|
|
||||||
def avgpool_real_asymmetric_padding():
|
def avgpool_real_asymmetric_padding():
|
||||||
@@ -899,7 +926,7 @@ def avgpool_real_asymmetric_padding():
|
|||||||
kernel_shape=[3, 3], strides=[1, 2], pads=[0, 1, 2, 1], count_include_pad=0)
|
kernel_shape=[3, 3], strides=[1, 2], pads=[0, 1, 2, 1], count_include_pad=0)
|
||||||
graph = helper.make_graph([node], "avgpool_real_asymmetric_padding", [X], [Y])
|
graph = helper.make_graph([node], "avgpool_real_asymmetric_padding", [X], [Y])
|
||||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||||
save_model(model, "pool/avg_real_asymmetric_padding", "avgpool_real_asymmetric_padding.onnx")
|
save_model(model, "pool/avg_real_asymmetric_padding", "pool_avg_real_asymmetric_padding.onnx")
|
||||||
|
|
||||||
|
|
||||||
def maxpool_non_square_kernel():
|
def maxpool_non_square_kernel():
|
||||||
@@ -910,7 +937,7 @@ def maxpool_non_square_kernel():
|
|||||||
kernel_shape=[2, 3], strides=[1, 2], pads=[0, 0, 0, 0])
|
kernel_shape=[2, 3], strides=[1, 2], pads=[0, 0, 0, 0])
|
||||||
graph = helper.make_graph([node], "maxpool_non_square_kernel", [X], [Y])
|
graph = helper.make_graph([node], "maxpool_non_square_kernel", [X], [Y])
|
||||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||||
save_model(model, "pool/max_non_square_kernel", "maxpool_non_square_kernel.onnx")
|
save_model(model, "pool/max_non_square_kernel", "pool_max_non_square_kernel.onnx")
|
||||||
|
|
||||||
|
|
||||||
def avgpool_non_uniform_stride():
|
def avgpool_non_uniform_stride():
|
||||||
@@ -921,7 +948,7 @@ def avgpool_non_uniform_stride():
|
|||||||
kernel_shape=[2, 3], strides=[1, 2], pads=[0, 0, 0, 0])
|
kernel_shape=[2, 3], strides=[1, 2], pads=[0, 0, 0, 0])
|
||||||
graph = helper.make_graph([node], "avgpool_non_uniform_stride", [X], [Y])
|
graph = helper.make_graph([node], "avgpool_non_uniform_stride", [X], [Y])
|
||||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||||
save_model(model, "pool/avg_non_uniform_stride", "avgpool_non_uniform_stride.onnx")
|
save_model(model, "pool/avg_non_uniform_stride", "pool_avg_non_uniform_stride.onnx")
|
||||||
|
|
||||||
|
|
||||||
def maxpool_global_style_kernel_equals_input():
|
def maxpool_global_style_kernel_equals_input():
|
||||||
@@ -931,7 +958,7 @@ def maxpool_global_style_kernel_equals_input():
|
|||||||
node = helper.make_node("MaxPool", ["X"], ["Y"], kernel_shape=[4, 4], strides=[1, 1], pads=[0, 0, 0, 0])
|
node = helper.make_node("MaxPool", ["X"], ["Y"], kernel_shape=[4, 4], strides=[1, 1], pads=[0, 0, 0, 0])
|
||||||
graph = helper.make_graph([node], "maxpool_global_style_kernel_equals_input", [X], [Y])
|
graph = helper.make_graph([node], "maxpool_global_style_kernel_equals_input", [X], [Y])
|
||||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||||
save_model(model, "pool/max_global_style_kernel_equals_input", "maxpool_global_style_kernel_equals_input.onnx")
|
save_model(model, "pool/max_global_style_kernel_equals_input", "pool_max_global_style_kernel_equals_input.onnx")
|
||||||
|
|
||||||
|
|
||||||
def avgpool_large_channels():
|
def avgpool_large_channels():
|
||||||
@@ -941,7 +968,7 @@ def avgpool_large_channels():
|
|||||||
node = helper.make_node("AveragePool", ["X"], ["Y"], kernel_shape=[2, 2], strides=[1, 1], pads=[0, 0, 0, 0])
|
node = helper.make_node("AveragePool", ["X"], ["Y"], kernel_shape=[2, 2], strides=[1, 1], pads=[0, 0, 0, 0])
|
||||||
graph = helper.make_graph([node], "avgpool_large_channels", [X], [Y])
|
graph = helper.make_graph([node], "avgpool_large_channels", [X], [Y])
|
||||||
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])
|
||||||
save_model(model, "pool/avg_large_channels", "avgpool_large_channels.onnx")
|
save_model(model, "pool/avg_large_channels", "pool_avg_large_channels.onnx")
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -2005,6 +2032,7 @@ if __name__ == "__main__":
|
|||||||
conv_dynamic()
|
conv_dynamic()
|
||||||
conv_huge_pointwise_1024()
|
conv_huge_pointwise_1024()
|
||||||
conv_huge_pointwise_1024_dynamic()
|
conv_huge_pointwise_1024_dynamic()
|
||||||
|
conv_pointwise_tiled_chain()
|
||||||
conv_large_output_channels_1x1()
|
conv_large_output_channels_1x1()
|
||||||
conv_large_input_channels_1x1()
|
conv_large_input_channels_1x1()
|
||||||
conv_depthwise_1024_channels()
|
conv_depthwise_1024_channels()
|
||||||
|
|||||||
+12
-16
@@ -174,22 +174,18 @@ def main():
|
|||||||
# Summary
|
# Summary
|
||||||
n_passed = sum(1 for passed in results.values() if passed)
|
n_passed = sum(1 for passed in results.values() if passed)
|
||||||
n_total = len(results)
|
n_total = len(results)
|
||||||
failing = [rel for rel, passed in results.items() if not passed]
|
status_width = len("Result")
|
||||||
if a.verbose or failing:
|
path_width = max(len("Operation"), *(len(rel) for rel in results))
|
||||||
status_width = len("Result")
|
separator = f"+-{'-' * path_width}-+-{'-' * status_width}-+"
|
||||||
path_width = max(len("Operation"), *(len(rel) for rel in results))
|
print(separator)
|
||||||
separator = f"+-{'-' * path_width}-+-{'-' * status_width}-+"
|
print(f"| {'Operation'.ljust(path_width)} | {'Result'.ljust(status_width)} |")
|
||||||
print(separator)
|
print(separator)
|
||||||
print(f"| {'Operation'.ljust(path_width)} | {'Result'.ljust(status_width)} |")
|
for rel, passed in results.items():
|
||||||
print(separator)
|
plain_status = "PASS" if passed else "FAIL"
|
||||||
for rel, passed in results.items():
|
status = Fore.GREEN + plain_status.ljust(status_width) + Style.RESET_ALL if passed else \
|
||||||
if not a.verbose and passed:
|
Fore.RED + plain_status.ljust(status_width) + Style.RESET_ALL
|
||||||
continue
|
print(f"| {rel.ljust(path_width)} | {status} |")
|
||||||
plain_status = "PASS" if passed else "FAIL"
|
print(separator)
|
||||||
status = Fore.GREEN + plain_status.ljust(status_width) + Style.RESET_ALL if passed else \
|
|
||||||
Fore.RED + plain_status.ljust(status_width) + Style.RESET_ALL
|
|
||||||
print(f"| {rel.ljust(path_width)} | {status} |")
|
|
||||||
print(separator)
|
|
||||||
print("\n" + Style.BRIGHT + Fore.CYAN + "Summary" + Style.RESET_ALL)
|
print("\n" + Style.BRIGHT + Fore.CYAN + "Summary" + Style.RESET_ALL)
|
||||||
print(Style.BRIGHT + f"Passed: {n_passed}" + Style.RESET_ALL)
|
print(Style.BRIGHT + f"Passed: {n_passed}" + Style.RESET_ALL)
|
||||||
print(Style.BRIGHT + f"Failed: {n_total - n_passed}" + Style.RESET_ALL)
|
print(Style.BRIGHT + f"Failed: {n_total - n_passed}" + Style.RESET_ALL)
|
||||||
|
|||||||
Reference in New Issue
Block a user