blob: e57d6d922e5ba66add6117b4738194de51a16780 [file] [edit]
//===- ComplexToSPIRV.cpp - Complex 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 Complex dialect to SPIR-V dialect.
//
//===----------------------------------------------------------------------===//
#include "mlir/Conversion/ComplexToSPIRV/ComplexToSPIRV.h"
#include "mlir/Dialect/Complex/IR/Complex.h"
#include "mlir/Dialect/SPIRV/IR/SPIRVOps.h"
#include "mlir/Dialect/SPIRV/Transforms/SPIRVConversion.h"
#include "mlir/Transforms/DialectConversion.h"
#define DEBUG_TYPE "complex-to-spirv-pattern"
using namespace mlir;
//===----------------------------------------------------------------------===//
// Operation conversion
//===----------------------------------------------------------------------===//
namespace {
/// Creates a scalar floating-point constant of the given value.
Value createFPConstant(OpBuilder &builder, Location loc, Type type,
double value) {
return spirv::ConstantOp::create(
builder, loc, type, builder.getFloatAttr(cast<FloatType>(type), value));
}
struct ConstantOpPattern final : OpConversionPattern<complex::ConstantOp> {
using Base::Base;
LogicalResult
matchAndRewrite(complex::ConstantOp constOp, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
auto spirvType =
getTypeConverter()->convertType<ShapedType>(constOp.getType());
if (!spirvType)
return rewriter.notifyMatchFailure(constOp,
"unable to convert result type");
rewriter.replaceOpWithNewOp<spirv::ConstantOp>(
constOp, spirvType,
DenseElementsAttr::get(spirvType, constOp.getValue().getValue()));
return success();
}
};
struct CreateOpPattern final : OpConversionPattern<complex::CreateOp> {
using Base::Base;
LogicalResult
matchAndRewrite(complex::CreateOp createOp, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
Type spirvType = getTypeConverter()->convertType(createOp.getType());
if (!spirvType)
return rewriter.notifyMatchFailure(createOp,
"unable to convert result type");
rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
createOp, spirvType, adaptor.getOperands());
return success();
}
};
struct ReOpPattern final : OpConversionPattern<complex::ReOp> {
using Base::Base;
LogicalResult
matchAndRewrite(complex::ReOp reOp, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
Type spirvType = getTypeConverter()->convertType(reOp.getType());
if (!spirvType)
return rewriter.notifyMatchFailure(reOp, "unable to convert result type");
rewriter.replaceOpWithNewOp<spirv::CompositeExtractOp>(
reOp, adaptor.getComplex(), llvm::ArrayRef(0));
return success();
}
};
struct ImOpPattern final : OpConversionPattern<complex::ImOp> {
using Base::Base;
LogicalResult
matchAndRewrite(complex::ImOp imOp, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
Type spirvType = getTypeConverter()->convertType(imOp.getType());
if (!spirvType)
return rewriter.notifyMatchFailure(imOp, "unable to convert result type");
rewriter.replaceOpWithNewOp<spirv::CompositeExtractOp>(
imOp, adaptor.getComplex(), llvm::ArrayRef(1));
return success();
}
};
template <typename ComplexOp, typename SPIRVOp>
struct ElementwiseBinaryOpPattern final : OpConversionPattern<ComplexOp> {
using OpConversionPattern<ComplexOp>::OpConversionPattern;
using OpAdaptor = typename ComplexOp::Adaptor;
LogicalResult
matchAndRewrite(ComplexOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
Type spirvType =
this->getTypeConverter()->convertType(op.getResult().getType());
if (!spirvType)
return rewriter.notifyMatchFailure(op, "unable to convert result type");
Location loc = op.getLoc();
Value lhs = adaptor.getLhs();
Value rhs = adaptor.getRhs();
Value lhsRe = spirv::CompositeExtractOp::create(rewriter, loc, lhs, {0});
Value lhsIm = spirv::CompositeExtractOp::create(rewriter, loc, lhs, {1});
Value rhsRe = spirv::CompositeExtractOp::create(rewriter, loc, rhs, {0});
Value rhsIm = spirv::CompositeExtractOp::create(rewriter, loc, rhs, {1});
Value resultRe = SPIRVOp::create(rewriter, loc, lhsRe, rhsRe);
Value resultIm = SPIRVOp::create(rewriter, loc, lhsIm, rhsIm);
rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
op, spirvType, llvm::ArrayRef<Value>{resultRe, resultIm});
return success();
}
};
template <typename ComplexOp, typename SPIRVCompareOp, typename SPIRVCombinerOp>
struct ComparisonOpPattern final : OpConversionPattern<ComplexOp> {
using OpConversionPattern<ComplexOp>::OpConversionPattern;
using OpAdaptor = typename ComplexOp::Adaptor;
LogicalResult
matchAndRewrite(ComplexOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
Type spirvType =
this->getTypeConverter()->convertType(op.getResult().getType());
if (!spirvType)
return rewriter.notifyMatchFailure(op, "unable to convert result type");
Location loc = op.getLoc();
Value lhs = adaptor.getLhs();
Value rhs = adaptor.getRhs();
Value lhsRe = spirv::CompositeExtractOp::create(rewriter, loc, lhs, {0});
Value lhsIm = spirv::CompositeExtractOp::create(rewriter, loc, lhs, {1});
Value rhsRe = spirv::CompositeExtractOp::create(rewriter, loc, rhs, {0});
Value rhsIm = spirv::CompositeExtractOp::create(rewriter, loc, rhs, {1});
Value cmpRe = SPIRVCompareOp::create(rewriter, loc, lhsRe, rhsRe);
Value cmpIm = SPIRVCompareOp::create(rewriter, loc, lhsIm, rhsIm);
rewriter.replaceOpWithNewOp<SPIRVCombinerOp>(op, spirvType, cmpRe, cmpIm);
return success();
}
};
struct MulOpPattern final : OpConversionPattern<complex::MulOp> {
using Base::Base;
LogicalResult
matchAndRewrite(complex::MulOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
Type spirvType = getTypeConverter()->convertType(op.getResult().getType());
if (!spirvType)
return rewriter.notifyMatchFailure(op, "unable to convert result type");
Location loc = op.getLoc();
Value lhs = adaptor.getLhs();
Value rhs = adaptor.getRhs();
Value a = spirv::CompositeExtractOp::create(rewriter, loc, lhs, {0});
Value b = spirv::CompositeExtractOp::create(rewriter, loc, lhs, {1});
Value c = spirv::CompositeExtractOp::create(rewriter, loc, rhs, {0});
Value d = spirv::CompositeExtractOp::create(rewriter, loc, rhs, {1});
Value ac = spirv::FMulOp::create(rewriter, loc, a, c);
Value bd = spirv::FMulOp::create(rewriter, loc, b, d);
Value ad = spirv::FMulOp::create(rewriter, loc, a, d);
Value bc = spirv::FMulOp::create(rewriter, loc, b, c);
Value resultRe = spirv::FSubOp::create(rewriter, loc, ac, bd);
Value resultIm = spirv::FAddOp::create(rewriter, loc, ad, bc);
rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
op, spirvType, llvm::ArrayRef<Value>{resultRe, resultIm});
return success();
}
};
template <typename SqrtOp>
struct AbsOpPattern final : OpConversionPattern<complex::AbsOp> {
using OpConversionPattern<complex::AbsOp>::OpConversionPattern;
LogicalResult
matchAndRewrite(complex::AbsOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
Type spirvType =
this->getTypeConverter()->convertType(op.getResult().getType());
if (!spirvType)
return rewriter.notifyMatchFailure(op, "unable to convert result type");
Location loc = op.getLoc();
Value complexVal = adaptor.getComplex();
Value re =
spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {0});
Value im =
spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {1});
Value reSq = spirv::FMulOp::create(rewriter, loc, re, re);
Value imSq = spirv::FMulOp::create(rewriter, loc, im, im);
Value sum = spirv::FAddOp::create(rewriter, loc, reSq, imSq);
rewriter.replaceOpWithNewOp<SqrtOp>(op, sum);
return success();
}
};
template <typename ComplexOp, bool NegateReal>
struct NegationOpPattern final : OpConversionPattern<ComplexOp> {
using OpConversionPattern<ComplexOp>::OpConversionPattern;
using OpAdaptor = typename ComplexOp::Adaptor;
LogicalResult
matchAndRewrite(ComplexOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
Type spirvType =
this->getTypeConverter()->convertType(op.getResult().getType());
if (!spirvType)
return rewriter.notifyMatchFailure(op, "unable to convert result type");
Location loc = op.getLoc();
Value complexVal = adaptor.getComplex();
Value re =
spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {0});
Value im =
spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {1});
Value resultRe =
NegateReal ? spirv::FNegateOp::create(rewriter, loc, re) : re;
Value resultIm = spirv::FNegateOp::create(rewriter, loc, im);
rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
op, spirvType, llvm::ArrayRef<Value>{resultRe, resultIm});
return success();
}
};
struct DivOpPattern final : OpConversionPattern<complex::DivOp> {
using Base::Base;
LogicalResult
matchAndRewrite(complex::DivOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
Type spirvType = getTypeConverter()->convertType(op.getResult().getType());
if (!spirvType)
return rewriter.notifyMatchFailure(op, "unable to convert result type");
Location loc = op.getLoc();
Value lhs = adaptor.getLhs();
Value rhs = adaptor.getRhs();
Value a = spirv::CompositeExtractOp::create(rewriter, loc, lhs, {0});
Value b = spirv::CompositeExtractOp::create(rewriter, loc, lhs, {1});
Value c = spirv::CompositeExtractOp::create(rewriter, loc, rhs, {0});
Value d = spirv::CompositeExtractOp::create(rewriter, loc, rhs, {1});
Value ac = spirv::FMulOp::create(rewriter, loc, a, c);
Value bd = spirv::FMulOp::create(rewriter, loc, b, d);
Value bc = spirv::FMulOp::create(rewriter, loc, b, c);
Value ad = spirv::FMulOp::create(rewriter, loc, a, d);
Value cc = spirv::FMulOp::create(rewriter, loc, c, c);
Value dd = spirv::FMulOp::create(rewriter, loc, d, d);
Value denom = spirv::FAddOp::create(rewriter, loc, cc, dd);
Value numRe = spirv::FAddOp::create(rewriter, loc, ac, bd);
Value numIm = spirv::FSubOp::create(rewriter, loc, bc, ad);
Value resultRe = spirv::FDivOp::create(rewriter, loc, numRe, denom);
Value resultIm = spirv::FDivOp::create(rewriter, loc, numIm, denom);
rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
op, spirvType, llvm::ArrayRef<Value>{resultRe, resultIm});
return success();
}
};
template <typename Atan2Op>
struct AngleOpPattern final : OpConversionPattern<complex::AngleOp> {
using OpConversionPattern<complex::AngleOp>::OpConversionPattern;
LogicalResult
matchAndRewrite(complex::AngleOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
Type spirvType =
this->getTypeConverter()->convertType(op.getResult().getType());
if (!spirvType)
return rewriter.notifyMatchFailure(op, "unable to convert result type");
Location loc = op.getLoc();
Value complexVal = adaptor.getComplex();
Value re =
spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {0});
Value im =
spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {1});
rewriter.replaceOpWithNewOp<Atan2Op>(op, im, re);
return success();
}
};
/// Computes complex.sign as z / abs(z), selecting a zero result when z is
/// zero to avoid a division by zero. abs(z) is computed as
/// max * sqrt(1 + (min / max)^2) to avoid overflow in squaring.
template <typename SqrtOp, typename FAbsOp, typename FMaxOp, typename FMinOp>
struct SignOpPattern final : OpConversionPattern<complex::SignOp> {
using OpConversionPattern<complex::SignOp>::OpConversionPattern;
LogicalResult
matchAndRewrite(complex::SignOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
Type spirvType =
this->getTypeConverter()->convertType(op.getResult().getType());
if (!spirvType)
return rewriter.notifyMatchFailure(op, "unable to convert result type");
Location loc = op.getLoc();
Value complexVal = adaptor.getComplex();
Value re =
spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {0});
Value im =
spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {1});
Value absRe = FAbsOp::create(rewriter, loc, re);
Value absIm = FAbsOp::create(rewriter, loc, im);
Value max = FMaxOp::create(rewriter, loc, absRe, absIm);
Value min = FMinOp::create(rewriter, loc, absRe, absIm);
Value ratio = spirv::FDivOp::create(rewriter, loc, min, max);
Value ratioSq = spirv::FMulOp::create(rewriter, loc, ratio, ratio);
Value one = createFPConstant(rewriter, loc, re.getType(), 1.0);
Value ratioSqPlusOne = spirv::FAddOp::create(rewriter, loc, ratioSq, one);
Value sqrt = SqrtOp::create(rewriter, loc, ratioSqPlusOne);
Value abs = spirv::FMulOp::create(rewriter, loc, max, sqrt);
Value signRe = spirv::FDivOp::create(rewriter, loc, re, abs);
Value signIm = spirv::FDivOp::create(rewriter, loc, im, abs);
Value zero = createFPConstant(rewriter, loc, re.getType(), 0.0);
Value reIsZero = spirv::FOrdEqualOp::create(rewriter, loc, re, zero);
Value imIsZero = spirv::FOrdEqualOp::create(rewriter, loc, im, zero);
Value isZero =
spirv::LogicalAndOp::create(rewriter, loc, reIsZero, imIsZero);
// Select per component: spirv.Select with a scalar condition and a
// composite result requires SPIR-V 1.4, so operate on scalars here and
// assemble the result afterwards.
Value resultRe = spirv::SelectOp::create(rewriter, loc, isZero, re, signRe);
Value resultIm = spirv::SelectOp::create(rewriter, loc, isZero, im, signIm);
rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
op, spirvType, llvm::ArrayRef<Value>{resultRe, resultIm});
return success();
}
};
} // namespace
//===----------------------------------------------------------------------===//
// Pattern population
//===----------------------------------------------------------------------===//
void mlir::populateComplexToSPIRVPatterns(
const SPIRVTypeConverter &typeConverter, RewritePatternSet &patterns) {
MLIRContext *context = patterns.getContext();
patterns
.add<ConstantOpPattern, CreateOpPattern, ReOpPattern, ImOpPattern,
ElementwiseBinaryOpPattern<complex::AddOp, spirv::FAddOp>,
ElementwiseBinaryOpPattern<complex::SubOp, spirv::FSubOp>,
ComparisonOpPattern<complex::EqualOp, spirv::FOrdEqualOp,
spirv::LogicalAndOp>,
ComparisonOpPattern<complex::NotEqualOp, spirv::FUnordNotEqualOp,
spirv::LogicalOrOp>,
MulOpPattern, DivOpPattern,
NegationOpPattern<complex::NegOp, /*NegateReal=*/true>,
NegationOpPattern<complex::ConjOp, /*NegateReal=*/false>,
AbsOpPattern<spirv::GLSqrtOp>, AbsOpPattern<spirv::CLSqrtOp>,
AngleOpPattern<spirv::GLAtan2Op>, AngleOpPattern<spirv::CLAtan2Op>,
SignOpPattern<spirv::GLSqrtOp, spirv::GLFAbsOp, spirv::GLFMaxOp,
spirv::GLFMinOp>,
SignOpPattern<spirv::CLSqrtOp, spirv::CLFAbsOp, spirv::CLFMaxOp,
spirv::CLFMinOp>>(typeConverter, context);
}