MLIR 24.0.0git
ComplexOps.cpp
Go to the documentation of this file.
1//===- ComplexOps.cpp - MLIR Complex Operations ---------------------------===//
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
11#include "mlir/IR/Builders.h"
14
15using namespace mlir;
16using namespace mlir::complex;
17
18//===----------------------------------------------------------------------===//
19// ConstantOp
20//===----------------------------------------------------------------------===//
21
22OpFoldResult ConstantOp::fold(FoldAdaptor adaptor) { return getValue(); }
23
24void ConstantOp::getAsmResultNames(
25 function_ref<void(Value, StringRef)> setNameFn) {
26 setNameFn(getResult(), "cst");
27}
28
29bool ConstantOp::isBuildableWith(Attribute value, Type type) {
30 if (auto arrAttr = llvm::dyn_cast<ArrayAttr>(value)) {
31 auto complexTy = llvm::dyn_cast<ComplexType>(type);
32 if (!complexTy || arrAttr.size() != 2)
33 return false;
34 auto complexEltTy = complexTy.getElementType();
35 if (auto fre = llvm::dyn_cast<FloatAttr>(arrAttr[0])) {
36 auto im = llvm::dyn_cast<FloatAttr>(arrAttr[1]);
37 return im && fre.getType() == complexEltTy &&
38 im.getType() == complexEltTy;
39 }
40 if (auto ire = llvm::dyn_cast<IntegerAttr>(arrAttr[0])) {
41 auto im = llvm::dyn_cast<IntegerAttr>(arrAttr[1]);
42 return im && ire.getType() == complexEltTy &&
43 im.getType() == complexEltTy;
44 }
45 }
46 return false;
47}
48
49LogicalResult ConstantOp::verify() {
50 ArrayAttr arrayAttr = getValue();
51 if (arrayAttr.size() != 2) {
52 return emitOpError(
53 "requires 'value' to be a complex constant, represented as array of "
54 "two values");
55 }
56
57 auto complexEltTy = getType().getElementType();
58 if (!isa<FloatAttr, IntegerAttr>(arrayAttr[0]) ||
59 !isa<FloatAttr, IntegerAttr>(arrayAttr[1]))
60 return emitOpError(
61 "requires attribute's elements to be float or integer attributes");
62 auto re = llvm::dyn_cast<TypedAttr>(arrayAttr[0]);
63 auto im = llvm::dyn_cast<TypedAttr>(arrayAttr[1]);
64 if (complexEltTy != re.getType() || complexEltTy != im.getType()) {
65 return emitOpError()
66 << "requires attribute's element types (" << re.getType() << ", "
67 << im.getType()
68 << ") to match the element type of the op's return type ("
69 << complexEltTy << ")";
70 }
71 return success();
72}
73
74//===----------------------------------------------------------------------===//
75// BitcastOp
76//===----------------------------------------------------------------------===//
77
78OpFoldResult BitcastOp::fold(FoldAdaptor bitcast) {
79 if (getOperand().getType() == getType())
80 return getOperand();
81
82 return {};
83}
84
85LogicalResult BitcastOp::verify() {
86 auto operandType = getOperand().getType();
87 auto resultType = getType();
88
89 // We allow this to be legal as it can be folded away.
90 if (operandType == resultType)
91 return success();
92
93 if (!operandType.isIntOrFloat() && !isa<ComplexType>(operandType)) {
94 return emitOpError("operand must be int/float/complex");
95 }
96
97 if (!resultType.isIntOrFloat() && !isa<ComplexType>(resultType)) {
98 return emitOpError("result must be int/float/complex");
99 }
100
101 if (isa<ComplexType>(operandType) == isa<ComplexType>(resultType)) {
102 return emitOpError(
103 "requires that either input or output has a complex type");
104 }
105
106 if (isa<ComplexType>(resultType))
107 std::swap(operandType, resultType);
108
109 int32_t operandBitwidth = dyn_cast<ComplexType>(operandType)
110 .getElementType()
111 .getIntOrFloatBitWidth() *
112 2;
113 int32_t resultBitwidth = resultType.getIntOrFloatBitWidth();
114
115 if (operandBitwidth != resultBitwidth) {
116 return emitOpError("casting bitwidths do not match");
117 }
118
119 return success();
120}
121
122struct MergeComplexBitcast final : OpRewritePattern<BitcastOp> {
123 using OpRewritePattern<BitcastOp>::OpRewritePattern;
124
125 LogicalResult matchAndRewrite(BitcastOp op,
126 PatternRewriter &rewriter) const override {
127 if (auto defining = op.getOperand().getDefiningOp<BitcastOp>()) {
128 if (isa<ComplexType>(op.getType()) ||
129 isa<ComplexType>(defining.getOperand().getType())) {
130 // complex.bitcast requires that input or output is complex.
131 rewriter.replaceOpWithNewOp<BitcastOp>(op, op.getType(),
132 defining.getOperand());
133 } else {
134 rewriter.replaceOpWithNewOp<arith::BitcastOp>(op, op.getType(),
135 defining.getOperand());
136 }
137 return success();
138 }
139
140 if (auto defining = op.getOperand().getDefiningOp<arith::BitcastOp>()) {
141 rewriter.replaceOpWithNewOp<BitcastOp>(op, op.getType(),
142 defining.getOperand());
143 return success();
144 }
145
146 return failure();
147 }
148};
149
150struct MergeArithBitcast final : OpRewritePattern<arith::BitcastOp> {
151 using OpRewritePattern<arith::BitcastOp>::OpRewritePattern;
152
153 LogicalResult matchAndRewrite(arith::BitcastOp op,
154 PatternRewriter &rewriter) const override {
155 if (auto defining = op.getOperand().getDefiningOp<complex::BitcastOp>()) {
156 rewriter.replaceOpWithNewOp<complex::BitcastOp>(op, op.getType(),
157 defining.getOperand());
158 return success();
159 }
160
161 return failure();
162 }
163};
164
165void BitcastOp::getCanonicalizationPatterns(RewritePatternSet &results,
166 MLIRContext *context) {
167 results.add<MergeComplexBitcast, MergeArithBitcast>(context);
168}
169
170//===----------------------------------------------------------------------===//
171// CreateOp
172//===----------------------------------------------------------------------===//
173
174OpFoldResult CreateOp::fold(FoldAdaptor adaptor) {
175 // Fold complex.create(complex.re(op), complex.im(op)).
176 if (auto reOp = getOperand(0).getDefiningOp<ReOp>()) {
177 if (auto imOp = getOperand(1).getDefiningOp<ImOp>()) {
178 if (reOp.getOperand() == imOp.getOperand()) {
179 return reOp.getOperand();
180 }
181 }
182 }
183 return {};
184}
185
186//===----------------------------------------------------------------------===//
187// ImOp
188//===----------------------------------------------------------------------===//
189
190OpFoldResult ImOp::fold(FoldAdaptor adaptor) {
191 ArrayAttr arrayAttr =
192 llvm::dyn_cast_if_present<ArrayAttr>(adaptor.getComplex());
193 if (arrayAttr && arrayAttr.size() == 2)
194 return arrayAttr[1];
195 if (auto createOp = getOperand().getDefiningOp<CreateOp>())
196 return createOp.getOperand(1);
197 return {};
198}
199
200namespace {
201template <typename OpKind, int ComponentIndex>
202struct FoldComponentNeg final : OpRewritePattern<OpKind> {
203 using OpRewritePattern<OpKind>::OpRewritePattern;
204
205 LogicalResult matchAndRewrite(OpKind op,
206 PatternRewriter &rewriter) const override {
207 auto negOp = op.getOperand().template getDefiningOp<NegOp>();
208 if (!negOp)
209 return failure();
210
211 auto createOp = negOp.getComplex().template getDefiningOp<CreateOp>();
212 if (!createOp)
213 return failure();
214
215 Type elementType = createOp.getType().getElementType();
216 assert(isa<FloatType>(elementType));
217
218 rewriter.replaceOpWithNewOp<arith::NegFOp>(
219 op, elementType, createOp.getOperand(ComponentIndex));
220 return success();
221 }
222};
223} // namespace
224
225void ImOp::getCanonicalizationPatterns(RewritePatternSet &results,
226 MLIRContext *context) {
227 results.add<FoldComponentNeg<ImOp, 1>>(context);
228}
229
230//===----------------------------------------------------------------------===//
231// ReOp
232//===----------------------------------------------------------------------===//
233
234OpFoldResult ReOp::fold(FoldAdaptor adaptor) {
235 ArrayAttr arrayAttr =
236 llvm::dyn_cast_if_present<ArrayAttr>(adaptor.getComplex());
237 if (arrayAttr && arrayAttr.size() == 2)
238 return arrayAttr[0];
239 if (auto createOp = getOperand().getDefiningOp<CreateOp>())
240 return createOp.getOperand(0);
241 return {};
242}
243
244void ReOp::getCanonicalizationPatterns(RewritePatternSet &results,
245 MLIRContext *context) {
246 results.add<FoldComponentNeg<ReOp, 0>>(context);
247}
248
249//===----------------------------------------------------------------------===//
250// AddOp
251//===----------------------------------------------------------------------===//
252
253OpFoldResult AddOp::fold(FoldAdaptor adaptor) {
254 // complex.add(complex.sub(a, b), b) -> a
255 // complex.add(b, complex.sub(a, b)) -> a
256 // The intermediate result is rounded, so both ops need `reassoc`, and
257 // (-0.0 - b) + b is +0.0, so the add also needs `nsz`.
258 if (arith::bitEnumContainsAll(getFastmath(), arith::FastMathFlags::reassoc |
259 arith::FastMathFlags::nsz)) {
260 if (auto sub = getLhs().getDefiningOp<SubOp>())
261 if (getRhs() == sub.getRhs() &&
262 arith::bitEnumContainsAll(sub.getFastmath(),
263 arith::FastMathFlags::reassoc))
264 return sub.getLhs();
265
266 if (auto sub = getRhs().getDefiningOp<SubOp>())
267 if (getLhs() == sub.getRhs() &&
268 arith::bitEnumContainsAll(sub.getFastmath(),
269 arith::FastMathFlags::reassoc))
270 return sub.getLhs();
271 }
272
273 // complex.add(a, complex.constant<-0.0, -0.0>) -> a
274 if (auto constantOp = getRhs().getDefiningOp<ConstantOp>()) {
275 auto arrayAttr = constantOp.getValue();
276 if (llvm::cast<FloatAttr>(arrayAttr[0]).getValue().isNegZero() &&
277 llvm::cast<FloatAttr>(arrayAttr[1]).getValue().isNegZero()) {
278 return getLhs();
279 }
280 }
281
282 return {};
283}
284
285//===----------------------------------------------------------------------===//
286// SubOp
287//===----------------------------------------------------------------------===//
288
289OpFoldResult SubOp::fold(FoldAdaptor adaptor) {
290 // complex.sub(complex.add(a, b), b) -> a
291 // The intermediate result is rounded, so both ops need `reassoc`, and
292 // (-0.0 + b) - b is +0.0, so the sub also needs `nsz`.
293 if (arith::bitEnumContainsAll(getFastmath(), arith::FastMathFlags::reassoc |
294 arith::FastMathFlags::nsz))
295 if (auto add = getLhs().getDefiningOp<AddOp>())
296 if (getRhs() == add.getRhs() &&
297 arith::bitEnumContainsAll(add.getFastmath(),
298 arith::FastMathFlags::reassoc))
299 return add.getLhs();
300
301 // complex.sub(a, complex.constant<0.0, 0.0>) -> a
302 if (auto constantOp = getRhs().getDefiningOp<ConstantOp>()) {
303 auto arrayAttr = constantOp.getValue();
304 if (llvm::cast<FloatAttr>(arrayAttr[0]).getValue().isZero() &&
305 llvm::cast<FloatAttr>(arrayAttr[1]).getValue().isZero()) {
306 return getLhs();
307 }
308 }
309
310 return {};
311}
312
313//===----------------------------------------------------------------------===//
314// NegOp
315//===----------------------------------------------------------------------===//
316
317OpFoldResult NegOp::fold(FoldAdaptor adaptor) {
318 // complex.neg(complex.neg(a)) -> a
319 if (auto negOp = getOperand().getDefiningOp<NegOp>())
320 return negOp.getOperand();
321
322 return {};
323}
324
325//===----------------------------------------------------------------------===//
326// ExpOp
327//===----------------------------------------------------------------------===//
328
329OpFoldResult ExpOp::fold(FoldAdaptor adaptor) {
330 // complex.exp(complex.log(a)) -> a
331 // exp(log(a)) is only an approximation of a, so both ops need `reassoc`.
332 // log(a) is not finite for a zero or non-finite a, and its result depends
333 // on the sign of a zero, so the log also needs `nnan`, `ninf` and `nsz`.
334 if (arith::bitEnumContainsAll(getFastmath(), arith::FastMathFlags::reassoc))
335 if (auto logOp = getOperand().getDefiningOp<LogOp>())
336 if (arith::bitEnumContainsAll(
337 logOp.getFastmath(),
338 arith::FastMathFlags::reassoc | arith::FastMathFlags::nnan |
339 arith::FastMathFlags::ninf | arith::FastMathFlags::nsz))
340 return logOp.getOperand();
341
342 return {};
343}
344
345//===----------------------------------------------------------------------===//
346// ConjOp
347//===----------------------------------------------------------------------===//
348
349OpFoldResult ConjOp::fold(FoldAdaptor adaptor) {
350 // complex.conj(complex.conj(a)) -> a
351 if (auto conjOp = getOperand().getDefiningOp<ConjOp>())
352 return conjOp.getOperand();
353
354 return {};
355}
356
357//===----------------------------------------------------------------------===//
358// MulOp
359//===----------------------------------------------------------------------===//
360
361OpFoldResult MulOp::fold(FoldAdaptor adaptor) {
362 auto constant = getRhs().getDefiningOp<ConstantOp>();
363 if (!constant)
364 return {};
365
366 ArrayAttr arrayAttr = constant.getValue();
367 APFloat real = cast<FloatAttr>(arrayAttr[0]).getValue();
368 APFloat imag = cast<FloatAttr>(arrayAttr[1]).getValue();
369
370 if (!imag.isZero())
371 return {};
372
373 // complex.mul(a, complex.constant<1.0, 0.0>) -> a
374 if (real == APFloat(real.getSemantics(), 1))
375 return getLhs();
376
377 return {};
378}
379
380//===----------------------------------------------------------------------===//
381// DivOp
382//===----------------------------------------------------------------------===//
383
384OpFoldResult DivOp::fold(FoldAdaptor adaptor) {
385 Attribute rhs = adaptor.getRhs();
386 Attribute lhs = adaptor.getLhs();
387
388 // complex.div(complex.constant<NaN, NaN>, a) -> complex.constant<NaN, NaN>
389 // complex.div(complex.constant<NaN, a>, b) -> complex.constant<NaN, NaN>
390 // complex.div(complex.constant<a, NaN>, b) -> complex.constant<NaN, NaN>
391 bool isLhsComplexHasNan = false;
392 ArrayAttr lhsArrayAttr = dyn_cast_if_present<ArrayAttr>(lhs);
393 if (lhsArrayAttr && lhsArrayAttr.size() == 2) {
394 APFloat lhsReal = cast<FloatAttr>(lhsArrayAttr[0]).getValue();
395 APFloat lhsImag = cast<FloatAttr>(lhsArrayAttr[1]).getValue();
396 isLhsComplexHasNan = lhsReal.isNaN() || lhsImag.isNaN();
397 if (isLhsComplexHasNan) {
398 Attribute nanValue = lhsReal.isNaN() ? lhsArrayAttr[0] : lhsArrayAttr[1];
399 return ArrayAttr::get(getContext(), {nanValue, nanValue});
400 }
401 }
402
403 ArrayAttr rhsArrayAttr = dyn_cast_if_present<ArrayAttr>(rhs);
404 if (!rhsArrayAttr || rhsArrayAttr.size() != 2)
405 return {};
406
407 // Fold only if RHS is complex.constant<1.0, 0.0>
408 APFloat rhsImag = cast<FloatAttr>(rhsArrayAttr[1]).getValue();
409 APFloat rhsReal = cast<FloatAttr>(rhsArrayAttr[0]).getValue();
410 if (!rhsImag.isZero() || rhsReal != APFloat(rhsReal.getSemantics(), 1))
411 return {};
412
413 // Fold to LHS if it doesn't contains NaNs or fast math flag nan is set
414 // complex.div(a, complex.constant<1.0, 0.0>) fastmath<nnan> -> a
415 if ((lhsArrayAttr && !isLhsComplexHasNan) ||
416 arith::bitEnumContainsAll(getFastmath(), arith::FastMathFlags::nnan))
417 return getLhs();
418
419 return {};
420}
421
422//===----------------------------------------------------------------------===//
423// TableGen'd op method definitions
424//===----------------------------------------------------------------------===//
425
426#define GET_OP_CLASSES
427#include "mlir/Dialect/Complex/IR/ComplexOps.cpp.inc"
return success()
ArrayAttr()
b getContext())
#define add(a, b)
Attributes are known-constant values of operations.
Definition Attributes.h:25
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:64
This class represents a single result from folding an operation.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
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
Include the generated interface declarations.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:311
constexpr T real(const NonFloatComplex< T > &x)
Definition Complex.h:255
constexpr T imag(const NonFloatComplex< T > &x)
Definition Complex.h:260
llvm::function_ref< Fn > function_ref
Definition LLVM.h:155
LogicalResult matchAndRewrite(arith::BitcastOp op, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(BitcastOp op, PatternRewriter &rewriter) const override
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})