CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
429 lines
17 KiB
C++
429 lines
17 KiB
C++
//===- SPIRVTosaOps.cpp - MLIR SPIR-V Tosa operations ---------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines the Tosa operations in the SPIR-V dialect.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "mlir/Dialect/SPIRV/IR/SPIRVOps.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/Support/InterleavedRange.h"
|
|
#include <algorithm>
|
|
|
|
namespace mlir::spirv {
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// SPIRV Tosa Custom formatters
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
ParseResult parseSPIRV_I32_1DArmTensor(OpAsmParser &parser,
|
|
DenseIntElementsAttr &attr) {
|
|
SmallVector<int32_t, 6> elements;
|
|
auto f = [&]() {
|
|
int32_t value;
|
|
ParseResult r = parser.parseInteger(value);
|
|
elements.push_back(value);
|
|
return r;
|
|
};
|
|
if (parser.parseCommaSeparatedList(
|
|
OpAsmParser::Delimiter::Square, f,
|
|
"parsing values in integer list attribute")) {
|
|
return failure();
|
|
}
|
|
|
|
auto i32Type = IntegerType::get(parser.getContext(), 32);
|
|
auto type = TensorArmType::get(
|
|
ArrayRef{static_cast<int64_t>(elements.size())}, i32Type);
|
|
attr = DenseIntElementsAttr::get(type, elements);
|
|
return success();
|
|
}
|
|
|
|
void printSPIRV_I32_1DArmTensor(OpAsmPrinter &printer, Operation *,
|
|
DenseIntElementsAttr attr) {
|
|
printer << llvm::interleaved_array(
|
|
llvm::map_range(attr.getValues<APInt>(),
|
|
[](const APInt &a) { return a.getSExtValue(); }));
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// SPIRV Tosa Custom verifiers
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
|
|
int64_t getIntValue(DenseIntElementsAttr attr, size_t idx) {
|
|
return attr.getValues<APInt>()[idx].getSExtValue();
|
|
}
|
|
|
|
LogicalResult verifyPool2DOutputDim(Operation *op, int64_t inputSize,
|
|
int64_t outputSize, int64_t kernelSize,
|
|
int64_t strideSize, int64_t padBefore,
|
|
int64_t padAfter, StringRef dimName,
|
|
StringRef dimAxis, StringRef padBeforeName,
|
|
StringRef padAfterName) {
|
|
if (ShapedType::isDynamic(inputSize))
|
|
return success();
|
|
|
|
const int64_t numerator = inputSize + padBefore + padAfter - kernelSize;
|
|
if (numerator % strideSize != 0)
|
|
return op->emitOpError("expected input_")
|
|
<< dimName << " + pad_" << padBeforeName << " + pad_" << padAfterName
|
|
<< " - kernel_" << dimAxis << " to be wholly divisible by stride_"
|
|
<< dimAxis << ", got (" << inputSize << " + " << padBefore << " + "
|
|
<< padAfter << " - " << kernelSize << ") / " << strideSize;
|
|
|
|
const int64_t calculatedOutput = numerator / strideSize + 1;
|
|
if (!ShapedType::isDynamic(outputSize) && outputSize != calculatedOutput)
|
|
return op->emitOpError("failed to verify that shapes of input and output "
|
|
"must satisfy [N,IH,IW,C] and [N,OH,OW,C], with "
|
|
"OH = ((IH + pad_top + pad_bottom - kernel_y) / "
|
|
"stride_y) + 1 and OW = ((IW + pad_left + "
|
|
"pad_right - kernel_x) / stride_x) + 1");
|
|
|
|
return success();
|
|
}
|
|
|
|
LogicalResult verifyPool2DOp(Operation *op, DenseIntElementsAttr kernel,
|
|
DenseIntElementsAttr stride,
|
|
DenseIntElementsAttr pad, TensorArmType inputType,
|
|
TensorArmType outputType) {
|
|
|
|
if (!inputType.hasRank() || !outputType.hasRank())
|
|
return success();
|
|
|
|
if (failed(verifyPool2DOutputDim(
|
|
op, inputType.getDimSize(1), outputType.getDimSize(1),
|
|
getIntValue(kernel, 0), getIntValue(stride, 0), getIntValue(pad, 0),
|
|
getIntValue(pad, 1), "height", "y", "top", "bottom")))
|
|
return failure();
|
|
|
|
if (failed(verifyPool2DOutputDim(
|
|
op, inputType.getDimSize(2), outputType.getDimSize(2),
|
|
getIntValue(kernel, 1), getIntValue(stride, 1), getIntValue(pad, 2),
|
|
getIntValue(pad, 3), "width", "x", "left", "right")))
|
|
return failure();
|
|
|
|
return success();
|
|
}
|
|
|
|
LogicalResult verifyConvolutionOutputDim(int64_t inputSize, int64_t kernelSize,
|
|
int64_t outputSize, int64_t padBefore,
|
|
int64_t padAfter, int64_t strideSize,
|
|
int64_t dilationSize) {
|
|
if (ShapedType::isDynamic(inputSize) || ShapedType::isDynamic(kernelSize))
|
|
return success();
|
|
|
|
const int64_t numerator =
|
|
inputSize - 1 + padBefore + padAfter - (kernelSize - 1) * dilationSize;
|
|
if (numerator % strideSize != 0)
|
|
return failure();
|
|
|
|
const int64_t calculatedOutput = numerator / strideSize + 1;
|
|
if (!ShapedType::isDynamic(outputSize) && outputSize != calculatedOutput)
|
|
return failure();
|
|
|
|
return success();
|
|
}
|
|
|
|
LogicalResult
|
|
verifyTransposeConvolutionOutputDim(int64_t inputSize, int64_t kernelSize,
|
|
int64_t outputSize, int64_t padBefore,
|
|
int64_t padAfter, int64_t strideSize) {
|
|
if (ShapedType::isDynamic(inputSize) || ShapedType::isDynamic(kernelSize))
|
|
return success();
|
|
|
|
const int64_t calculatedOutput =
|
|
(inputSize - 1) * strideSize + padBefore + padAfter + kernelSize;
|
|
if (!ShapedType::isDynamic(outputSize) && outputSize != calculatedOutput)
|
|
return failure();
|
|
|
|
return success();
|
|
}
|
|
|
|
LogicalResult verifyConv2DOutputShape(Operation *op, DenseIntElementsAttr pad,
|
|
DenseIntElementsAttr stride,
|
|
DenseIntElementsAttr dilation,
|
|
TensorArmType inputType,
|
|
TensorArmType weightType,
|
|
TensorArmType outputType) {
|
|
constexpr StringLiteral errorMessage =
|
|
"failed to verify that shapes of input, weight, and output must satisfy "
|
|
"[N,IH,IW,*], [*,KH,KW,*], [N,OH,OW,*], with OH = ((IH - 1 + pad_top + "
|
|
"pad_bottom - (KH - 1) * dilation_y) / stride_y) + 1 and OW = ((IW - 1 "
|
|
"+ pad_left + pad_right - (KW - 1) * dilation_x) / stride_x) + 1";
|
|
if (!inputType.hasRank() || !weightType.hasRank() || !outputType.hasRank())
|
|
return success();
|
|
|
|
if (failed(verifyConvolutionOutputDim(
|
|
inputType.getDimSize(1), weightType.getDimSize(1),
|
|
outputType.getDimSize(1), getIntValue(pad, 0), getIntValue(pad, 1),
|
|
getIntValue(stride, 0), getIntValue(dilation, 0))))
|
|
return op->emitOpError(errorMessage);
|
|
|
|
if (failed(verifyConvolutionOutputDim(
|
|
inputType.getDimSize(2), weightType.getDimSize(2),
|
|
outputType.getDimSize(2), getIntValue(pad, 2), getIntValue(pad, 3),
|
|
getIntValue(stride, 1), getIntValue(dilation, 1))))
|
|
return op->emitOpError(errorMessage);
|
|
|
|
return success();
|
|
}
|
|
|
|
LogicalResult verifyConv3DOutputShape(Operation *op, DenseIntElementsAttr pad,
|
|
DenseIntElementsAttr stride,
|
|
DenseIntElementsAttr dilation,
|
|
TensorArmType inputType,
|
|
TensorArmType weightType,
|
|
TensorArmType outputType) {
|
|
constexpr StringLiteral errorMessage =
|
|
"failed to verify that shapes of input, weight, and output must satisfy "
|
|
"[N,ID,IH,IW,*], [*,KD,KH,KW,*], [N,OD,OH,OW,*], with OD = ((ID - 1 + "
|
|
"pad_front + pad_back - (KD - 1) * dilation_d) / stride_d) + 1, OH = "
|
|
"((IH - 1 + pad_top + pad_bottom - (KH - 1) * dilation_y) / stride_y) "
|
|
"+ 1 and OW = ((IW - 1 + pad_left + pad_right - (KW - 1) * dilation_x) "
|
|
"/ stride_x) + 1";
|
|
if (!inputType.hasRank() || !weightType.hasRank() || !outputType.hasRank())
|
|
return success();
|
|
|
|
if (failed(verifyConvolutionOutputDim(
|
|
inputType.getDimSize(1), weightType.getDimSize(1),
|
|
outputType.getDimSize(1), getIntValue(pad, 0), getIntValue(pad, 1),
|
|
getIntValue(stride, 0), getIntValue(dilation, 0))))
|
|
return op->emitOpError(errorMessage);
|
|
|
|
if (failed(verifyConvolutionOutputDim(
|
|
inputType.getDimSize(2), weightType.getDimSize(2),
|
|
outputType.getDimSize(2), getIntValue(pad, 2), getIntValue(pad, 3),
|
|
getIntValue(stride, 1), getIntValue(dilation, 1))))
|
|
return op->emitOpError(errorMessage);
|
|
|
|
if (failed(verifyConvolutionOutputDim(
|
|
inputType.getDimSize(3), weightType.getDimSize(3),
|
|
outputType.getDimSize(3), getIntValue(pad, 4), getIntValue(pad, 5),
|
|
getIntValue(stride, 2), getIntValue(dilation, 2))))
|
|
return op->emitOpError(errorMessage);
|
|
|
|
return success();
|
|
}
|
|
|
|
LogicalResult verifyDepthwiseConv2DOutputShape(
|
|
Operation *op, DenseIntElementsAttr pad, DenseIntElementsAttr stride,
|
|
DenseIntElementsAttr dilation, TensorArmType inputType,
|
|
TensorArmType weightType, TensorArmType outputType) {
|
|
constexpr StringLiteral errorMessage =
|
|
"failed to verify that shapes of input, weight, and output must satisfy "
|
|
"[N,IH,IW,*], [KH,KW,*,*], [N,OH,OW,*], with OH = ((IH - 1 + pad_top + "
|
|
"pad_bottom - (KH - 1) * dilation_y) / stride_y) + 1 and OW = ((IW - 1 "
|
|
"+ pad_left + pad_right - (KW - 1) * dilation_x) / stride_x) + 1";
|
|
if (!inputType.hasRank() || !weightType.hasRank() || !outputType.hasRank())
|
|
return success();
|
|
|
|
if (failed(verifyConvolutionOutputDim(
|
|
inputType.getDimSize(1), weightType.getDimSize(0),
|
|
outputType.getDimSize(1), getIntValue(pad, 0), getIntValue(pad, 1),
|
|
getIntValue(stride, 0), getIntValue(dilation, 0))))
|
|
return op->emitOpError(errorMessage);
|
|
|
|
if (failed(verifyConvolutionOutputDim(
|
|
inputType.getDimSize(2), weightType.getDimSize(1),
|
|
outputType.getDimSize(2), getIntValue(pad, 2), getIntValue(pad, 3),
|
|
getIntValue(stride, 1), getIntValue(dilation, 1))))
|
|
return op->emitOpError(errorMessage);
|
|
|
|
return success();
|
|
}
|
|
|
|
LogicalResult verifyTransposeConv2DOutputShape(Operation *op,
|
|
DenseIntElementsAttr outPad,
|
|
DenseIntElementsAttr stride,
|
|
TensorArmType inputType,
|
|
TensorArmType weightType,
|
|
TensorArmType outputType) {
|
|
constexpr StringLiteral errorMessage =
|
|
"failed to verify that shapes of input, weight, and output must satisfy "
|
|
"[N,IH,IW,*], [*,KH,KW,*], [N,OH,OW,*], with OH = (IH - 1) * stride_y + "
|
|
"out_pad_top + out_pad_bottom + KH and OW = (IW - 1) * stride_x + "
|
|
"out_pad_left + out_pad_right + KW";
|
|
if (!inputType.hasRank() || !weightType.hasRank() || !outputType.hasRank())
|
|
return success();
|
|
|
|
const int64_t kernelHeight = weightType.getDimSize(1);
|
|
if (ShapedType::isStatic(kernelHeight) &&
|
|
(getIntValue(outPad, 0) <= -kernelHeight ||
|
|
getIntValue(outPad, 1) <= -kernelHeight))
|
|
return op->emitOpError("expected out_pad_top and out_pad_bottom to be > "
|
|
"-KH");
|
|
|
|
const int64_t kernelWidth = weightType.getDimSize(2);
|
|
if (ShapedType::isStatic(kernelWidth) &&
|
|
(getIntValue(outPad, 2) <= -kernelWidth ||
|
|
getIntValue(outPad, 3) <= -kernelWidth))
|
|
return op->emitOpError("expected out_pad_left and out_pad_right to be > "
|
|
"-KW");
|
|
|
|
if (failed(verifyTransposeConvolutionOutputDim(
|
|
inputType.getDimSize(1), kernelHeight, outputType.getDimSize(1),
|
|
getIntValue(outPad, 0), getIntValue(outPad, 1),
|
|
getIntValue(stride, 0))))
|
|
return op->emitOpError(errorMessage);
|
|
|
|
if (failed(verifyTransposeConvolutionOutputDim(
|
|
inputType.getDimSize(2), kernelWidth, outputType.getDimSize(2),
|
|
getIntValue(outPad, 2), getIntValue(outPad, 3),
|
|
getIntValue(stride, 1))))
|
|
return op->emitOpError(errorMessage);
|
|
|
|
return success();
|
|
}
|
|
|
|
LogicalResult verifyConcatOutputShape(Operation *op, TypeRange inputTypes,
|
|
TensorArmType outputType, int32_t axis) {
|
|
constexpr StringLiteral errorMessage =
|
|
"failed to verify that shape of output must match the concatenation of "
|
|
"input1 along axis";
|
|
if (!outputType.hasRank())
|
|
return success();
|
|
|
|
if (llvm::any_of(inputTypes, [](Type type) {
|
|
return !cast<TensorArmType>(type).hasRank();
|
|
}))
|
|
return success();
|
|
|
|
for (int64_t dim = 0, rank = outputType.getRank(); dim < rank; ++dim) {
|
|
int64_t outputDim = outputType.getDimSize(dim);
|
|
if (ShapedType::isDynamic(outputDim))
|
|
continue;
|
|
|
|
if (dim != axis) {
|
|
for (Type type : inputTypes) {
|
|
int64_t inputDim = cast<TensorArmType>(type).getDimSize(dim);
|
|
if (ShapedType::isStatic(inputDim) && inputDim != outputDim)
|
|
return op->emitOpError(errorMessage);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
int64_t concatDim = 0;
|
|
for (Type type : inputTypes) {
|
|
int64_t inputDim = cast<TensorArmType>(type).getDimSize(dim);
|
|
if (ShapedType::isDynamic(inputDim)) {
|
|
concatDim = ShapedType::kDynamic;
|
|
break;
|
|
}
|
|
concatDim += inputDim;
|
|
}
|
|
|
|
if (ShapedType::isStatic(concatDim) && concatDim != outputDim)
|
|
return op->emitOpError(errorMessage);
|
|
}
|
|
|
|
return success();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
LogicalResult TosaAvgPool2DOp::verify() {
|
|
return verifyPool2DOp(getOperation(), getKernel(), getStride(), getPad(),
|
|
getInputType(), getResultType());
|
|
}
|
|
|
|
LogicalResult TosaConv2DOp::verify() {
|
|
return verifyConv2DOutputShape(getOperation(), getPad(), getStride(),
|
|
getDilation(), getInputType(), getWeightType(),
|
|
getResultType());
|
|
}
|
|
|
|
LogicalResult TosaConv3DOp::verify() {
|
|
return verifyConv3DOutputShape(getOperation(), getPad(), getStride(),
|
|
getDilation(), getInputType(), getWeightType(),
|
|
getResultType());
|
|
}
|
|
|
|
LogicalResult TosaDepthwiseConv2DOp::verify() {
|
|
return verifyDepthwiseConv2DOutputShape(getOperation(), getPad(), getStride(),
|
|
getDilation(), getInputType(),
|
|
getWeightType(), getResultType());
|
|
}
|
|
|
|
LogicalResult TosaMaxPool2DOp::verify() {
|
|
return verifyPool2DOp(getOperation(), getKernel(), getStride(), getPad(),
|
|
getInputType(), getResultType());
|
|
}
|
|
|
|
LogicalResult TosaTransposeConv2DOp::verify() {
|
|
return verifyTransposeConv2DOutputShape(getOperation(), getOutPad(),
|
|
getStride(), getInputType(),
|
|
getWeightType(), getResultType());
|
|
}
|
|
|
|
LogicalResult TosaConcatOp::verify() {
|
|
return verifyConcatOutputShape(getOperation(), getInput1Types(),
|
|
getResultType(), getAxis());
|
|
}
|
|
|
|
LogicalResult TosaSelectOp::verify() {
|
|
TensorArmType condType = getConditionType();
|
|
TensorArmType trueValType = getTrueValueType();
|
|
TensorArmType falseValType = getFalseValueType();
|
|
TensorArmType resultType = getResultType();
|
|
|
|
if (llvm::any_of(ArrayRef<TensorArmType>{condType, trueValType, falseValType,
|
|
resultType},
|
|
[](TensorArmType type) { return !type.hasRank(); }))
|
|
return success();
|
|
|
|
ArrayRef<int64_t> condShape = condType.getShape();
|
|
ArrayRef<int64_t> trueValShape = trueValType.getShape();
|
|
ArrayRef<int64_t> falseValShape = falseValType.getShape();
|
|
ArrayRef<int64_t> resultShape = resultType.getShape();
|
|
|
|
if (!llvm::all_equal({condShape.size(), trueValShape.size(),
|
|
falseValShape.size(), resultShape.size()})) {
|
|
// The AllRanksMatch predicate enforces that all ranks are equal.
|
|
// This is just an extra safe guard for the code coming after that
|
|
// assumes that all ranks are equal.
|
|
return failure();
|
|
}
|
|
|
|
for (auto dims :
|
|
llvm::zip_equal(condShape, trueValShape, falseValShape, resultShape)) {
|
|
auto [condDim, trueValDim, falseValDim, resultDim] = dims;
|
|
|
|
if (llvm::any_of(
|
|
ArrayRef<int64_t>{condDim, trueValDim, falseValDim, resultDim},
|
|
[](int64_t dim) { return ShapedType::isDynamic(dim); })) {
|
|
continue;
|
|
}
|
|
|
|
auto isPairBroadcastable = [](int64_t lhs, int64_t rhs) {
|
|
return lhs == rhs || lhs == 1 || rhs == 1;
|
|
};
|
|
|
|
if (!isPairBroadcastable(condDim, trueValDim) ||
|
|
!isPairBroadcastable(condDim, falseValDim) ||
|
|
!isPairBroadcastable(trueValDim, falseValDim)) {
|
|
return emitOpError(
|
|
"failed to verify that the shape of inputs: condition, "
|
|
"true_value, and false_value are compatible for "
|
|
"broadcasting");
|
|
}
|
|
|
|
int64_t bradcastedInputDim =
|
|
std::max(condDim, std::max(trueValDim, falseValDim));
|
|
if (bradcastedInputDim != resultDim) {
|
|
return emitOpError(
|
|
"failed to verify that the broadcast shape of inputs: condition, "
|
|
"true_value, and false_value is equal to "
|
|
"the output shape");
|
|
}
|
|
}
|
|
return success();
|
|
}
|
|
|
|
} // namespace mlir::spirv
|