This commit is contained in:
@@ -117,7 +117,6 @@ add_pim_library(OMPIMAccel
|
||||
SpatialOps
|
||||
PimOps
|
||||
OMONNXToSpatial
|
||||
OMSpatialToGraphviz
|
||||
OMSpatialToPim
|
||||
OMPimCommon
|
||||
OMPimBufferization
|
||||
|
||||
@@ -74,6 +74,21 @@ walkPimCoreBlock(mlir::Block& block,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto ifOp = mlir::dyn_cast<mlir::scf::IfOp>(op)) {
|
||||
auto condition = resolveIndexValue(ifOp.getCondition(), knowledge);
|
||||
if (failed(condition)) {
|
||||
ifOp.emitOpError("requires statically evaluable scf.if condition for PIM codegen");
|
||||
hasFailure = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
mlir::Region& selectedRegion = *condition != 0 ? ifOp.getThenRegion() : ifOp.getElseRegion();
|
||||
if (!selectedRegion.empty())
|
||||
if (failed(walkPimCoreBlock(selectedRegion.front(), knowledge, callback)))
|
||||
hasFailure = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (failed(callback(op, knowledge)))
|
||||
hasFailure = true;
|
||||
}
|
||||
@@ -128,6 +143,22 @@ mlir::LogicalResult walkPimCoreBlockStructurally(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto ifOp = mlir::dyn_cast<mlir::scf::IfOp>(op)) {
|
||||
if (failed(resolveIndexValue(ifOp.getCondition(), knowledge))) {
|
||||
ifOp.emitOpError("requires statically evaluable scf.if condition for PIM verification");
|
||||
hasFailure = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ifOp.getThenRegion().empty())
|
||||
if (failed(walkPimCoreBlockStructurally(ifOp.getThenRegion().front(), knowledge, callback)))
|
||||
hasFailure = true;
|
||||
if (!ifOp.getElseRegion().empty())
|
||||
if (failed(walkPimCoreBlockStructurally(ifOp.getElseRegion().front(), knowledge, callback)))
|
||||
hasFailure = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (failed(callback(op, knowledge)))
|
||||
hasFailure = true;
|
||||
}
|
||||
|
||||
@@ -414,31 +414,35 @@ size_t PimAcceleratorMemory::getValueAddress(mlir::Value value,
|
||||
const StaticValueKnowledge& knowledge,
|
||||
std::optional<unsigned> lane) const {
|
||||
value = resolveCachedAlias(value, knowledge);
|
||||
auto compiledIt = compiledAddressExprs.find(value);
|
||||
if (compiledIt == compiledAddressExprs.end()) {
|
||||
auto compiledExpr = compileContiguousAddressExpr(value);
|
||||
if (failed(compiledExpr)) {
|
||||
errs() << "Failed to compile contiguous address for value: ";
|
||||
|
||||
FailureOr<ResolvedContiguousAddress> resolvedAddress = resolveContiguousAddress(value, knowledge);
|
||||
if (failed(resolvedAddress)) {
|
||||
auto compiledIt = compiledAddressExprs.find(value);
|
||||
if (compiledIt == compiledAddressExprs.end()) {
|
||||
auto compiledExpr = compileContiguousAddressExpr(value);
|
||||
if (failed(compiledExpr)) {
|
||||
errs() << "Failed to compile contiguous address for value: ";
|
||||
value.print(errs());
|
||||
errs() << " : " << value.getType();
|
||||
errs() << "\n";
|
||||
llvm_unreachable("Failed to compile contiguous address");
|
||||
}
|
||||
compiledIt = compiledAddressExprs.try_emplace(value, *compiledExpr).first;
|
||||
}
|
||||
|
||||
resolvedAddress = compiledIt->second.evaluate(knowledge, lane);
|
||||
if (failed(resolvedAddress)) {
|
||||
errs() << "Failed to evaluate contiguous address for value: ";
|
||||
value.print(errs());
|
||||
errs() << " : " << value.getType();
|
||||
errs() << "\n";
|
||||
llvm_unreachable("Failed to compile contiguous address");
|
||||
if (auto* definingOp = value.getDefiningOp()) {
|
||||
errs() << "Defining op:\n";
|
||||
definingOp->print(errs());
|
||||
errs() << "\n";
|
||||
}
|
||||
llvm_unreachable("Failed to resolve contiguous address");
|
||||
}
|
||||
compiledIt = compiledAddressExprs.try_emplace(value, *compiledExpr).first;
|
||||
}
|
||||
|
||||
auto resolvedAddress = compiledIt->second.evaluate(knowledge, lane);
|
||||
if (failed(resolvedAddress)) {
|
||||
errs() << "Failed to evaluate contiguous address for value: ";
|
||||
value.print(errs());
|
||||
errs() << " : " << value.getType();
|
||||
errs() << "\n";
|
||||
if (auto* definingOp = value.getDefiningOp()) {
|
||||
errs() << "Defining op:\n";
|
||||
definingOp->print(errs());
|
||||
errs() << "\n";
|
||||
}
|
||||
llvm_unreachable("Failed to resolve contiguous address");
|
||||
}
|
||||
|
||||
MemoryValueKey key = getMemoryValueKey(resolvedAddress->base, lane);
|
||||
@@ -1114,7 +1118,8 @@ enum class CompiledCoreOpKind : uint8_t {
|
||||
struct CompiledCoreNode {
|
||||
enum class Kind : uint8_t {
|
||||
Op,
|
||||
Loop
|
||||
Loop,
|
||||
If
|
||||
};
|
||||
|
||||
Kind kind = Kind::Op;
|
||||
@@ -1123,7 +1128,10 @@ struct CompiledCoreNode {
|
||||
CompiledIndexExpr lowerBound;
|
||||
CompiledIndexExpr upperBound;
|
||||
CompiledIndexExpr step;
|
||||
CompiledIndexExpr condition;
|
||||
std::unique_ptr<llvm::SmallVector<CompiledCoreNode, 8>> loopBody;
|
||||
std::unique_ptr<llvm::SmallVector<CompiledCoreNode, 8>> thenBody;
|
||||
std::unique_ptr<llvm::SmallVector<CompiledCoreNode, 8>> elseBody;
|
||||
};
|
||||
|
||||
static FailureOr<CompiledCoreOpKind> classifyCompiledCoreOpKind(Operation& op) {
|
||||
@@ -1201,6 +1209,28 @@ compileCoreEmissionPlan(Block& block, Operation* weightOwner, llvm::SmallVectorI
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto ifOp = dyn_cast<mlir::scf::IfOp>(op)) {
|
||||
auto condition = compileIndexExpr(ifOp.getCondition());
|
||||
if (failed(condition)) {
|
||||
ifOp.emitOpError("requires statically evaluable scf.if condition for PIM codegen");
|
||||
return failure();
|
||||
}
|
||||
|
||||
CompiledCoreNode ifNode;
|
||||
ifNode.kind = CompiledCoreNode::Kind::If;
|
||||
ifNode.op = ifOp.getOperation();
|
||||
ifNode.condition = *condition;
|
||||
ifNode.thenBody = std::make_unique<llvm::SmallVector<CompiledCoreNode, 8>>();
|
||||
if (failed(compileCoreEmissionPlan(ifOp.getThenRegion().front(), weightOwner, *ifNode.thenBody)))
|
||||
return failure();
|
||||
ifNode.elseBody = std::make_unique<llvm::SmallVector<CompiledCoreNode, 8>>();
|
||||
if (!ifOp.getElseRegion().empty())
|
||||
if (failed(compileCoreEmissionPlan(ifOp.getElseRegion().front(), weightOwner, *ifNode.elseBody)))
|
||||
return failure();
|
||||
plan.push_back(std::move(ifNode));
|
||||
continue;
|
||||
}
|
||||
|
||||
auto opKind = classifyCompiledCoreOpKind(op);
|
||||
if (failed(opKind)) {
|
||||
InFlightDiagnostic diag = op.emitError() << "unsupported codegen for op '" << op.getName().getStringRef() << "'";
|
||||
@@ -1263,6 +1293,26 @@ static LogicalResult executeCompiledCorePlan(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node.kind == CompiledCoreNode::Kind::If) {
|
||||
auto condition = node.condition.evaluate(knowledge);
|
||||
auto ifOp = cast<mlir::scf::IfOp>(node.op);
|
||||
if (failed(condition)) {
|
||||
ifOp.emitOpError("requires statically evaluable scf.if condition for PIM codegen");
|
||||
return failure();
|
||||
}
|
||||
|
||||
const auto& selectedBody = *condition != 0 ? node.thenBody : node.elseBody;
|
||||
if (selectedBody && failed(executeCompiledCorePlan(*selectedBody,
|
||||
coreCodeGen,
|
||||
knowledge,
|
||||
resolveWeightSlot,
|
||||
processedOperations,
|
||||
batchLane,
|
||||
batchLaneCount)))
|
||||
return failure();
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (node.opKind) {
|
||||
case CompiledCoreOpKind::Load:
|
||||
coreCodeGen.codeGenLoadOp(cast<pim::PimMemCopyHostToDevOp>(node.op), knowledge);
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
add_subdirectory(ONNXToSpatial)
|
||||
add_subdirectory(SpatialToGraphviz)
|
||||
add_subdirectory(SpatialToPim)
|
||||
add_subdirectory(SpatialToPim)
|
||||
|
||||
@@ -31,8 +31,10 @@ add_pim_library(OMONNXToSpatial
|
||||
SpatialLayoutPlanningPass.cpp
|
||||
LowerSpatialPlansPass.cpp
|
||||
Common/AttributeUtils.cpp
|
||||
Common/BiasAddUtils.cpp
|
||||
Common/ComputeRegionBuilder.cpp
|
||||
Common/MatrixProductLowering.cpp
|
||||
Common/RowStripLayoutUtils.cpp
|
||||
Common/ShapeTilingUtils.cpp
|
||||
Common/WeightMaterialization.cpp
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
#include "mlir/IR/BuiltinAttributes.h"
|
||||
#include "mlir/IR/BuiltinTypes.h"
|
||||
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
|
||||
#include "src/Accelerators/PIM/Common/IR/ConstantUtils.hpp"
|
||||
#include "src/Accelerators/PIM/Common/IR/ShapeUtils.hpp"
|
||||
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/CompileTime.hpp"
|
||||
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/BiasAddUtils.hpp"
|
||||
|
||||
using namespace mlir;
|
||||
|
||||
namespace onnx_mlir {
|
||||
|
||||
LogicalResult isSupportedBiasAddShape(RankedTensorType biasType, RankedTensorType resultType) {
|
||||
if (!biasType || !resultType || !biasType.hasStaticShape() || !resultType.hasStaticShape())
|
||||
return failure();
|
||||
if (resultType.getRank() != 4)
|
||||
return failure();
|
||||
if (biasType.getElementType() != resultType.getElementType())
|
||||
return failure();
|
||||
|
||||
const int64_t channels = resultType.getDimSize(1);
|
||||
ArrayRef<int64_t> shape = biasType.getShape();
|
||||
if (shape.empty())
|
||||
return success();
|
||||
if (shape.size() == 1)
|
||||
return success(shape[0] == channels);
|
||||
if (shape.size() == 2)
|
||||
return success(shape[0] == 1 && shape[1] == channels);
|
||||
if (shape.size() == 4)
|
||||
return success(shape[0] == 1 && shape[1] == channels && shape[2] == 1 && shape[3] == 1);
|
||||
return failure();
|
||||
}
|
||||
|
||||
FailureOr<SmallVector<Attribute>> getBiasChannelValues(DenseElementsAttr denseAttr, RankedTensorType resultType) {
|
||||
auto biasType = dyn_cast<RankedTensorType>(denseAttr.getType());
|
||||
if (!biasType || failed(isSupportedBiasAddShape(biasType, resultType)))
|
||||
return failure();
|
||||
|
||||
const int64_t channels = resultType.getDimSize(1);
|
||||
if (denseAttr.isSplat()) {
|
||||
return SmallVector<Attribute>(channels, denseAttr.getSplatValue<Attribute>());
|
||||
}
|
||||
|
||||
SmallVector<Attribute> flattened(denseAttr.getValues<Attribute>());
|
||||
if (biasType.getRank() == 1)
|
||||
return flattened;
|
||||
if (biasType.getRank() == 2)
|
||||
return flattened;
|
||||
|
||||
SmallVector<Attribute> channelValues;
|
||||
channelValues.reserve(channels);
|
||||
const int64_t channelStride = biasType.getDimSize(2) * biasType.getDimSize(3);
|
||||
for (int64_t channel = 0; channel < channels; ++channel)
|
||||
channelValues.push_back(flattened[channel * channelStride]);
|
||||
return channelValues;
|
||||
}
|
||||
|
||||
bool isSupportedBiasAddValue(Value bias, RankedTensorType resultType, DenseElementsAttr* denseAttr) {
|
||||
auto attr = getHostConstDenseElementsAttr(bias);
|
||||
if (!attr)
|
||||
return false;
|
||||
auto biasType = dyn_cast<RankedTensorType>(attr.getType());
|
||||
if (!biasType || failed(isSupportedBiasAddShape(biasType, resultType)))
|
||||
return false;
|
||||
if (failed(getBiasChannelValues(attr, resultType)))
|
||||
return false;
|
||||
if (denseAttr)
|
||||
*denseAttr = attr;
|
||||
return true;
|
||||
}
|
||||
|
||||
FailureOr<BiasAddPlanCandidate> classifyBiasAddPlanCandidate(Value lhs, Value rhs, RankedTensorType resultType) {
|
||||
auto lhsType = dyn_cast<RankedTensorType>(lhs.getType());
|
||||
auto rhsType = dyn_cast<RankedTensorType>(rhs.getType());
|
||||
if (!lhsType || !rhsType)
|
||||
return failure();
|
||||
if (lhsType == resultType && isSupportedBiasAddValue(rhs, resultType))
|
||||
return BiasAddPlanCandidate {lhs, rhs};
|
||||
if (rhsType == resultType && isSupportedBiasAddValue(lhs, resultType))
|
||||
return BiasAddPlanCandidate {rhs, lhs};
|
||||
return failure();
|
||||
}
|
||||
|
||||
FailureOr<Value>
|
||||
materializeDenseBiasAddTensor(Value bias, RankedTensorType resultType, RewriterBase& rewriter, Location loc) {
|
||||
DenseElementsAttr denseAttr;
|
||||
if (!isSupportedBiasAddValue(bias, resultType, &denseAttr))
|
||||
return failure();
|
||||
|
||||
FailureOr<SmallVector<Attribute>> channelValues = getBiasChannelValues(denseAttr, resultType);
|
||||
if (failed(channelValues))
|
||||
return failure();
|
||||
|
||||
SmallVector<Attribute> resultValues;
|
||||
resultValues.reserve(resultType.getNumElements());
|
||||
const int64_t batches = resultType.getDimSize(0);
|
||||
const int64_t channels = resultType.getDimSize(1);
|
||||
const int64_t height = resultType.getDimSize(2);
|
||||
const int64_t width = resultType.getDimSize(3);
|
||||
for (int64_t n = 0; n < batches; ++n)
|
||||
for (int64_t c = 0; c < channels; ++c)
|
||||
for (int64_t h = 0; h < height; ++h)
|
||||
for (int64_t w = 0; w < width; ++w)
|
||||
resultValues.push_back((*channelValues)[c]);
|
||||
|
||||
auto resultAttr = DenseElementsAttr::get(resultType, resultValues);
|
||||
return getOrCreateConstant(rewriter, rewriter.getInsertionBlock()->getParentOp(), resultAttr, resultType);
|
||||
}
|
||||
|
||||
} // namespace onnx_mlir
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "mlir/IR/BuiltinAttributes.h"
|
||||
#include "mlir/IR/BuiltinTypes.h"
|
||||
#include "mlir/IR/PatternMatch.h"
|
||||
#include "mlir/IR/Value.h"
|
||||
#include "mlir/Support/LogicalResult.h"
|
||||
|
||||
namespace onnx_mlir {
|
||||
|
||||
struct BiasAddPlanCandidate {
|
||||
mlir::Value data;
|
||||
mlir::Value bias;
|
||||
};
|
||||
|
||||
mlir::LogicalResult isSupportedBiasAddShape(mlir::RankedTensorType biasType, mlir::RankedTensorType resultType);
|
||||
bool isSupportedBiasAddValue(mlir::Value bias,
|
||||
mlir::RankedTensorType resultType,
|
||||
mlir::DenseElementsAttr* denseAttr = nullptr);
|
||||
mlir::FailureOr<llvm::SmallVector<mlir::Attribute>>
|
||||
getBiasChannelValues(mlir::DenseElementsAttr denseAttr, mlir::RankedTensorType resultType);
|
||||
mlir::FailureOr<BiasAddPlanCandidate> classifyBiasAddPlanCandidate(mlir::Value lhs,
|
||||
mlir::Value rhs,
|
||||
mlir::RankedTensorType resultType);
|
||||
mlir::FailureOr<mlir::Value> materializeDenseBiasAddTensor(mlir::Value bias,
|
||||
mlir::RankedTensorType resultType,
|
||||
mlir::RewriterBase& rewriter,
|
||||
mlir::Location loc);
|
||||
|
||||
} // namespace onnx_mlir
|
||||
@@ -0,0 +1,239 @@
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
|
||||
#include "src/Accelerators/PIM/Common/IR/AffineUtils.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/Common.hpp"
|
||||
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/RowStripLayoutUtils.hpp"
|
||||
#include "src/Accelerators/PIM/Dialect/Spatial/SpatialOps.hpp"
|
||||
#include "src/Dialect/ONNX/ONNXOps.hpp"
|
||||
|
||||
using namespace mlir;
|
||||
|
||||
namespace onnx_mlir {
|
||||
|
||||
RankedTensorType getRowStripFragmentType(RankedTensorType logicalType) {
|
||||
return RankedTensorType::get({logicalType.getDimSize(0), logicalType.getDimSize(1), 1, logicalType.getDimSize(3)},
|
||||
logicalType.getElementType(),
|
||||
logicalType.getEncoding());
|
||||
}
|
||||
|
||||
RankedTensorType getRowStripStorageType(RankedTensorType logicalType) {
|
||||
return RankedTensorType::get({logicalType.getDimSize(2), logicalType.getDimSize(1), 1, logicalType.getDimSize(3)},
|
||||
logicalType.getElementType(),
|
||||
logicalType.getEncoding());
|
||||
}
|
||||
|
||||
std::pair<SmallVector<int64_t>, SmallVector<int64_t>> buildRowStripMetadata(RankedTensorType type) {
|
||||
SmallVector<int64_t> offsets;
|
||||
SmallVector<int64_t> sizes;
|
||||
const int64_t channels = type.getDimSize(1);
|
||||
const int64_t height = type.getDimSize(2);
|
||||
const int64_t width = type.getDimSize(3);
|
||||
offsets.reserve(height * 4);
|
||||
sizes.reserve(height * 4);
|
||||
for (int64_t row = 0; row < height; ++row) {
|
||||
offsets.append({0, 0, row, 0});
|
||||
sizes.append({1, channels, 1, width});
|
||||
}
|
||||
return {offsets, sizes};
|
||||
}
|
||||
|
||||
SmallVector<OpFoldResult> buildRowStripFragmentOffsets(PatternRewriter& rewriter, OpFoldResult row) {
|
||||
return {row, rewriter.getIndexAttr(0), rewriter.getIndexAttr(0), rewriter.getIndexAttr(0)};
|
||||
}
|
||||
|
||||
SmallVector<OpFoldResult> buildRowStripFragmentSizes(PatternRewriter& rewriter, RankedTensorType logicalType) {
|
||||
return {rewriter.getIndexAttr(1),
|
||||
rewriter.getIndexAttr(logicalType.getDimSize(1)),
|
||||
rewriter.getIndexAttr(1),
|
||||
rewriter.getIndexAttr(logicalType.getDimSize(3))};
|
||||
}
|
||||
|
||||
Value extractRowStripFragment(Value storage,
|
||||
RankedTensorType logicalType,
|
||||
OpFoldResult row,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
return tensor::ExtractSliceOp::create(rewriter,
|
||||
loc,
|
||||
getRowStripFragmentType(logicalType),
|
||||
storage,
|
||||
buildRowStripFragmentOffsets(rewriter, row),
|
||||
buildRowStripFragmentSizes(rewriter, logicalType),
|
||||
getUnitStrides(rewriter, 4));
|
||||
}
|
||||
|
||||
void insertRowStripFragment(Value fragment,
|
||||
Value output,
|
||||
RankedTensorType logicalType,
|
||||
OpFoldResult row,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
createParallelInsertSliceIntoBatchOutput(rewriter,
|
||||
loc,
|
||||
fragment,
|
||||
output,
|
||||
buildRowStripFragmentOffsets(rewriter, row),
|
||||
buildRowStripFragmentSizes(rewriter, logicalType),
|
||||
getUnitStrides(rewriter, 4));
|
||||
}
|
||||
|
||||
FailureOr<Value> createPerChannelConstantFragment(DenseElementsAttr denseAttr,
|
||||
RankedTensorType fragmentType,
|
||||
PatternRewriter& rewriter) {
|
||||
FailureOr<SmallVector<Attribute>> channelValues = getBiasChannelValues(denseAttr, fragmentType);
|
||||
if (failed(channelValues))
|
||||
return failure();
|
||||
|
||||
SmallVector<Attribute> values;
|
||||
values.reserve(fragmentType.getNumElements());
|
||||
for (int64_t n = 0; n < fragmentType.getDimSize(0); ++n)
|
||||
for (int64_t channel = 0; channel < fragmentType.getDimSize(1); ++channel)
|
||||
for (int64_t h = 0; h < fragmentType.getDimSize(2); ++h)
|
||||
for (int64_t w = 0; w < fragmentType.getDimSize(3); ++w)
|
||||
values.push_back((*channelValues)[channel]);
|
||||
|
||||
auto attr = DenseElementsAttr::get(fragmentType, values);
|
||||
return getOrCreateConstant(rewriter, rewriter.getInsertionBlock()->getParentOp(), attr, fragmentType);
|
||||
}
|
||||
|
||||
FailureOr<Value> createRowStripStorageFromRows(Value rows,
|
||||
RankedTensorType logicalType,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
auto rowsType = dyn_cast<RankedTensorType>(rows.getType());
|
||||
if (!rowsType || !rowsType.hasStaticShape() || rowsType.getRank() != 2)
|
||||
return failure();
|
||||
if (!logicalType || !logicalType.hasStaticShape() || logicalType.getRank() != 4)
|
||||
return failure();
|
||||
if (logicalType.getDimSize(0) != 1)
|
||||
return failure();
|
||||
if (rowsType.getElementType() != logicalType.getElementType())
|
||||
return failure();
|
||||
|
||||
const int64_t channels = logicalType.getDimSize(1);
|
||||
const int64_t height = logicalType.getDimSize(2);
|
||||
const int64_t width = logicalType.getDimSize(3);
|
||||
if (rowsType.getDimSize(0) != height * width)
|
||||
return failure();
|
||||
if (rowsType.getDimSize(1) != channels)
|
||||
return failure();
|
||||
|
||||
auto rowSliceType = RankedTensorType::get({width, channels}, logicalType.getElementType(), rowsType.getEncoding());
|
||||
auto channelWidthType = RankedTensorType::get({channels, width}, logicalType.getElementType(), rowsType.getEncoding());
|
||||
auto fragmentType = getRowStripFragmentType(logicalType);
|
||||
auto storageType = getRowStripStorageType(logicalType);
|
||||
auto batchOp = createSpatComputeBatch(
|
||||
rewriter, loc, TypeRange {storageType}, height, {}, ValueRange {rows}, [&](detail::SpatComputeBatchBodyArgs args) {
|
||||
Operation* anchorOp = rewriter.getInsertionBlock()->getParentOp();
|
||||
Value rowStart = affineMulConst(rewriter, loc, args.lane, width, anchorOp);
|
||||
SmallVector<OpFoldResult> rowOffsets {rowStart, rewriter.getIndexAttr(0)};
|
||||
SmallVector<OpFoldResult> rowSizes {rewriter.getIndexAttr(width), rewriter.getIndexAttr(channels)};
|
||||
Value rowSlice = tensor::ExtractSliceOp::create(
|
||||
rewriter, loc, rowSliceType, args.inputs.front(), rowOffsets, rowSizes, getUnitStrides(rewriter, 2));
|
||||
Value channelWidth = ONNXTransposeOp::create(
|
||||
rewriter, loc, channelWidthType, rowSlice, rewriter.getI64ArrayAttr({1, 0})).getResult();
|
||||
Value fragment = tensor::ExpandShapeOp::create(
|
||||
rewriter, loc, fragmentType, channelWidth, SmallVector<ReassociationIndices> {{0, 1}, {2, 3}});
|
||||
insertRowStripFragment(fragment, args.outputs.front(), logicalType, args.lane, rewriter, loc);
|
||||
return success();
|
||||
});
|
||||
if (failed(batchOp))
|
||||
return failure();
|
||||
return batchOp->getResult(0);
|
||||
}
|
||||
|
||||
FailureOr<Value>
|
||||
materializeRowStripStorageToDense(Value storage, RankedTensorType logicalType, PatternRewriter& rewriter, Location loc) {
|
||||
auto storageType = dyn_cast<RankedTensorType>(storage.getType());
|
||||
if (!storageType || storageType != getRowStripStorageType(logicalType))
|
||||
return failure();
|
||||
|
||||
auto batchOp = createSpatComputeBatch(
|
||||
rewriter, loc, TypeRange {logicalType}, logicalType.getDimSize(2), {}, ValueRange {storage},
|
||||
[&](detail::SpatComputeBatchBodyArgs args) {
|
||||
Value fragment = extractRowStripFragment(args.inputs.front(), logicalType, args.lane, rewriter, loc);
|
||||
createParallelInsertSliceIntoBatchOutput(rewriter,
|
||||
loc,
|
||||
fragment,
|
||||
args.outputs.front(),
|
||||
SmallVector<OpFoldResult> {rewriter.getIndexAttr(0),
|
||||
rewriter.getIndexAttr(0),
|
||||
args.lane,
|
||||
rewriter.getIndexAttr(0)},
|
||||
buildRowStripFragmentSizes(rewriter, logicalType),
|
||||
getUnitStrides(rewriter, 4));
|
||||
return success();
|
||||
});
|
||||
if (failed(batchOp))
|
||||
return failure();
|
||||
return batchOp->getResult(0);
|
||||
}
|
||||
|
||||
FailureOr<Value>
|
||||
applyRowStripRelu(Value storage, RankedTensorType logicalType, PatternRewriter& rewriter, Location loc) {
|
||||
auto fragmentType = getRowStripFragmentType(logicalType);
|
||||
auto storageType = getRowStripStorageType(logicalType);
|
||||
auto batchOp = createSpatComputeBatch(rewriter,
|
||||
loc,
|
||||
TypeRange {storageType},
|
||||
logicalType.getDimSize(2),
|
||||
{},
|
||||
ValueRange {storage},
|
||||
[&](detail::SpatComputeBatchBodyArgs args) {
|
||||
Value fragment =
|
||||
extractRowStripFragment(args.inputs.front(), logicalType, args.lane, rewriter, loc);
|
||||
fragment = spatial::SpatReluOp::create(rewriter, loc, fragmentType, fragment).getResult();
|
||||
insertRowStripFragment(
|
||||
fragment, args.outputs.front(), logicalType, args.lane, rewriter, loc);
|
||||
return success();
|
||||
});
|
||||
if (failed(batchOp))
|
||||
return failure();
|
||||
return batchOp->getResult(0);
|
||||
}
|
||||
|
||||
FailureOr<Value>
|
||||
applyRowStripBiasAdd(Value storage, RankedTensorType logicalType, Value bias, PatternRewriter& rewriter, Location loc) {
|
||||
DenseElementsAttr denseAttr;
|
||||
if (!isSupportedBiasAddValue(bias, logicalType, &denseAttr))
|
||||
return failure();
|
||||
auto fragmentType = getRowStripFragmentType(logicalType);
|
||||
auto storageType = getRowStripStorageType(logicalType);
|
||||
auto batchOp = createSpatComputeBatch(rewriter,
|
||||
loc,
|
||||
TypeRange {storageType},
|
||||
logicalType.getDimSize(2),
|
||||
{},
|
||||
ValueRange {storage},
|
||||
[&](detail::SpatComputeBatchBodyArgs args) {
|
||||
Value fragment =
|
||||
extractRowStripFragment(args.inputs.front(), logicalType, args.lane, rewriter, loc);
|
||||
Value constant;
|
||||
if (denseAttr.isSplat()) {
|
||||
constant = getOrCreateConstant(
|
||||
rewriter,
|
||||
rewriter.getInsertionBlock()->getParentOp(),
|
||||
DenseElementsAttr::get(fragmentType, denseAttr.getSplatValue<Attribute>()),
|
||||
fragmentType);
|
||||
}
|
||||
else {
|
||||
FailureOr<Value> perChannel =
|
||||
createPerChannelConstantFragment(denseAttr, fragmentType, rewriter);
|
||||
if (failed(perChannel))
|
||||
return failure();
|
||||
constant = *perChannel;
|
||||
}
|
||||
fragment =
|
||||
spatial::SpatVAddOp::create(rewriter, loc, fragmentType, fragment, constant).getResult();
|
||||
insertRowStripFragment(
|
||||
fragment, args.outputs.front(), logicalType, args.lane, rewriter, loc);
|
||||
return success();
|
||||
});
|
||||
if (failed(batchOp))
|
||||
return failure();
|
||||
return batchOp->getResult(0);
|
||||
}
|
||||
|
||||
} // namespace onnx_mlir
|
||||
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include "mlir/IR/BuiltinAttributes.h"
|
||||
#include "mlir/IR/BuiltinTypes.h"
|
||||
#include "mlir/IR/PatternMatch.h"
|
||||
|
||||
namespace onnx_mlir {
|
||||
|
||||
inline constexpr llvm::StringLiteral kRowStripIndexMap = "nchw_row_strip_fragments";
|
||||
|
||||
struct RowStripPhysicalValue {
|
||||
mlir::Value storage;
|
||||
mlir::RankedTensorType logicalType;
|
||||
llvm::SmallVector<int64_t, 16> fragmentOffsets;
|
||||
llvm::SmallVector<int64_t, 16> fragmentSizes;
|
||||
};
|
||||
|
||||
std::pair<llvm::SmallVector<int64_t>, llvm::SmallVector<int64_t>>
|
||||
buildRowStripMetadata(mlir::RankedTensorType type);
|
||||
|
||||
mlir::RankedTensorType getRowStripFragmentType(mlir::RankedTensorType logicalType);
|
||||
|
||||
mlir::RankedTensorType getRowStripStorageType(mlir::RankedTensorType logicalType);
|
||||
|
||||
llvm::SmallVector<mlir::OpFoldResult> buildRowStripFragmentOffsets(mlir::PatternRewriter& rewriter,
|
||||
mlir::OpFoldResult row);
|
||||
|
||||
llvm::SmallVector<mlir::OpFoldResult> buildRowStripFragmentSizes(mlir::PatternRewriter& rewriter,
|
||||
mlir::RankedTensorType logicalType);
|
||||
|
||||
mlir::Value extractRowStripFragment(mlir::Value storage,
|
||||
mlir::RankedTensorType logicalType,
|
||||
mlir::OpFoldResult row,
|
||||
mlir::PatternRewriter& rewriter,
|
||||
mlir::Location loc);
|
||||
|
||||
void insertRowStripFragment(mlir::Value fragment,
|
||||
mlir::Value output,
|
||||
mlir::RankedTensorType logicalType,
|
||||
mlir::OpFoldResult row,
|
||||
mlir::PatternRewriter& rewriter,
|
||||
mlir::Location loc);
|
||||
|
||||
mlir::FailureOr<mlir::Value> createPerChannelConstantFragment(mlir::DenseElementsAttr denseAttr,
|
||||
mlir::RankedTensorType fragmentType,
|
||||
mlir::PatternRewriter& rewriter);
|
||||
|
||||
mlir::FailureOr<mlir::Value> createRowStripStorageFromRows(mlir::Value rows,
|
||||
mlir::RankedTensorType logicalType,
|
||||
mlir::PatternRewriter& rewriter,
|
||||
mlir::Location loc);
|
||||
|
||||
mlir::FailureOr<mlir::Value> materializeRowStripStorageToDense(mlir::Value storage,
|
||||
mlir::RankedTensorType logicalType,
|
||||
mlir::PatternRewriter& rewriter,
|
||||
mlir::Location loc);
|
||||
|
||||
mlir::FailureOr<mlir::Value> applyRowStripRelu(mlir::Value storage,
|
||||
mlir::RankedTensorType logicalType,
|
||||
mlir::PatternRewriter& rewriter,
|
||||
mlir::Location loc);
|
||||
|
||||
mlir::FailureOr<mlir::Value> applyRowStripBiasAdd(mlir::Value storage,
|
||||
mlir::RankedTensorType logicalType,
|
||||
mlir::Value bias,
|
||||
mlir::PatternRewriter& rewriter,
|
||||
mlir::Location loc);
|
||||
|
||||
} // namespace onnx_mlir
|
||||
@@ -13,7 +13,9 @@
|
||||
#include "Conversion/ONNXToSpatial/ONNXToSpatialVerifier.hpp"
|
||||
#include "src/Accelerators/PIM/Common/PimCommon.hpp"
|
||||
#include "src/Accelerators/PIM/Common/Support/DebugDump.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/RowStripLayoutUtils.hpp"
|
||||
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Patterns.hpp"
|
||||
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/PlanLowering.hpp"
|
||||
#include "src/Accelerators/PIM/Dialect/Spatial/Transforms/MergeComputeNodes/SpatialDataflowCsvExporter.hpp"
|
||||
@@ -29,14 +31,6 @@ namespace {
|
||||
static constexpr StringLiteral kDenseLayout = "dense_nchw";
|
||||
static constexpr StringLiteral kRowStripLayout = "nchw_row_strip";
|
||||
|
||||
struct RowStripPhysicalValue {
|
||||
Value physicalValue;
|
||||
RankedTensorType logicalType;
|
||||
SmallVector<int64_t, 16> fragmentOffsets;
|
||||
SmallVector<int64_t, 16> fragmentSizes;
|
||||
std::string indexMap;
|
||||
};
|
||||
|
||||
static FailureOr<RowStripPhysicalValue> getRowStripValue(llvm::DenseMap<Value, RowStripPhysicalValue>& rowStripValues,
|
||||
Value value) {
|
||||
auto it = rowStripValues.find(value);
|
||||
@@ -46,112 +40,42 @@ static FailureOr<RowStripPhysicalValue> getRowStripValue(llvm::DenseMap<Value, R
|
||||
}
|
||||
|
||||
static FailureOr<RowStripPhysicalValue> buildRowStripValue(spatial::SpatBlueprintOp blueprint,
|
||||
Value physicalValue) {
|
||||
Value storage) {
|
||||
auto logicalType = dyn_cast<RankedTensorType>(blueprint.getOutput().getType());
|
||||
if (!logicalType)
|
||||
return blueprint.emitOpError("requires ranked logical output type"), failure();
|
||||
RowStripPhysicalValue value;
|
||||
value.physicalValue = physicalValue;
|
||||
value.storage = storage;
|
||||
value.logicalType = logicalType;
|
||||
value.fragmentOffsets.append(blueprint.getFragmentOffsets().begin(), blueprint.getFragmentOffsets().end());
|
||||
value.fragmentSizes.append(blueprint.getFragmentSizes().begin(), blueprint.getFragmentSizes().end());
|
||||
value.indexMap = blueprint.getIndexMap().str();
|
||||
if (blueprint.getIndexMap() != kRowStripIndexMap)
|
||||
return blueprint.emitOpError("requires the canonical row-strip index map"), failure();
|
||||
auto storageType = dyn_cast<RankedTensorType>(storage.getType());
|
||||
if (!storageType || storageType != getRowStripStorageType(logicalType))
|
||||
return blueprint.emitOpError("requires physical row-strip fragment storage"), failure();
|
||||
return value;
|
||||
}
|
||||
|
||||
static FailureOr<Value>
|
||||
lowerRowStripRelu(const RowStripPhysicalValue& input, spatial::SpatReluPlanOp planOp, PatternRewriter& rewriter) {
|
||||
auto packedType = cast<RankedTensorType>(input.physicalValue.getType());
|
||||
auto computeOp =
|
||||
createSpatCompute<1>(rewriter, planOp.getLoc(), TypeRange {packedType}, {}, input.physicalValue, [&](Value x) {
|
||||
auto relu = spatial::SpatReluOp::create(rewriter, planOp.getLoc(), packedType, x);
|
||||
spatial::SpatYieldOp::create(rewriter, planOp.getLoc(), relu.getResult());
|
||||
});
|
||||
return computeOp.getResult(0);
|
||||
return applyRowStripRelu(input.storage, input.logicalType, rewriter, planOp.getLoc());
|
||||
}
|
||||
|
||||
static FailureOr<Value> lowerRowStripBiasAdd(const RowStripPhysicalValue& input,
|
||||
spatial::SpatBiasAddPlanOp planOp,
|
||||
PatternRewriter& rewriter) {
|
||||
return applyRowStripBiasAdd(input.storage, input.logicalType, planOp.getBias(), rewriter, planOp.getLoc());
|
||||
}
|
||||
|
||||
static FailureOr<Value>
|
||||
materializeRowStripToDense(const RowStripPhysicalValue& rowStripValue, Location loc, PatternRewriter& rewriter) {
|
||||
auto packedType = dyn_cast<RankedTensorType>(rowStripValue.physicalValue.getType());
|
||||
if (!packedType || packedType.getRank() != 3 || !packedType.hasStaticShape())
|
||||
return failure();
|
||||
if (rowStripValue.logicalType.getRank() != 4 || !rowStripValue.logicalType.hasStaticShape())
|
||||
return failure();
|
||||
if (rowStripValue.indexMap != "packed_hwc_rows_to_nchw")
|
||||
auto [expectedOffsets, expectedSizes] = buildRowStripMetadata(rowStripValue.logicalType);
|
||||
if (!llvm::equal(rowStripValue.fragmentOffsets, expectedOffsets) || !llvm::equal(rowStripValue.fragmentSizes, expectedSizes))
|
||||
return failure();
|
||||
|
||||
const int64_t rank = rowStripValue.logicalType.getRank();
|
||||
const int64_t fragmentCount = rowStripValue.fragmentOffsets.size() / rank;
|
||||
const int64_t packedWidth = packedType.getDimSize(1);
|
||||
const int64_t packedChannels = packedType.getDimSize(2);
|
||||
if (fragmentCount != packedType.getDimSize(0))
|
||||
return failure();
|
||||
for (int64_t fragmentIndex = 0; fragmentIndex < fragmentCount; ++fragmentIndex) {
|
||||
if (rowStripValue.fragmentOffsets[fragmentIndex * rank + 0] != 0
|
||||
|| rowStripValue.fragmentOffsets[fragmentIndex * rank + 1] != 0
|
||||
|| rowStripValue.fragmentOffsets[fragmentIndex * rank + 2] != fragmentIndex
|
||||
|| rowStripValue.fragmentOffsets[fragmentIndex * rank + 3] != 0)
|
||||
return failure();
|
||||
if (rowStripValue.fragmentSizes[fragmentIndex * rank + 0] != 1
|
||||
|| rowStripValue.fragmentSizes[fragmentIndex * rank + 1] != packedChannels
|
||||
|| rowStripValue.fragmentSizes[fragmentIndex * rank + 2] != 1
|
||||
|| rowStripValue.fragmentSizes[fragmentIndex * rank + 3] != packedWidth)
|
||||
return failure();
|
||||
}
|
||||
|
||||
auto packedSliceType =
|
||||
RankedTensorType::get({1, packedWidth, packedChannels}, packedType.getElementType(), packedType.getEncoding());
|
||||
auto expandedType =
|
||||
RankedTensorType::get({1, 1, packedWidth, packedChannels}, packedType.getElementType(), packedType.getEncoding());
|
||||
auto logicalFragmentType =
|
||||
RankedTensorType::get({1, packedChannels, 1, packedWidth}, packedType.getElementType(), packedType.getEncoding());
|
||||
auto batchOp = createSpatComputeBatch(
|
||||
rewriter,
|
||||
loc,
|
||||
TypeRange {rowStripValue.logicalType},
|
||||
fragmentCount,
|
||||
{},
|
||||
ValueRange {rowStripValue.physicalValue},
|
||||
[&](detail::SpatComputeBatchBodyArgs args) {
|
||||
SmallVector<OpFoldResult> packedOffsets {args.lane, rewriter.getIndexAttr(0), rewriter.getIndexAttr(0)};
|
||||
SmallVector<OpFoldResult> packedSizes {
|
||||
rewriter.getIndexAttr(1), rewriter.getIndexAttr(packedWidth), rewriter.getIndexAttr(packedChannels)};
|
||||
Value packedSlice = tensor::ExtractSliceOp::create(
|
||||
rewriter, loc, packedSliceType, args.inputs.front(), packedOffsets, packedSizes, getUnitStrides(rewriter, 3));
|
||||
|
||||
Value expanded = tensor::ExpandShapeOp::create(rewriter,
|
||||
loc,
|
||||
expandedType,
|
||||
packedSlice,
|
||||
SmallVector<ReassociationIndices> {
|
||||
{0, 1},
|
||||
{2},
|
||||
{3}
|
||||
});
|
||||
Value transposeInit =
|
||||
tensor::EmptyOp::create(rewriter, loc, logicalFragmentType.getShape(), logicalFragmentType.getElementType());
|
||||
Value logicalFragment =
|
||||
linalg::TransposeOp::create(rewriter, loc, expanded, transposeInit, SmallVector<int64_t> {0, 3, 1, 2})
|
||||
.getResult()[0];
|
||||
|
||||
SmallVector<OpFoldResult> logicalOffsets {
|
||||
rewriter.getIndexAttr(0), rewriter.getIndexAttr(0), args.lane, rewriter.getIndexAttr(0)};
|
||||
SmallVector<OpFoldResult> logicalSizes {rewriter.getIndexAttr(1),
|
||||
rewriter.getIndexAttr(packedChannels),
|
||||
rewriter.getIndexAttr(1),
|
||||
rewriter.getIndexAttr(packedWidth)};
|
||||
createParallelInsertSliceIntoBatchOutput(rewriter,
|
||||
loc,
|
||||
logicalFragment,
|
||||
args.outputs.front(),
|
||||
logicalOffsets,
|
||||
logicalSizes,
|
||||
getUnitStrides(rewriter, 4));
|
||||
return success();
|
||||
});
|
||||
if (failed(batchOp))
|
||||
return failure();
|
||||
return batchOp->getResult(0);
|
||||
return materializeRowStripStorageToDense(rowStripValue.storage, rowStripValue.logicalType, rewriter, loc);
|
||||
}
|
||||
|
||||
struct LowerSpatialPlansPass final : PassWrapper<LowerSpatialPlansPass, OperationPass<ModuleOp>> {
|
||||
@@ -194,7 +118,7 @@ struct LowerSpatialPlansPass final : PassWrapper<LowerSpatialPlansPass, Operatio
|
||||
rewriter.setInsertionPoint(planOp);
|
||||
FailureOr<Value> lowered = lowerSelectedConv2DPlan(
|
||||
planOp,
|
||||
succeeded(rowStripInput) ? std::optional<Value> {rowStripInput->physicalValue} : std::nullopt,
|
||||
succeeded(rowStripInput) ? std::optional<Value> {rowStripInput->storage} : std::nullopt,
|
||||
/*emitRowStripLayout=*/true,
|
||||
rewriter);
|
||||
if (failed(lowered)) {
|
||||
@@ -266,6 +190,64 @@ struct LowerSpatialPlansPass final : PassWrapper<LowerSpatialPlansPass, Operatio
|
||||
rewriter.replaceOp(planOp, computeOp.getResults());
|
||||
continue;
|
||||
}
|
||||
if (auto planOp = dyn_cast<spatial::SpatBiasAddPlanOp>(&op)) {
|
||||
if (succeeded(getRowStripValue(rowStripValues, planOp.getInput()))) {
|
||||
auto outputBlueprint = llvm::find_if(planOp.getResult().getUsers(), [](Operation* user) {
|
||||
auto blueprint = dyn_cast<spatial::SpatBlueprintOp>(user);
|
||||
return blueprint && blueprint.getPhysicalLayout() == kRowStripLayout;
|
||||
});
|
||||
if (outputBlueprint == planOp.getResult().getUsers().end()) {
|
||||
planOp.emitOpError("row-strip bias_add plan requires a row-strip blueprint result");
|
||||
signalPassFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
FailureOr<RowStripPhysicalValue> input = getRowStripValue(rowStripValues, planOp.getInput());
|
||||
rewriter.setInsertionPoint(planOp);
|
||||
FailureOr<Value> lowered = lowerRowStripBiasAdd(*input, planOp, rewriter);
|
||||
if (failed(lowered)) {
|
||||
planOp.emitOpError("failed to lower selected row-strip Spatial bias_add plan");
|
||||
signalPassFailure();
|
||||
return;
|
||||
}
|
||||
auto blueprint = cast<spatial::SpatBlueprintOp>(*outputBlueprint);
|
||||
FailureOr<RowStripPhysicalValue> output = buildRowStripValue(blueprint, *lowered);
|
||||
if (failed(output)) {
|
||||
signalPassFailure();
|
||||
return;
|
||||
}
|
||||
rowStripValues[blueprint.getResult()] = *output;
|
||||
eraseAfterLowering.insert(planOp);
|
||||
eraseAfterLowering.insert(blueprint);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto resultType = dyn_cast<RankedTensorType>(planOp.getOutput().getType());
|
||||
if (!resultType) {
|
||||
planOp.emitOpError("requires ranked output type");
|
||||
signalPassFailure();
|
||||
return;
|
||||
}
|
||||
rewriter.setInsertionPoint(planOp);
|
||||
FailureOr<Value> denseBias = materializeDenseBiasAddTensor(planOp.getBias(), resultType, rewriter, planOp.getLoc());
|
||||
if (failed(denseBias)) {
|
||||
planOp.emitOpError("failed to materialize dense Conv-style bias");
|
||||
signalPassFailure();
|
||||
return;
|
||||
}
|
||||
auto computeOp = createSpatCompute<2>(rewriter,
|
||||
planOp.getLoc(),
|
||||
planOp.getOutput().getType(),
|
||||
{},
|
||||
ValueRange {planOp.getInput(), *denseBias},
|
||||
[&](Value x, Value y) {
|
||||
auto added = spatial::SpatVAddOp::create(
|
||||
rewriter, planOp.getLoc(), planOp.getOutput().getType(), x, y);
|
||||
spatial::SpatYieldOp::create(rewriter, planOp.getLoc(), added.getResult());
|
||||
});
|
||||
rewriter.replaceOp(planOp, computeOp.getResults());
|
||||
continue;
|
||||
}
|
||||
if (auto materializeOp = dyn_cast<spatial::SpatMaterializeLayoutOp>(&op)) {
|
||||
if (materializeOp.getSourcePhysicalLayout() == kDenseLayout
|
||||
&& materializeOp.getTargetPhysicalLayout() == kDenseLayout) {
|
||||
@@ -385,6 +367,7 @@ struct LowerSpatialPlansPass final : PassWrapper<LowerSpatialPlansPass, Operatio
|
||||
if (isa<ONNXEntryPointOp>(op))
|
||||
return;
|
||||
if (isa<spatial::SpatConv2DPlanOp,
|
||||
spatial::SpatBiasAddPlanOp,
|
||||
spatial::SpatReluPlanOp,
|
||||
spatial::SpatBlueprintOp,
|
||||
spatial::SpatMaterializeLayoutOp>(op)
|
||||
|
||||
@@ -45,11 +45,12 @@ static void populateEmptyFunction(func::FuncOp funcOp) {
|
||||
SmallVector<spatial::SpatGraphCompute> computes(funcOp.getOps<spatial::SpatGraphCompute>());
|
||||
SmallVector<spatial::SpatGraphComputeBatch> computeBatches(funcOp.getOps<spatial::SpatGraphComputeBatch>());
|
||||
SmallVector<spatial::SpatConv2DPlanOp> convPlans(funcOp.getOps<spatial::SpatConv2DPlanOp>());
|
||||
SmallVector<spatial::SpatBiasAddPlanOp> biasAddPlans(funcOp.getOps<spatial::SpatBiasAddPlanOp>());
|
||||
SmallVector<spatial::SpatReluPlanOp> reluPlans(funcOp.getOps<spatial::SpatReluPlanOp>());
|
||||
SmallVector<spatial::SpatBlueprintOp> blueprints(funcOp.getOps<spatial::SpatBlueprintOp>());
|
||||
SmallVector<spatial::SpatMaterializeLayoutOp> materializers(funcOp.getOps<spatial::SpatMaterializeLayoutOp>());
|
||||
if (!computes.empty() || !computeBatches.empty() || !convPlans.empty() || !reluPlans.empty() || !blueprints.empty()
|
||||
|| !materializers.empty()) {
|
||||
if (!computes.empty() || !computeBatches.empty() || !convPlans.empty() || !biasAddPlans.empty() || !reluPlans.empty()
|
||||
|| !blueprints.empty() || !materializers.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -132,6 +132,7 @@ void verifyLogicalTopLevelOps(func::FuncOp funcOp, pim::CappedDiagnosticReporter
|
||||
spatial::SpatGraphCompute,
|
||||
spatial::SpatGraphComputeBatch,
|
||||
spatial::SpatConv2DPlanOp,
|
||||
spatial::SpatBiasAddPlanOp,
|
||||
spatial::SpatReluPlanOp,
|
||||
spatial::SpatBlueprintOp,
|
||||
spatial::SpatMaterializeLayoutOp>(&op)) {
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
#include "src/Accelerators/PIM/Common/Support/Diagnostics.hpp"
|
||||
#include "src/Accelerators/PIM/Common/Support/ReportUtils.hpp"
|
||||
#include "src/Accelerators/PIM/Compiler/PimCompilerOptions.hpp"
|
||||
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/BiasAddUtils.hpp"
|
||||
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/Common.hpp"
|
||||
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/RowStripLayoutUtils.hpp"
|
||||
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/CompileTime.hpp"
|
||||
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/PlanLowering.hpp"
|
||||
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Patterns/Math/ConvGeometry.hpp"
|
||||
@@ -221,10 +223,6 @@ struct DistributedConvReportTotals {
|
||||
};
|
||||
|
||||
static Value createZeroGemmBias(RankedTensorType gemmResultType, PatternRewriter& rewriter);
|
||||
static FailureOr<Value> createRowStripPackedRows(Value rows,
|
||||
const ConvLoweringState& state,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc);
|
||||
|
||||
static FailureOr<ConvLoweringState> analyzeConvLoweringState(ONNXConvOp convOp, Value x, Value w, Value b);
|
||||
|
||||
@@ -467,59 +465,6 @@ classifyDistributedBinaryConsumer(Operation* user,
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static bool covers(RowInterval acquired, RowInterval needed) {
|
||||
return acquired.begin <= needed.begin && acquired.end >= needed.end;
|
||||
}
|
||||
|
||||
static bool canConsumeRowStripHwcInput(const ConvLoweringState& state, StringRef& failureReason) {
|
||||
if (state.batchSize != 1) {
|
||||
failureReason = "unsupported_batch";
|
||||
return false;
|
||||
}
|
||||
if (state.group != 1) {
|
||||
failureReason = "unsupported_groups";
|
||||
return false;
|
||||
}
|
||||
if (isDepthwiseConv(state.group, state.numChannelsIn, state.numChannelsOut, state.numChannelsInPerGroup)) {
|
||||
failureReason = "unsupported_depthwise";
|
||||
return false;
|
||||
}
|
||||
if (state.strideHeight != 1 || state.strideWidth != 1) {
|
||||
failureReason = "unsupported_stride";
|
||||
return false;
|
||||
}
|
||||
if (state.dilationHeight != 1 || state.dilationWidth != 1) {
|
||||
failureReason = "unsupported_dilation";
|
||||
return false;
|
||||
}
|
||||
if (state.padHeightBegin != state.padHeightEnd || state.padWidthBegin != state.padWidthEnd) {
|
||||
failureReason = "unsupported_padding";
|
||||
return false;
|
||||
}
|
||||
if (state.padHeightBegin != 1 || state.padWidthBegin != 1) {
|
||||
failureReason = "unsupported_padding";
|
||||
return false;
|
||||
}
|
||||
if (state.wHeight != 3 || state.wWidth != 3) {
|
||||
failureReason = "unsupported_kernel";
|
||||
return false;
|
||||
}
|
||||
if (state.outHeight != state.xHeight || state.outWidth != state.xWidth) {
|
||||
failureReason = "unsupported_output_shape";
|
||||
return false;
|
||||
}
|
||||
if (!getHostConstDenseElementsAttr(state.w)) {
|
||||
failureReason = "non_constant_weights";
|
||||
return false;
|
||||
}
|
||||
if (state.hasBias && !getHostConstDenseElementsAttr(state.b)) {
|
||||
failureReason = "non_constant_bias";
|
||||
return false;
|
||||
}
|
||||
failureReason = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string stringifyDistributedTensorOpKind(DistributedTensorOpKind kind) {
|
||||
switch (kind) {
|
||||
case DistributedTensorOpKind::Relu: return "Relu";
|
||||
@@ -2493,21 +2438,15 @@ static Value rewriteStreamedConv(const ConvLoweringState& state,
|
||||
|
||||
} // namespace standard
|
||||
|
||||
static RankedTensorType getRowStripFragmentType(RankedTensorType tensorType, int64_t width) {
|
||||
return RankedTensorType::get(
|
||||
{tensorType.getDimSize(0), tensorType.getDimSize(1), 1, width}, tensorType.getElementType(), tensorType.getEncoding());
|
||||
}
|
||||
|
||||
static SmallVector<DistributedFragmentInfo, 8> buildRowStripFragments(RankedTensorType tensorType) {
|
||||
SmallVector<DistributedFragmentInfo, 8> fragments;
|
||||
const int64_t height = tensorType.getDimSize(2);
|
||||
const int64_t width = tensorType.getDimSize(3);
|
||||
const int64_t channels = tensorType.getDimSize(1);
|
||||
fragments.reserve(height);
|
||||
for (int64_t row = 0; row < height; ++row) {
|
||||
auto [offsets, sizes] = buildRowStripMetadata(tensorType);
|
||||
const int64_t rank = tensorType.getRank();
|
||||
fragments.reserve(offsets.size() / rank);
|
||||
for (int64_t row = 0; row < static_cast<int64_t>(offsets.size() / rank); ++row) {
|
||||
fragments.push_back(DistributedFragmentInfo {
|
||||
{0, 0, row, 0},
|
||||
{1, channels, 1, width},
|
||||
{offsets.begin() + row * rank, offsets.begin() + (row + 1) * rank},
|
||||
{sizes.begin() + row * rank, sizes.begin() + (row + 1) * rank},
|
||||
{1, 1, 1, 1},
|
||||
row,
|
||||
});
|
||||
@@ -2527,101 +2466,266 @@ static DistributedTensorInfo makeDistributedTensorInfo(Value storage, RankedTens
|
||||
return info;
|
||||
}
|
||||
|
||||
static Value createPerChannelConstantFragment(DenseElementsAttr denseAttr,
|
||||
RankedTensorType fragmentType,
|
||||
PatternRewriter& rewriter) {
|
||||
auto denseType = cast<RankedTensorType>(denseAttr.getType());
|
||||
SmallVector<Attribute> channelValues;
|
||||
channelValues.reserve(fragmentType.getDimSize(1));
|
||||
SmallVector<Attribute> flattened(denseAttr.getValues<Attribute>());
|
||||
if (denseType.getRank() == 1) {
|
||||
channelValues = flattened;
|
||||
}
|
||||
else if (denseType.getRank() == 2) {
|
||||
channelValues = flattened;
|
||||
}
|
||||
else {
|
||||
for (int64_t channel = 0; channel < denseType.getDimSize(1); ++channel)
|
||||
channelValues.push_back(flattened[channel]);
|
||||
}
|
||||
|
||||
SmallVector<Attribute> values;
|
||||
values.reserve(fragmentType.getNumElements());
|
||||
for (int64_t n = 0; n < fragmentType.getDimSize(0); ++n)
|
||||
for (int64_t channel = 0; channel < fragmentType.getDimSize(1); ++channel)
|
||||
for (int64_t h = 0; h < fragmentType.getDimSize(2); ++h)
|
||||
for (int64_t w = 0; w < fragmentType.getDimSize(3); ++w)
|
||||
values.push_back(channelValues[channel]);
|
||||
|
||||
auto attr = DenseElementsAttr::get(fragmentType, values);
|
||||
return getOrCreateConstant(rewriter, rewriter.getInsertionBlock()->getParentOp(), attr, fragmentType);
|
||||
}
|
||||
|
||||
static Value createZeroGemmBias(RankedTensorType gemmResultType, PatternRewriter& rewriter) {
|
||||
auto zeroAttr = DenseElementsAttr::get(gemmResultType, rewriter.getZeroAttr(gemmResultType.getElementType()));
|
||||
return getOrCreateConstant(rewriter, rewriter.getInsertionBlock()->getParentOp(), zeroAttr, gemmResultType);
|
||||
}
|
||||
|
||||
static bool canDirectLowerRowStripConv(const ConvLoweringState& state, StringRef& failureReason) {
|
||||
if (!canConsumeRowStripHwcInput(state, failureReason))
|
||||
return false;
|
||||
|
||||
ConvGeometry geometry = buildConvGeometry(state);
|
||||
if (state.numChannelsOut > geometry.xbarSize) {
|
||||
failureReason = "unsupported_output_channels";
|
||||
static bool canConsumeNchwRowStripFragments(const ConvLoweringState& state, StringRef& failureReason) {
|
||||
if (state.batchSize != 1) {
|
||||
failureReason = "batch_not_one";
|
||||
return false;
|
||||
}
|
||||
if (state.group != 1) {
|
||||
failureReason = "grouped_conv";
|
||||
return false;
|
||||
}
|
||||
if (!state.xType.hasStaticShape() || !state.wType.hasStaticShape() || !state.outType.hasStaticShape()) {
|
||||
failureReason = "dynamic_shape";
|
||||
return false;
|
||||
}
|
||||
if (!isa<FloatType>(state.xType.getElementType())) {
|
||||
failureReason = "non_float_input";
|
||||
return false;
|
||||
}
|
||||
if (state.strideHeight != 1 || state.strideWidth != 1) {
|
||||
failureReason = "stride_not_one";
|
||||
return false;
|
||||
}
|
||||
if (state.dilationHeight != 1 || state.dilationWidth != 1) {
|
||||
failureReason = "dilation_not_one";
|
||||
return false;
|
||||
}
|
||||
if (state.wHeight != 3 || state.wWidth != 3) {
|
||||
failureReason = "kernel_not_3x3";
|
||||
return false;
|
||||
}
|
||||
if (state.padHeightBegin != 1 || state.padHeightEnd != 1 || state.padWidthBegin != 1 || state.padWidthEnd != 1) {
|
||||
failureReason = "padding_not_1";
|
||||
return false;
|
||||
}
|
||||
if (state.outHeight != state.xHeight || state.outWidth != state.xWidth) {
|
||||
failureReason = "not_same_spatial_shape";
|
||||
return false;
|
||||
}
|
||||
if (!getHostConstDenseElementsAttr(state.w)) {
|
||||
failureReason = "non_constant_weight";
|
||||
return false;
|
||||
}
|
||||
if (state.hasBias && !isSupportedBiasAddValue(state.b, state.outType)) {
|
||||
failureReason = "unsupported_bias";
|
||||
return false;
|
||||
}
|
||||
ConvGeometry geometry = buildConvGeometry(state);
|
||||
if (geometry.c > geometry.xbarSize) {
|
||||
failureReason = "output_channels_exceed_crossbar";
|
||||
return false;
|
||||
}
|
||||
|
||||
failureReason = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
static FailureOr<Value> createRowStripPackedRows(Value rows,
|
||||
const ConvLoweringState& state,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
auto rowsType = dyn_cast<RankedTensorType>(rows.getType());
|
||||
if (!rowsType || !rowsType.hasStaticShape() || rowsType.getRank() != 2)
|
||||
return failure();
|
||||
|
||||
if (state.batchSize != 1)
|
||||
return failure();
|
||||
if (state.outType.getRank() != 4 || !state.outType.hasStaticShape())
|
||||
return failure();
|
||||
|
||||
const int64_t outHeight = state.outType.getDimSize(2);
|
||||
const int64_t outWidth = state.outType.getDimSize(3);
|
||||
const int64_t outChannels = state.outType.getDimSize(1);
|
||||
if (rowsType.getDimSize(0) != outHeight * outWidth || rowsType.getDimSize(1) != outChannels)
|
||||
return failure();
|
||||
|
||||
auto packedType = RankedTensorType::get({outHeight, outWidth, outChannels}, rowsType.getElementType(), rowsType.getEncoding());
|
||||
auto packedRows =
|
||||
createSpatCompute<1>(rewriter, loc, TypeRange {packedType}, {}, rows, [&](Value rowValues) {
|
||||
Value packed = tensor::ExpandShapeOp::create(
|
||||
rewriter, loc, packedType, rowValues, SmallVector<ReassociationIndices> {{0, 1}, {2}});
|
||||
spatial::SpatYieldOp::create(rewriter, loc, packed);
|
||||
});
|
||||
return packedRows.getResult(0);
|
||||
static Value createZeroTensorConstant(RankedTensorType type, PatternRewriter& rewriter) {
|
||||
auto zeroAttr = DenseElementsAttr::get(type, rewriter.getZeroAttr(type.getElementType()));
|
||||
return getOrCreateConstant(rewriter, rewriter.getInsertionBlock()->getParentOp(), zeroAttr, type);
|
||||
}
|
||||
|
||||
static FailureOr<Value> createConvOutputFromRowStripHwc(Value inputHwc,
|
||||
const ConvLoweringState& state,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
auto inputType = dyn_cast<RankedTensorType>(inputHwc.getType());
|
||||
if (!inputType || !inputType.hasStaticShape() || inputType.getRank() != 3)
|
||||
static FailureOr<Value> createPaddedBiasRowConstant(const ConvLoweringState& state,
|
||||
int64_t paddedChannels,
|
||||
PatternRewriter& rewriter) {
|
||||
DenseElementsAttr denseAttr;
|
||||
if (!isSupportedBiasAddValue(state.b, state.outType, &denseAttr))
|
||||
return failure();
|
||||
if (inputType.getDimSize(0) != state.xHeight || inputType.getDimSize(1) != state.xWidth
|
||||
|| inputType.getDimSize(2) != state.numChannelsIn)
|
||||
FailureOr<SmallVector<Attribute>> channelValues = getBiasChannelValues(denseAttr, state.outType);
|
||||
if (failed(channelValues))
|
||||
return failure();
|
||||
|
||||
auto biasType = RankedTensorType::get({1, paddedChannels}, state.outType.getElementType());
|
||||
SmallVector<Attribute> values(biasType.getNumElements(), cast<Attribute>(rewriter.getZeroAttr(biasType.getElementType())));
|
||||
for (int64_t channel = 0; channel < state.numChannelsOut; ++channel)
|
||||
values[channel] = (*channelValues)[channel];
|
||||
auto biasAttr = DenseElementsAttr::get(biasType, values);
|
||||
return getOrCreateConstant(rewriter, rewriter.getInsertionBlock()->getParentOp(), biasAttr, biasType);
|
||||
}
|
||||
|
||||
static Value createHorizontallyPaddedRowStripFragment(Value fragment,
|
||||
const ConvLoweringState& state,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
auto paddedType = RankedTensorType::get({1, state.numChannelsIn, 1, state.xWidth + 2},
|
||||
state.xType.getElementType(),
|
||||
state.xType.getEncoding());
|
||||
return createZeroPaddedTensor(fragment, paddedType, {0, 0, 0, 1}, {0, 0, 0, 1}, rewriter, loc);
|
||||
}
|
||||
|
||||
static Value createRowStripWindowSourceRowTable(const ConvLoweringState& state, PatternRewriter& rewriter) {
|
||||
Operation* anchorOp = rewriter.getInsertionBlock()->getParentOp();
|
||||
auto tableType = RankedTensorType::get({state.outHeight * state.wHeight}, rewriter.getIndexType());
|
||||
SmallVector<Attribute> values;
|
||||
values.reserve(tableType.getNumElements());
|
||||
for (int64_t outputRow = 0; outputRow < state.outHeight; ++outputRow) {
|
||||
for (int64_t kernelRow = 0; kernelRow < state.wHeight; ++kernelRow) {
|
||||
int64_t sourceRow = outputRow + kernelRow - state.padHeightBegin;
|
||||
sourceRow = std::clamp(sourceRow, int64_t {0}, state.xHeight - 1);
|
||||
values.push_back(rewriter.getIndexAttr(sourceRow));
|
||||
}
|
||||
}
|
||||
|
||||
return getOrCreateConstant(rewriter, anchorOp, DenseElementsAttr::get(tableType, values), tableType);
|
||||
}
|
||||
|
||||
static Value createRowStripWindowTableIndex(Value outputHeight,
|
||||
Value kernelRow,
|
||||
const ConvLoweringState& state,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
Operation* anchorOp = rewriter.getInsertionBlock()->getParentOp();
|
||||
MLIRContext* ctx = rewriter.getContext();
|
||||
AffineExpr outputRowExpr = getAffineDimExpr(0, ctx);
|
||||
AffineExpr kernelRowExpr = getAffineDimExpr(1, ctx);
|
||||
return createOrFoldAffineApply(
|
||||
rewriter, loc, outputRowExpr * state.wHeight + kernelRowExpr, ValueRange {outputHeight, kernelRow}, anchorOp);
|
||||
}
|
||||
|
||||
static Value extractProjectedRowStripWindowRow(Value rowStripStorage,
|
||||
Value sourceRowTable,
|
||||
const ConvLoweringState& state,
|
||||
Value outputHeight,
|
||||
Value kernelRow,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
Value tableIndex = createRowStripWindowTableIndex(outputHeight, kernelRow, state, rewriter, loc);
|
||||
Value sourceRow = tensor::ExtractOp::create(rewriter, loc, sourceRowTable, ValueRange {tableIndex}).getResult();
|
||||
return extractRowStripFragment(rowStripStorage, state.xType, sourceRow, rewriter, loc);
|
||||
}
|
||||
|
||||
static FailureOr<Value> createRowStripWindowMaskTable(const ConvLoweringState& state, PatternRewriter& rewriter) {
|
||||
auto elementType = state.xType.getElementType();
|
||||
auto floatType = dyn_cast<FloatType>(elementType);
|
||||
if (!floatType)
|
||||
return failure();
|
||||
|
||||
Operation* anchorOp = rewriter.getInsertionBlock()->getParentOp();
|
||||
auto tableType = RankedTensorType::get({state.outHeight * state.wHeight, state.numChannelsIn, 1, state.xWidth},
|
||||
elementType,
|
||||
state.xType.getEncoding());
|
||||
Attribute zero = rewriter.getZeroAttr(elementType);
|
||||
Attribute one = rewriter.getFloatAttr(floatType, 1.0);
|
||||
SmallVector<Attribute> values;
|
||||
values.reserve(tableType.getNumElements());
|
||||
for (int64_t outputRow = 0; outputRow < state.outHeight; ++outputRow) {
|
||||
for (int64_t kernelRow = 0; kernelRow < state.wHeight; ++kernelRow) {
|
||||
int64_t sourceRow = outputRow + kernelRow - state.padHeightBegin;
|
||||
Attribute value = (sourceRow < 0 || sourceRow >= state.xHeight) ? zero : one;
|
||||
for (int64_t channel = 0; channel < state.numChannelsIn; ++channel)
|
||||
for (int64_t width = 0; width < state.xWidth; ++width)
|
||||
values.push_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
return getOrCreateConstant(rewriter, anchorOp, DenseElementsAttr::get(tableType, values), tableType);
|
||||
}
|
||||
|
||||
static Value extractProjectedRowStripWindowMask(Value maskTable,
|
||||
const ConvLoweringState& state,
|
||||
Value outputHeight,
|
||||
Value kernelRow,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
Value tableIndex = createRowStripWindowTableIndex(outputHeight, kernelRow, state, rewriter, loc);
|
||||
auto fragmentType = getRowStripFragmentType(state.xType);
|
||||
SmallVector<OpFoldResult> offsets {
|
||||
tableIndex, rewriter.getIndexAttr(0), rewriter.getIndexAttr(0), rewriter.getIndexAttr(0)};
|
||||
SmallVector<OpFoldResult> sizes {rewriter.getIndexAttr(1),
|
||||
rewriter.getIndexAttr(state.numChannelsIn),
|
||||
rewriter.getIndexAttr(1),
|
||||
rewriter.getIndexAttr(state.xWidth)};
|
||||
return tensor::ExtractSliceOp::create(rewriter,
|
||||
loc,
|
||||
fragmentType,
|
||||
maskTable,
|
||||
offsets,
|
||||
sizes,
|
||||
getUnitStrides(rewriter, 4));
|
||||
}
|
||||
|
||||
static FailureOr<Value> createNchwRowStripConvWindow(Value rowStripStorage,
|
||||
const ConvLoweringState& state,
|
||||
Value outputHeight,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
auto fragmentType = getRowStripFragmentType(state.xType);
|
||||
auto paddedWindowType = RankedTensorType::get({1, state.numChannelsIn, state.wHeight, state.xWidth + 2},
|
||||
state.xType.getElementType(),
|
||||
state.xType.getEncoding());
|
||||
Value sourceRowTable = createRowStripWindowSourceRowTable(state, rewriter);
|
||||
FailureOr<Value> maskTable = createRowStripWindowMaskTable(state, rewriter);
|
||||
if (failed(maskTable))
|
||||
return failure();
|
||||
Value initWindow = createZeroTensorConstant(paddedWindowType, rewriter);
|
||||
|
||||
Operation* anchorOp = rewriter.getInsertionBlock()->getParentOp();
|
||||
Value window = initWindow;
|
||||
for (int64_t kernelRowIndex = 0; kernelRowIndex < state.wHeight; ++kernelRowIndex) {
|
||||
Value kernelRow = getOrCreateIndexConstant(rewriter, anchorOp, kernelRowIndex);
|
||||
Value sourceRow =
|
||||
extractProjectedRowStripWindowRow(rowStripStorage, sourceRowTable, state, outputHeight, kernelRow, rewriter, loc);
|
||||
Value mask = extractProjectedRowStripWindowMask(*maskTable, state, outputHeight, kernelRow, rewriter, loc);
|
||||
Value semanticRow = spatial::SpatVMulOp::create(rewriter, loc, fragmentType, sourceRow, mask).getResult();
|
||||
Value paddedRow = createHorizontallyPaddedRowStripFragment(semanticRow, state, rewriter, loc);
|
||||
window = tensor::InsertSliceOp::create(rewriter,
|
||||
loc,
|
||||
paddedRow,
|
||||
window,
|
||||
SmallVector<OpFoldResult> {rewriter.getIndexAttr(0),
|
||||
rewriter.getIndexAttr(0),
|
||||
rewriter.getIndexAttr(kernelRowIndex),
|
||||
rewriter.getIndexAttr(0)},
|
||||
SmallVector<OpFoldResult> {rewriter.getIndexAttr(1),
|
||||
rewriter.getIndexAttr(state.numChannelsIn),
|
||||
rewriter.getIndexAttr(1),
|
||||
rewriter.getIndexAttr(state.xWidth + 2)},
|
||||
getUnitStrides(rewriter, 4));
|
||||
}
|
||||
return window;
|
||||
}
|
||||
|
||||
static FailureOr<Value> createNchwRowStripConvPatchRow(Value paddedWindow,
|
||||
const ConvLoweringState& state,
|
||||
Value outputWidth,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
Operation* anchorOp = rewriter.getInsertionBlock()->getParentOp();
|
||||
const int64_t patchSize = state.numChannelsIn * state.wHeight * state.wWidth;
|
||||
auto patchType = RankedTensorType::get({1, state.numChannelsIn, state.wHeight, state.wWidth},
|
||||
state.xType.getElementType(),
|
||||
state.xType.getEncoding());
|
||||
auto rowType = RankedTensorType::get({1, patchSize}, state.xType.getElementType(), state.xType.getEncoding());
|
||||
Value c0 = getOrCreateIndexConstant(rewriter, anchorOp, 0);
|
||||
Value patch = createConvInputPatch(paddedWindow,
|
||||
patchType,
|
||||
c0,
|
||||
c0,
|
||||
c0,
|
||||
outputWidth,
|
||||
state.dilationHeight,
|
||||
state.dilationWidth,
|
||||
rewriter,
|
||||
loc);
|
||||
return tensor::CollapseShapeOp::create(
|
||||
rewriter, loc, rowType, patch, SmallVector<ReassociationIndices> {{0}, {1, 2, 3}})
|
||||
.getResult();
|
||||
}
|
||||
|
||||
static FailureOr<Value> createConvOutputFromNchwRowStripFragments(Value rowStripStorage,
|
||||
const ConvLoweringState& state,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
auto inputType = dyn_cast<RankedTensorType>(rowStripStorage.getType());
|
||||
if (!inputType || inputType != getRowStripStorageType(state.xType))
|
||||
return failure();
|
||||
|
||||
StringRef failureReason;
|
||||
if (!canDirectLowerRowStripConv(state, failureReason))
|
||||
return failure();
|
||||
|
||||
ConvRowDemand demand = buildConvRowDemand(RowInterval {0, state.outHeight}, state);
|
||||
if (!covers(demand.acquiredInputRows, demand.neededInputRows))
|
||||
if (!canConsumeNchwRowStripFragments(state, failureReason))
|
||||
return failure();
|
||||
|
||||
ConvGeometry geometry = buildConvGeometry(state);
|
||||
@@ -2629,121 +2733,61 @@ static FailureOr<Value> createConvOutputFromRowStripHwc(Value inputHwc,
|
||||
const int64_t patchSize = state.numChannelsIn * state.wHeight * state.wWidth;
|
||||
const int64_t numKSlices = ceilIntegerDivide(patchSize, xbarDim);
|
||||
const int64_t paddedK = numKSlices * xbarDim;
|
||||
auto elementType = inputType.getElementType();
|
||||
auto paddedInputType = RankedTensorType::get({state.xHeight + state.padHeightBegin + state.padHeightEnd,
|
||||
state.xWidth + state.padWidthBegin + state.padWidthEnd,
|
||||
state.numChannelsIn},
|
||||
elementType,
|
||||
inputType.getEncoding());
|
||||
auto paddedPatchType =
|
||||
RankedTensorType::get({state.wHeight, state.wWidth, 1}, elementType, inputType.getEncoding());
|
||||
auto flatPatchType = RankedTensorType::get({state.wHeight * state.wWidth}, elementType, inputType.getEncoding());
|
||||
auto rowChunkType = RankedTensorType::get({1, state.wHeight * state.wWidth}, elementType, inputType.getEncoding());
|
||||
auto elementType = state.outType.getElementType();
|
||||
auto rowType = RankedTensorType::get({1, state.numChannelsOut}, state.outType.getElementType());
|
||||
auto packedOutputType =
|
||||
RankedTensorType::get({state.outHeight, state.outWidth, state.numChannelsOut}, state.outType.getElementType());
|
||||
auto packedOutputSliceType =
|
||||
RankedTensorType::get({1, 1, state.numChannelsOut}, state.outType.getElementType());
|
||||
auto outputPixelType = RankedTensorType::get({1, state.numChannelsOut, 1, 1}, elementType);
|
||||
auto paddedRowType = RankedTensorType::get({1, xbarDim}, state.outType.getElementType());
|
||||
auto paddedPatchRowType = RankedTensorType::get({1, paddedK}, elementType, inputType.getEncoding());
|
||||
auto paddedWeightTileType = RankedTensorType::get({xbarDim, xbarDim}, state.wType.getElementType());
|
||||
auto outputStorageType = getRowStripStorageType(state.outType);
|
||||
auto weightDenseAttr = getHostConstDenseElementsAttr(state.w);
|
||||
if (!weightDenseAttr)
|
||||
return failure();
|
||||
Value paddedWeights = standard::createPaddedInputKTiledWeightConstant(weightDenseAttr, state, paddedK, xbarDim, rewriter);
|
||||
|
||||
Value paddedBias;
|
||||
if (state.hasBias) {
|
||||
Value biasMatrix = expandBiasIfNeeded(state.b, rewriter, loc);
|
||||
auto biasMatrixType = cast<RankedTensorType>(biasMatrix.getType());
|
||||
auto paddedBiasType = RankedTensorType::get({1, xbarDim}, state.outType.getElementType());
|
||||
if (auto biasDenseAttr = getHostConstDenseElementsAttr(state.b))
|
||||
paddedBias = standard::createPaddedConstantMatrix(biasDenseAttr, biasMatrixType, paddedBiasType, rewriter);
|
||||
else
|
||||
paddedBias = materializeOrComputeUnary(
|
||||
biasMatrix, paddedBiasType, rewriter, loc, [&](Value biasValue) {
|
||||
return standard::createPaddedConvMatrix(biasValue, biasMatrixType, paddedBiasType, rewriter, loc);
|
||||
});
|
||||
}
|
||||
|
||||
auto paddedInputOp =
|
||||
createSpatCompute<1>(rewriter, loc, TypeRange {paddedInputType}, {}, inputHwc, [&](Value hwcInputArg) {
|
||||
Value paddedInput = createZeroPaddedTensor(hwcInputArg,
|
||||
paddedInputType,
|
||||
{state.padHeightBegin, state.padWidthBegin, 0},
|
||||
{state.padHeightEnd, state.padWidthEnd, 0},
|
||||
rewriter,
|
||||
loc);
|
||||
spatial::SpatYieldOp::create(rewriter, loc, paddedInput);
|
||||
});
|
||||
|
||||
SmallVector<Value> batchInputs {paddedInputOp.getResult(0)};
|
||||
FailureOr<Value> paddedBias = failure();
|
||||
if (state.hasBias)
|
||||
batchInputs.push_back(paddedBias);
|
||||
paddedBias = createPaddedBiasRowConstant(state, xbarDim, rewriter);
|
||||
if (state.hasBias && failed(paddedBias))
|
||||
return failure();
|
||||
|
||||
auto batchOp = createSpatComputeBatch(
|
||||
rewriter,
|
||||
loc,
|
||||
TypeRange {packedOutputType},
|
||||
TypeRange {outputStorageType},
|
||||
state.outHeight,
|
||||
ValueRange {paddedWeights},
|
||||
batchInputs,
|
||||
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);
|
||||
Value cOutWidth = getOrCreateIndexConstant(rewriter, anchorOp, state.outWidth);
|
||||
Value cNumChannels = getOrCreateIndexConstant(rewriter, anchorOp, state.numChannelsIn);
|
||||
Value localHeightOffset = args.lane;
|
||||
Value packedRowInit =
|
||||
tensor::EmptyOp::create(rewriter, loc, ArrayRef<int64_t> {1, state.outWidth, state.numChannelsOut}, elementType);
|
||||
Value cXbar = getOrCreateIndexConstant(rewriter, anchorOp, xbarDim);
|
||||
auto fragmentType = getRowStripFragmentType(state.outType);
|
||||
FailureOr<Value> inputWindow = createNchwRowStripConvWindow(args.inputs.front(), state, args.lane, rewriter, loc);
|
||||
if (failed(inputWindow))
|
||||
return failure();
|
||||
Value fragmentInit = tensor::EmptyOp::create(rewriter, loc, fragmentType.getShape(), elementType);
|
||||
auto widthLoop = buildNormalizedScfFor(
|
||||
rewriter,
|
||||
loc,
|
||||
c0,
|
||||
cOutWidth,
|
||||
c1,
|
||||
ValueRange {packedRowInit},
|
||||
ValueRange {fragmentInit},
|
||||
[&](OpBuilder&, Location widthLoc, Value widthIndex, ValueRange widthIterArgs, SmallVectorImpl<Value>& widthYielded) {
|
||||
Value localWidthOffset = widthIndex;
|
||||
Value rowInit = tensor::EmptyOp::create(rewriter, widthLoc, ArrayRef<int64_t> {1, patchSize}, elementType);
|
||||
auto rowLoop = buildNormalizedScfFor(
|
||||
rewriter,
|
||||
widthLoc,
|
||||
c0,
|
||||
cNumChannels,
|
||||
c1,
|
||||
ValueRange {rowInit},
|
||||
[&](OpBuilder&, Location rowLoc, Value channel, ValueRange rowIterArgs, SmallVectorImpl<Value>& rowYielded) {
|
||||
SmallVector<OpFoldResult> patchOffsets {localHeightOffset, localWidthOffset, channel};
|
||||
SmallVector<OpFoldResult> patchSizes {
|
||||
rewriter.getIndexAttr(state.wHeight), rewriter.getIndexAttr(state.wWidth), rewriter.getIndexAttr(1)};
|
||||
Value channelPatch = tensor::ExtractSliceOp::create(
|
||||
rewriter, rowLoc, paddedPatchType, args.inputs.front(), patchOffsets, patchSizes, getUnitStrides(rewriter, 3));
|
||||
Value flatPatch = tensor::CollapseShapeOp::create(
|
||||
rewriter, rowLoc, flatPatchType, channelPatch, SmallVector<ReassociationIndices> {{0, 1, 2}});
|
||||
Value rowChunk = tensor::ExpandShapeOp::create(
|
||||
rewriter, rowLoc, rowChunkType, flatPatch, SmallVector<ReassociationIndices> {{0, 1}});
|
||||
Value flatOffset = affineMulConst(
|
||||
rewriter, rowLoc, channel, state.wHeight * state.wWidth, anchorOp);
|
||||
SmallVector<OpFoldResult> rowOffsets {rewriter.getIndexAttr(0), flatOffset};
|
||||
SmallVector<OpFoldResult> rowSizes {
|
||||
rewriter.getIndexAttr(1), rewriter.getIndexAttr(state.wHeight * state.wWidth)};
|
||||
Value nextRow = tensor::InsertSliceOp::create(
|
||||
rewriter, rowLoc, rowChunk, rowIterArgs.front(), rowOffsets, rowSizes, getUnitStrides(rewriter, 2));
|
||||
rowYielded.push_back(nextRow);
|
||||
return success();
|
||||
});
|
||||
if (failed(rowLoop))
|
||||
FailureOr<Value> patchRow =
|
||||
createNchwRowStripConvPatchRow(*inputWindow, state, widthIndex, rewriter, widthLoc);
|
||||
if (failed(patchRow))
|
||||
return failure();
|
||||
|
||||
Value paddedRow = rowLoop->results.front();
|
||||
Value paddedRow = *patchRow;
|
||||
if (patchSize != paddedK)
|
||||
paddedRow = createZeroPaddedTensor(
|
||||
paddedRow, paddedPatchRowType, {0, 0}, {0, paddedK - patchSize}, rewriter, widthLoc);
|
||||
|
||||
auto zeroAttr = DenseElementsAttr::get(paddedRowType, rewriter.getZeroAttr(state.outType.getElementType()));
|
||||
Value zeroRow = getOrCreateConstant(rewriter, anchorOp, zeroAttr, paddedRowType);
|
||||
Value zeroRow = createZeroTensorConstant(paddedRowType, rewriter);
|
||||
auto kLoop = buildNormalizedScfFor(
|
||||
rewriter,
|
||||
widthLoc,
|
||||
@@ -2752,7 +2796,7 @@ static FailureOr<Value> createConvOutputFromRowStripHwc(Value inputHwc,
|
||||
c1,
|
||||
ValueRange {zeroRow},
|
||||
[&](OpBuilder&, Location reduceLoc, Value kSlice, ValueRange reduceIterArgs, SmallVectorImpl<Value>& reduceYielded) {
|
||||
Value kOffset = affineMulConst(rewriter, reduceLoc, kSlice, xbarDim, anchorOp);
|
||||
Value kOffset = arith::MulIOp::create(rewriter, reduceLoc, kSlice, cXbar);
|
||||
SmallVector<OpFoldResult> aOffsets {rewriter.getIndexAttr(0), kOffset};
|
||||
SmallVector<OpFoldResult> aSizes {rewriter.getIndexAttr(1), rewriter.getIndexAttr(xbarDim)};
|
||||
Value aTile = tensor::ExtractSliceOp::create(
|
||||
@@ -2776,9 +2820,7 @@ static FailureOr<Value> createConvOutputFromRowStripHwc(Value inputHwc,
|
||||
|
||||
Value rowResult = kLoop->results.front();
|
||||
if (state.hasBias)
|
||||
rowResult =
|
||||
spatial::SpatVAddOp::create(rewriter, widthLoc, paddedRowType, rowResult, args.inputs[1]).getResult();
|
||||
|
||||
rowResult = spatial::SpatVAddOp::create(rewriter, widthLoc, paddedRowType, rowResult, args.inputs[1]).getResult();
|
||||
Value outputRow = rowResult;
|
||||
if (state.numChannelsOut != xbarDim) {
|
||||
SmallVector<OpFoldResult> outputOffsets {rewriter.getIndexAttr(0), rewriter.getIndexAttr(0)};
|
||||
@@ -2790,25 +2832,23 @@ static FailureOr<Value> createConvOutputFromRowStripHwc(Value inputHwc,
|
||||
|
||||
Value outputFragment = tensor::ExpandShapeOp::create(rewriter,
|
||||
widthLoc,
|
||||
packedOutputSliceType,
|
||||
outputPixelType,
|
||||
outputRow,
|
||||
SmallVector<ReassociationIndices> {{0}, {1, 2}});
|
||||
SmallVector<OpFoldResult> rowOffsets {rewriter.getIndexAttr(0), widthIndex, rewriter.getIndexAttr(0)};
|
||||
SmallVector<ReassociationIndices> {{0}, {1, 2, 3}});
|
||||
SmallVector<OpFoldResult> rowOffsets {
|
||||
rewriter.getIndexAttr(0), rewriter.getIndexAttr(0), rewriter.getIndexAttr(0), widthIndex};
|
||||
SmallVector<OpFoldResult> rowSizes {
|
||||
rewriter.getIndexAttr(1), rewriter.getIndexAttr(1), rewriter.getIndexAttr(state.numChannelsOut)};
|
||||
Value nextPackedRow = tensor::InsertSliceOp::create(
|
||||
rewriter, widthLoc, outputFragment, widthIterArgs.front(), rowOffsets, rowSizes, getUnitStrides(rewriter, 3));
|
||||
widthYielded.push_back(nextPackedRow);
|
||||
rewriter.getIndexAttr(1), rewriter.getIndexAttr(state.numChannelsOut), rewriter.getIndexAttr(1),
|
||||
rewriter.getIndexAttr(1)};
|
||||
Value nextFragment = tensor::InsertSliceOp::create(
|
||||
rewriter, widthLoc, outputFragment, widthIterArgs.front(), rowOffsets, rowSizes, getUnitStrides(rewriter, 4));
|
||||
widthYielded.push_back(nextFragment);
|
||||
return success();
|
||||
});
|
||||
if (failed(widthLoop))
|
||||
return failure();
|
||||
|
||||
SmallVector<OpFoldResult> batchOffsets {args.lane, rewriter.getIndexAttr(0), rewriter.getIndexAttr(0)};
|
||||
SmallVector<OpFoldResult> batchSizes {
|
||||
rewriter.getIndexAttr(1), rewriter.getIndexAttr(state.outWidth), rewriter.getIndexAttr(state.numChannelsOut)};
|
||||
createParallelInsertSliceIntoBatchOutput(
|
||||
rewriter, loc, widthLoop->results.front(), args.outputs.front(), batchOffsets, batchSizes, getUnitStrides(rewriter, 3));
|
||||
insertRowStripFragment(widthLoop->results.front(), args.outputs.front(), state.outType, args.lane, rewriter, loc);
|
||||
return success();
|
||||
});
|
||||
if (failed(batchOp))
|
||||
@@ -2816,19 +2856,22 @@ static FailureOr<Value> createConvOutputFromRowStripHwc(Value inputHwc,
|
||||
return batchOp->getResult(0);
|
||||
}
|
||||
|
||||
static FailureOr<Value> createConvRowsFromRowStripInput(const ConvLoweringState& state,
|
||||
[[maybe_unused]] const ConvLoweringDecision& decision,
|
||||
Value rowStripInput,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
return createConvOutputFromRowStripHwc(rowStripInput, state, rewriter, loc);
|
||||
static FailureOr<Value> createConvOutputFromRowStripInput(const ConvLoweringState& state,
|
||||
[[maybe_unused]] const ConvLoweringDecision& decision,
|
||||
Value rowStripInput,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
return createConvOutputFromNchwRowStripFragments(rowStripInput, state, rewriter, loc);
|
||||
}
|
||||
|
||||
static Value createFragmentConstant(const DistributedTensorStep& step,
|
||||
RankedTensorType fragmentType,
|
||||
PatternRewriter& rewriter) {
|
||||
if (step.constantKind == DistributedTensorConstantKind::PerChannel)
|
||||
return createPerChannelConstantFragment(step.constantAttr, fragmentType, rewriter);
|
||||
if (step.constantKind == DistributedTensorConstantKind::PerChannel) {
|
||||
FailureOr<Value> constant = createPerChannelConstantFragment(step.constantAttr, fragmentType, rewriter);
|
||||
assert(succeeded(constant) && "distributed per-channel constants are classified before lowering");
|
||||
return *constant;
|
||||
}
|
||||
|
||||
Attribute splatValue = step.constantAttr.getSplatValue<Attribute>();
|
||||
return getOrCreateConstant(rewriter,
|
||||
@@ -2938,67 +2981,22 @@ static Value createFragmentReciprocalConstant(const DistributedTensorStep& step,
|
||||
}
|
||||
}
|
||||
|
||||
[[maybe_unused]] static FailureOr<DistributedTensorInfo> createDistributedTensorFromRows(Value rows,
|
||||
RankedTensorType logicalType,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
const int64_t width = logicalType.getDimSize(3);
|
||||
const int64_t height = logicalType.getDimSize(2);
|
||||
auto rowsType = cast<RankedTensorType>(rows.getType());
|
||||
auto rowSliceType =
|
||||
RankedTensorType::get({width, logicalType.getDimSize(1)}, logicalType.getElementType(), rowsType.getEncoding());
|
||||
auto channelWidthType =
|
||||
RankedTensorType::get({logicalType.getDimSize(1), width}, logicalType.getElementType(), rowsType.getEncoding());
|
||||
auto fragmentType = getRowStripFragmentType(logicalType, width);
|
||||
auto batchOp = createSpatComputeBatch(
|
||||
rewriter, loc, TypeRange {logicalType}, height, {}, ValueRange {rows}, [&](detail::SpatComputeBatchBodyArgs args) {
|
||||
Operation* anchorOp = rewriter.getInsertionBlock()->getParentOp();
|
||||
Value rowStart = affineMulConst(rewriter, loc, args.lane, width, anchorOp);
|
||||
SmallVector<OpFoldResult> rowOffsets {rowStart, rewriter.getIndexAttr(0)};
|
||||
SmallVector<OpFoldResult> rowSizes {rewriter.getIndexAttr(width), rewriter.getIndexAttr(logicalType.getDimSize(1))};
|
||||
Value rowSlice = tensor::ExtractSliceOp::create(
|
||||
rewriter, loc, rowSliceType, args.inputs.front(), rowOffsets, rowSizes, getUnitStrides(rewriter, 2));
|
||||
Value channelWidth = ONNXTransposeOp::create(
|
||||
rewriter, loc, channelWidthType, rowSlice, rewriter.getI64ArrayAttr({1, 0})).getResult();
|
||||
Value fragment = tensor::ExpandShapeOp::create(rewriter,
|
||||
loc,
|
||||
fragmentType,
|
||||
channelWidth,
|
||||
SmallVector<ReassociationIndices> {{0, 1}, {2, 3}});
|
||||
SmallVector<OpFoldResult> outputOffsets {rewriter.getIndexAttr(0), rewriter.getIndexAttr(0), args.lane,
|
||||
rewriter.getIndexAttr(0)};
|
||||
SmallVector<OpFoldResult> outputSizes {rewriter.getIndexAttr(1), rewriter.getIndexAttr(logicalType.getDimSize(1)),
|
||||
rewriter.getIndexAttr(1), rewriter.getIndexAttr(width)};
|
||||
createParallelInsertSliceIntoBatchOutput(
|
||||
rewriter, loc, fragment, args.outputs.front(), outputOffsets, outputSizes, getUnitStrides(rewriter, 4));
|
||||
return success();
|
||||
});
|
||||
if (failed(batchOp))
|
||||
return failure();
|
||||
return makeDistributedTensorInfo(batchOp->getResult(0), logicalType);
|
||||
}
|
||||
|
||||
[[maybe_unused]] static FailureOr<DistributedTensorInfo> applyDistributedPreservingStep(const DistributedTensorInfo& inputInfo,
|
||||
const DistributedTensorStep& step,
|
||||
PatternRewriter& rewriter,
|
||||
Location loc) {
|
||||
auto logicalType = inputInfo.logicalType;
|
||||
const int64_t width = logicalType.getDimSize(3);
|
||||
auto fragmentType = getRowStripFragmentType(logicalType, width);
|
||||
auto fragmentType = getRowStripFragmentType(logicalType);
|
||||
auto storageType = getRowStripStorageType(logicalType);
|
||||
auto batchOp = createSpatComputeBatch(rewriter,
|
||||
loc,
|
||||
TypeRange {logicalType},
|
||||
TypeRange {storageType},
|
||||
inputInfo.laneCount,
|
||||
{},
|
||||
ValueRange {inputInfo.storage},
|
||||
[&](detail::SpatComputeBatchBodyArgs args) {
|
||||
SmallVector<OpFoldResult> offsets {rewriter.getIndexAttr(0), rewriter.getIndexAttr(0),
|
||||
args.lane, rewriter.getIndexAttr(0)};
|
||||
SmallVector<OpFoldResult> sizes {
|
||||
rewriter.getIndexAttr(1), rewriter.getIndexAttr(logicalType.getDimSize(1)),
|
||||
rewriter.getIndexAttr(1), rewriter.getIndexAttr(width)};
|
||||
Value fragment = tensor::ExtractSliceOp::create(
|
||||
rewriter, loc, fragmentType, args.inputs.front(), offsets, sizes, getUnitStrides(rewriter, 4));
|
||||
Value fragment =
|
||||
extractRowStripFragment(args.inputs.front(), logicalType, args.lane, rewriter, loc);
|
||||
switch (step.kind) {
|
||||
case DistributedTensorOpKind::Relu:
|
||||
fragment = spatial::SpatReluOp::create(rewriter, loc, fragmentType, fragment).getResult();
|
||||
@@ -3034,8 +3032,8 @@ static Value createFragmentReciprocalConstant(const DistributedTensorStep& step,
|
||||
case DistributedTensorOpKind::Conv:
|
||||
return failure();
|
||||
}
|
||||
createParallelInsertSliceIntoBatchOutput(
|
||||
rewriter, loc, fragment, args.outputs.front(), offsets, sizes, getUnitStrides(rewriter, 4));
|
||||
insertRowStripFragment(
|
||||
fragment, args.outputs.front(), logicalType, args.lane, rewriter, loc);
|
||||
return success();
|
||||
});
|
||||
if (failed(batchOp))
|
||||
@@ -3743,6 +3741,8 @@ LogicalResult canLowerConvPlanToRowStrip(spatial::SpatConv2DPlanOp planOp) {
|
||||
return failure();
|
||||
if (state->outType.getRank() != 4 || !state->outType.hasStaticShape())
|
||||
return failure();
|
||||
if (state->hasBias && !isSupportedBiasAddValue(state->b, state->outType))
|
||||
return failure();
|
||||
|
||||
FailureOr<PimConvLoweringType> requestedStrategy = resolveRequestedConvLoweringStrategy(planOp.getOperation());
|
||||
if (failed(requestedStrategy))
|
||||
@@ -3786,7 +3786,7 @@ LogicalResult canConsumeAndProduceRowStrip(spatial::SpatConv2DPlanOp planOp) {
|
||||
return failure();
|
||||
|
||||
StringRef failureReason;
|
||||
return canDirectLowerRowStripConv(*state, failureReason) ? success() : failure();
|
||||
return canConsumeNchwRowStripFragments(*state, failureReason) ? success() : failure();
|
||||
}
|
||||
|
||||
FailureOr<Value>
|
||||
@@ -3822,17 +3822,33 @@ lowerSelectedConv2DPlan(spatial::SpatConv2DPlanOp planOp,
|
||||
if (rowStripInput) {
|
||||
if (failed(canConsumeAndProduceRowStrip(planOp)))
|
||||
return planOp.emitOpError("selected row-strip input/output layout is not supported for this Conv plan"), failure();
|
||||
return createConvRowsFromRowStripInput(*state, decision, *rowStripInput, rewriter, planOp.getLoc());
|
||||
return createConvOutputFromRowStripInput(*state, decision, *rowStripInput, rewriter, planOp.getLoc());
|
||||
}
|
||||
if (failed(canLowerConvPlanToRowStrip(planOp)))
|
||||
return planOp.emitOpError("selected row-strip layout is not supported for this Conv plan"), failure();
|
||||
FailureOr<Value> rows = createConvRowsForStrategy(*state, decision, rewriter, planOp.getLoc());
|
||||
ConvLoweringState rowState = *state;
|
||||
const bool applyBiasAfterStorage = rowState.hasBias;
|
||||
Value originalBias = rowState.b;
|
||||
if (applyBiasAfterStorage) {
|
||||
if (!isSupportedBiasAddValue(originalBias, rowState.outType))
|
||||
return planOp.emitOpError("selected row-strip Conv bias must be host-constant scalar/per-channel NCHW"),
|
||||
failure();
|
||||
rowState.b = Value();
|
||||
rowState.hasBias = false;
|
||||
}
|
||||
|
||||
FailureOr<Value> rows = createConvRowsForStrategy(rowState, decision, rewriter, planOp.getLoc());
|
||||
if (failed(rows))
|
||||
return failure();
|
||||
FailureOr<Value> packedRows = createRowStripPackedRows(*rows, *state, rewriter, planOp.getLoc());
|
||||
if (failed(packedRows))
|
||||
return planOp.emitOpError("failed to pack Conv rows into the selected row-strip physical layout"), failure();
|
||||
return *packedRows;
|
||||
FailureOr<Value> rowStripStorage = createRowStripStorageFromRows(*rows, state->outType, rewriter, planOp.getLoc());
|
||||
if (failed(rowStripStorage))
|
||||
return planOp.emitOpError("failed to build row-strip fragment storage for the selected Conv plan"), failure();
|
||||
if (applyBiasAfterStorage) {
|
||||
rowStripStorage = applyRowStripBiasAdd(*rowStripStorage, rowState.outType, originalBias, rewriter, planOp.getLoc());
|
||||
if (failed(rowStripStorage))
|
||||
return planOp.emitOpError("failed to apply row-strip Conv bias per fragment"), failure();
|
||||
}
|
||||
return *rowStripStorage;
|
||||
}
|
||||
|
||||
if (decision.strategy == PimConvLoweringDepthwise)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
|
||||
#include "src/Accelerators/PIM/Common/IR/ShapeUtils.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/Patterns.hpp"
|
||||
#include "src/Accelerators/PIM/Dialect/Spatial/SpatialOps.hpp"
|
||||
@@ -47,38 +47,28 @@ static FailureOr<Value> materializeBroadcastedConstantTensor(Value value,
|
||||
return failure();
|
||||
|
||||
const int64_t rankOffset = static_cast<int64_t>(resultShape.size() - sourceShape.size());
|
||||
for (int64_t i = 0; i < static_cast<int64_t>(resultShape.size()); ++i) {
|
||||
const int64_t sourceIndex = i - rankOffset;
|
||||
const int64_t sourceDim = sourceIndex < 0 ? 1 : sourceShape[sourceIndex];
|
||||
const int64_t resultDim = resultShape[i];
|
||||
if (sourceDim != 1 && sourceDim != resultDim)
|
||||
return failure();
|
||||
}
|
||||
|
||||
SmallVector<Attribute> sourceValues(denseAttr.getValues<Attribute>());
|
||||
SmallVector<int64_t> sourceStrides = computeRowMajorStrides(sourceShape);
|
||||
SmallVector<int64_t> resultStrides = computeRowMajorStrides(resultShape);
|
||||
|
||||
SmallVector<Attribute> sourceValues(denseAttr.getValues<Attribute>());
|
||||
SmallVector<Attribute> resultValues;
|
||||
resultValues.reserve(resultType.getNumElements());
|
||||
|
||||
for (int64_t flatIndex = 0; flatIndex < resultType.getNumElements(); ++flatIndex) {
|
||||
int64_t remaining = flatIndex;
|
||||
int64_t sourceFlatIndex = 0;
|
||||
|
||||
for (int64_t i = 0; i < static_cast<int64_t>(resultShape.size()); ++i) {
|
||||
const int64_t resultIndex = resultStrides.empty() ? 0 : remaining / resultStrides[i];
|
||||
remaining = resultStrides.empty() ? 0 : remaining % resultStrides[i];
|
||||
|
||||
const int64_t sourceIndex = i - rankOffset;
|
||||
if (sourceIndex < 0)
|
||||
continue;
|
||||
|
||||
const int64_t sourceDim = sourceShape[sourceIndex];
|
||||
const int64_t resultDim = resultShape[i];
|
||||
if (sourceDim != 1 && sourceDim != resultDim)
|
||||
return failure();
|
||||
const int64_t mappedIndex = sourceDim == 1 ? 0 : resultIndex;
|
||||
sourceFlatIndex += mappedIndex * sourceStrides[sourceIndex];
|
||||
}
|
||||
|
||||
resultValues.push_back(sourceValues[sourceFlatIndex]);
|
||||
}
|
||||
|
||||
@@ -106,7 +96,7 @@ static FailureOr<Value> materializeReciprocalTensor(Value value,
|
||||
if (failed(broadcastedValue))
|
||||
return failure();
|
||||
|
||||
auto denseAttr = dyn_cast<DenseFPElementsAttr>(getDenseConstantAttr(*broadcastedValue));
|
||||
auto denseAttr = dyn_cast<DenseFPElementsAttr>(getHostConstDenseElementsAttr(*broadcastedValue));
|
||||
if (!denseAttr)
|
||||
return failure();
|
||||
|
||||
@@ -185,10 +175,45 @@ struct DivToSpatialCompute : OpConversionPattern<ONNXDivOp> {
|
||||
}
|
||||
};
|
||||
|
||||
struct AddToSpatialCompute : OpConversionPattern<ONNXAddOp> {
|
||||
using OpConversionPattern::OpConversionPattern;
|
||||
|
||||
LogicalResult
|
||||
matchAndRewrite(ONNXAddOp op, ONNXAddOpAdaptor adaptor, ConversionPatternRewriter& rewriter) const override {
|
||||
auto resultType = dyn_cast<RankedTensorType>(op.getResult().getType());
|
||||
if (!resultType || !resultType.hasStaticShape())
|
||||
return failure();
|
||||
|
||||
FailureOr<BiasAddPlanCandidate> candidate =
|
||||
classifyBiasAddPlanCandidate(adaptor.getA(), adaptor.getB(), resultType);
|
||||
if (succeeded(candidate)) {
|
||||
auto plan = spatial::SpatBiasAddPlanOp::create(
|
||||
rewriter, op.getLoc(), resultType, candidate->data, candidate->bias, rewriter.getStringAttr("nchw"));
|
||||
rewriter.replaceOp(op, plan.getResult());
|
||||
return success();
|
||||
}
|
||||
|
||||
auto lhs = prepareElementwiseOperand(adaptor.getA(), resultType, rewriter, op.getLoc());
|
||||
if (failed(lhs))
|
||||
return failure();
|
||||
auto rhs = prepareElementwiseOperand(adaptor.getB(), resultType, rewriter, op.getLoc());
|
||||
if (failed(rhs))
|
||||
return failure();
|
||||
|
||||
auto computeOp =
|
||||
createSpatCompute<2>(rewriter, op.getLoc(), resultType, {}, ValueRange {*lhs, *rhs}, [&](Value x, Value y) {
|
||||
auto loweredOp = spatial::SpatVAddOp::create(rewriter, op.getLoc(), resultType, x, y);
|
||||
spatial::SpatYieldOp::create(rewriter, op.getLoc(), loweredOp.getResult());
|
||||
});
|
||||
rewriter.replaceOp(op, computeOp);
|
||||
return success();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void populateElementwisePatterns(RewritePatternSet& patterns, MLIRContext* ctx) {
|
||||
patterns.add<BinaryElementwiseToSpatialCompute<ONNXAddOp, spatial::SpatVAddOp>>(ctx);
|
||||
patterns.add<AddToSpatialCompute>(ctx);
|
||||
patterns.add<BinaryElementwiseToSpatialCompute<ONNXSubOp, spatial::SpatVSubOp>>(ctx);
|
||||
patterns.add<BinaryElementwiseToSpatialCompute<ONNXMulOp, spatial::SpatVMulOp>>(ctx);
|
||||
patterns.add<DivToSpatialCompute>(ctx);
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
|
||||
#include "Conversion/ONNXToSpatial/ONNXToSpatialVerifier.hpp"
|
||||
#include "src/Accelerators/PIM/Common/PimCommon.hpp"
|
||||
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/BiasAddUtils.hpp"
|
||||
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/Common/RowStripLayoutUtils.hpp"
|
||||
#include "src/Accelerators/PIM/Conversion/ONNXToSpatial/PlanLowering.hpp"
|
||||
#include "src/Accelerators/PIM/Dialect/Spatial/SpatialOps.hpp"
|
||||
#include "src/Accelerators/PIM/Pass/PIMPasses.h"
|
||||
#include "src/Accelerators/PIM/Pass/PIMPasses.h"
|
||||
|
||||
using namespace mlir;
|
||||
|
||||
@@ -19,7 +20,6 @@ namespace {
|
||||
static constexpr StringLiteral kLogicalLayout = "nchw";
|
||||
static constexpr StringLiteral kDenseLayout = "dense_nchw";
|
||||
static constexpr StringLiteral kRowStripLayout = "nchw_row_strip";
|
||||
static constexpr StringLiteral kRowStripIndexMap = "packed_hwc_rows_to_nchw";
|
||||
|
||||
enum class SelectedLayout {
|
||||
DenseNchw,
|
||||
@@ -34,6 +34,8 @@ static SelectedLayout getSelectedLayout(llvm::DenseMap<Value, SelectedLayout>& l
|
||||
static bool usesSelectedRowStrip(Operation* user, llvm::DenseMap<Value, SelectedLayout>& layouts) {
|
||||
if (auto reluPlan = dyn_cast<spatial::SpatReluPlanOp>(user))
|
||||
return getSelectedLayout(layouts, reluPlan.getResult()) == SelectedLayout::NchwRowStrip;
|
||||
if (auto biasAddPlan = dyn_cast<spatial::SpatBiasAddPlanOp>(user))
|
||||
return getSelectedLayout(layouts, biasAddPlan.getResult()) == SelectedLayout::NchwRowStrip;
|
||||
if (auto convPlan = dyn_cast<spatial::SpatConv2DPlanOp>(user))
|
||||
return getSelectedLayout(layouts, convPlan.getResult()) == SelectedLayout::NchwRowStrip;
|
||||
return false;
|
||||
@@ -49,21 +51,26 @@ static bool allUsersCanHandleRowStrip(Value value, llvm::DenseMap<Value, Selecte
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::pair<SmallVector<int64_t>, SmallVector<int64_t>> buildRowStripMetadata(RankedTensorType type) {
|
||||
SmallVector<int64_t> offsets;
|
||||
SmallVector<int64_t> sizes;
|
||||
const int64_t channels = type.getDimSize(1);
|
||||
const int64_t height = type.getDimSize(2);
|
||||
const int64_t width = type.getDimSize(3);
|
||||
offsets.reserve(height * 4);
|
||||
sizes.reserve(height * 4);
|
||||
for (int64_t row = 0; row < height; ++row) {
|
||||
offsets.append({0, 0, row, 0});
|
||||
sizes.append({1, channels, 1, width});
|
||||
static bool canConsumeRowStripAsUser(Operation* user) {
|
||||
if (isa<spatial::SpatReluPlanOp>(user))
|
||||
return true;
|
||||
if (auto biasAddPlan = dyn_cast<spatial::SpatBiasAddPlanOp>(user)) {
|
||||
auto resultType = dyn_cast<RankedTensorType>(biasAddPlan.getOutput().getType());
|
||||
return resultType && isSupportedBiasAddValue(biasAddPlan.getBias(), resultType);
|
||||
}
|
||||
return {offsets, sizes};
|
||||
if (auto convPlan = dyn_cast<spatial::SpatConv2DPlanOp>(user))
|
||||
return succeeded(canConsumeAndProduceRowStrip(convPlan));
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasRowStripConsumer(Value value) {
|
||||
for (Operation* user : value.getUsers())
|
||||
if (canConsumeRowStripAsUser(user))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool canSelectConvRowStrip(spatial::SpatConv2DPlanOp convPlan,
|
||||
llvm::DenseMap<Value, SelectedLayout>& layouts) {
|
||||
SelectedLayout inputLayout = getSelectedLayout(layouts, convPlan.getInput());
|
||||
@@ -76,6 +83,9 @@ static SelectedLayout chooseConvLayout(spatial::SpatConv2DPlanOp convPlan,
|
||||
llvm::DenseMap<Value, SelectedLayout>& layouts) {
|
||||
if (!canSelectConvRowStrip(convPlan, layouts))
|
||||
return SelectedLayout::DenseNchw;
|
||||
if (getSelectedLayout(layouts, convPlan.getInput()) != SelectedLayout::NchwRowStrip
|
||||
&& !hasRowStripConsumer(convPlan.getResult()))
|
||||
return SelectedLayout::DenseNchw;
|
||||
if (!allUsersCanHandleRowStrip(convPlan.getResult(), layouts))
|
||||
return SelectedLayout::DenseNchw;
|
||||
return SelectedLayout::NchwRowStrip;
|
||||
@@ -85,11 +95,27 @@ static SelectedLayout chooseReluLayout(spatial::SpatReluPlanOp reluPlan,
|
||||
llvm::DenseMap<Value, SelectedLayout>& layouts) {
|
||||
if (getSelectedLayout(layouts, reluPlan.getInput()) != SelectedLayout::NchwRowStrip)
|
||||
return SelectedLayout::DenseNchw;
|
||||
if (!hasRowStripConsumer(reluPlan.getResult()))
|
||||
return SelectedLayout::DenseNchw;
|
||||
if (!allUsersCanHandleRowStrip(reluPlan.getResult(), layouts))
|
||||
return SelectedLayout::DenseNchw;
|
||||
return SelectedLayout::NchwRowStrip;
|
||||
}
|
||||
|
||||
static SelectedLayout chooseBiasAddLayout(spatial::SpatBiasAddPlanOp biasAddPlan,
|
||||
llvm::DenseMap<Value, SelectedLayout>& layouts) {
|
||||
if (getSelectedLayout(layouts, biasAddPlan.getInput()) != SelectedLayout::NchwRowStrip)
|
||||
return SelectedLayout::DenseNchw;
|
||||
auto resultType = dyn_cast<RankedTensorType>(biasAddPlan.getOutput().getType());
|
||||
if (!resultType || !isSupportedBiasAddValue(biasAddPlan.getBias(), resultType))
|
||||
return SelectedLayout::DenseNchw;
|
||||
if (!hasRowStripConsumer(biasAddPlan.getResult()))
|
||||
return SelectedLayout::DenseNchw;
|
||||
if (!allUsersCanHandleRowStrip(biasAddPlan.getResult(), layouts))
|
||||
return SelectedLayout::DenseNchw;
|
||||
return SelectedLayout::NchwRowStrip;
|
||||
}
|
||||
|
||||
static spatial::SpatBlueprintOp insertRowStripBlueprint(IRRewriter& rewriter, Value value) {
|
||||
auto outputType = cast<RankedTensorType>(value.getType());
|
||||
auto [offsets, sizes] = buildRowStripMetadata(outputType);
|
||||
@@ -173,6 +199,14 @@ struct SpatialLayoutPlanningPass final : PassWrapper<SpatialLayoutPlanningPass,
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (auto biasAddPlan = dyn_cast<spatial::SpatBiasAddPlanOp>(&op)) {
|
||||
SelectedLayout selected = chooseBiasAddLayout(biasAddPlan, layouts);
|
||||
if (layouts[biasAddPlan.getResult()] != selected) {
|
||||
layouts[biasAddPlan.getResult()] = selected;
|
||||
changed = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,6 +214,8 @@ struct SpatialLayoutPlanningPass final : PassWrapper<SpatialLayoutPlanningPass,
|
||||
Value producedValue;
|
||||
if (auto convPlan = dyn_cast<spatial::SpatConv2DPlanOp>(&op))
|
||||
producedValue = convPlan.getResult();
|
||||
else if (auto biasAddPlan = dyn_cast<spatial::SpatBiasAddPlanOp>(&op))
|
||||
producedValue = biasAddPlan.getResult();
|
||||
else if (auto reluPlan = dyn_cast<spatial::SpatReluPlanOp>(&op))
|
||||
producedValue = reluPlan.getResult();
|
||||
else
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
add_onnx_mlir_rewriter(SpatialToGraphviz)
|
||||
|
||||
add_pim_library(OMSpatialToGraphviz
|
||||
SpatialToGraphviz.cpp
|
||||
|
||||
EXCLUDE_FROM_OM_LIBS
|
||||
|
||||
LINK_LIBS PUBLIC
|
||||
MLIRTosaDialect
|
||||
OMCompilerOptions
|
||||
OMPimCommon
|
||||
OMONNXOps
|
||||
SpatialOps
|
||||
|
||||
ACCEL_INCLUDE_DIRS PRIVATE
|
||||
${PIM_GENERATED_INCLUDE_DIRS}
|
||||
)
|
||||
@@ -1,259 +0,0 @@
|
||||
#include "mlir/Dialect/Tensor/IR/Tensor.h"
|
||||
#include "mlir/Dialect/Tosa/IR/TosaOps.h"
|
||||
#include "mlir/IR/Block.h"
|
||||
#include "mlir/IR/Diagnostics.h"
|
||||
#include "mlir/IR/Value.h"
|
||||
#include "mlir/Pass/Pass.h"
|
||||
#include "mlir/Support/LLVM.h"
|
||||
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
|
||||
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
|
||||
#include "src/Accelerators/PIM/Common/PimCommon.hpp"
|
||||
#include "src/Accelerators/PIM/Dialect/Spatial/SpatialOps.hpp"
|
||||
#include "src/Accelerators/PIM/Pass/PIMPasses.h"
|
||||
#include "src/Dialect/ONNX/ONNXOps.hpp"
|
||||
|
||||
#define FORMAT_OPERATION(op) 'x' << llvm::format_hex_no_prefix(reinterpret_cast<size_t>(op), 0)
|
||||
#define FORMAT_ARGUMENT(computeOpPointer, argumentNum) llvm::format("Arg_%p_%u", computeOpPointer, argumentNum)
|
||||
|
||||
using namespace mlir;
|
||||
|
||||
namespace onnx_mlir {
|
||||
|
||||
namespace {
|
||||
|
||||
struct SpatialToGraphvizPass : public PassWrapper<SpatialToGraphvizPass, OperationPass<ModuleOp>> {
|
||||
|
||||
MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(SpatialToGraphvizPass)
|
||||
|
||||
StringRef getArgument() const override { return "convert-spatial-to-graphviz"; }
|
||||
|
||||
StringRef getDescription() const override { return "Lower ONNX ops to Spatial ops."; }
|
||||
|
||||
SpatialToGraphvizPass(raw_ostream& os = llvm::errs())
|
||||
: os(os) {}
|
||||
SpatialToGraphvizPass(const SpatialToGraphvizPass& pass)
|
||||
: SpatialToGraphvizPass(pass.os) {}
|
||||
void runOnOperation() final;
|
||||
|
||||
private:
|
||||
raw_ostream& os;
|
||||
|
||||
/**
|
||||
* Draws the subgraph for a given spatial::SpatCompute, including:
|
||||
* 1. Input nodes (block arguments)
|
||||
* 2. Operations
|
||||
* 3. Edges between yield (output) and its users
|
||||
*
|
||||
* @param op The spatial::SpatCompute to draw the subgraph for.
|
||||
* @param computeNum The number of the compute operation.
|
||||
*/
|
||||
void drawComputeOpSubgraph(spatial::SpatCompute op, size_t computeNum) {
|
||||
os << "\tsubgraph cluster" << computeNum << " {\n\t\tlabel=\"Compute" << computeNum << "\";\n"
|
||||
<< "\t\tstyle=filled;\n"
|
||||
<< "\t\tcolor=lightblue;\n";
|
||||
|
||||
Block& block = op.getBody().front();
|
||||
|
||||
// Inputs
|
||||
size_t inputNum = 0;
|
||||
for (BlockArgument& input : block.getArguments()) {
|
||||
|
||||
auto fromOp = FORMAT_ARGUMENT(op.getOperation(), inputNum);
|
||||
|
||||
os << "\t\t" << fromOp << " [label=\"Arg" << inputNum << "\",shape=box];\n";
|
||||
for (auto userOp : input.getUsers())
|
||||
os << "\t\t" << fromOp << " -> " << FORMAT_OPERATION(userOp) << ";\n";
|
||||
inputNum++;
|
||||
}
|
||||
|
||||
// Iterate operations
|
||||
for (auto& childOp : block.getOperations()) {
|
||||
os << "\t\t" << FORMAT_OPERATION(&childOp) << " [label=\"" << childOp.getName() << "\"];\n";
|
||||
|
||||
drawEdgesFromOpToItsUsers(&childOp);
|
||||
}
|
||||
|
||||
os << "\t}\n";
|
||||
|
||||
// Draw edges from the yield to the users of this computeOp
|
||||
Operation* yieldOp = block.getTerminator();
|
||||
if (!isa<spatial::SpatYieldOp>(yieldOp)) {
|
||||
yieldOp->emitError("Terminator of block must be YieldOp ???");
|
||||
signalPassFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto computeOpResult : op->getResults()) {
|
||||
for (auto& computeOpUse : computeOpResult.getUses()) {
|
||||
auto toOp = FORMAT_ARGUMENT(computeOpUse.getOwner(), computeOpUse.getOperandNumber());
|
||||
os << "\t" << FORMAT_OPERATION(yieldOp) << " -> " << toOp << ";\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Draws the subgraph for a concatOp.
|
||||
*
|
||||
* This function draws a subgraph for a concatOp. The subgraph consists of a
|
||||
* node for each input of the concatOp, as well as an output node. Edges are
|
||||
* created from the output node to each user of the concatOp.
|
||||
*
|
||||
* @param concatOp The concatOp for which the subgraph is drawn.
|
||||
* @param concatOpNum The number of the concatOp.
|
||||
*/
|
||||
void drawConcatOpSubgraph(Operation* concatOp, size_t concatOpNum) {
|
||||
os << "\tsubgraph clusterconcat" << concatOpNum << " {\n\t\tlabel=\"ConcatOp" << concatOpNum << "\";\n"
|
||||
<< "\t\tstyle=filled;\n"
|
||||
<< "\t\tcolor=orange;\n";
|
||||
|
||||
// Inputs
|
||||
size_t inputNum = 0;
|
||||
for (Value input : concatOp->getOperands()) {
|
||||
auto fromOp = FORMAT_ARGUMENT(concatOp, inputNum);
|
||||
|
||||
os << "\t\t" << fromOp << " [label=\"Input" << inputNum << "\"];\n";
|
||||
for (auto userOp : input.getUsers())
|
||||
os << "\t\t" << fromOp << " -> " << FORMAT_OPERATION(userOp) << ";\n";
|
||||
inputNum++;
|
||||
}
|
||||
|
||||
// Output
|
||||
os << "\t\t" << FORMAT_OPERATION(concatOp) << " [label=Out];\n";
|
||||
|
||||
os << "\t}\n";
|
||||
|
||||
// Edges from output to users
|
||||
|
||||
for (auto& computeOpUse : concatOp->getResult(0).getUses()) {
|
||||
os << "\t" << FORMAT_OPERATION(concatOp) << " -> "
|
||||
<< FORMAT_ARGUMENT(computeOpUse.getOwner(), computeOpUse.getOperandNumber()) << ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the ExtractSliceOp in the graph visualization.
|
||||
*
|
||||
* This function takes a tensor::ExtractSliceOp and adds the corresponding
|
||||
* node and edges to the graph visualization. It creates a node with the
|
||||
* label as the static offsets attribute of the sliceOp, and connects it to
|
||||
* the compute operations that use the result of the sliceOp.
|
||||
*
|
||||
* @param sliceOp The tensor::ExtractSliceOp to be drawn in the graph
|
||||
* visualization.
|
||||
*/
|
||||
void drawExtractSliceOp(tensor::ExtractSliceOp sliceOp) {
|
||||
auto nodeId = FORMAT_ARGUMENT(sliceOp.getOperation(), 0);
|
||||
os << "\t" << nodeId << " [label=\"Slice: ";
|
||||
sliceOp.getStaticOffsetsAttr().print(os);
|
||||
os << "\",color=lawngreen];\n";
|
||||
|
||||
for (auto& computeOpUse : sliceOp.getResult().getUses()) {
|
||||
os << "\t" << nodeId << " -> " << FORMAT_ARGUMENT(computeOpUse.getOwner(), computeOpUse.getOperandNumber())
|
||||
<< ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
void drawBiasTileOp(tensor::ExtractSliceOp sliceOp) {
|
||||
auto nodeId = FORMAT_ARGUMENT(sliceOp.getOperation(), 0);
|
||||
os << "\t" << nodeId << " [label=\"Bias: ";
|
||||
sliceOp.getStaticOffsetsAttr().print(os);
|
||||
os << "\",color=lightpink];\n";
|
||||
|
||||
for (auto user : sliceOp.getResult().getUsers())
|
||||
os << "\t" << nodeId << " -> " << FORMAT_OPERATION(user) << ";\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws edges from the given operation to its users.
|
||||
*
|
||||
* @param fromOp The operation from which the edges are drawn.
|
||||
*/
|
||||
void drawEdgesFromOpToItsUsers(mlir::Operation* fromOp) {
|
||||
for (auto result : fromOp->getResults())
|
||||
for (auto userOp : result.getUsers())
|
||||
os << "\t\t" << FORMAT_OPERATION(fromOp) << " -> " << FORMAT_OPERATION(userOp) << ";\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws input node and edges for the given `funcOp`.
|
||||
*
|
||||
* @param funcOp The `funcOp` for which to draw input nodes and edges.
|
||||
*/
|
||||
void drawInputNodesAndEdges(func::FuncOp& funcOp) {
|
||||
os << "\tinput [label=\"Module Input\",color=green];\n";
|
||||
|
||||
size_t funcOpArgNum = 0;
|
||||
for (BlockArgument& arg : funcOp.getArguments()) {
|
||||
|
||||
for (auto& useOp : arg.getUses()) {
|
||||
os << "\tinput -> " << FORMAT_ARGUMENT(useOp.getOwner(), useOp.getOperandNumber()) << "[label=" << funcOpArgNum
|
||||
<< "];\n";
|
||||
}
|
||||
funcOpArgNum++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void SpatialToGraphvizPass::runOnOperation() {
|
||||
ModuleOp module = getOperation();
|
||||
|
||||
auto entryFunc = getPimEntryFunc(module);
|
||||
if (failed(entryFunc)) {
|
||||
module.emitError("failed to locate the PIM entry function for Spatial graph visualization");
|
||||
signalPassFailure();
|
||||
return;
|
||||
}
|
||||
func::FuncOp func = *entryFunc;
|
||||
|
||||
os << "digraph G {\n"
|
||||
<< "\tnode [style=filled,color=white];\n";
|
||||
|
||||
size_t computeNum = 0;
|
||||
size_t concatNum = 0;
|
||||
|
||||
// Iterate over the ComputeOps within FuncOp:
|
||||
// 1. Print their subgraph
|
||||
// 2. Print the edges from its inputs to its outputs
|
||||
for (Operation& op : func.getOps()) {
|
||||
if (auto computeOp = dyn_cast<spatial::SpatCompute>(op)) {
|
||||
drawComputeOpSubgraph(computeOp, computeNum++);
|
||||
}
|
||||
else if (auto concatOp = dyn_cast<tensor::ConcatOp>(op)) {
|
||||
drawConcatOpSubgraph(concatOp, concatNum++);
|
||||
}
|
||||
else if (auto extractSliceOp = dyn_cast<tensor::ExtractSliceOp>(op)) {
|
||||
auto producerOp = extractSliceOp->getOperand(0).getDefiningOp();
|
||||
if (producerOp) {
|
||||
// Skip extractSliceOp if producer is constant weights (ONNXConstantOp)
|
||||
if (llvm::isa<ONNXConstantOp>(producerOp))
|
||||
continue;
|
||||
// If produced by tosa::ReshapeOp (i.e. it is a bias tile) connect
|
||||
// directly to its user, which is not a ComputeOp argument.
|
||||
if (llvm::isa<tosa::ReshapeOp>(producerOp)) {
|
||||
drawBiasTileOp(extractSliceOp);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
drawExtractSliceOp(extractSliceOp);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw input node, and edges to it users
|
||||
drawInputNodesAndEdges(func);
|
||||
|
||||
// Draw output node (use the return Operation - argument number=0 - as nodeId)
|
||||
auto returnOp = func.getBody().front().getTerminator();
|
||||
os << '\t' << FORMAT_ARGUMENT(returnOp, 0) << " [label=\"Module Output\",color=green];\n";
|
||||
|
||||
os << "}\n";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Pass> createSpatialToGraphvizPass() { return std::make_unique<SpatialToGraphvizPass>(); }
|
||||
|
||||
} // namespace onnx_mlir
|
||||
@@ -302,76 +302,87 @@ void PimBufferizationPass::annotateWeightsMemrefs(ModuleOp moduleOp, func::FuncO
|
||||
|
||||
LogicalResult PimBufferizationPass::verifyContiguousRuntimeOperands(ModuleOp moduleOp) const {
|
||||
bool hasFailure = false;
|
||||
moduleOp.walk([&](Operation* op) {
|
||||
auto verifyOperand = [&](Value operand, unsigned operandIndex) {
|
||||
if (!isa<BaseMemRefType>(operand.getType()))
|
||||
return;
|
||||
if (succeeded(resolveContiguousAddress(operand)) || succeeded(compileContiguousAddressExpr(operand)))
|
||||
return;
|
||||
op->emitOpError() << "operand #" << operandIndex
|
||||
<< " is not backed by contiguous addressable storage after PIM bufferization";
|
||||
hasFailure = true;
|
||||
};
|
||||
|
||||
if (auto memCopyOp = dyn_cast<PimMemCopyOp>(op)) {
|
||||
if (!pim::isNormalizedCopyOp(memCopyOp)) {
|
||||
memCopyOp.emitOpError("must use base memref operands plus explicit byte offsets after bufferization");
|
||||
hasFailure = true;
|
||||
}
|
||||
verifyOperand(memCopyOp.getTarget(), 0);
|
||||
verifyOperand(memCopyOp.getSource(), 1);
|
||||
return;
|
||||
}
|
||||
if (auto loadOp = dyn_cast<PimMemCopyHostToDevOp>(op)) {
|
||||
if (!pim::isNormalizedCopyOp(loadOp)) {
|
||||
loadOp.emitOpError("must use base memref operands plus explicit byte offsets after bufferization");
|
||||
hasFailure = true;
|
||||
}
|
||||
verifyOperand(loadOp.getDeviceTarget(), 2);
|
||||
verifyOperand(loadOp.getHostSource(), 3);
|
||||
return;
|
||||
}
|
||||
if (auto storeOp = dyn_cast<PimMemCopyDevToHostOp>(op)) {
|
||||
if (!pim::isNormalizedCopyOp(storeOp)) {
|
||||
storeOp.emitOpError("must use base memref operands plus explicit byte offsets after bufferization");
|
||||
hasFailure = true;
|
||||
}
|
||||
verifyOperand(storeOp.getHostTarget(), 2);
|
||||
verifyOperand(storeOp.getDeviceSource(), 3);
|
||||
return;
|
||||
}
|
||||
if (auto sendOp = dyn_cast<PimSendOp>(op)) {
|
||||
verifyOperand(sendOp.getInput(), 0);
|
||||
return;
|
||||
}
|
||||
if (auto receiveOp = dyn_cast<PimReceiveOp>(op)) {
|
||||
verifyOperand(receiveOp.getOutputBuffer(), 0);
|
||||
return;
|
||||
}
|
||||
if (auto concatOp = dyn_cast<PimConcatOp>(op)) {
|
||||
verifyOperand(concatOp.getOutputBuffer(), 0);
|
||||
for (auto inputAndIndex : llvm::enumerate(concatOp.getInputs()))
|
||||
verifyOperand(inputAndIndex.value(), inputAndIndex.index() + 1);
|
||||
return;
|
||||
}
|
||||
if (isa<PimTransposeOp,
|
||||
PimVMMOp,
|
||||
PimVVAddOp,
|
||||
PimVVSubOp,
|
||||
PimVVMulOp,
|
||||
PimVVMaxOp,
|
||||
PimVVDMulOp,
|
||||
PimVAvgOp,
|
||||
PimVReluOp,
|
||||
PimVTanhOp,
|
||||
PimVSigmOp,
|
||||
PimVSoftmaxOp>(op)) {
|
||||
for (auto operandAndIndex : llvm::enumerate(op->getOperands())) {
|
||||
if (auto vmmOp = dyn_cast<PimVMMOp>(op); vmmOp && operandAndIndex.index() == 0)
|
||||
continue;
|
||||
verifyOperand(operandAndIndex.value(), operandAndIndex.index());
|
||||
}
|
||||
}
|
||||
auto verifyWithKnowledge = [&](auto coreLikeOp, const StaticValueKnowledge& initialKnowledge) {
|
||||
(void) walkPimCoreBlockStructurally(
|
||||
coreLikeOp.getBody().front(), initialKnowledge, [&](Operation& op, const StaticValueKnowledge& knowledge) {
|
||||
auto verifyOperand = [&](Value operand, unsigned operandIndex) {
|
||||
if (!isa<BaseMemRefType>(operand.getType()))
|
||||
return;
|
||||
if (succeeded(resolveContiguousAddress(operand, knowledge)) || succeeded(compileContiguousAddressExpr(operand)))
|
||||
return;
|
||||
op.emitOpError() << "operand #" << operandIndex
|
||||
<< " is not backed by contiguous addressable storage after PIM bufferization";
|
||||
hasFailure = true;
|
||||
};
|
||||
|
||||
if (auto memCopyOp = dyn_cast<PimMemCopyOp>(&op)) {
|
||||
if (!pim::isNormalizedCopyOp(memCopyOp)) {
|
||||
memCopyOp.emitOpError("must use base memref operands plus explicit byte offsets after bufferization");
|
||||
hasFailure = true;
|
||||
}
|
||||
verifyOperand(memCopyOp.getTarget(), 0);
|
||||
verifyOperand(memCopyOp.getSource(), 1);
|
||||
return success();
|
||||
}
|
||||
if (auto loadOp = dyn_cast<PimMemCopyHostToDevOp>(&op)) {
|
||||
if (!pim::isNormalizedCopyOp(loadOp)) {
|
||||
loadOp.emitOpError("must use base memref operands plus explicit byte offsets after bufferization");
|
||||
hasFailure = true;
|
||||
}
|
||||
verifyOperand(loadOp.getDeviceTarget(), 2);
|
||||
verifyOperand(loadOp.getHostSource(), 3);
|
||||
return success();
|
||||
}
|
||||
if (auto storeOp = dyn_cast<PimMemCopyDevToHostOp>(&op)) {
|
||||
if (!pim::isNormalizedCopyOp(storeOp)) {
|
||||
storeOp.emitOpError("must use base memref operands plus explicit byte offsets after bufferization");
|
||||
hasFailure = true;
|
||||
}
|
||||
verifyOperand(storeOp.getHostTarget(), 2);
|
||||
verifyOperand(storeOp.getDeviceSource(), 3);
|
||||
return success();
|
||||
}
|
||||
if (auto sendOp = dyn_cast<PimSendOp>(&op)) {
|
||||
verifyOperand(sendOp.getInput(), 0);
|
||||
return success();
|
||||
}
|
||||
if (auto receiveOp = dyn_cast<PimReceiveOp>(&op)) {
|
||||
verifyOperand(receiveOp.getOutputBuffer(), 0);
|
||||
return success();
|
||||
}
|
||||
if (auto concatOp = dyn_cast<PimConcatOp>(&op)) {
|
||||
verifyOperand(concatOp.getOutputBuffer(), 0);
|
||||
for (auto inputAndIndex : llvm::enumerate(concatOp.getInputs()))
|
||||
verifyOperand(inputAndIndex.value(), inputAndIndex.index() + 1);
|
||||
return success();
|
||||
}
|
||||
if (isa<PimTransposeOp,
|
||||
PimVMMOp,
|
||||
PimVVAddOp,
|
||||
PimVVSubOp,
|
||||
PimVVMulOp,
|
||||
PimVVMaxOp,
|
||||
PimVVDMulOp,
|
||||
PimVAvgOp,
|
||||
PimVReluOp,
|
||||
PimVTanhOp,
|
||||
PimVSigmOp,
|
||||
PimVSoftmaxOp>(&op)) {
|
||||
for (auto operandAndIndex : llvm::enumerate(op.getOperands())) {
|
||||
if (auto vmmOp = dyn_cast<PimVMMOp>(&op); vmmOp && operandAndIndex.index() == 0)
|
||||
continue;
|
||||
verifyOperand(operandAndIndex.value(), operandAndIndex.index());
|
||||
}
|
||||
}
|
||||
return success();
|
||||
});
|
||||
};
|
||||
|
||||
moduleOp.walk([&](pim::PimCoreOp coreOp) { verifyWithKnowledge(coreOp, seedCoreKnowledge(coreOp)); });
|
||||
moduleOp.walk([&](pim::PimCoreBatchOp coreBatchOp) {
|
||||
StaticValueKnowledge knowledge = seedCoreBatchKnowledge(coreBatchOp, 0);
|
||||
verifyWithKnowledge(coreBatchOp, knowledge);
|
||||
});
|
||||
|
||||
if (hasFailure) {
|
||||
|
||||
@@ -232,6 +232,22 @@ def SpatReluPlanOp : SpatOp<"relu_plan", []> {
|
||||
let hasVerifier = 1;
|
||||
}
|
||||
|
||||
def SpatBiasAddPlanOp : SpatOp<"bias_add_plan", []> {
|
||||
let summary = "Layout-aware Conv-style bias add planning op";
|
||||
|
||||
let arguments = (ins
|
||||
SpatTensor:$input,
|
||||
SpatTensor:$bias,
|
||||
StrAttr:$logicalLayout
|
||||
);
|
||||
|
||||
let results = (outs
|
||||
SpatTensor:$output
|
||||
);
|
||||
|
||||
let hasVerifier = 1;
|
||||
}
|
||||
|
||||
def SpatBlueprintOp : SpatOp<"blueprint", []> {
|
||||
let summary = "Blueprint for assembling logical tensors from published fragments";
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "mlir/Dialect/Affine/IR/AffineOps.h"
|
||||
#include "mlir/Dialect/Arith/IR/Arith.h"
|
||||
#include "mlir/Dialect/SCF/IR/SCF.h"
|
||||
#include "mlir/Dialect/Tensor/IR/Tensor.h"
|
||||
#include "mlir/IR/AffineExpr.h"
|
||||
#include "mlir/IR/Block.h"
|
||||
@@ -59,6 +60,21 @@ static LogicalResult verifyStaticWeights(ComputeOpTy computeOp, StringRef kind)
|
||||
return success();
|
||||
}
|
||||
|
||||
static bool isStaticScfForInductionVar(Value value) {
|
||||
auto blockArg = dyn_cast<BlockArgument>(value);
|
||||
if (!blockArg)
|
||||
return false;
|
||||
|
||||
auto loop = dyn_cast_or_null<scf::ForOp>(blockArg.getOwner()->getParentOp());
|
||||
if (!loop || loop.getInductionVar() != value)
|
||||
return false;
|
||||
|
||||
std::optional<int64_t> lowerBound = matchConstantIndexValue(loop.getLowerBound());
|
||||
std::optional<int64_t> upperBound = matchConstantIndexValue(loop.getUpperBound());
|
||||
std::optional<int64_t> step = matchConstantIndexValue(loop.getStep());
|
||||
return lowerBound && upperBound && step && *step > 0 && *upperBound >= *lowerBound;
|
||||
}
|
||||
|
||||
static bool isStaticIndexExpr(Value value) {
|
||||
if (matchConstantIndexValue(value))
|
||||
return true;
|
||||
@@ -80,7 +96,7 @@ static bool isStaticIndexExpr(Value value) {
|
||||
}
|
||||
|
||||
static bool isSupportedLaneOffsetExpr(Value value, BlockArgument laneArg) {
|
||||
if (value == laneArg || isStaticIndexExpr(value))
|
||||
if (value == laneArg || isStaticIndexExpr(value) || isStaticScfForInductionVar(value))
|
||||
return true;
|
||||
|
||||
auto affineApply = value.getDefiningOp<affine::AffineApplyOp>();
|
||||
@@ -436,6 +452,39 @@ LogicalResult SpatReluPlanOp::verify() {
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult SpatBiasAddPlanOp::verify() {
|
||||
if (failed(verifyPlanTensorTypes(getOperation(), getInput(), getOutput(), "spat.bias_add_plan")))
|
||||
return failure();
|
||||
if (!isKnownLogicalLayout(getLogicalLayout()))
|
||||
return emitError("requires a known logical layout");
|
||||
|
||||
auto inputType = dyn_cast<RankedTensorType>(getInput().getType());
|
||||
auto biasType = dyn_cast<RankedTensorType>(getBias().getType());
|
||||
auto outputType = dyn_cast<RankedTensorType>(getOutput().getType());
|
||||
if (!inputType || !biasType || !outputType)
|
||||
return emitError("requires ranked tensor input, bias, and output");
|
||||
if (!inputType.hasStaticShape() || !biasType.hasStaticShape() || !outputType.hasStaticShape())
|
||||
return emitError("requires static tensor input, bias, and output");
|
||||
if (inputType != outputType)
|
||||
return emitError("requires matching input and output tensor types");
|
||||
if (outputType.getRank() != 4)
|
||||
return emitError("requires rank-4 input/output tensors");
|
||||
if (getLogicalLayout() != "nchw")
|
||||
return emitError("requires logical layout \"nchw\"");
|
||||
if (biasType.getElementType() != outputType.getElementType())
|
||||
return emitError("requires bias element type to match the output element type");
|
||||
|
||||
ArrayRef<int64_t> biasShape = biasType.getShape();
|
||||
const int64_t channels = outputType.getDimSize(1);
|
||||
const bool supported = biasShape.empty() || (biasShape.size() == 1 && biasShape[0] == channels)
|
||||
|| (biasShape.size() == 2 && biasShape[0] == 1 && biasShape[1] == channels)
|
||||
|| (biasShape.size() == 4 && biasShape[0] == 1 && biasShape[1] == channels
|
||||
&& biasShape[2] == 1 && biasShape[3] == 1);
|
||||
if (!supported)
|
||||
return emitError("requires scalar or per-channel bias broadcastable over NCHW");
|
||||
return success();
|
||||
}
|
||||
|
||||
LogicalResult SpatBlueprintOp::verify() {
|
||||
auto modeAttr = getModeAttr();
|
||||
bool isFragmentAssembly = modeAttr && modeAttr.getValue() == "fragment_assembly";
|
||||
|
||||
+2478
-462
File diff suppressed because it is too large
Load Diff
@@ -121,6 +121,51 @@ struct CompactRunPlan {
|
||||
llvm::SmallVector<RunOutputDemand, 4> outputs;
|
||||
};
|
||||
|
||||
struct ScalarPeerEdgeKey {
|
||||
int64_t sourceCore = 0;
|
||||
int64_t targetCore = 0;
|
||||
mlir::Type payloadType;
|
||||
};
|
||||
|
||||
struct ScalarPeerReceiveKey {
|
||||
int64_t sourceCore = 0;
|
||||
int64_t targetCore = 0;
|
||||
mlir::Type payloadType;
|
||||
std::optional<int64_t> channelId;
|
||||
};
|
||||
|
||||
struct PendingScalarSend {
|
||||
ClassId sourceClass = 0;
|
||||
int64_t sourceCore = 0;
|
||||
int64_t targetCore = 0;
|
||||
mlir::Type payloadType;
|
||||
ScalarPeerEdgeKey waitForReceive;
|
||||
mlir::Operation* payloadAnchor = nullptr;
|
||||
mlir::Value payload;
|
||||
MessageVector messages;
|
||||
mlir::Location loc;
|
||||
};
|
||||
|
||||
struct PendingProjectedScalarSend {
|
||||
ClassId sourceClass = 0;
|
||||
int64_t sourceCore = 0;
|
||||
int64_t targetCore = 0;
|
||||
mlir::Type payloadType;
|
||||
ScalarPeerEdgeKey waitForReceive;
|
||||
mlir::Operation* payloadAnchor = nullptr;
|
||||
mlir::Value payload;
|
||||
MessageVector messages;
|
||||
ProjectedTransferDescriptor descriptor;
|
||||
mlir::Location loc;
|
||||
};
|
||||
|
||||
struct PendingProjectedInputSend {
|
||||
ClassId sourceClass = 0;
|
||||
mlir::Value payload;
|
||||
llvm::SmallVector<ProjectedInputTransferFragment*, 4> fragments;
|
||||
mlir::Location loc;
|
||||
};
|
||||
|
||||
enum class BatchInputDemandKind {
|
||||
LaneFragment,
|
||||
ProjectedFragment,
|
||||
@@ -235,10 +280,19 @@ struct MaterializerState {
|
||||
projectedTransfers;
|
||||
llvm::DenseMap<mlir::Operation*, llvm::DenseMap<ClassId, ProjectedExtractReplacement>>
|
||||
projectedExtractReplacements;
|
||||
llvm::DenseMap<mlir::Operation*, llvm::DenseMap<ClassId, ProjectedInputTransferPlan>>
|
||||
projectedInputTransferPlans;
|
||||
AvailableValueStore availableValues;
|
||||
llvm::DenseMap<mlir::Value, mlir::Value> hostReplacements;
|
||||
llvm::DenseMap<mlir::Value, ClassId> hostOutputOwners;
|
||||
llvm::SmallVector<PendingProjectedHostOutputFragment, 32> pendingProjectedHostOutputFragments;
|
||||
llvm::SmallVector<ScalarPeerEdgeKey, 16> plannedScalarPeerReceives;
|
||||
llvm::SmallVector<ScalarPeerEdgeKey, 8> materializedScalarPeerReceives;
|
||||
llvm::SmallVector<PendingScalarSend, 8> pendingScalarSends;
|
||||
llvm::SmallVector<PendingProjectedScalarSend, 8> pendingProjectedScalarSends;
|
||||
llvm::SmallVector<PendingProjectedInputSend, 16> pendingProjectedInputSends;
|
||||
llvm::DenseSet<ClassId> projectedInputPhaseBarrierClasses;
|
||||
llvm::DenseMap<ClassId, unsigned> pendingProjectedHighToLowReceives;
|
||||
llvm::DenseSet<mlir::Operation*> oldComputeOps;
|
||||
|
||||
MaterializerState(mlir::func::FuncOp func, const MergeScheduleResult& schedule, int64_t& nextChannelId)
|
||||
|
||||
@@ -47,6 +47,24 @@ struct ProjectedExtractReplacement {
|
||||
ProjectedFragmentLayout layout;
|
||||
};
|
||||
|
||||
struct ProjectedInputTransferFragment {
|
||||
ProducerKey producer;
|
||||
llvm::SmallVector<int64_t, 4> fragmentOffsets;
|
||||
unsigned targetLane = 0;
|
||||
unsigned ordinal = 0;
|
||||
int64_t channelId = 0;
|
||||
int32_t sourceCoreId = 0;
|
||||
int32_t targetCoreId = 0;
|
||||
bool sendEmitted = false;
|
||||
};
|
||||
|
||||
struct ProjectedInputTransferPlan {
|
||||
ProjectedBatchInputKey inputKey;
|
||||
mlir::Operation* extractOp = nullptr;
|
||||
ProjectedFragmentLayout layout;
|
||||
llvm::SmallVector<ProjectedInputTransferFragment, 8> fragments;
|
||||
};
|
||||
|
||||
struct PendingProjectedHostOutputFragment {
|
||||
mlir::Value originalOutput;
|
||||
ClassId sourceClass = 0;
|
||||
|
||||
@@ -11,8 +11,6 @@ std::unique_ptr<mlir::Pass> createONNXToSpatialPass();
|
||||
std::unique_ptr<mlir::Pass> createSpatialLayoutPlanningPass();
|
||||
std::unique_ptr<mlir::Pass> createLowerSpatialPlansPass();
|
||||
|
||||
std::unique_ptr<mlir::Pass> createSpatialToGraphvizPass();
|
||||
|
||||
std::unique_ptr<mlir::Pass> createSpatialToPimPass();
|
||||
|
||||
std::unique_ptr<mlir::Pass> createPimBufferizationPass();
|
||||
|
||||
@@ -74,7 +74,6 @@ void PimAccelerator::registerPasses(int optLevel) const {
|
||||
registerPass(createONNXToSpatialPass);
|
||||
registerPass(createSpatialLayoutPlanningPass);
|
||||
registerPass(createLowerSpatialPlansPass);
|
||||
registerPass(createSpatialToGraphvizPass);
|
||||
registerPass(createSpatialToPimPass);
|
||||
registerPass(createPimBufferizationPass);
|
||||
registerPass(createPimMemoryCoalescingPass);
|
||||
|
||||
Regular → Executable
Reference in New Issue
Block a user