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
685 lines
27 KiB
C++
685 lines
27 KiB
C++
//===- MathToSPIRV.cpp - Math to SPIR-V Patterns --------------------------===//
|
|
//
|
|
// 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 implements patterns to convert Math dialect to SPIR-V dialect.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "../SPIRVCommon/Pattern.h"
|
|
#include "mlir/Dialect/Math/IR/Math.h"
|
|
#include "mlir/Dialect/SPIRV/IR/SPIRVOps.h"
|
|
#include "mlir/Dialect/SPIRV/Transforms/SPIRVConversion.h"
|
|
#include "mlir/IR/BuiltinTypes.h"
|
|
#include "mlir/IR/Matchers.h"
|
|
#include "mlir/IR/TypeUtilities.h"
|
|
#include "mlir/Transforms/DialectConversion.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/TypeSwitch.h"
|
|
#include "llvm/Support/FormatVariadic.h"
|
|
|
|
#define DEBUG_TYPE "math-to-spirv-pattern"
|
|
|
|
using namespace mlir;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Utility functions
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Creates a 32-bit scalar/vector integer constant. Returns nullptr if the
|
|
/// given type is not a 32-bit scalar/vector type.
|
|
static Value getScalarOrVectorI32Constant(Type type, int value,
|
|
OpBuilder &builder, Location loc) {
|
|
if (auto vectorType = dyn_cast<VectorType>(type)) {
|
|
if (!vectorType.getElementType().isInteger(32))
|
|
return nullptr;
|
|
SmallVector<int> values(vectorType.getNumElements(), value);
|
|
return spirv::ConstantOp::create(builder, loc, type,
|
|
builder.getI32VectorAttr(values));
|
|
}
|
|
if (type.isInteger(32))
|
|
return spirv::ConstantOp::create(builder, loc, type,
|
|
builder.getI32IntegerAttr(value));
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/// Check if the type is supported by math-to-spirv conversion. We expect to
|
|
/// only see scalars and vectors at this point, with higher-level types already
|
|
/// lowered.
|
|
static bool isSupportedSourceType(Type originalType) {
|
|
if (originalType.isIntOrIndexOrFloat())
|
|
return true;
|
|
|
|
if (auto vecTy = dyn_cast<VectorType>(originalType)) {
|
|
if (!vecTy.getElementType().isIntOrIndexOrFloat())
|
|
return false;
|
|
if (vecTy.isScalable())
|
|
return false;
|
|
if (vecTy.getRank() > 1)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Check if all `sourceOp` types are supported by math-to-spirv conversion.
|
|
/// Notify of a match failure othwerise and return a `failure` result.
|
|
/// This is intended to simplify type checks in `OpConversionPattern`s.
|
|
static LogicalResult checkSourceOpTypes(ConversionPatternRewriter &rewriter,
|
|
Operation *sourceOp) {
|
|
auto allTypes = llvm::to_vector(sourceOp->getOperandTypes());
|
|
llvm::append_range(allTypes, sourceOp->getResultTypes());
|
|
|
|
for (Type ty : allTypes) {
|
|
if (!isSupportedSourceType(ty)) {
|
|
return rewriter.notifyMatchFailure(
|
|
sourceOp,
|
|
llvm::formatv(
|
|
"unsupported source type for Math to SPIR-V conversion: {0}",
|
|
ty));
|
|
}
|
|
}
|
|
|
|
return success();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Operation conversion
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Note that DRR cannot be used for the patterns in this file: we may need to
|
|
// convert type along the way, which requires ConversionPattern. DRR generates
|
|
// normal RewritePattern.
|
|
|
|
namespace {
|
|
/// Converts elementwise unary, binary, and ternary standard operations to
|
|
/// SPIR-V operations. Checks that source `Op` types are supported.
|
|
template <typename Op, typename SPIRVOp>
|
|
struct CheckedElementwiseOpPattern final
|
|
: public spirv::ElementwiseOpPattern<Op, SPIRVOp> {
|
|
using BasePattern = typename spirv::ElementwiseOpPattern<Op, SPIRVOp>;
|
|
using BasePattern::BasePattern;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(Op op, typename Op::Adaptor adaptor,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
if (LogicalResult res = checkSourceOpTypes(rewriter, op); failed(res))
|
|
return res;
|
|
|
|
return BasePattern::matchAndRewrite(op, adaptor, rewriter);
|
|
}
|
|
};
|
|
|
|
/// Converts math.copysign to SPIR-V ops.
|
|
struct CopySignPattern final : public OpConversionPattern<math::CopySignOp> {
|
|
using Base::Base;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(math::CopySignOp copySignOp, OpAdaptor adaptor,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
if (LogicalResult res = checkSourceOpTypes(rewriter, copySignOp);
|
|
failed(res))
|
|
return res;
|
|
|
|
Type type = getTypeConverter()->convertType(copySignOp.getType());
|
|
if (!type)
|
|
return failure();
|
|
|
|
FloatType floatType;
|
|
if (auto scalarType = dyn_cast<FloatType>(copySignOp.getType())) {
|
|
floatType = scalarType;
|
|
} else if (auto vectorType = dyn_cast<VectorType>(copySignOp.getType())) {
|
|
floatType = cast<FloatType>(vectorType.getElementType());
|
|
} else {
|
|
return failure();
|
|
}
|
|
|
|
Location loc = copySignOp.getLoc();
|
|
int bitwidth = floatType.getWidth();
|
|
Type intType = rewriter.getIntegerType(bitwidth);
|
|
uint64_t intValue = uint64_t(1) << (bitwidth - 1);
|
|
|
|
Value signMask = spirv::ConstantOp::create(
|
|
rewriter, loc, intType, rewriter.getIntegerAttr(intType, intValue));
|
|
Value valueMask = spirv::ConstantOp::create(
|
|
rewriter, loc, intType,
|
|
rewriter.getIntegerAttr(intType, intValue - 1u));
|
|
|
|
if (auto vectorType = dyn_cast<VectorType>(type)) {
|
|
assert(vectorType.getRank() == 1);
|
|
int count = vectorType.getNumElements();
|
|
intType = VectorType::get(count, intType);
|
|
|
|
Repeated<Value> signSplat(count, signMask);
|
|
signMask = spirv::CompositeConstructOp::create(rewriter, loc, intType,
|
|
signSplat);
|
|
|
|
Repeated<Value> valueSplat(count, valueMask);
|
|
valueMask = spirv::CompositeConstructOp::create(rewriter, loc, intType,
|
|
valueSplat);
|
|
}
|
|
|
|
Value lhsCast =
|
|
spirv::BitcastOp::create(rewriter, loc, intType, adaptor.getLhs());
|
|
Value rhsCast =
|
|
spirv::BitcastOp::create(rewriter, loc, intType, adaptor.getRhs());
|
|
|
|
Value value = spirv::BitwiseAndOp::create(rewriter, loc, intType,
|
|
ValueRange{lhsCast, valueMask});
|
|
Value sign = spirv::BitwiseAndOp::create(rewriter, loc, intType,
|
|
ValueRange{rhsCast, signMask});
|
|
|
|
Value result = spirv::BitwiseOrOp::create(rewriter, loc, intType,
|
|
ValueRange{value, sign});
|
|
rewriter.replaceOpWithNewOp<spirv::BitcastOp>(copySignOp, type, result);
|
|
return success();
|
|
}
|
|
};
|
|
|
|
/// Converts math.ctlz to SPIR-V ops.
|
|
///
|
|
/// OpenCL targets lower math.ctlz directly to OpenCL.std clz via the generic
|
|
/// elementwise pattern. This pattern handles the shader fallback.
|
|
///
|
|
/// SPIR-V does not have a direct operations for counting leading zeros for
|
|
/// glsl. If Shader capability is supported, we can leverage GL FindUMsb to
|
|
/// calculate it.
|
|
struct CountLeadingZerosPattern final
|
|
: public OpConversionPattern<math::CountLeadingZerosOp> {
|
|
using Base::Base;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(math::CountLeadingZerosOp countOp, OpAdaptor adaptor,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
if (LogicalResult res = checkSourceOpTypes(rewriter, countOp); failed(res))
|
|
return res;
|
|
|
|
Type type = getTypeConverter()->convertType(countOp.getType());
|
|
if (!type)
|
|
return failure();
|
|
|
|
auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
|
|
if (!typeConverter.getTargetEnv().allows(spirv::Capability::Shader))
|
|
return rewriter.notifyMatchFailure(countOp, "requires Shader capability");
|
|
|
|
// The GL FindUMsb fallback only supports 32-bit integer types for now.
|
|
unsigned bitwidth = 0;
|
|
if (isa<IntegerType>(type))
|
|
bitwidth = type.getIntOrFloatBitWidth();
|
|
if (auto vectorType = dyn_cast<VectorType>(type))
|
|
bitwidth = vectorType.getElementTypeBitWidth();
|
|
if (bitwidth != 32)
|
|
return failure();
|
|
|
|
Location loc = countOp.getLoc();
|
|
Value input = adaptor.getOperand();
|
|
Value val1 = getScalarOrVectorI32Constant(type, 1, rewriter, loc);
|
|
Value val31 = getScalarOrVectorI32Constant(type, 31, rewriter, loc);
|
|
Value val32 = getScalarOrVectorI32Constant(type, 32, rewriter, loc);
|
|
|
|
Value msb = spirv::GLFindUMsbOp::create(rewriter, loc, input);
|
|
// We need to subtract from 31 given that the index returned by GLSL
|
|
// FindUMsb is counted from the least significant bit. Theoretically this
|
|
// also gives the correct result even if the integer has all zero bits, in
|
|
// which case GL FindUMsb would return -1.
|
|
Value subMsb = spirv::ISubOp::create(rewriter, loc, val31, msb);
|
|
// However, certain Vulkan implementations have driver bugs for the corner
|
|
// case where the input is zero. And.. it can be smart to optimize a select
|
|
// only involving the corner case. So separately compute the result when the
|
|
// input is either zero or one.
|
|
Value subInput = spirv::ISubOp::create(rewriter, loc, val32, input);
|
|
Value cmp = spirv::ULessThanEqualOp::create(rewriter, loc, input, val1);
|
|
rewriter.replaceOpWithNewOp<spirv::SelectOp>(countOp, cmp, subInput,
|
|
subMsb);
|
|
return success();
|
|
}
|
|
};
|
|
|
|
/// Converts math.cttz to GL FindILsb. GL FindILsb returns -1 for a zero
|
|
/// input while math.cttz must return the bitwidth, so the zero case is
|
|
/// patched up with a select.
|
|
struct CountTrailingZerosPattern final
|
|
: public OpConversionPattern<math::CountTrailingZerosOp> {
|
|
using Base::Base;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(math::CountTrailingZerosOp countOp, OpAdaptor adaptor,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
if (LogicalResult res = checkSourceOpTypes(rewriter, countOp); failed(res))
|
|
return res;
|
|
|
|
Type type = getTypeConverter()->convertType(countOp.getType());
|
|
if (!type)
|
|
return failure();
|
|
|
|
unsigned bitwidth = 0;
|
|
if (isa<IntegerType>(type))
|
|
bitwidth = type.getIntOrFloatBitWidth();
|
|
else if (auto vectorType = dyn_cast<VectorType>(type))
|
|
bitwidth = vectorType.getElementTypeBitWidth();
|
|
if (bitwidth != 32)
|
|
return failure();
|
|
|
|
Location loc = countOp.getLoc();
|
|
Value input = adaptor.getOperand();
|
|
Value val0 = getScalarOrVectorI32Constant(type, 0, rewriter, loc);
|
|
Value valBitwidth =
|
|
getScalarOrVectorI32Constant(type, bitwidth, rewriter, loc);
|
|
|
|
Value lsb = spirv::GLFindILsbOp::create(rewriter, loc, input);
|
|
Value isZero = spirv::IEqualOp::create(rewriter, loc, input, val0);
|
|
rewriter.replaceOpWithNewOp<spirv::SelectOp>(countOp, isZero, valBitwidth,
|
|
lsb);
|
|
return success();
|
|
}
|
|
};
|
|
|
|
/// Converts math.expm1 to SPIR-V ops.
|
|
///
|
|
/// SPIR-V does not have a direct operations for exp(x)-1. Explicitly lower to
|
|
/// these operations.
|
|
template <typename ExpOp>
|
|
struct ExpM1OpPattern final : public OpConversionPattern<math::ExpM1Op> {
|
|
using Base::Base;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(math::ExpM1Op operation, OpAdaptor adaptor,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
assert(adaptor.getOperands().size() == 1);
|
|
if (LogicalResult res = checkSourceOpTypes(rewriter, operation);
|
|
failed(res))
|
|
return res;
|
|
|
|
Location loc = operation.getLoc();
|
|
Type type = this->getTypeConverter()->convertType(operation.getType());
|
|
if (!type)
|
|
return failure();
|
|
|
|
Value exp = ExpOp::create(rewriter, loc, type, adaptor.getOperand());
|
|
auto one = spirv::ConstantOp::getOne(type, loc, rewriter);
|
|
rewriter.replaceOpWithNewOp<spirv::FSubOp>(operation, exp, one);
|
|
return success();
|
|
}
|
|
};
|
|
|
|
/// Converts math.log1p to SPIR-V ops.
|
|
///
|
|
/// SPIR-V does not have a direct operations for log(1+x). Explicitly lower to
|
|
/// these operations.
|
|
template <typename LogOp>
|
|
struct Log1pOpPattern final : public OpConversionPattern<math::Log1pOp> {
|
|
using Base::Base;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(math::Log1pOp operation, OpAdaptor adaptor,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
assert(adaptor.getOperands().size() == 1);
|
|
if (LogicalResult res = checkSourceOpTypes(rewriter, operation);
|
|
failed(res))
|
|
return res;
|
|
|
|
Location loc = operation.getLoc();
|
|
Type type = this->getTypeConverter()->convertType(operation.getType());
|
|
if (!type)
|
|
return failure();
|
|
|
|
auto one = spirv::ConstantOp::getOne(type, operation.getLoc(), rewriter);
|
|
Value onePlus =
|
|
spirv::FAddOp::create(rewriter, loc, one, adaptor.getOperand());
|
|
rewriter.replaceOpWithNewOp<LogOp>(operation, type, onePlus);
|
|
return success();
|
|
}
|
|
};
|
|
|
|
/// Converts math.log10 to GLSL SPIR-V ops.
|
|
///
|
|
/// GLSL.std.450 has no Log10 instruction. Lower it as:
|
|
/// log10(x) = log(x) * 1/log(10)
|
|
struct Log10OpPattern final : public OpConversionPattern<math::Log10Op> {
|
|
using Base::Base;
|
|
|
|
static constexpr double log10Reciprocal =
|
|
0.4342944819032518276511289189166050822943970058036665661144537832;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(math::Log10Op operation, OpAdaptor adaptor,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
assert(adaptor.getOperands().size() == 1);
|
|
if (LogicalResult res = checkSourceOpTypes(rewriter, operation);
|
|
failed(res))
|
|
return res;
|
|
|
|
Location loc = operation.getLoc();
|
|
Type type = this->getTypeConverter()->convertType(operation.getType());
|
|
if (!type)
|
|
return rewriter.notifyMatchFailure(operation, "type conversion failed");
|
|
|
|
auto getConstantValue = [&](double value) {
|
|
if (auto floatType = dyn_cast<FloatType>(type)) {
|
|
return spirv::ConstantOp::create(
|
|
rewriter, loc, type, rewriter.getFloatAttr(floatType, value));
|
|
}
|
|
if (auto vectorType = dyn_cast<VectorType>(type)) {
|
|
Type elemType = vectorType.getElementType();
|
|
|
|
if (isa<FloatType>(elemType)) {
|
|
return spirv::ConstantOp::create(
|
|
rewriter, loc, type,
|
|
DenseFPElementsAttr::get(
|
|
vectorType, FloatAttr::get(elemType, value).getValue()));
|
|
}
|
|
}
|
|
llvm_unreachable("unimplemented type for log10");
|
|
};
|
|
|
|
Value constantValue = getConstantValue(log10Reciprocal);
|
|
Value log = spirv::GLLogOp::create(rewriter, loc, adaptor.getOperand());
|
|
rewriter.replaceOpWithNewOp<spirv::FMulOp>(operation, type, log,
|
|
constantValue);
|
|
return success();
|
|
}
|
|
};
|
|
|
|
/// Converts math.powf to SPIRV-Ops.
|
|
struct PowFOpPattern final : public OpConversionPattern<math::PowFOp> {
|
|
using Base::Base;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(math::PowFOp powfOp, OpAdaptor adaptor,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
if (LogicalResult res = checkSourceOpTypes(rewriter, powfOp); failed(res))
|
|
return res;
|
|
|
|
Type dstType = getTypeConverter()->convertType(powfOp.getType());
|
|
if (!dstType)
|
|
return failure();
|
|
|
|
Location loc = powfOp.getLoc();
|
|
Type operandType = adaptor.getRhs().getType();
|
|
|
|
// Parity-based lowering requires an integer-valued constant exponent.
|
|
// Otherwise fall back to exp(y*log(x)), which yields NaN for x<0 (matches
|
|
// C).
|
|
auto isOdd = [](const APFloat &v) {
|
|
APSInt i(/*BitWidth=*/64, /*isUnsigned=*/false);
|
|
bool ignored;
|
|
v.convertToInteger(i, APFloat::rmTowardZero, &ignored);
|
|
return i[0];
|
|
};
|
|
|
|
SmallVector<bool> oddMask;
|
|
Attribute rhsAttr;
|
|
if (matchPattern(adaptor.getRhs(), m_Constant(&rhsAttr))) {
|
|
TypeSwitch<Attribute>(rhsAttr)
|
|
.Case([&](FloatAttr a) {
|
|
if (a.getValue().isInteger())
|
|
oddMask.push_back(isOdd(a.getValue()));
|
|
})
|
|
.Case([&](SplatElementsAttr a) {
|
|
APFloat splat = a.getSplatValue<APFloat>();
|
|
if (splat.isInteger())
|
|
oddMask.push_back(isOdd(splat));
|
|
})
|
|
.Case([&](DenseElementsAttr a) {
|
|
SmallVector<bool> mask;
|
|
for (const APFloat &elt : a.getValues<APFloat>()) {
|
|
if (!elt.isInteger())
|
|
return;
|
|
mask.push_back(isOdd(elt));
|
|
}
|
|
oddMask = std::move(mask);
|
|
});
|
|
}
|
|
|
|
if (oddMask.empty()) {
|
|
Value log = spirv::GLLogOp::create(rewriter, loc, adaptor.getLhs());
|
|
Value mul = spirv::FMulOp::create(rewriter, loc, adaptor.getRhs(), log);
|
|
rewriter.replaceOpWithNewOp<spirv::GLExpOp>(powfOp, mul);
|
|
return success();
|
|
}
|
|
|
|
// GL.Pow is undefined for x < 0; take abs and conditionally negate the
|
|
// result for lanes whose exponent is odd.
|
|
Value abs = spirv::GLFAbsOp::create(rewriter, loc, adaptor.getLhs());
|
|
Value pow = spirv::GLPowOp::create(rewriter, loc, abs, adaptor.getRhs());
|
|
|
|
// No odd-parity element: result has the same sign as |lhs|^rhs >= 0.
|
|
if (llvm::none_of(oddMask, [](bool b) { return b; })) {
|
|
rewriter.replaceOp(powfOp, pow);
|
|
return success();
|
|
}
|
|
|
|
Value zero = spirv::ConstantOp::getZero(operandType, loc, rewriter);
|
|
Value lessThan =
|
|
spirv::FOrdLessThanOp::create(rewriter, loc, adaptor.getLhs(), zero);
|
|
Value negate = spirv::FNegateOp::create(rewriter, loc, pow);
|
|
|
|
Value shouldNegate;
|
|
if (llvm::all_equal(oddMask)) {
|
|
// Every lane has odd exponent: negate iff lhs < 0.
|
|
shouldNegate = lessThan;
|
|
} else {
|
|
// Mixed parity (non-splat dense vector): AND lhs<0 with a per-element
|
|
// constant odd-mask.
|
|
auto vecType = cast<VectorType>(operandType);
|
|
auto maskType = VectorType::get(vecType.getShape(), rewriter.getI1Type());
|
|
Value oddConst = spirv::ConstantOp::create(
|
|
rewriter, loc, maskType, DenseElementsAttr::get(maskType, oddMask));
|
|
shouldNegate =
|
|
spirv::LogicalAndOp::create(rewriter, loc, lessThan, oddConst);
|
|
}
|
|
|
|
rewriter.replaceOpWithNewOp<spirv::SelectOp>(powfOp, shouldNegate, negate,
|
|
pow);
|
|
return success();
|
|
}
|
|
};
|
|
|
|
/// Converts math.fpowi to spirv.CL.pown.
|
|
struct PowIOpPattern final : public OpConversionPattern<math::FPowIOp> {
|
|
using Base::Base;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(math::FPowIOp op, OpAdaptor adaptor,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
if (LogicalResult res = checkSourceOpTypes(rewriter, op); failed(res))
|
|
return res;
|
|
|
|
Type dstType = getTypeConverter()->convertType(op.getType());
|
|
if (!dstType)
|
|
return failure();
|
|
|
|
rewriter.replaceOpWithNewOp<spirv::CLPownOp>(op, dstType, adaptor.getLhs(),
|
|
adaptor.getRhs());
|
|
return success();
|
|
}
|
|
};
|
|
|
|
/// Converts math.fpowi to GLSL SPIR-V ops. GL has no integer-power op, so the
|
|
/// exponent is converted to float and lowered through spirv.GL.Pow. As GL.Pow
|
|
/// is undefined for a negative base, the base is made positive and the result
|
|
/// is negated when the base is negative and the exponent is odd.
|
|
struct PowIOpGLPattern final : public OpConversionPattern<math::FPowIOp> {
|
|
using Base::Base;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(math::FPowIOp op, OpAdaptor adaptor,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
if (LogicalResult res = checkSourceOpTypes(rewriter, op); failed(res))
|
|
return res;
|
|
|
|
Type dstType = getTypeConverter()->convertType(op.getType());
|
|
if (!dstType)
|
|
return failure();
|
|
|
|
Location loc = op.getLoc();
|
|
Value base = adaptor.getLhs();
|
|
Value power = adaptor.getRhs();
|
|
|
|
Value expFloat =
|
|
spirv::ConvertSToFOp::create(rewriter, loc, dstType, power);
|
|
Value abs = spirv::GLFAbsOp::create(rewriter, loc, base);
|
|
Value pow = spirv::GLPowOp::create(rewriter, loc, abs, expFloat);
|
|
|
|
Value zeroF = spirv::ConstantOp::getZero(dstType, loc, rewriter);
|
|
Value lessThan = spirv::FOrdLessThanOp::create(rewriter, loc, base, zeroF);
|
|
|
|
Type powerType = power.getType();
|
|
Value oneI = spirv::ConstantOp::getOne(powerType, loc, rewriter);
|
|
Value lowBit = spirv::BitwiseAndOp::create(rewriter, loc, power, oneI);
|
|
Value isOdd = spirv::IEqualOp::create(rewriter, loc, lowBit, oneI);
|
|
|
|
Value shouldNegate =
|
|
spirv::LogicalAndOp::create(rewriter, loc, lessThan, isOdd);
|
|
Value negate = spirv::FNegateOp::create(rewriter, loc, pow);
|
|
rewriter.replaceOpWithNewOp<spirv::SelectOp>(op, shouldNegate, negate, pow);
|
|
return success();
|
|
}
|
|
};
|
|
|
|
/// Converts math.round to GLSL SPIRV extended ops.
|
|
struct RoundOpPattern final : public OpConversionPattern<math::RoundOp> {
|
|
using Base::Base;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(math::RoundOp roundOp, OpAdaptor adaptor,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
if (LogicalResult res = checkSourceOpTypes(rewriter, roundOp); failed(res))
|
|
return res;
|
|
|
|
Location loc = roundOp.getLoc();
|
|
auto ty = getTypeConverter()->convertType(adaptor.getOperand().getType());
|
|
if (!ty) {
|
|
return rewriter.notifyMatchFailure(
|
|
roundOp->getLoc(),
|
|
llvm::formatv("failed to convert type {0} for SPIR-V",
|
|
roundOp.getType()));
|
|
}
|
|
|
|
Type ety = getElementTypeOrSelf(ty);
|
|
|
|
auto zero = spirv::ConstantOp::getZero(ty, loc, rewriter);
|
|
auto one = spirv::ConstantOp::getOne(ty, loc, rewriter);
|
|
Value half;
|
|
if (VectorType vty = dyn_cast<VectorType>(ty)) {
|
|
half = spirv::ConstantOp::create(
|
|
rewriter, loc, vty,
|
|
DenseElementsAttr::get(vty,
|
|
rewriter.getFloatAttr(ety, 0.5).getValue()));
|
|
} else {
|
|
half = spirv::ConstantOp::create(rewriter, loc, ty,
|
|
rewriter.getFloatAttr(ety, 0.5));
|
|
}
|
|
|
|
auto abs = spirv::GLFAbsOp::create(rewriter, loc, adaptor.getOperand());
|
|
auto floor = spirv::GLFloorOp::create(rewriter, loc, abs);
|
|
auto sub = spirv::FSubOp::create(rewriter, loc, abs, floor);
|
|
auto greater =
|
|
spirv::FOrdGreaterThanEqualOp::create(rewriter, loc, sub, half);
|
|
auto select = spirv::SelectOp::create(rewriter, loc, greater, one, zero);
|
|
auto add = spirv::FAddOp::create(rewriter, loc, floor, select);
|
|
rewriter.replaceOpWithNewOp<math::CopySignOp>(roundOp, add,
|
|
adaptor.getOperand());
|
|
return success();
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Pattern population
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace mlir {
|
|
void populateMathToSPIRVPatterns(const SPIRVTypeConverter &typeConverter,
|
|
RewritePatternSet &patterns) {
|
|
// Core patterns
|
|
patterns
|
|
.add<CopySignPattern,
|
|
CheckedElementwiseOpPattern<math::CtPopOp, spirv::BitCountOp>,
|
|
CheckedElementwiseOpPattern<math::IsInfOp, spirv::IsInfOp>,
|
|
CheckedElementwiseOpPattern<math::IsNaNOp, spirv::IsNanOp>,
|
|
CheckedElementwiseOpPattern<math::IsFiniteOp, spirv::IsFiniteOp>,
|
|
CheckedElementwiseOpPattern<math::IsNormalOp, spirv::IsNormalOp>>(
|
|
typeConverter, patterns.getContext());
|
|
|
|
// GLSL patterns
|
|
patterns
|
|
.add<CountLeadingZerosPattern, CountTrailingZerosPattern,
|
|
Log1pOpPattern<spirv::GLLogOp>, Log10OpPattern,
|
|
ExpM1OpPattern<spirv::GLExpOp>, PowFOpPattern, PowIOpGLPattern,
|
|
RoundOpPattern,
|
|
CheckedElementwiseOpPattern<math::AbsFOp, spirv::GLFAbsOp>,
|
|
CheckedElementwiseOpPattern<math::AbsIOp, spirv::GLSAbsOp>,
|
|
CheckedElementwiseOpPattern<math::AtanOp, spirv::GLAtanOp>,
|
|
CheckedElementwiseOpPattern<math::CeilOp, spirv::GLCeilOp>,
|
|
CheckedElementwiseOpPattern<math::ClampFOp, spirv::GLFClampOp>,
|
|
CheckedElementwiseOpPattern<math::CosOp, spirv::GLCosOp>,
|
|
CheckedElementwiseOpPattern<math::ExpOp, spirv::GLExpOp>,
|
|
CheckedElementwiseOpPattern<math::Exp2Op, spirv::GLExp2Op>,
|
|
CheckedElementwiseOpPattern<math::FloorOp, spirv::GLFloorOp>,
|
|
CheckedElementwiseOpPattern<math::FmaOp, spirv::GLFmaOp>,
|
|
CheckedElementwiseOpPattern<math::LogOp, spirv::GLLogOp>,
|
|
CheckedElementwiseOpPattern<math::Log2Op, spirv::GLLog2Op>,
|
|
CheckedElementwiseOpPattern<math::RoundEvenOp, spirv::GLRoundEvenOp>,
|
|
CheckedElementwiseOpPattern<math::RsqrtOp, spirv::GLInverseSqrtOp>,
|
|
CheckedElementwiseOpPattern<math::SinOp, spirv::GLSinOp>,
|
|
CheckedElementwiseOpPattern<math::SqrtOp, spirv::GLSqrtOp>,
|
|
CheckedElementwiseOpPattern<math::TanhOp, spirv::GLTanhOp>,
|
|
CheckedElementwiseOpPattern<math::TanOp, spirv::GLTanOp>,
|
|
CheckedElementwiseOpPattern<math::TruncOp, spirv::GLTruncOp>,
|
|
CheckedElementwiseOpPattern<math::AsinOp, spirv::GLAsinOp>,
|
|
CheckedElementwiseOpPattern<math::AcosOp, spirv::GLAcosOp>,
|
|
CheckedElementwiseOpPattern<math::SinhOp, spirv::GLSinhOp>,
|
|
CheckedElementwiseOpPattern<math::CoshOp, spirv::GLCoshOp>,
|
|
CheckedElementwiseOpPattern<math::AsinhOp, spirv::GLAsinhOp>,
|
|
CheckedElementwiseOpPattern<math::AcoshOp, spirv::GLAcoshOp>,
|
|
CheckedElementwiseOpPattern<math::AtanhOp, spirv::GLAtanhOp>>(
|
|
typeConverter, patterns.getContext());
|
|
|
|
// OpenCL patterns
|
|
patterns.add<
|
|
Log1pOpPattern<spirv::CLLogOp>, ExpM1OpPattern<spirv::CLExpOp>,
|
|
CheckedElementwiseOpPattern<math::AbsFOp, spirv::CLFAbsOp>,
|
|
CheckedElementwiseOpPattern<math::AbsIOp, spirv::CLSAbsOp>,
|
|
CheckedElementwiseOpPattern<math::CountLeadingZerosOp, spirv::CLClzOp>,
|
|
CheckedElementwiseOpPattern<math::AtanOp, spirv::CLAtanOp>,
|
|
CheckedElementwiseOpPattern<math::Atan2Op, spirv::CLAtan2Op>,
|
|
CheckedElementwiseOpPattern<math::CeilOp, spirv::CLCeilOp>,
|
|
CheckedElementwiseOpPattern<math::CosOp, spirv::CLCosOp>,
|
|
CheckedElementwiseOpPattern<math::ErfOp, spirv::CLErfOp>,
|
|
CheckedElementwiseOpPattern<math::ExpOp, spirv::CLExpOp>,
|
|
CheckedElementwiseOpPattern<math::Exp2Op, spirv::CLExp2Op>,
|
|
CheckedElementwiseOpPattern<math::FloorOp, spirv::CLFloorOp>,
|
|
CheckedElementwiseOpPattern<math::FmaOp, spirv::CLFmaOp>,
|
|
CheckedElementwiseOpPattern<math::LogOp, spirv::CLLogOp>,
|
|
CheckedElementwiseOpPattern<math::Log2Op, spirv::CLLog2Op>,
|
|
CheckedElementwiseOpPattern<math::Log10Op, spirv::CLLog10Op>,
|
|
CheckedElementwiseOpPattern<math::PowFOp, spirv::CLPowOp>, PowIOpPattern,
|
|
CheckedElementwiseOpPattern<math::RoundEvenOp, spirv::CLRintOp>,
|
|
CheckedElementwiseOpPattern<math::RoundOp, spirv::CLRoundOp>,
|
|
CheckedElementwiseOpPattern<math::RsqrtOp, spirv::CLRsqrtOp>,
|
|
CheckedElementwiseOpPattern<math::SinOp, spirv::CLSinOp>,
|
|
CheckedElementwiseOpPattern<math::SqrtOp, spirv::CLSqrtOp>,
|
|
CheckedElementwiseOpPattern<math::TanhOp, spirv::CLTanhOp>,
|
|
CheckedElementwiseOpPattern<math::TanOp, spirv::CLTanOp>,
|
|
CheckedElementwiseOpPattern<math::TruncOp, spirv::CLTruncOp>,
|
|
CheckedElementwiseOpPattern<math::AsinOp, spirv::CLAsinOp>,
|
|
CheckedElementwiseOpPattern<math::AcosOp, spirv::CLAcosOp>,
|
|
CheckedElementwiseOpPattern<math::SinhOp, spirv::CLSinhOp>,
|
|
CheckedElementwiseOpPattern<math::CoshOp, spirv::CLCoshOp>,
|
|
CheckedElementwiseOpPattern<math::AsinhOp, spirv::CLAsinhOp>,
|
|
CheckedElementwiseOpPattern<math::AcoshOp, spirv::CLAcoshOp>,
|
|
CheckedElementwiseOpPattern<math::AtanhOp, spirv::CLAtanhOp>>(
|
|
typeConverter, patterns.getContext());
|
|
}
|
|
|
|
} // namespace mlir
|