MLIR 24.0.0git
ComplexToStandard.cpp
Go to the documentation of this file.
1//===- ComplexToStandard.cpp - conversion from Complex to Standard dialect ===//
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
10
17#include <type_traits>
18
19namespace mlir {
20#define GEN_PASS_DEF_CONVERTCOMPLEXTOSTANDARDPASS
21#include "mlir/Conversion/Passes.h.inc"
22} // namespace mlir
23
24using namespace mlir;
25
26namespace {
27
28enum class AbsFn { abs, sqrt, rsqrt };
29
30// Returns the absolute value, its square root or its reciprocal square root.
31Value computeAbs(Value real, Value imag, arith::FastMathFlags fmf,
32 ImplicitLocOpBuilder &b, AbsFn fn = AbsFn::abs) {
33 Value one = arith::ConstantOp::create(b, real.getType(),
34 b.getFloatAttr(real.getType(), 1.0));
35
36 Value absReal = math::AbsFOp::create(b, real, fmf);
37 Value absImag = math::AbsFOp::create(b, imag, fmf);
38
39 Value max = arith::MaximumFOp::create(b, absReal, absImag, fmf);
40 Value min = arith::MinimumFOp::create(b, absReal, absImag, fmf);
41
42 // The lowering below requires NaNs and infinities to work correctly.
43 arith::FastMathFlags fmfWithNaNInf = arith::bitEnumClear(
44 fmf, arith::FastMathFlags::nnan | arith::FastMathFlags::ninf);
45 Value ratio = arith::DivFOp::create(b, min, max, fmfWithNaNInf);
46 Value ratioSq = arith::MulFOp::create(b, ratio, ratio, fmfWithNaNInf);
47 Value ratioSqPlusOne = arith::AddFOp::create(b, ratioSq, one, fmfWithNaNInf);
49
50 if (fn == AbsFn::rsqrt) {
51 ratioSqPlusOne = math::RsqrtOp::create(b, ratioSqPlusOne, fmfWithNaNInf);
52 min = math::RsqrtOp::create(b, min, fmfWithNaNInf);
53 max = math::RsqrtOp::create(b, max, fmfWithNaNInf);
54 }
55
56 if (fn == AbsFn::sqrt) {
57 Value quarter = arith::ConstantOp::create(
58 b, real.getType(), b.getFloatAttr(real.getType(), 0.25));
59 // sqrt(sqrt(a*b)) would avoid the pow, but will overflow more easily.
60 Value sqrt = math::SqrtOp::create(b, max, fmfWithNaNInf);
61 Value p025 =
62 math::PowFOp::create(b, ratioSqPlusOne, quarter, fmfWithNaNInf);
63 result = arith::MulFOp::create(b, sqrt, p025, fmfWithNaNInf);
64 } else {
65 Value sqrt = math::SqrtOp::create(b, ratioSqPlusOne, fmfWithNaNInf);
66 result = arith::MulFOp::create(b, max, sqrt, fmfWithNaNInf);
67 }
68
69 Value isNaN = arith::CmpFOp::create(b, arith::CmpFPredicate::UNO, result,
70 result, fmfWithNaNInf);
71 return arith::SelectOp::create(b, isNaN, min, result);
72}
73
74struct AbsOpConversion : public OpConversionPattern<complex::AbsOp> {
75 using OpConversionPattern<complex::AbsOp>::OpConversionPattern;
76
77 LogicalResult
78 matchAndRewrite(complex::AbsOp op, OpAdaptor adaptor,
79 ConversionPatternRewriter &rewriter) const override {
80 ImplicitLocOpBuilder b(op.getLoc(), rewriter);
81
82 arith::FastMathFlags fmf = op.getFastMathFlagsAttr().getValue();
83
84 Value real = complex::ReOp::create(b, adaptor.getComplex());
85 Value imag = complex::ImOp::create(b, adaptor.getComplex());
86 rewriter.replaceOp(op, computeAbs(real, imag, fmf, b));
87
88 return success();
89 }
90};
91
92// atan2(y,x) = -i * log((x + i * y)/sqrt(x**2+y**2))
93struct Atan2OpConversion : public OpConversionPattern<complex::Atan2Op> {
94 using OpConversionPattern<complex::Atan2Op>::OpConversionPattern;
95
96 LogicalResult
97 matchAndRewrite(complex::Atan2Op op, OpAdaptor adaptor,
98 ConversionPatternRewriter &rewriter) const override {
99 mlir::ImplicitLocOpBuilder b(op.getLoc(), rewriter);
100
101 auto type = cast<ComplexType>(op.getType());
102 Type elementType = type.getElementType();
103 arith::FastMathFlagsAttr fmf = op.getFastMathFlagsAttr();
104
105 Value lhs = adaptor.getLhs();
106 Value rhs = adaptor.getRhs();
107
108 Value rhsSquared = complex::MulOp::create(b, type, rhs, rhs, fmf);
109 Value lhsSquared = complex::MulOp::create(b, type, lhs, lhs, fmf);
110 Value rhsSquaredPlusLhsSquared =
111 complex::AddOp::create(b, type, rhsSquared, lhsSquared, fmf);
112 Value sqrtOfRhsSquaredPlusLhsSquared =
113 complex::SqrtOp::create(b, type, rhsSquaredPlusLhsSquared, fmf);
114
115 Value zero =
116 arith::ConstantOp::create(b, elementType, b.getZeroAttr(elementType));
117 Value one = arith::ConstantOp::create(b, elementType,
118 b.getFloatAttr(elementType, 1));
119 Value i = complex::CreateOp::create(b, type, zero, one);
120 Value iTimesLhs = complex::MulOp::create(b, i, lhs, fmf);
121 Value rhsPlusILhs = complex::AddOp::create(b, rhs, iTimesLhs, fmf);
122
123 Value divResult = complex::DivOp::create(
124 b, rhsPlusILhs, sqrtOfRhsSquaredPlusLhsSquared, fmf);
125 Value logResult = complex::LogOp::create(b, divResult, fmf);
126
127 Value negativeOne = arith::ConstantOp::create(
128 b, elementType, b.getFloatAttr(elementType, -1));
129 Value negativeI = complex::CreateOp::create(b, type, zero, negativeOne);
130
131 rewriter.replaceOpWithNewOp<complex::MulOp>(op, negativeI, logResult, fmf);
132 return success();
133 }
134};
135
136template <typename ComparisonOp, arith::CmpFPredicate p>
137struct ComparisonOpConversion : public OpConversionPattern<ComparisonOp> {
138 using OpConversionPattern<ComparisonOp>::OpConversionPattern;
139 using ResultCombiner =
140 std::conditional_t<std::is_same<ComparisonOp, complex::EqualOp>::value,
141 arith::AndIOp, arith::OrIOp>;
142
143 LogicalResult
144 matchAndRewrite(ComparisonOp op, typename ComparisonOp::Adaptor adaptor,
145 ConversionPatternRewriter &rewriter) const override {
146 auto loc = op.getLoc();
147 auto type = cast<ComplexType>(adaptor.getLhs().getType()).getElementType();
148
149 Value realLhs =
150 complex::ReOp::create(rewriter, loc, type, adaptor.getLhs());
151 Value imagLhs =
152 complex::ImOp::create(rewriter, loc, type, adaptor.getLhs());
153 Value realRhs =
154 complex::ReOp::create(rewriter, loc, type, adaptor.getRhs());
155 Value imagRhs =
156 complex::ImOp::create(rewriter, loc, type, adaptor.getRhs());
157 Value realComparison =
158 arith::CmpFOp::create(rewriter, loc, p, realLhs, realRhs);
159 Value imagComparison =
160 arith::CmpFOp::create(rewriter, loc, p, imagLhs, imagRhs);
161
162 rewriter.replaceOpWithNewOp<ResultCombiner>(op, realComparison,
163 imagComparison);
164 return success();
165 }
166};
167
168// Default conversion which applies the BinaryStandardOp separately on the real
169// and imaginary parts. Can for example be used for complex::AddOp and
170// complex::SubOp.
171template <typename BinaryComplexOp, typename BinaryStandardOp>
172struct BinaryComplexOpConversion : public OpConversionPattern<BinaryComplexOp> {
173 using OpConversionPattern<BinaryComplexOp>::OpConversionPattern;
174
175 LogicalResult
176 matchAndRewrite(BinaryComplexOp op, typename BinaryComplexOp::Adaptor adaptor,
177 ConversionPatternRewriter &rewriter) const override {
178 auto type = cast<ComplexType>(adaptor.getLhs().getType());
179 auto elementType = cast<FloatType>(type.getElementType());
180 mlir::ImplicitLocOpBuilder b(op.getLoc(), rewriter);
181 arith::FastMathFlagsAttr fmf = op.getFastMathFlagsAttr();
182
183 Value realLhs = complex::ReOp::create(b, elementType, adaptor.getLhs());
184 Value realRhs = complex::ReOp::create(b, elementType, adaptor.getRhs());
185 Value resultReal = BinaryStandardOp::create(b, elementType, realLhs,
186 realRhs, fmf.getValue());
187 Value imagLhs = complex::ImOp::create(b, elementType, adaptor.getLhs());
188 Value imagRhs = complex::ImOp::create(b, elementType, adaptor.getRhs());
189 Value resultImag = BinaryStandardOp::create(b, elementType, imagLhs,
190 imagRhs, fmf.getValue());
191 rewriter.replaceOpWithNewOp<complex::CreateOp>(op, type, resultReal,
192 resultImag);
193 return success();
194 }
195};
196
197template <typename TrigonometricOp>
198struct TrigonometricOpConversion : public OpConversionPattern<TrigonometricOp> {
199 using OpAdaptor = typename OpConversionPattern<TrigonometricOp>::OpAdaptor;
200
201 using OpConversionPattern<TrigonometricOp>::OpConversionPattern;
202
203 LogicalResult
204 matchAndRewrite(TrigonometricOp op, OpAdaptor adaptor,
205 ConversionPatternRewriter &rewriter) const override {
206 auto loc = op.getLoc();
207 auto type = cast<ComplexType>(adaptor.getComplex().getType());
208 auto elementType = cast<FloatType>(type.getElementType());
209 arith::FastMathFlagsAttr fmf = op.getFastMathFlagsAttr();
210
211 Value real =
212 complex::ReOp::create(rewriter, loc, elementType, adaptor.getComplex());
213 Value imag =
214 complex::ImOp::create(rewriter, loc, elementType, adaptor.getComplex());
215
216 // Trigonometric ops use a set of common building blocks to convert to real
217 // ops. Here we create these building blocks and call into an op-specific
218 // implementation in the subclass to combine them.
219 Value half = arith::ConstantOp::create(
220 rewriter, loc, elementType, rewriter.getFloatAttr(elementType, 0.5));
221 Value exp = math::ExpOp::create(rewriter, loc, imag, fmf);
222 Value scaledExp = arith::MulFOp::create(rewriter, loc, half, exp, fmf);
223 Value reciprocalExp = arith::DivFOp::create(rewriter, loc, half, exp, fmf);
224 Value sin = math::SinOp::create(rewriter, loc, real, fmf);
225 Value cos = math::CosOp::create(rewriter, loc, real, fmf);
226
227 auto resultPair =
228 combine(loc, scaledExp, reciprocalExp, sin, cos, rewriter, fmf);
229
230 rewriter.replaceOpWithNewOp<complex::CreateOp>(op, type, resultPair.first,
231 resultPair.second);
232 return success();
233 }
234
235 virtual std::pair<Value, Value>
236 combine(Location loc, Value scaledExp, Value reciprocalExp, Value sin,
237 Value cos, ConversionPatternRewriter &rewriter,
238 arith::FastMathFlagsAttr fmf) const = 0;
239};
240
241struct CosOpConversion : public TrigonometricOpConversion<complex::CosOp> {
242 using TrigonometricOpConversion<complex::CosOp>::TrigonometricOpConversion;
243
244 std::pair<Value, Value> combine(Location loc, Value scaledExp,
245 Value reciprocalExp, Value sin, Value cos,
246 ConversionPatternRewriter &rewriter,
247 arith::FastMathFlagsAttr fmf) const override {
248 // Complex cosine is defined as;
249 // cos(x + iy) = 0.5 * (exp(i(x + iy)) + exp(-i(x + iy)))
250 // Plugging in:
251 // exp(i(x+iy)) = exp(-y + ix) = exp(-y)(cos(x) + i sin(x))
252 // exp(-i(x+iy)) = exp(y + i(-x)) = exp(y)(cos(x) + i (-sin(x)))
253 // and defining t := exp(y)
254 // We get:
255 // Re(cos(x + iy)) = (0.5/t + 0.5*t) * cos x
256 // Im(cos(x + iy)) = (0.5/t - 0.5*t) * sin x
257 Value sum =
258 arith::AddFOp::create(rewriter, loc, reciprocalExp, scaledExp, fmf);
259 Value resultReal = arith::MulFOp::create(rewriter, loc, sum, cos, fmf);
260 Value diff =
261 arith::SubFOp::create(rewriter, loc, reciprocalExp, scaledExp, fmf);
262 Value resultImag = arith::MulFOp::create(rewriter, loc, diff, sin, fmf);
263 return {resultReal, resultImag};
264 }
265};
266
267struct DivOpConversion : public OpConversionPattern<complex::DivOp> {
268 DivOpConversion(MLIRContext *context, complex::ComplexRangeFlags target)
269 : OpConversionPattern<complex::DivOp>(context), complexRange(target) {}
270
271 using OpConversionPattern<complex::DivOp>::OpConversionPattern;
272
273 LogicalResult
274 matchAndRewrite(complex::DivOp op, OpAdaptor adaptor,
275 ConversionPatternRewriter &rewriter) const override {
276 auto loc = op.getLoc();
277 auto type = cast<ComplexType>(adaptor.getLhs().getType());
278 auto elementType = cast<FloatType>(type.getElementType());
279 arith::FastMathFlagsAttr fmf = op.getFastMathFlagsAttr();
280
281 Value lhsReal =
282 complex::ReOp::create(rewriter, loc, elementType, adaptor.getLhs());
283 Value lhsImag =
284 complex::ImOp::create(rewriter, loc, elementType, adaptor.getLhs());
285 Value rhsReal =
286 complex::ReOp::create(rewriter, loc, elementType, adaptor.getRhs());
287 Value rhsImag =
288 complex::ImOp::create(rewriter, loc, elementType, adaptor.getRhs());
289
290 Value resultReal, resultImag;
291
292 if (complexRange == complex::ComplexRangeFlags::basic ||
293 complexRange == complex::ComplexRangeFlags::none) {
295 rewriter, loc, lhsReal, lhsImag, rhsReal, rhsImag, fmf, &resultReal,
296 &resultImag);
297 } else if (complexRange == complex::ComplexRangeFlags::improved) {
299 rewriter, loc, lhsReal, lhsImag, rhsReal, rhsImag, fmf, &resultReal,
300 &resultImag);
301 }
302
303 rewriter.replaceOpWithNewOp<complex::CreateOp>(op, type, resultReal,
304 resultImag);
305
306 return success();
307 }
308
309private:
310 complex::ComplexRangeFlags complexRange;
311};
312
313struct ExpOpConversion : public OpConversionPattern<complex::ExpOp> {
314 using OpConversionPattern<complex::ExpOp>::OpConversionPattern;
315
316 // exp(x+I*y) = exp(x)*(cos(y)+I*sin(y))
317 // Handle special cases as StableHLO implementation does:
318 // 1. When b == 0, set imag(exp(z)) = 0
319 // 2. When exp(x) == inf, use exp(x/2)*(cos(y)+I*sin(y))*exp(x/2)
320 // Handle non-finite inputs according to C99 annex G:
321 // 3. When x == -inf and y is non-finite, return 0 + I*0.
322 // 4. When x == +inf and y is non-finite, return inf + I*NaN.
323 LogicalResult
324 matchAndRewrite(complex::ExpOp op, OpAdaptor adaptor,
325 ConversionPatternRewriter &rewriter) const override {
326 auto loc = op.getLoc();
327 auto type = cast<ComplexType>(adaptor.getComplex().getType());
328 auto ET = cast<FloatType>(type.getElementType());
329 arith::FastMathFlags fmf = op.getFastMathFlagsAttr().getValue();
330 const auto &floatSemantics = ET.getFloatSemantics();
331 ImplicitLocOpBuilder b(loc, rewriter);
332
333 Value x = complex::ReOp::create(b, ET, adaptor.getComplex());
334 Value y = complex::ImOp::create(b, ET, adaptor.getComplex());
335 Value zero = arith::ConstantOp::create(b, ET, b.getZeroAttr(ET));
336 Value half = arith::ConstantOp::create(b, ET, b.getFloatAttr(ET, 0.5));
337 Value inf = arith::ConstantOp::create(
338 b, ET, b.getFloatAttr(ET, APFloat::getInf(floatSemantics)));
339
340 Value exp = math::ExpOp::create(b, x, fmf);
341 Value xHalf = arith::MulFOp::create(b, x, half, fmf);
342 Value expHalf = math::ExpOp::create(b, xHalf, fmf);
343 Value cos = math::CosOp::create(b, y, fmf);
344 Value sin = math::SinOp::create(b, y, fmf);
345
346 Value expIsInf =
347 arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ, exp, inf, fmf);
348 Value yIsZero =
349 arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ, y, zero);
350
351 // Real path: select between exp(x)*cos(y) and exp(x/2)*cos(y)*exp(x/2)
352 Value realNormal = arith::MulFOp::create(b, exp, cos, fmf);
353 Value expHalfCos = arith::MulFOp::create(b, expHalf, cos, fmf);
354 Value realOverflow = arith::MulFOp::create(b, expHalfCos, expHalf, fmf);
355 Value resultReal =
356 arith::SelectOp::create(b, expIsInf, realOverflow, realNormal);
357
358 // Imaginary part: if y == 0 return 0 else select between exp(x)*sin(y) and
359 // exp(x/2)*sin(y)*exp(x/2)
360 Value imagNormal = arith::MulFOp::create(b, exp, sin, fmf);
361 Value expHalfSin = arith::MulFOp::create(b, expHalf, sin, fmf);
362 Value imagOverflow = arith::MulFOp::create(b, expHalfSin, expHalf, fmf);
363 Value imagNonZero =
364 arith::SelectOp::create(b, expIsInf, imagOverflow, imagNormal);
365 Value resultImag = arith::SelectOp::create(b, yIsZero, zero, imagNonZero);
366
367 // When x is +-inf and y is +-inf or NaN, exp(x) * cos(y) is inf * NaN or
368 // 0 * NaN. C99 Annex G gives exp(-inf + I*y) = +-0 +- 0*I and
369 // exp(+inf + I*y) = +-inf + NaN*I for these y.
370 if (!arith::bitEnumContainsAll(fmf, arith::FastMathFlags::ninf)) {
371 Value absX = math::AbsFOp::create(b, x);
372 Value absY = math::AbsFOp::create(b, y);
373 Value yIsNotFinite =
374 arith::CmpFOp::create(b, arith::CmpFPredicate::UGE, absY, inf);
375 Value xIsInf =
376 arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ, absX, inf);
377 Value xIsNeg =
378 arith::CmpFOp::create(b, arith::CmpFPredicate::OLT, x, zero);
379 Value useExpX = arith::AndIOp::create(b, xIsInf, yIsNotFinite);
380 Value useZeroImag = arith::AndIOp::create(b, useExpX, xIsNeg);
381 resultReal = arith::SelectOp::create(b, useExpX, exp, resultReal);
382 resultImag = arith::SelectOp::create(b, useZeroImag, zero, resultImag);
383 }
384
385 rewriter.replaceOpWithNewOp<complex::CreateOp>(op, type, resultReal,
386 resultImag);
387 return success();
388 }
389};
390
391Value evaluatePolynomial(ImplicitLocOpBuilder &b, Value arg,
392 ArrayRef<double> coefficients,
393 arith::FastMathFlagsAttr fmf) {
394 auto argType = mlir::cast<FloatType>(arg.getType());
395 Value poly =
396 arith::ConstantOp::create(b, b.getFloatAttr(argType, coefficients[0]));
397 for (unsigned i = 1; i < coefficients.size(); ++i) {
398 poly = math::FmaOp::create(
399 b, poly, arg,
400 arith::ConstantOp::create(b, b.getFloatAttr(argType, coefficients[i])),
401 fmf);
402 }
403 return poly;
404}
405
406struct Expm1OpConversion : public OpConversionPattern<complex::Expm1Op> {
407 using OpConversionPattern<complex::Expm1Op>::OpConversionPattern;
408
409 // e^(a+bi)-1 = (e^a*cos(b)-1)+e^a*sin(b)i
410 // [handle inaccuracies when a and/or b are small]
411 // = ((e^a - 1) * cos(b) + cos(b) - 1) + e^a*sin(b)i
412 // = (expm1(a) * cos(b) + cosm1(b)) + e^a*sin(b)i
413 LogicalResult
414 matchAndRewrite(complex::Expm1Op op, OpAdaptor adaptor,
415 ConversionPatternRewriter &rewriter) const override {
416 auto type = op.getType();
417 auto elemType = mlir::cast<FloatType>(type.getElementType());
418
419 arith::FastMathFlagsAttr fmf = op.getFastMathFlagsAttr();
420 ImplicitLocOpBuilder b(op.getLoc(), rewriter);
421 Value real = complex::ReOp::create(b, adaptor.getComplex());
422 Value imag = complex::ImOp::create(b, adaptor.getComplex());
423
424 Value zero = arith::ConstantOp::create(b, b.getFloatAttr(elemType, 0.0));
425 Value one = arith::ConstantOp::create(b, b.getFloatAttr(elemType, 1.0));
426
427 Value expm1Real = math::ExpM1Op::create(b, real, fmf);
428 Value expReal = arith::AddFOp::create(b, expm1Real, one, fmf);
429
430 Value sinImag = math::SinOp::create(b, imag, fmf);
431 Value cosm1Imag = emitCosm1(imag, fmf, b);
432 Value cosImag = arith::AddFOp::create(b, cosm1Imag, one, fmf);
433
434 Value realResult = arith::AddFOp::create(
435 b, arith::MulFOp::create(b, expm1Real, cosImag, fmf), cosm1Imag, fmf);
436
437 Value imagIsZero = arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ, imag,
438 zero, fmf.getValue());
439 Value imagResult = arith::SelectOp::create(
440 b, imagIsZero, zero, arith::MulFOp::create(b, expReal, sinImag, fmf));
441
442 rewriter.replaceOpWithNewOp<complex::CreateOp>(op, type, realResult,
443 imagResult);
444 return success();
445 }
446
447private:
448 Value emitCosm1(Value arg, arith::FastMathFlagsAttr fmf,
449 ImplicitLocOpBuilder &b) const {
450 auto argType = mlir::cast<FloatType>(arg.getType());
451 auto negHalf = arith::ConstantOp::create(b, b.getFloatAttr(argType, -0.5));
452 auto one = arith::ConstantOp::create(b, b.getFloatAttr(argType, 1.0));
453
454 // Algorithm copied from cephes cosm1.
455 SmallVector<double, 7> kCoeffs{
456 4.7377507964246204691685E-14, -1.1470284843425359765671E-11,
457 2.0876754287081521758361E-9, -2.7557319214999787979814E-7,
458 2.4801587301570552304991E-5, -1.3888888888888872993737E-3,
459 4.1666666666666666609054E-2,
460 };
461 Value cos = math::CosOp::create(b, arg, fmf);
462 Value forLargeArg = arith::SubFOp::create(b, cos, one, fmf);
463
464 Value argPow2 = arith::MulFOp::create(b, arg, arg, fmf);
465 Value argPow4 = arith::MulFOp::create(b, argPow2, argPow2, fmf);
466 Value poly = evaluatePolynomial(b, argPow2, kCoeffs, fmf);
467
468 auto forSmallArg =
469 arith::AddFOp::create(b, arith::MulFOp::create(b, argPow4, poly, fmf),
470 arith::MulFOp::create(b, negHalf, argPow2, fmf));
471
472 // (pi/4)^2 is approximately 0.61685
473 Value piOver4Pow2 =
474 arith::ConstantOp::create(b, b.getFloatAttr(argType, 0.61685));
475 Value cond = arith::CmpFOp::create(b, arith::CmpFPredicate::OGE, argPow2,
476 piOver4Pow2, fmf.getValue());
477 return arith::SelectOp::create(b, cond, forLargeArg, forSmallArg);
478 }
479};
480
481struct LogOpConversion : public OpConversionPattern<complex::LogOp> {
482 using OpConversionPattern<complex::LogOp>::OpConversionPattern;
483
484 LogicalResult
485 matchAndRewrite(complex::LogOp op, OpAdaptor adaptor,
486 ConversionPatternRewriter &rewriter) const override {
487 auto type = cast<ComplexType>(adaptor.getComplex().getType());
488 auto elementType = cast<FloatType>(type.getElementType());
489 arith::FastMathFlagsAttr fmf = op.getFastMathFlagsAttr();
490 mlir::ImplicitLocOpBuilder b(op.getLoc(), rewriter);
491
492 Value abs = complex::AbsOp::create(b, elementType, adaptor.getComplex(),
493 fmf.getValue());
494 Value resultReal = math::LogOp::create(b, elementType, abs, fmf.getValue());
495 Value real = complex::ReOp::create(b, elementType, adaptor.getComplex());
496 Value imag = complex::ImOp::create(b, elementType, adaptor.getComplex());
497 Value resultImag =
498 math::Atan2Op::create(b, elementType, imag, real, fmf.getValue());
499 rewriter.replaceOpWithNewOp<complex::CreateOp>(op, type, resultReal,
500 resultImag);
501 return success();
502 }
503};
504
505struct Log1pOpConversion : public OpConversionPattern<complex::Log1pOp> {
506 using OpConversionPattern<complex::Log1pOp>::OpConversionPattern;
507
508 LogicalResult
509 matchAndRewrite(complex::Log1pOp op, OpAdaptor adaptor,
510 ConversionPatternRewriter &rewriter) const override {
511 auto type = cast<ComplexType>(adaptor.getComplex().getType());
512 auto elementType = cast<FloatType>(type.getElementType());
513 arith::FastMathFlags fmf = op.getFastMathFlagsAttr().getValue();
514 mlir::ImplicitLocOpBuilder b(op.getLoc(), rewriter);
515
516 Value real = complex::ReOp::create(b, adaptor.getComplex());
517 Value imag = complex::ImOp::create(b, adaptor.getComplex());
518
519 Value half = arith::ConstantOp::create(b, elementType,
520 b.getFloatAttr(elementType, 0.5));
521 Value one = arith::ConstantOp::create(b, elementType,
522 b.getFloatAttr(elementType, 1));
523 Value realPlusOne = arith::AddFOp::create(b, real, one, fmf);
524 Value absRealPlusOne = math::AbsFOp::create(b, realPlusOne, fmf);
525 Value absImag = math::AbsFOp::create(b, imag, fmf);
526
527 Value maxAbs = arith::MaximumFOp::create(b, absRealPlusOne, absImag, fmf);
528 Value minAbs = arith::MinimumFOp::create(b, absRealPlusOne, absImag, fmf);
529
530 Value useReal = arith::CmpFOp::create(b, arith::CmpFPredicate::OGT,
531 realPlusOne, absImag, fmf);
532 Value maxMinusOne = arith::SubFOp::create(b, maxAbs, one, fmf);
533 Value maxAbsOfRealPlusOneAndImagMinusOne =
534 arith::SelectOp::create(b, useReal, real, maxMinusOne);
535 arith::FastMathFlags fmfWithNaNInf = arith::bitEnumClear(
536 fmf, arith::FastMathFlags::nnan | arith::FastMathFlags::ninf);
537 Value minMaxRatio = arith::DivFOp::create(b, minAbs, maxAbs, fmfWithNaNInf);
538 Value logOfMaxAbsOfRealPlusOneAndImag =
539 math::Log1pOp::create(b, maxAbsOfRealPlusOneAndImagMinusOne, fmf);
540 Value logOfSqrtPart = math::Log1pOp::create(
541 b, arith::MulFOp::create(b, minMaxRatio, minMaxRatio, fmfWithNaNInf),
542 fmfWithNaNInf);
543 Value r = arith::AddFOp::create(
544 b, arith::MulFOp::create(b, half, logOfSqrtPart, fmfWithNaNInf),
545 logOfMaxAbsOfRealPlusOneAndImag, fmfWithNaNInf);
546 Value resultReal = arith::SelectOp::create(
547 b,
548 arith::CmpFOp::create(b, arith::CmpFPredicate::UNO, r, r,
549 fmfWithNaNInf),
550 minAbs, r);
551 Value resultImag = math::Atan2Op::create(b, imag, realPlusOne, fmf);
552 rewriter.replaceOpWithNewOp<complex::CreateOp>(op, type, resultReal,
553 resultImag);
554 return success();
555 }
556};
557
558struct MulOpConversion : public OpConversionPattern<complex::MulOp> {
559 using OpConversionPattern<complex::MulOp>::OpConversionPattern;
560
561 LogicalResult
562 matchAndRewrite(complex::MulOp op, OpAdaptor adaptor,
563 ConversionPatternRewriter &rewriter) const override {
564 mlir::ImplicitLocOpBuilder b(op.getLoc(), rewriter);
565 auto type = cast<ComplexType>(adaptor.getLhs().getType());
566 auto elementType = cast<FloatType>(type.getElementType());
567 arith::FastMathFlagsAttr fmf = op.getFastMathFlagsAttr();
568 auto fmfValue = fmf.getValue();
569 Value lhsReal = complex::ReOp::create(b, elementType, adaptor.getLhs());
570 Value lhsImag = complex::ImOp::create(b, elementType, adaptor.getLhs());
571 Value rhsReal = complex::ReOp::create(b, elementType, adaptor.getRhs());
572 Value rhsImag = complex::ImOp::create(b, elementType, adaptor.getRhs());
573 Value real;
574 Value imag;
575 if (arith::bitEnumContainsAll(fmfValue, arith::FastMathFlags::contract)) {
576 Value lhsImagTimesRhsImag =
577 arith::MulFOp::create(b, lhsImag, rhsImag, fmfValue);
578 Value negLhsImagTimesRhsImag =
579 arith::NegFOp::create(b, lhsImagTimesRhsImag, fmfValue);
580 real = math::FmaOp::create(b, lhsReal, rhsReal, negLhsImagTimesRhsImag,
581 fmfValue);
582
583 Value lhsImagTimesRhsReal =
584 arith::MulFOp::create(b, lhsImag, rhsReal, fmfValue);
585 imag = math::FmaOp::create(b, lhsReal, rhsImag, lhsImagTimesRhsReal,
586 fmfValue);
587 } else {
588 Value lhsRealTimesRhsReal =
589 arith::MulFOp::create(b, lhsReal, rhsReal, fmfValue);
590 Value lhsImagTimesRhsImag =
591 arith::MulFOp::create(b, lhsImag, rhsImag, fmfValue);
592 Value lhsImagTimesRhsReal =
593 arith::MulFOp::create(b, lhsImag, rhsReal, fmfValue);
594 Value lhsRealTimesRhsImag =
595 arith::MulFOp::create(b, lhsReal, rhsImag, fmfValue);
596
597 real = arith::SubFOp::create(b, lhsRealTimesRhsReal, lhsImagTimesRhsImag,
598 fmfValue);
599 imag = arith::AddFOp::create(b, lhsImagTimesRhsReal, lhsRealTimesRhsImag,
600 fmfValue);
601 }
602 rewriter.replaceOpWithNewOp<complex::CreateOp>(op, type, real, imag);
603 return success();
604 }
605};
606
607struct NegOpConversion : public OpConversionPattern<complex::NegOp> {
608 using OpConversionPattern<complex::NegOp>::OpConversionPattern;
609
610 LogicalResult
611 matchAndRewrite(complex::NegOp op, OpAdaptor adaptor,
612 ConversionPatternRewriter &rewriter) const override {
613 auto loc = op.getLoc();
614 auto type = cast<ComplexType>(adaptor.getComplex().getType());
615 auto elementType = cast<FloatType>(type.getElementType());
616 arith::FastMathFlags fmf = op.getFastMathFlagsAttr().getValue();
617
618 Value real =
619 complex::ReOp::create(rewriter, loc, elementType, adaptor.getComplex());
620 Value imag =
621 complex::ImOp::create(rewriter, loc, elementType, adaptor.getComplex());
622 Value negReal = arith::NegFOp::create(rewriter, loc, real, fmf);
623 Value negImag = arith::NegFOp::create(rewriter, loc, imag, fmf);
624 rewriter.replaceOpWithNewOp<complex::CreateOp>(op, type, negReal, negImag);
625 return success();
626 }
627};
628
629struct SinOpConversion : public TrigonometricOpConversion<complex::SinOp> {
630 using TrigonometricOpConversion<complex::SinOp>::TrigonometricOpConversion;
631
632 std::pair<Value, Value> combine(Location loc, Value scaledExp,
633 Value reciprocalExp, Value sin, Value cos,
634 ConversionPatternRewriter &rewriter,
635 arith::FastMathFlagsAttr fmf) const override {
636 // Complex sine is defined as;
637 // sin(x + iy) = -0.5i * (exp(i(x + iy)) - exp(-i(x + iy)))
638 // Plugging in:
639 // exp(i(x+iy)) = exp(-y + ix) = exp(-y)(cos(x) + i sin(x))
640 // exp(-i(x+iy)) = exp(y + i(-x)) = exp(y)(cos(x) + i (-sin(x)))
641 // and defining t := exp(y)
642 // We get:
643 // Re(sin(x + iy)) = (0.5*t + 0.5/t) * sin x
644 // Im(sin(x + iy)) = (0.5*t - 0.5/t) * cos x
645 Value sum =
646 arith::AddFOp::create(rewriter, loc, scaledExp, reciprocalExp, fmf);
647 Value resultReal = arith::MulFOp::create(rewriter, loc, sum, sin, fmf);
648 Value diff =
649 arith::SubFOp::create(rewriter, loc, scaledExp, reciprocalExp, fmf);
650 Value resultImag = arith::MulFOp::create(rewriter, loc, diff, cos, fmf);
651 return {resultReal, resultImag};
652 }
653};
654
655// The algorithm is listed in https://dl.acm.org/doi/pdf/10.1145/363717.363780.
656struct SqrtOpConversion : public OpConversionPattern<complex::SqrtOp> {
657 using OpConversionPattern<complex::SqrtOp>::OpConversionPattern;
658
659 LogicalResult
660 matchAndRewrite(complex::SqrtOp op, OpAdaptor adaptor,
661 ConversionPatternRewriter &rewriter) const override {
662 ImplicitLocOpBuilder b(op.getLoc(), rewriter);
663
664 auto type = cast<ComplexType>(op.getType());
665 auto elementType = cast<FloatType>(type.getElementType());
666 arith::FastMathFlags fmf = op.getFastMathFlagsAttr().getValue();
667
668 auto cst = [&](APFloat v) {
669 return arith::ConstantOp::create(b, elementType,
670 b.getFloatAttr(elementType, v));
671 };
672 const auto &floatSemantics = elementType.getFloatSemantics();
673 Value zero = cst(APFloat::getZero(floatSemantics));
674 Value half = arith::ConstantOp::create(b, elementType,
675 b.getFloatAttr(elementType, 0.5));
676
677 Value real = complex::ReOp::create(b, elementType, adaptor.getComplex());
678 Value imag = complex::ImOp::create(b, elementType, adaptor.getComplex());
679 Value absSqrt = computeAbs(real, imag, fmf, b, AbsFn::sqrt);
680 Value argArg = math::Atan2Op::create(b, imag, real, fmf);
681 Value sqrtArg = arith::MulFOp::create(b, argArg, half, fmf);
682 Value cos = math::CosOp::create(b, sqrtArg, fmf);
683 Value sin = math::SinOp::create(b, sqrtArg, fmf);
684 // sin(atan2(0, inf)) = 0, sqrt(abs(inf)) = inf, but we can't multiply
685 // 0 * inf.
686 Value sinIsZero =
687 arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ, sin, zero, fmf);
688
689 Value resultReal = arith::MulFOp::create(b, absSqrt, cos, fmf);
690 Value resultImag = arith::SelectOp::create(
691 b, sinIsZero, zero, arith::MulFOp::create(b, absSqrt, sin, fmf));
692 if (!arith::bitEnumContainsAll(fmf, arith::FastMathFlags::nnan |
693 arith::FastMathFlags::ninf)) {
694 Value inf = cst(APFloat::getInf(floatSemantics));
695 Value negInf = cst(APFloat::getInf(floatSemantics, true));
696 Value nan = cst(APFloat::getNaN(floatSemantics));
697 Value absImag = math::AbsFOp::create(b, elementType, imag, fmf);
698
699 Value absImagIsInf = arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ,
700 absImag, inf, fmf);
701 Value absImagIsNotInf = arith::CmpFOp::create(
702 b, arith::CmpFPredicate::ONE, absImag, inf, fmf);
703 Value realIsInf =
704 arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ, real, inf, fmf);
705 Value realIsNegInf = arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ,
706 real, negInf, fmf);
707
708 resultReal = arith::SelectOp::create(
709 b, arith::AndIOp::create(b, realIsNegInf, absImagIsNotInf), zero,
710 resultReal);
711 resultReal = arith::SelectOp::create(
712 b, arith::OrIOp::create(b, absImagIsInf, realIsInf), inf, resultReal);
713
714 Value imagSignInf = math::CopySignOp::create(b, inf, imag, fmf);
715 resultImag = arith::SelectOp::create(
716 b,
717 arith::CmpFOp::create(b, arith::CmpFPredicate::UNO, absSqrt, absSqrt),
718 nan, resultImag);
719 resultImag = arith::SelectOp::create(
720 b, arith::OrIOp::create(b, absImagIsInf, realIsNegInf), imagSignInf,
721 resultImag);
722 }
723
724 Value resultIsZero =
725 arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ, absSqrt, zero, fmf);
726 resultReal = arith::SelectOp::create(b, resultIsZero, zero, resultReal);
727 resultImag = arith::SelectOp::create(b, resultIsZero, zero, resultImag);
728
729 rewriter.replaceOpWithNewOp<complex::CreateOp>(op, type, resultReal,
730 resultImag);
731 return success();
732 }
733};
734
735struct SignOpConversion : public OpConversionPattern<complex::SignOp> {
736 using OpConversionPattern<complex::SignOp>::OpConversionPattern;
737
738 LogicalResult
739 matchAndRewrite(complex::SignOp op, OpAdaptor adaptor,
740 ConversionPatternRewriter &rewriter) const override {
741 auto type = cast<ComplexType>(adaptor.getComplex().getType());
742 auto elementType = cast<FloatType>(type.getElementType());
743 mlir::ImplicitLocOpBuilder b(op.getLoc(), rewriter);
744 arith::FastMathFlagsAttr fmf = op.getFastMathFlagsAttr();
745
746 Value real = complex::ReOp::create(b, elementType, adaptor.getComplex());
747 Value imag = complex::ImOp::create(b, elementType, adaptor.getComplex());
748 Value zero =
749 arith::ConstantOp::create(b, elementType, b.getZeroAttr(elementType));
750 Value realIsZero =
751 arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ, real, zero);
752 Value imagIsZero =
753 arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ, imag, zero);
754 Value isZero = arith::AndIOp::create(b, realIsZero, imagIsZero);
755 auto abs =
756 complex::AbsOp::create(b, elementType, adaptor.getComplex(), fmf);
757 Value realSign = arith::DivFOp::create(b, real, abs, fmf);
758 Value imagSign = arith::DivFOp::create(b, imag, abs, fmf);
759 Value sign = complex::CreateOp::create(b, type, realSign, imagSign);
760 rewriter.replaceOpWithNewOp<arith::SelectOp>(op, isZero,
761 adaptor.getComplex(), sign);
762 return success();
763 }
764};
765
766template <typename Op>
767struct TanTanhOpConversion : public OpConversionPattern<Op> {
768 using OpConversionPattern<Op>::OpConversionPattern;
769
770 LogicalResult
771 matchAndRewrite(Op op, typename Op::Adaptor adaptor,
772 ConversionPatternRewriter &rewriter) const override {
773 ImplicitLocOpBuilder b(op.getLoc(), rewriter);
774 auto loc = op.getLoc();
775 auto type = cast<ComplexType>(adaptor.getComplex().getType());
776 auto elementType = cast<FloatType>(type.getElementType());
777 arith::FastMathFlags fmf = op.getFastMathFlagsAttr().getValue();
778 const auto &floatSemantics = elementType.getFloatSemantics();
779
780 Value real =
781 complex::ReOp::create(b, loc, elementType, adaptor.getComplex());
782 Value imag =
783 complex::ImOp::create(b, loc, elementType, adaptor.getComplex());
784
785 if constexpr (std::is_same_v<Op, complex::TanOp>) {
786 // tan(x+yi) = -i*tanh(-y + xi)
787 std::swap(real, imag);
788 real = arith::NegFOp::create(b, real, fmf);
789 }
790
791 auto cst = [&](APFloat v) {
792 return arith::ConstantOp::create(b, elementType,
793 b.getFloatAttr(elementType, v));
794 };
795 Value inf = cst(APFloat::getInf(floatSemantics));
796 Value four = arith::ConstantOp::create(b, elementType,
797 b.getFloatAttr(elementType, 4.0));
798 Value twoReal = arith::AddFOp::create(b, real, real, fmf);
799 Value negTwoReal = arith::NegFOp::create(b, twoReal, fmf);
800 Value expTwoRealMinusOne = math::ExpM1Op::create(b, twoReal, fmf);
801 Value expNegTwoRealMinusOne = math::ExpM1Op::create(b, negTwoReal, fmf);
802 Value realNum = arith::SubFOp::create(b, expTwoRealMinusOne,
803 expNegTwoRealMinusOne, fmf);
804 Value expProduct = arith::MulFOp::create(b, expTwoRealMinusOne,
805 expNegTwoRealMinusOne, fmf);
806 Value expSumMinusTwo = arith::NegFOp::create(b, expProduct, fmf);
807
808 Value cosImag = math::CosOp::create(b, imag, fmf);
809 Value cosImagSq = arith::MulFOp::create(b, cosImag, cosImag, fmf);
810 Value twoCosTwoImagPlusOne = arith::MulFOp::create(b, cosImagSq, four, fmf);
811 Value sinImag = math::SinOp::create(b, imag, fmf);
812
813 Value imagNum = arith::MulFOp::create(
814 b, four, arith::MulFOp::create(b, cosImag, sinImag, fmf), fmf);
815
816 Value denom =
817 arith::AddFOp::create(b, expSumMinusTwo, twoCosTwoImagPlusOne, fmf);
818
819 Value isInf = arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ,
820 expSumMinusTwo, inf, fmf);
821 Value negOne = arith::ConstantOp::create(b, elementType,
822 b.getFloatAttr(elementType, -1.0));
823 Value realLimit = math::CopySignOp::create(b, negOne, real, fmf);
824
825 Value resultReal = arith::SelectOp::create(
826 b, isInf, realLimit, arith::DivFOp::create(b, realNum, denom, fmf));
827 Value resultImag = arith::DivFOp::create(b, imagNum, denom, fmf);
828
829 if (!arith::bitEnumContainsAll(fmf, arith::FastMathFlags::nnan |
830 arith::FastMathFlags::ninf)) {
831 Value absReal = math::AbsFOp::create(b, real, fmf);
832 Value zero = arith::ConstantOp::create(b, elementType,
833 b.getFloatAttr(elementType, 0.0));
834 Value nan = cst(APFloat::getNaN(floatSemantics));
835
836 Value absRealIsInf = arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ,
837 absReal, inf, fmf);
838 Value imagIsZero =
839 arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ, imag, zero, fmf);
840 Value absRealIsNotInf = arith::XOrIOp::create(
841 b, absRealIsInf, arith::ConstantIntOp::create(b, true, /*width=*/1));
842
843 Value imagNumIsNaN = arith::CmpFOp::create(b, arith::CmpFPredicate::UNO,
844 imagNum, imagNum, fmf);
845 Value resultRealIsNaN =
846 arith::AndIOp::create(b, imagNumIsNaN, absRealIsNotInf);
847 Value resultImagIsZero = arith::OrIOp::create(
848 b, imagIsZero, arith::AndIOp::create(b, absRealIsInf, imagNumIsNaN));
849
850 resultReal = arith::SelectOp::create(b, resultRealIsNaN, nan, resultReal);
851 resultImag =
852 arith::SelectOp::create(b, resultImagIsZero, zero, resultImag);
853 }
854
855 if constexpr (std::is_same_v<Op, complex::TanOp>) {
856 // tan(x+yi) = -i*tanh(-y + xi)
857 std::swap(resultReal, resultImag);
858 resultImag = arith::NegFOp::create(b, resultImag, fmf);
859 }
860
861 rewriter.replaceOpWithNewOp<complex::CreateOp>(op, type, resultReal,
862 resultImag);
863 return success();
864 }
865};
866
867struct ConjOpConversion : public OpConversionPattern<complex::ConjOp> {
868 using OpConversionPattern<complex::ConjOp>::OpConversionPattern;
869
870 LogicalResult
871 matchAndRewrite(complex::ConjOp op, OpAdaptor adaptor,
872 ConversionPatternRewriter &rewriter) const override {
873 auto loc = op.getLoc();
874 auto type = cast<ComplexType>(adaptor.getComplex().getType());
875 auto elementType = cast<FloatType>(type.getElementType());
876 arith::FastMathFlags fmf = op.getFastMathFlagsAttr().getValue();
877 Value real =
878 complex::ReOp::create(rewriter, loc, elementType, adaptor.getComplex());
879 Value imag =
880 complex::ImOp::create(rewriter, loc, elementType, adaptor.getComplex());
881 Value negImag =
882 arith::NegFOp::create(rewriter, loc, elementType, imag, fmf);
883
884 rewriter.replaceOpWithNewOp<complex::CreateOp>(op, type, real, negImag);
885
886 return success();
887 }
888};
889
890/// Converts lhs^y = (a+bi)^(c+di) to
891/// (a*a+b*b)^(0.5c) * exp(-d*atan2(b,a)) * (cos(q) + i*sin(q)),
892/// where q = c*atan2(b,a)+0.5d*ln(a*a+b*b)
893static Value powOpConversionImpl(mlir::ImplicitLocOpBuilder &builder,
894 ComplexType type, Value lhs, Value c, Value d,
895 arith::FastMathFlags fmf) {
896 auto elementType = cast<FloatType>(type.getElementType());
897
898 Value a = complex::ReOp::create(builder, lhs);
899 Value b = complex::ImOp::create(builder, lhs);
900
901 Value abs = complex::AbsOp::create(builder, lhs, fmf);
902 Value absToC = math::PowFOp::create(builder, abs, c, fmf);
903
904 Value negD = arith::NegFOp::create(builder, d, fmf);
905 Value argLhs = math::Atan2Op::create(builder, b, a, fmf);
906 Value negDArgLhs = arith::MulFOp::create(builder, negD, argLhs, fmf);
907 Value expNegDArgLhs = math::ExpOp::create(builder, negDArgLhs, fmf);
908
909 Value coeff = arith::MulFOp::create(builder, absToC, expNegDArgLhs, fmf);
910 Value lnAbs = math::LogOp::create(builder, abs, fmf);
911 Value cArgLhs = arith::MulFOp::create(builder, c, argLhs, fmf);
912 Value dLnAbs = arith::MulFOp::create(builder, d, lnAbs, fmf);
913 Value q = arith::AddFOp::create(builder, cArgLhs, dLnAbs, fmf);
914 Value cosQ = math::CosOp::create(builder, q, fmf);
915 Value sinQ = math::SinOp::create(builder, q, fmf);
916
917 Value inf = arith::ConstantOp::create(
918 builder, elementType,
919 builder.getFloatAttr(elementType,
920 APFloat::getInf(elementType.getFloatSemantics())));
921 Value zero = arith::ConstantOp::create(
922 builder, elementType, builder.getFloatAttr(elementType, 0.0));
923 Value one = arith::ConstantOp::create(builder, elementType,
924 builder.getFloatAttr(elementType, 1.0));
925 Value complexOne = complex::CreateOp::create(builder, type, one, zero);
926 Value complexZero = complex::CreateOp::create(builder, type, zero, zero);
927 Value complexInf = complex::CreateOp::create(builder, type, inf, zero);
928
929 // Case 0:
930 // d^c is 0 if d is 0 and c > 0. 0^0 is defined to be 1.0, see
931 // Branch Cuts for Complex Elementary Functions or Much Ado About
932 // Nothing's Sign Bit, W. Kahan, Section 10.
933 Value absEqZero =
934 arith::CmpFOp::create(builder, arith::CmpFPredicate::OEQ, abs, zero, fmf);
935 Value dEqZero =
936 arith::CmpFOp::create(builder, arith::CmpFPredicate::OEQ, d, zero, fmf);
937 Value cEqZero =
938 arith::CmpFOp::create(builder, arith::CmpFPredicate::OEQ, c, zero, fmf);
939 Value bEqZero =
940 arith::CmpFOp::create(builder, arith::CmpFPredicate::OEQ, b, zero, fmf);
941
942 Value zeroLeC =
943 arith::CmpFOp::create(builder, arith::CmpFPredicate::OLE, zero, c, fmf);
944 Value coeffCosQ = arith::MulFOp::create(builder, coeff, cosQ, fmf);
945 Value coeffSinQ = arith::MulFOp::create(builder, coeff, sinQ, fmf);
946 Value complexOneOrZero =
947 arith::SelectOp::create(builder, cEqZero, complexOne, complexZero);
948 Value coeffCosSin =
949 complex::CreateOp::create(builder, type, coeffCosQ, coeffSinQ);
950 Value cutoff0 = arith::SelectOp::create(
951 builder,
952 arith::AndIOp::create(
953 builder, arith::AndIOp::create(builder, absEqZero, dEqZero), zeroLeC),
954 complexOneOrZero, coeffCosSin);
955
956 // Case 1:
957 // x^0 is defined to be 1 for any x, see
958 // Branch Cuts for Complex Elementary Functions or Much Ado About
959 // Nothing's Sign Bit, W. Kahan, Section 10.
960 Value rhsEqZero = arith::AndIOp::create(builder, cEqZero, dEqZero);
961 Value cutoff1 =
962 arith::SelectOp::create(builder, rhsEqZero, complexOne, cutoff0);
963
964 // Case 2:
965 // 1^(c + d*i) = 1 + 0*i
966 Value lhsEqOne = arith::AndIOp::create(
967 builder,
968 arith::CmpFOp::create(builder, arith::CmpFPredicate::OEQ, a, one, fmf),
969 bEqZero);
970 Value cutoff2 =
971 arith::SelectOp::create(builder, lhsEqOne, complexOne, cutoff1);
972
973 // Case 3:
974 // inf^(c + 0*i) = inf + 0*i, c > 0
975 Value lhsEqInf = arith::AndIOp::create(
976 builder,
977 arith::CmpFOp::create(builder, arith::CmpFPredicate::OEQ, a, inf, fmf),
978 bEqZero);
979 Value rhsGt0 = arith::AndIOp::create(
980 builder, dEqZero,
981 arith::CmpFOp::create(builder, arith::CmpFPredicate::OGT, c, zero, fmf));
982 Value cutoff3 = arith::SelectOp::create(
983 builder, arith::AndIOp::create(builder, lhsEqInf, rhsGt0), complexInf,
984 cutoff2);
985
986 // Case 4:
987 // inf^(c + 0*i) = 0 + 0*i, c < 0
988 Value rhsLt0 = arith::AndIOp::create(
989 builder, dEqZero,
990 arith::CmpFOp::create(builder, arith::CmpFPredicate::OLT, c, zero, fmf));
991 Value cutoff4 = arith::SelectOp::create(
992 builder, arith::AndIOp::create(builder, lhsEqInf, rhsLt0), complexZero,
993 cutoff3);
994
995 return cutoff4;
996}
997
998struct PowiOpConversion : public OpConversionPattern<complex::PowiOp> {
999 using OpConversionPattern<complex::PowiOp>::OpConversionPattern;
1000
1001 LogicalResult
1002 matchAndRewrite(complex::PowiOp op, OpAdaptor adaptor,
1003 ConversionPatternRewriter &rewriter) const override {
1004 ImplicitLocOpBuilder builder(op.getLoc(), rewriter);
1005 auto type = cast<ComplexType>(op.getType());
1006 auto elementType = cast<FloatType>(type.getElementType());
1007
1008 Value floatExponent =
1009 arith::SIToFPOp::create(builder, elementType, adaptor.getRhs());
1010 Value zero = arith::ConstantOp::create(
1011 builder, elementType, builder.getFloatAttr(elementType, 0.0));
1012 Value complexExponent =
1013 complex::CreateOp::create(builder, type, floatExponent, zero);
1014
1015 auto pow = complex::PowOp::create(builder, type, adaptor.getLhs(),
1016 complexExponent, op.getFastmathAttr());
1017 rewriter.replaceOp(op, pow.getResult());
1018 return success();
1019 }
1020};
1021
1022struct PowOpConversion : public OpConversionPattern<complex::PowOp> {
1023 using OpConversionPattern<complex::PowOp>::OpConversionPattern;
1024
1025 LogicalResult
1026 matchAndRewrite(complex::PowOp op, OpAdaptor adaptor,
1027 ConversionPatternRewriter &rewriter) const override {
1028 mlir::ImplicitLocOpBuilder builder(op.getLoc(), rewriter);
1029 auto type = cast<ComplexType>(adaptor.getLhs().getType());
1030 auto elementType = cast<FloatType>(type.getElementType());
1031
1032 Value c = complex::ReOp::create(builder, elementType, adaptor.getRhs());
1033 Value d = complex::ImOp::create(builder, elementType, adaptor.getRhs());
1034
1035 rewriter.replaceOp(op, {powOpConversionImpl(builder, type, adaptor.getLhs(),
1036 c, d, op.getFastmath())});
1037 return success();
1038 }
1039};
1040
1041struct RsqrtOpConversion : public OpConversionPattern<complex::RsqrtOp> {
1042 using OpConversionPattern<complex::RsqrtOp>::OpConversionPattern;
1043
1044 LogicalResult
1045 matchAndRewrite(complex::RsqrtOp op, OpAdaptor adaptor,
1046 ConversionPatternRewriter &rewriter) const override {
1047 mlir::ImplicitLocOpBuilder b(op.getLoc(), rewriter);
1048 auto type = cast<ComplexType>(adaptor.getComplex().getType());
1049 auto elementType = cast<FloatType>(type.getElementType());
1050
1051 arith::FastMathFlags fmf = op.getFastMathFlagsAttr().getValue();
1052
1053 auto cst = [&](APFloat v) {
1054 return arith::ConstantOp::create(b, elementType,
1055 b.getFloatAttr(elementType, v));
1056 };
1057 const auto &floatSemantics = elementType.getFloatSemantics();
1058 Value zero = cst(APFloat::getZero(floatSemantics));
1059 Value inf = cst(APFloat::getInf(floatSemantics));
1060 Value negHalf = arith::ConstantOp::create(
1061 b, elementType, b.getFloatAttr(elementType, -0.5));
1062 Value nan = cst(APFloat::getNaN(floatSemantics));
1063
1064 Value real = complex::ReOp::create(b, elementType, adaptor.getComplex());
1065 Value imag = complex::ImOp::create(b, elementType, adaptor.getComplex());
1066 Value absRsqrt = computeAbs(real, imag, fmf, b, AbsFn::rsqrt);
1067 Value argArg = math::Atan2Op::create(b, imag, real, fmf);
1068 Value rsqrtArg = arith::MulFOp::create(b, argArg, negHalf, fmf);
1069 Value cos = math::CosOp::create(b, rsqrtArg, fmf);
1070 Value sin = math::SinOp::create(b, rsqrtArg, fmf);
1071
1072 Value resultReal = arith::MulFOp::create(b, absRsqrt, cos, fmf);
1073 Value resultImag = arith::MulFOp::create(b, absRsqrt, sin, fmf);
1074
1075 if (!arith::bitEnumContainsAll(fmf, arith::FastMathFlags::nnan |
1076 arith::FastMathFlags::ninf)) {
1077 Value realSignedZero = math::CopySignOp::create(b, zero, real, fmf);
1078 Value imagSignedZero = math::CopySignOp::create(b, zero, imag, fmf);
1079 Value negImagSignedZero = arith::NegFOp::create(b, imagSignedZero, fmf);
1080
1081 Value absReal = math::AbsFOp::create(b, real, fmf);
1082 Value absImag = math::AbsFOp::create(b, imag, fmf);
1083
1084 Value absImagIsInf = arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ,
1085 absImag, inf, fmf);
1086 Value realIsNan =
1087 arith::CmpFOp::create(b, arith::CmpFPredicate::UNO, real, real, fmf);
1088 Value realIsInf = arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ,
1089 absReal, inf, fmf);
1090 Value inIsNanInf = arith::AndIOp::create(b, absImagIsInf, realIsNan);
1091
1092 Value resultIsZero = arith::OrIOp::create(b, inIsNanInf, realIsInf);
1093
1094 resultReal =
1095 arith::SelectOp::create(b, resultIsZero, realSignedZero, resultReal);
1096 resultImag = arith::SelectOp::create(b, resultIsZero, negImagSignedZero,
1097 resultImag);
1098 }
1099
1100 Value isRealZero =
1101 arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ, real, zero, fmf);
1102 Value isImagZero =
1103 arith::CmpFOp::create(b, arith::CmpFPredicate::OEQ, imag, zero, fmf);
1104 Value isZero = arith::AndIOp::create(b, isRealZero, isImagZero);
1105
1106 resultReal = arith::SelectOp::create(b, isZero, inf, resultReal);
1107 resultImag = arith::SelectOp::create(b, isZero, nan, resultImag);
1108
1109 rewriter.replaceOpWithNewOp<complex::CreateOp>(op, type, resultReal,
1110 resultImag);
1111 return success();
1112 }
1113};
1114
1115struct AngleOpConversion : public OpConversionPattern<complex::AngleOp> {
1116 using OpConversionPattern<complex::AngleOp>::OpConversionPattern;
1117
1118 LogicalResult
1119 matchAndRewrite(complex::AngleOp op, OpAdaptor adaptor,
1120 ConversionPatternRewriter &rewriter) const override {
1121 auto loc = op.getLoc();
1122 auto type = op.getType();
1123 arith::FastMathFlagsAttr fmf = op.getFastMathFlagsAttr();
1124
1125 Value real =
1126 complex::ReOp::create(rewriter, loc, type, adaptor.getComplex());
1127 Value imag =
1128 complex::ImOp::create(rewriter, loc, type, adaptor.getComplex());
1129
1130 rewriter.replaceOpWithNewOp<math::Atan2Op>(op, imag, real, fmf);
1131
1132 return success();
1133 }
1134};
1135
1136} // namespace
1137
1139 RewritePatternSet &patterns, complex::ComplexRangeFlags complexRange) {
1140 // clang-format off
1141 patterns.add<
1142 AbsOpConversion,
1143 AngleOpConversion,
1144 Atan2OpConversion,
1145 BinaryComplexOpConversion<complex::AddOp, arith::AddFOp>,
1146 BinaryComplexOpConversion<complex::SubOp, arith::SubFOp>,
1147 ComparisonOpConversion<complex::EqualOp, arith::CmpFPredicate::OEQ>,
1148 ComparisonOpConversion<complex::NotEqualOp, arith::CmpFPredicate::UNE>,
1149 ConjOpConversion,
1150 CosOpConversion,
1151 ExpOpConversion,
1152 Expm1OpConversion,
1153 Log1pOpConversion,
1154 LogOpConversion,
1155 MulOpConversion,
1156 NegOpConversion,
1157 SignOpConversion,
1158 SinOpConversion,
1159 SqrtOpConversion,
1160 TanTanhOpConversion<complex::TanOp>,
1161 TanTanhOpConversion<complex::TanhOp>,
1162 PowiOpConversion,
1163 PowOpConversion,
1164 RsqrtOpConversion
1165 >(patterns.getContext());
1166
1167 patterns.add<DivOpConversion>(patterns.getContext(), complexRange);
1168
1169 // clang-format on
1170}
1171
1172namespace {
1173struct ConvertComplexToStandardPass
1175 ConvertComplexToStandardPass> {
1176 using Base::Base;
1177
1178 void runOnOperation() override;
1179};
1180
1181void ConvertComplexToStandardPass::runOnOperation() {
1182 // Convert to the Standard dialect using the converter defined above.
1183 RewritePatternSet patterns(&getContext());
1184 populateComplexToStandardConversionPatterns(patterns, complexRange);
1185
1186 ConversionTarget target(getContext());
1187 target.addLegalDialect<arith::ArithDialect, math::MathDialect>();
1188 target.addLegalOp<complex::CreateOp, complex::ImOp, complex::ReOp>();
1189 if (failed(
1190 applyPartialConversion(getOperation(), target, std::move(patterns))))
1191 signalPassFailure();
1192}
1193} // namespace
return success()
lhs
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
b getContext())
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
FloatAttr getFloatAttr(Type type, double value)
Definition Builders.cpp:263
ImplicitLocOpBuilder maintains a 'current location', allowing use of the create<> method without spec...
Definition Builders.h:632
Location getLoc()
The source location the operation was defined or derived from.
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.
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
static ConstantIntOp create(OpBuilder &builder, Location location, int64_t value, unsigned width)
Definition ArithOps.cpp:333
NestedPattern Op(FilterFunctionType filter=defaultFilterFunction)
void convertDivToStandardUsingAlgebraic(ConversionPatternRewriter &rewriter, Location loc, Value lhsRe, Value lhsIm, Value rhsRe, Value rhsIm, arith::FastMathFlagsAttr fmf, Value *resultRe, Value *resultIm)
convert a complex division to the arith/math dialects using algebraic method
void convertDivToStandardUsingRangeReduction(ConversionPatternRewriter &rewriter, Location loc, Value lhsRe, Value lhsIm, Value rhsRe, Value rhsIm, arith::FastMathFlagsAttr fmf, Value *resultRe, Value *resultIm)
convert a complex division to the arith/math dialects using Smith's method
Fraction abs(const Fraction &f)
Definition Fraction.h:107
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:744
OwningOpRef< spirv::ModuleOp > combine(ArrayRef< spirv::ModuleOp > inputModules, OpBuilder &combinedModuleBuilder, SymbolRenameListener symRenameListener)
Combines a list of SPIR-V inputModules into one.
Include the generated interface declarations.
constexpr T real(const NonFloatComplex< T > &x)
Definition Complex.h:255
void populateComplexToStandardConversionPatterns(RewritePatternSet &patterns, mlir::complex::ComplexRangeFlags complexRange=mlir::complex::ComplexRangeFlags::improved)
Populate the given list with patterns that convert from Complex to Standard.
constexpr T imag(const NonFloatComplex< T > &x)
Definition Complex.h:260