MLIR 24.0.0git
ComplexToSPIRV.cpp
Go to the documentation of this file.
1//===- ComplexToSPIRV.cpp - Complex to SPIR-V Patterns --------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements patterns to convert Complex dialect to SPIR-V dialect.
10//
11//===----------------------------------------------------------------------===//
12
18
19#define DEBUG_TYPE "complex-to-spirv-pattern"
20
21using namespace mlir;
22
23//===----------------------------------------------------------------------===//
24// Operation conversion
25//===----------------------------------------------------------------------===//
26
27namespace {
28/// Creates a scalar floating-point constant of the given value.
29Value createFPConstant(OpBuilder &builder, Location loc, Type type,
30 double value) {
31 return spirv::ConstantOp::create(
32 builder, loc, type, builder.getFloatAttr(cast<FloatType>(type), value));
33}
34
35struct ConstantOpPattern final : OpConversionPattern<complex::ConstantOp> {
36 using Base::Base;
37
38 LogicalResult
39 matchAndRewrite(complex::ConstantOp constOp, OpAdaptor adaptor,
40 ConversionPatternRewriter &rewriter) const override {
41 auto spirvType =
42 getTypeConverter()->convertType<ShapedType>(constOp.getType());
43 if (!spirvType)
44 return rewriter.notifyMatchFailure(constOp,
45 "unable to convert result type");
46
47 rewriter.replaceOpWithNewOp<spirv::ConstantOp>(
48 constOp, spirvType,
49 DenseElementsAttr::get(spirvType, constOp.getValue().getValue()));
50 return success();
51 }
52};
53
54struct CreateOpPattern final : OpConversionPattern<complex::CreateOp> {
55 using Base::Base;
56
57 LogicalResult
58 matchAndRewrite(complex::CreateOp createOp, OpAdaptor adaptor,
59 ConversionPatternRewriter &rewriter) const override {
60 Type spirvType = getTypeConverter()->convertType(createOp.getType());
61 if (!spirvType)
62 return rewriter.notifyMatchFailure(createOp,
63 "unable to convert result type");
64
65 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
66 createOp, spirvType, adaptor.getOperands());
67 return success();
68 }
69};
70
71struct ReOpPattern final : OpConversionPattern<complex::ReOp> {
72 using Base::Base;
73
74 LogicalResult
75 matchAndRewrite(complex::ReOp reOp, OpAdaptor adaptor,
76 ConversionPatternRewriter &rewriter) const override {
77 Type spirvType = getTypeConverter()->convertType(reOp.getType());
78 if (!spirvType)
79 return rewriter.notifyMatchFailure(reOp, "unable to convert result type");
80
81 rewriter.replaceOpWithNewOp<spirv::CompositeExtractOp>(
82 reOp, adaptor.getComplex(), llvm::ArrayRef(0));
83 return success();
84 }
85};
86
87struct ImOpPattern final : OpConversionPattern<complex::ImOp> {
88 using Base::Base;
89
90 LogicalResult
91 matchAndRewrite(complex::ImOp imOp, OpAdaptor adaptor,
92 ConversionPatternRewriter &rewriter) const override {
93 Type spirvType = getTypeConverter()->convertType(imOp.getType());
94 if (!spirvType)
95 return rewriter.notifyMatchFailure(imOp, "unable to convert result type");
96
97 rewriter.replaceOpWithNewOp<spirv::CompositeExtractOp>(
98 imOp, adaptor.getComplex(), llvm::ArrayRef(1));
99 return success();
100 }
101};
102
103template <typename ComplexOp, typename SPIRVOp>
104struct ElementwiseBinaryOpPattern final : OpConversionPattern<ComplexOp> {
105 using OpConversionPattern<ComplexOp>::OpConversionPattern;
106 using OpAdaptor = typename ComplexOp::Adaptor;
107
108 LogicalResult
109 matchAndRewrite(ComplexOp op, OpAdaptor adaptor,
110 ConversionPatternRewriter &rewriter) const override {
111 Type spirvType =
112 this->getTypeConverter()->convertType(op.getResult().getType());
113 if (!spirvType)
114 return rewriter.notifyMatchFailure(op, "unable to convert result type");
115
116 Location loc = op.getLoc();
117 Value lhs = adaptor.getLhs();
118 Value rhs = adaptor.getRhs();
119
120 Value lhsRe = spirv::CompositeExtractOp::create(rewriter, loc, lhs, {0});
121 Value lhsIm = spirv::CompositeExtractOp::create(rewriter, loc, lhs, {1});
122 Value rhsRe = spirv::CompositeExtractOp::create(rewriter, loc, rhs, {0});
123 Value rhsIm = spirv::CompositeExtractOp::create(rewriter, loc, rhs, {1});
124
125 Value resultRe = SPIRVOp::create(rewriter, loc, lhsRe, rhsRe);
126 Value resultIm = SPIRVOp::create(rewriter, loc, lhsIm, rhsIm);
127
128 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
129 op, spirvType, llvm::ArrayRef<Value>{resultRe, resultIm});
130 return success();
131 }
132};
133
134template <typename ComplexOp, typename SPIRVCompareOp, typename SPIRVCombinerOp>
135struct ComparisonOpPattern final : OpConversionPattern<ComplexOp> {
136 using OpConversionPattern<ComplexOp>::OpConversionPattern;
137 using OpAdaptor = typename ComplexOp::Adaptor;
138
139 LogicalResult
140 matchAndRewrite(ComplexOp op, OpAdaptor adaptor,
141 ConversionPatternRewriter &rewriter) const override {
142 Type spirvType =
143 this->getTypeConverter()->convertType(op.getResult().getType());
144 if (!spirvType)
145 return rewriter.notifyMatchFailure(op, "unable to convert result type");
146
147 Location loc = op.getLoc();
148 Value lhs = adaptor.getLhs();
149 Value rhs = adaptor.getRhs();
150
151 Value lhsRe = spirv::CompositeExtractOp::create(rewriter, loc, lhs, {0});
152 Value lhsIm = spirv::CompositeExtractOp::create(rewriter, loc, lhs, {1});
153 Value rhsRe = spirv::CompositeExtractOp::create(rewriter, loc, rhs, {0});
154 Value rhsIm = spirv::CompositeExtractOp::create(rewriter, loc, rhs, {1});
155
156 Value cmpRe = SPIRVCompareOp::create(rewriter, loc, lhsRe, rhsRe);
157 Value cmpIm = SPIRVCompareOp::create(rewriter, loc, lhsIm, rhsIm);
158
159 rewriter.replaceOpWithNewOp<SPIRVCombinerOp>(op, spirvType, cmpRe, cmpIm);
160 return success();
161 }
162};
163
164struct MulOpPattern final : OpConversionPattern<complex::MulOp> {
165 using Base::Base;
166
167 LogicalResult
168 matchAndRewrite(complex::MulOp op, OpAdaptor adaptor,
169 ConversionPatternRewriter &rewriter) const override {
170 Type spirvType = getTypeConverter()->convertType(op.getResult().getType());
171 if (!spirvType)
172 return rewriter.notifyMatchFailure(op, "unable to convert result type");
173
174 Location loc = op.getLoc();
175 Value lhs = adaptor.getLhs();
176 Value rhs = adaptor.getRhs();
177
178 Value a = spirv::CompositeExtractOp::create(rewriter, loc, lhs, {0});
179 Value b = spirv::CompositeExtractOp::create(rewriter, loc, lhs, {1});
180 Value c = spirv::CompositeExtractOp::create(rewriter, loc, rhs, {0});
181 Value d = spirv::CompositeExtractOp::create(rewriter, loc, rhs, {1});
182
183 Value ac = spirv::FMulOp::create(rewriter, loc, a, c);
184 Value bd = spirv::FMulOp::create(rewriter, loc, b, d);
185 Value ad = spirv::FMulOp::create(rewriter, loc, a, d);
186 Value bc = spirv::FMulOp::create(rewriter, loc, b, c);
187 Value resultRe = spirv::FSubOp::create(rewriter, loc, ac, bd);
188 Value resultIm = spirv::FAddOp::create(rewriter, loc, ad, bc);
189
190 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
191 op, spirvType, llvm::ArrayRef<Value>{resultRe, resultIm});
192 return success();
193 }
194};
195
196template <typename SqrtOp>
197struct AbsOpPattern final : OpConversionPattern<complex::AbsOp> {
198 using OpConversionPattern<complex::AbsOp>::OpConversionPattern;
199
200 LogicalResult
201 matchAndRewrite(complex::AbsOp op, OpAdaptor adaptor,
202 ConversionPatternRewriter &rewriter) const override {
203 Type spirvType =
204 this->getTypeConverter()->convertType(op.getResult().getType());
205 if (!spirvType)
206 return rewriter.notifyMatchFailure(op, "unable to convert result type");
207
208 Location loc = op.getLoc();
209 Value complexVal = adaptor.getComplex();
210
211 Value re =
212 spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {0});
213 Value im =
214 spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {1});
215
216 Value reSq = spirv::FMulOp::create(rewriter, loc, re, re);
217 Value imSq = spirv::FMulOp::create(rewriter, loc, im, im);
218 Value sum = spirv::FAddOp::create(rewriter, loc, reSq, imSq);
219
220 rewriter.replaceOpWithNewOp<SqrtOp>(op, sum);
221 return success();
222 }
223};
224
225template <typename ComplexOp, bool NegateReal>
226struct NegationOpPattern final : OpConversionPattern<ComplexOp> {
227 using OpConversionPattern<ComplexOp>::OpConversionPattern;
228 using OpAdaptor = typename ComplexOp::Adaptor;
229
230 LogicalResult
231 matchAndRewrite(ComplexOp op, OpAdaptor adaptor,
232 ConversionPatternRewriter &rewriter) const override {
233 Type spirvType =
234 this->getTypeConverter()->convertType(op.getResult().getType());
235 if (!spirvType)
236 return rewriter.notifyMatchFailure(op, "unable to convert result type");
237
238 Location loc = op.getLoc();
239 Value complexVal = adaptor.getComplex();
240
241 Value re =
242 spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {0});
243 Value im =
244 spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {1});
245
246 Value resultRe =
247 NegateReal ? spirv::FNegateOp::create(rewriter, loc, re) : re;
248 Value resultIm = spirv::FNegateOp::create(rewriter, loc, im);
249
250 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
251 op, spirvType, llvm::ArrayRef<Value>{resultRe, resultIm});
252 return success();
253 }
254};
255
256struct DivOpPattern final : OpConversionPattern<complex::DivOp> {
257 using Base::Base;
258
259 LogicalResult
260 matchAndRewrite(complex::DivOp op, OpAdaptor adaptor,
261 ConversionPatternRewriter &rewriter) const override {
262 Type spirvType = getTypeConverter()->convertType(op.getResult().getType());
263 if (!spirvType)
264 return rewriter.notifyMatchFailure(op, "unable to convert result type");
265
266 Location loc = op.getLoc();
267 Value lhs = adaptor.getLhs();
268 Value rhs = adaptor.getRhs();
269
270 Value a = spirv::CompositeExtractOp::create(rewriter, loc, lhs, {0});
271 Value b = spirv::CompositeExtractOp::create(rewriter, loc, lhs, {1});
272 Value c = spirv::CompositeExtractOp::create(rewriter, loc, rhs, {0});
273 Value d = spirv::CompositeExtractOp::create(rewriter, loc, rhs, {1});
274
275 Value ac = spirv::FMulOp::create(rewriter, loc, a, c);
276 Value bd = spirv::FMulOp::create(rewriter, loc, b, d);
277 Value bc = spirv::FMulOp::create(rewriter, loc, b, c);
278 Value ad = spirv::FMulOp::create(rewriter, loc, a, d);
279 Value cc = spirv::FMulOp::create(rewriter, loc, c, c);
280 Value dd = spirv::FMulOp::create(rewriter, loc, d, d);
281 Value denom = spirv::FAddOp::create(rewriter, loc, cc, dd);
282 Value numRe = spirv::FAddOp::create(rewriter, loc, ac, bd);
283 Value numIm = spirv::FSubOp::create(rewriter, loc, bc, ad);
284 Value resultRe = spirv::FDivOp::create(rewriter, loc, numRe, denom);
285 Value resultIm = spirv::FDivOp::create(rewriter, loc, numIm, denom);
286
287 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
288 op, spirvType, llvm::ArrayRef<Value>{resultRe, resultIm});
289 return success();
290 }
291};
292
293template <typename Atan2Op>
294struct AngleOpPattern final : OpConversionPattern<complex::AngleOp> {
295 using OpConversionPattern<complex::AngleOp>::OpConversionPattern;
296
297 LogicalResult
298 matchAndRewrite(complex::AngleOp op, OpAdaptor adaptor,
299 ConversionPatternRewriter &rewriter) const override {
300 Type spirvType =
301 this->getTypeConverter()->convertType(op.getResult().getType());
302 if (!spirvType)
303 return rewriter.notifyMatchFailure(op, "unable to convert result type");
304
305 Location loc = op.getLoc();
306 Value complexVal = adaptor.getComplex();
307
308 Value re =
309 spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {0});
310 Value im =
311 spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {1});
312
313 rewriter.replaceOpWithNewOp<Atan2Op>(op, im, re);
314 return success();
315 }
316};
317
318/// Computes complex.sign as z / abs(z), selecting a zero result when z is
319/// zero to avoid a division by zero. abs(z) is computed as
320/// max * sqrt(1 + (min / max)^2) to avoid overflow in squaring.
321template <typename SqrtOp, typename FAbsOp, typename FMaxOp, typename FMinOp>
322struct SignOpPattern final : OpConversionPattern<complex::SignOp> {
323 using OpConversionPattern<complex::SignOp>::OpConversionPattern;
324
325 LogicalResult
326 matchAndRewrite(complex::SignOp op, OpAdaptor adaptor,
327 ConversionPatternRewriter &rewriter) const override {
328 Type spirvType =
329 this->getTypeConverter()->convertType(op.getResult().getType());
330 if (!spirvType)
331 return rewriter.notifyMatchFailure(op, "unable to convert result type");
332
333 Location loc = op.getLoc();
334 Value complexVal = adaptor.getComplex();
335
336 Value re =
337 spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {0});
338 Value im =
339 spirv::CompositeExtractOp::create(rewriter, loc, complexVal, {1});
340
341 Value absRe = FAbsOp::create(rewriter, loc, re);
342 Value absIm = FAbsOp::create(rewriter, loc, im);
343 Value max = FMaxOp::create(rewriter, loc, absRe, absIm);
344 Value min = FMinOp::create(rewriter, loc, absRe, absIm);
345 Value ratio = spirv::FDivOp::create(rewriter, loc, min, max);
346 Value ratioSq = spirv::FMulOp::create(rewriter, loc, ratio, ratio);
347 Value one = createFPConstant(rewriter, loc, re.getType(), 1.0);
348 Value ratioSqPlusOne = spirv::FAddOp::create(rewriter, loc, ratioSq, one);
349 Value sqrt = SqrtOp::create(rewriter, loc, ratioSqPlusOne);
350 Value abs = spirv::FMulOp::create(rewriter, loc, max, sqrt);
351
352 Value signRe = spirv::FDivOp::create(rewriter, loc, re, abs);
353 Value signIm = spirv::FDivOp::create(rewriter, loc, im, abs);
354
355 Value zero = createFPConstant(rewriter, loc, re.getType(), 0.0);
356 Value reIsZero = spirv::FOrdEqualOp::create(rewriter, loc, re, zero);
357 Value imIsZero = spirv::FOrdEqualOp::create(rewriter, loc, im, zero);
358 Value isZero =
359 spirv::LogicalAndOp::create(rewriter, loc, reIsZero, imIsZero);
360
361 // Select per component: spirv.Select with a scalar condition and a
362 // composite result requires SPIR-V 1.4, so operate on scalars here and
363 // assemble the result afterwards.
364 Value resultRe = spirv::SelectOp::create(rewriter, loc, isZero, re, signRe);
365 Value resultIm = spirv::SelectOp::create(rewriter, loc, isZero, im, signIm);
366
367 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
368 op, spirvType, llvm::ArrayRef<Value>{resultRe, resultIm});
369 return success();
370 }
371};
372} // namespace
373
374//===----------------------------------------------------------------------===//
375// Pattern population
376//===----------------------------------------------------------------------===//
377
379 const SPIRVTypeConverter &typeConverter, RewritePatternSet &patterns) {
380 MLIRContext *context = patterns.getContext();
381
382 patterns
383 .add<ConstantOpPattern, CreateOpPattern, ReOpPattern, ImOpPattern,
384 ElementwiseBinaryOpPattern<complex::AddOp, spirv::FAddOp>,
385 ElementwiseBinaryOpPattern<complex::SubOp, spirv::FSubOp>,
386 ComparisonOpPattern<complex::EqualOp, spirv::FOrdEqualOp,
387 spirv::LogicalAndOp>,
388 ComparisonOpPattern<complex::NotEqualOp, spirv::FUnordNotEqualOp,
389 spirv::LogicalOrOp>,
390 MulOpPattern, DivOpPattern,
391 NegationOpPattern<complex::NegOp, /*NegateReal=*/true>,
392 NegationOpPattern<complex::ConjOp, /*NegateReal=*/false>,
393 AbsOpPattern<spirv::GLSqrtOp>, AbsOpPattern<spirv::CLSqrtOp>,
394 AngleOpPattern<spirv::GLAtan2Op>, AngleOpPattern<spirv::CLAtan2Op>,
395 SignOpPattern<spirv::GLSqrtOp, spirv::GLFAbsOp, spirv::GLFMaxOp,
396 spirv::GLFMinOp>,
397 SignOpPattern<spirv::CLSqrtOp, spirv::CLFAbsOp, spirv::CLFMaxOp,
398 spirv::CLFMinOp>>(typeConverter, context);
399}
return success()
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value createFPConstant(Location loc, Type srcType, Type dstType, PatternRewriter &rewriter, double value)
Creates llvm.mlir.constant with a floating-point scalar or vector value.
FloatAttr getFloatAttr(Type type, double value)
Definition Builders.cpp:263
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:64
This class helps build Operations.
Definition Builders.h:210
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
Type conversion from builtin types to SPIR-V types for shader interface.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
Include the generated interface declarations.
void populateComplexToSPIRVPatterns(const SPIRVTypeConverter &typeConverter, RewritePatternSet &patterns)
Appends to a pattern list additional patterns for translating Complex ops to SPIR-V ops.