MLIR 23.0.0git
ArithToEmitC.cpp
Go to the documentation of this file.
1//===- ArithToEmitC.cpp - Arith to EmitC Patterns ---------------*- C++ -*-===//
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 the Arith dialect to the EmitC
10// dialect.
11//
12//===----------------------------------------------------------------------===//
13
15
23
24using namespace mlir;
25
26namespace {
27/// Implement the interface to convert Arith to EmitC.
28struct ArithToEmitCDialectInterface : public ConvertToEmitCPatternInterface {
29 ArithToEmitCDialectInterface(Dialect *dialect)
30 : ConvertToEmitCPatternInterface(dialect) {}
31
32 /// Hook for derived dialect interface to provide conversion patterns
33 /// and mark dialect legal for the conversion target.
34 void populateConvertToEmitCConversionPatterns(
35 ConversionTarget &target, TypeConverter &typeConverter,
36 RewritePatternSet &patterns, std::optional<bool> lowerToCpp) const final {
37 populateArithToEmitCPatterns(typeConverter, patterns);
38 }
39};
40} // namespace
41
43 registry.addExtension(+[](MLIRContext *ctx, arith::ArithDialect *dialect) {
44 dialect->addInterfaces<ArithToEmitCDialectInterface>();
45 });
46}
47
48//===----------------------------------------------------------------------===//
49// Conversion Patterns
50//===----------------------------------------------------------------------===//
51
52namespace {
53class ArithConstantOpConversionPattern
54 : public OpConversionPattern<arith::ConstantOp> {
55public:
56 using Base::Base;
57
58 LogicalResult
59 matchAndRewrite(arith::ConstantOp arithConst,
60 arith::ConstantOp::Adaptor adaptor,
61 ConversionPatternRewriter &rewriter) const override {
62 if (isa<MemRefType>(arithConst.getType()))
63 return rewriter.notifyMatchFailure(arithConst,
64 "memref constants are not supported");
65 Type newTy = this->getTypeConverter()->convertType(arithConst.getType());
66 if (!newTy)
67 return rewriter.notifyMatchFailure(arithConst, "type conversion failed");
68 rewriter.replaceOpWithNewOp<emitc::ConstantOp>(arithConst, newTy,
69 adaptor.getValue());
70 return success();
71 }
72};
73
74/// Get the signed or unsigned type corresponding to \p ty.
75Type adaptIntegralTypeSignedness(Type ty, bool needsUnsigned) {
76 if (isa<IntegerType>(ty)) {
77 if (ty.isUnsignedInteger() != needsUnsigned) {
78 auto signedness = needsUnsigned
79 ? IntegerType::SignednessSemantics::Unsigned
80 : IntegerType::SignednessSemantics::Signed;
81 return IntegerType::get(ty.getContext(), ty.getIntOrFloatBitWidth(),
82 signedness);
83 }
84 } else if (emitc::isPointerWideType(ty)) {
85 if (isa<emitc::SizeTType>(ty) != needsUnsigned) {
86 if (needsUnsigned)
87 return emitc::SizeTType::get(ty.getContext());
88 return emitc::PtrDiffTType::get(ty.getContext());
89 }
90 }
91 return ty;
92}
93
94/// Insert a cast operation to type \p ty if \p val does not have this type.
95Value adaptValueType(Value val, ConversionPatternRewriter &rewriter, Type ty) {
96 return rewriter.createOrFold<emitc::CastOp>(val.getLoc(), ty, val);
97}
98
99class CmpFOpConversion : public OpConversionPattern<arith::CmpFOp> {
100public:
101 using Base::Base;
102
103 LogicalResult
104 matchAndRewrite(arith::CmpFOp op, OpAdaptor adaptor,
105 ConversionPatternRewriter &rewriter) const override {
106
107 if (!isa<FloatType>(adaptor.getRhs().getType())) {
108 return rewriter.notifyMatchFailure(op.getLoc(),
109 "cmpf currently only supported on "
110 "floats, not tensors/vectors thereof");
111 }
112
113 bool unordered = false;
114 emitc::CmpPredicate predicate;
115 switch (op.getPredicate()) {
116 case arith::CmpFPredicate::AlwaysFalse: {
117 auto constant =
118 emitc::ConstantOp::create(rewriter, op.getLoc(), rewriter.getI1Type(),
119 rewriter.getBoolAttr(/*value=*/false));
120 rewriter.replaceOp(op, constant);
121 return success();
122 }
123 case arith::CmpFPredicate::OEQ:
124 unordered = false;
125 predicate = emitc::CmpPredicate::eq;
126 break;
127 case arith::CmpFPredicate::OGT:
128 unordered = false;
129 predicate = emitc::CmpPredicate::gt;
130 break;
131 case arith::CmpFPredicate::OGE:
132 unordered = false;
133 predicate = emitc::CmpPredicate::ge;
134 break;
135 case arith::CmpFPredicate::OLT:
136 unordered = false;
137 predicate = emitc::CmpPredicate::lt;
138 break;
139 case arith::CmpFPredicate::OLE:
140 unordered = false;
141 predicate = emitc::CmpPredicate::le;
142 break;
143 case arith::CmpFPredicate::ONE:
144 unordered = false;
145 predicate = emitc::CmpPredicate::ne;
146 break;
147 case arith::CmpFPredicate::ORD: {
148 // ordered, i.e. none of the operands is NaN
149 auto cmp = createCheckIsOrdered(rewriter, op.getLoc(), adaptor.getLhs(),
150 adaptor.getRhs());
151 rewriter.replaceOp(op, cmp);
152 return success();
153 }
154 case arith::CmpFPredicate::UEQ:
155 unordered = true;
156 predicate = emitc::CmpPredicate::eq;
157 break;
158 case arith::CmpFPredicate::UGT:
159 unordered = true;
160 predicate = emitc::CmpPredicate::gt;
161 break;
162 case arith::CmpFPredicate::UGE:
163 unordered = true;
164 predicate = emitc::CmpPredicate::ge;
165 break;
166 case arith::CmpFPredicate::ULT:
167 unordered = true;
168 predicate = emitc::CmpPredicate::lt;
169 break;
170 case arith::CmpFPredicate::ULE:
171 unordered = true;
172 predicate = emitc::CmpPredicate::le;
173 break;
174 case arith::CmpFPredicate::UNE:
175 unordered = true;
176 predicate = emitc::CmpPredicate::ne;
177 break;
178 case arith::CmpFPredicate::UNO: {
179 // unordered, i.e. either operand is nan
180 auto cmp = createCheckIsUnordered(rewriter, op.getLoc(), adaptor.getLhs(),
181 adaptor.getRhs());
182 rewriter.replaceOp(op, cmp);
183 return success();
184 }
185 case arith::CmpFPredicate::AlwaysTrue: {
186 auto constant =
187 emitc::ConstantOp::create(rewriter, op.getLoc(), rewriter.getI1Type(),
188 rewriter.getBoolAttr(/*value=*/true));
189 rewriter.replaceOp(op, constant);
190 return success();
191 }
192 }
193
194 // Compare the values naively
195 auto cmpResult =
196 emitc::CmpOp::create(rewriter, op.getLoc(), op.getType(), predicate,
197 adaptor.getLhs(), adaptor.getRhs());
198
199 // Adjust the results for unordered/ordered semantics
200 if (unordered) {
201 auto isUnordered = createCheckIsUnordered(
202 rewriter, op.getLoc(), adaptor.getLhs(), adaptor.getRhs());
203 rewriter.replaceOpWithNewOp<emitc::LogicalOrOp>(op, op.getType(),
204 isUnordered, cmpResult);
205 return success();
206 }
207
208 auto isOrdered = createCheckIsOrdered(rewriter, op.getLoc(),
209 adaptor.getLhs(), adaptor.getRhs());
210 rewriter.replaceOpWithNewOp<emitc::LogicalAndOp>(op, op.getType(),
211 isOrdered, cmpResult);
212 return success();
213 }
214
215private:
216 /// Return a value that is true if \p operand is NaN.
217 Value isNaN(ConversionPatternRewriter &rewriter, Location loc,
218 Value operand) const {
219 // A value is NaN exactly when it compares unequal to itself.
220 return emitc::CmpOp::create(rewriter, loc, rewriter.getI1Type(),
221 emitc::CmpPredicate::ne, operand, operand);
222 }
223
224 /// Return a value that is true if \p operand is not NaN.
225 Value isNotNaN(ConversionPatternRewriter &rewriter, Location loc,
226 Value operand) const {
227 // A value is not NaN exactly when it compares equal to itself.
228 return emitc::CmpOp::create(rewriter, loc, rewriter.getI1Type(),
229 emitc::CmpPredicate::eq, operand, operand);
230 }
231
232 /// Return a value that is true if the operands \p first and \p second are
233 /// unordered (i.e., at least one of them is NaN).
234 Value createCheckIsUnordered(ConversionPatternRewriter &rewriter,
235 Location loc, Value first, Value second) const {
236 auto firstIsNaN = isNaN(rewriter, loc, first);
237 auto secondIsNaN = isNaN(rewriter, loc, second);
238 return emitc::LogicalOrOp::create(rewriter, loc, rewriter.getI1Type(),
239 firstIsNaN, secondIsNaN);
240 }
241
242 /// Return a value that is true if the operands \p first and \p second are
243 /// both ordered (i.e., none one of them is NaN).
244 Value createCheckIsOrdered(ConversionPatternRewriter &rewriter, Location loc,
245 Value first, Value second) const {
246 auto firstIsNotNaN = isNotNaN(rewriter, loc, first);
247 auto secondIsNotNaN = isNotNaN(rewriter, loc, second);
248 return emitc::LogicalAndOp::create(rewriter, loc, rewriter.getI1Type(),
249 firstIsNotNaN, secondIsNotNaN);
250 }
251};
252
253class CmpIOpConversion : public OpConversionPattern<arith::CmpIOp> {
254public:
255 using Base::Base;
256
257 bool needsUnsignedCmp(arith::CmpIPredicate pred) const {
258 switch (pred) {
259 case arith::CmpIPredicate::eq:
260 case arith::CmpIPredicate::ne:
261 case arith::CmpIPredicate::slt:
262 case arith::CmpIPredicate::sle:
263 case arith::CmpIPredicate::sgt:
264 case arith::CmpIPredicate::sge:
265 return false;
266 case arith::CmpIPredicate::ult:
267 case arith::CmpIPredicate::ule:
268 case arith::CmpIPredicate::ugt:
269 case arith::CmpIPredicate::uge:
270 return true;
271 }
272 llvm_unreachable("unknown cmpi predicate kind");
273 }
274
275 emitc::CmpPredicate toEmitCPred(arith::CmpIPredicate pred) const {
276 switch (pred) {
277 case arith::CmpIPredicate::eq:
278 return emitc::CmpPredicate::eq;
279 case arith::CmpIPredicate::ne:
280 return emitc::CmpPredicate::ne;
281 case arith::CmpIPredicate::slt:
282 case arith::CmpIPredicate::ult:
283 return emitc::CmpPredicate::lt;
284 case arith::CmpIPredicate::sle:
285 case arith::CmpIPredicate::ule:
286 return emitc::CmpPredicate::le;
287 case arith::CmpIPredicate::sgt:
288 case arith::CmpIPredicate::ugt:
289 return emitc::CmpPredicate::gt;
290 case arith::CmpIPredicate::sge:
291 case arith::CmpIPredicate::uge:
292 return emitc::CmpPredicate::ge;
293 }
294 llvm_unreachable("unknown cmpi predicate kind");
295 }
296
297 LogicalResult
298 matchAndRewrite(arith::CmpIOp op, OpAdaptor adaptor,
299 ConversionPatternRewriter &rewriter) const override {
300
301 Type type = adaptor.getLhs().getType();
302 if (!type || !(isa<IntegerType>(type) || emitc::isPointerWideType(type))) {
303 return rewriter.notifyMatchFailure(
304 op, "expected integer or size_t/ssize_t/ptrdiff_t type");
305 }
306
307 bool needsUnsigned = needsUnsignedCmp(op.getPredicate());
308 emitc::CmpPredicate pred = toEmitCPred(op.getPredicate());
309
310 Type arithmeticType = adaptIntegralTypeSignedness(type, needsUnsigned);
311 Value lhs = adaptValueType(adaptor.getLhs(), rewriter, arithmeticType);
312 Value rhs = adaptValueType(adaptor.getRhs(), rewriter, arithmeticType);
313
314 rewriter.replaceOpWithNewOp<emitc::CmpOp>(op, op.getType(), pred, lhs, rhs);
315 return success();
316 }
317};
318
319class NegFOpConversion : public OpConversionPattern<arith::NegFOp> {
320public:
321 using Base::Base;
322
323 LogicalResult
324 matchAndRewrite(arith::NegFOp op, OpAdaptor adaptor,
325 ConversionPatternRewriter &rewriter) const override {
326
327 auto adaptedOp = adaptor.getOperand();
328 auto adaptedOpType = adaptedOp.getType();
329
330 if (isa<TensorType>(adaptedOpType) || isa<VectorType>(adaptedOpType)) {
331 return rewriter.notifyMatchFailure(
332 op.getLoc(),
333 "negf currently only supports scalar types, not vectors or tensors");
334 }
335
336 if (!emitc::isSupportedFloatType(adaptedOpType)) {
337 return rewriter.notifyMatchFailure(
338 op.getLoc(), "floating-point type is not supported by EmitC");
339 }
340
341 rewriter.replaceOpWithNewOp<emitc::UnaryMinusOp>(op, adaptedOpType,
342 adaptedOp);
343 return success();
344 }
345};
346
347template <typename ArithOp, bool castToUnsigned>
348class CastConversion : public OpConversionPattern<ArithOp> {
349public:
350 using OpConversionPattern<ArithOp>::OpConversionPattern;
351
352 LogicalResult
353 matchAndRewrite(ArithOp op, typename ArithOp::Adaptor adaptor,
354 ConversionPatternRewriter &rewriter) const override {
355
356 Type opReturnType = this->getTypeConverter()->convertType(op.getType());
357 if (!opReturnType || !(isa<IntegerType>(opReturnType) ||
358 emitc::isPointerWideType(opReturnType)))
359 return rewriter.notifyMatchFailure(
360 op, "expected integer or size_t/ssize_t/ptrdiff_t result type");
361
362 if (adaptor.getOperands().size() != 1) {
363 return rewriter.notifyMatchFailure(
364 op, "CastConversion only supports unary ops");
365 }
366
367 Type operandType = adaptor.getIn().getType();
368 if (!operandType || !(isa<IntegerType>(operandType) ||
369 emitc::isPointerWideType(operandType)))
370 return rewriter.notifyMatchFailure(
371 op, "expected integer or size_t/ssize_t/ptrdiff_t operand type");
372
373 // Signed (sign-extending) casts from i1 are not supported.
374 if (operandType.isInteger(1) && !castToUnsigned)
375 return rewriter.notifyMatchFailure(op,
376 "operation not supported on i1 type");
377
378 // to-i1 conversions: arith semantics want truncation, whereas (bool)(v) is
379 // equivalent to (v != 0). Implementing as (bool)(v & 0x01) gives
380 // truncation.
381 if (opReturnType.isInteger(1)) {
382 Type attrType = (emitc::isPointerWideType(operandType))
383 ? rewriter.getIndexType()
384 : operandType;
385 auto constOne = emitc::ConstantOp::create(
386 rewriter, op.getLoc(), operandType, rewriter.getOneAttr(attrType));
387 auto oneAndOperand = emitc::BitwiseAndOp::create(
388 rewriter, op.getLoc(), operandType, adaptor.getIn(), constOne);
389 rewriter.replaceOpWithNewOp<emitc::CastOp>(op, opReturnType,
390 oneAndOperand);
391 return success();
392 }
393
394 bool isTruncation =
395 (isa<IntegerType>(operandType) && isa<IntegerType>(opReturnType) &&
396 operandType.getIntOrFloatBitWidth() >
397 opReturnType.getIntOrFloatBitWidth());
398 bool doUnsigned = castToUnsigned || isTruncation;
399
400 // Adapt the signedness of the result (bitwidth-preserving cast)
401 // This is needed e.g., if the return type is signless.
402 Type castDestType = adaptIntegralTypeSignedness(opReturnType, doUnsigned);
403
404 // Adapt the signedness of the operand (bitwidth-preserving cast)
405 Type castSrcType = adaptIntegralTypeSignedness(operandType, doUnsigned);
406 Value actualOp = adaptValueType(adaptor.getIn(), rewriter, castSrcType);
407
408 // Actual cast (may change bitwidth)
409 auto cast =
410 emitc::CastOp::create(rewriter, op.getLoc(), castDestType, actualOp);
411
412 // Cast to the expected output type
413 auto result = adaptValueType(cast, rewriter, opReturnType);
414
415 rewriter.replaceOp(op, result);
416 return success();
417 }
418};
419
420template <typename ArithOp>
421class UnsignedCastConversion : public CastConversion<ArithOp, true> {
422 using CastConversion<ArithOp, true>::CastConversion;
423};
424
425template <typename ArithOp>
426class SignedCastConversion : public CastConversion<ArithOp, false> {
427 using CastConversion<ArithOp, false>::CastConversion;
428};
429
430template <typename ArithOp, typename EmitCOp>
431class ArithOpConversion final : public OpConversionPattern<ArithOp> {
432public:
433 using OpConversionPattern<ArithOp>::OpConversionPattern;
434
435 LogicalResult
436 matchAndRewrite(ArithOp arithOp, typename ArithOp::Adaptor adaptor,
437 ConversionPatternRewriter &rewriter) const override {
438
439 Type newTy = this->getTypeConverter()->convertType(arithOp.getType());
440 if (!newTy)
441 return rewriter.notifyMatchFailure(arithOp,
442 "converting result type failed");
443 rewriter.template replaceOpWithNewOp<EmitCOp>(arithOp, newTy,
444 adaptor.getOperands());
445
446 return success();
447 }
448};
449
450template <class ArithOp, class EmitCOp>
451class BinaryUIOpConversion final : public OpConversionPattern<ArithOp> {
452public:
453 using OpConversionPattern<ArithOp>::OpConversionPattern;
454
455 LogicalResult
456 matchAndRewrite(ArithOp uiBinOp, typename ArithOp::Adaptor adaptor,
457 ConversionPatternRewriter &rewriter) const override {
458 Type newRetTy = this->getTypeConverter()->convertType(uiBinOp.getType());
459 if (!newRetTy)
460 return rewriter.notifyMatchFailure(uiBinOp,
461 "converting result type failed");
462 if (!isa<IntegerType>(newRetTy)) {
463 return rewriter.notifyMatchFailure(uiBinOp, "expected integer type");
464 }
465 Type unsignedType =
466 adaptIntegralTypeSignedness(newRetTy, /*needsUnsigned=*/true);
467 if (!unsignedType)
468 return rewriter.notifyMatchFailure(uiBinOp,
469 "converting result type failed");
470 Value lhsAdapted = adaptValueType(uiBinOp.getLhs(), rewriter, unsignedType);
471 Value rhsAdapted = adaptValueType(uiBinOp.getRhs(), rewriter, unsignedType);
472
473 auto newDivOp = EmitCOp::create(rewriter, uiBinOp.getLoc(), unsignedType,
474 ArrayRef<Value>{lhsAdapted, rhsAdapted});
475 Value resultAdapted = adaptValueType(newDivOp, rewriter, newRetTy);
476 rewriter.replaceOp(uiBinOp, resultAdapted);
477 return success();
478 }
479};
480
481template <typename ArithOp, typename EmitCOp>
482class IntegerOpConversion final : public OpConversionPattern<ArithOp> {
483public:
484 using OpConversionPattern<ArithOp>::OpConversionPattern;
485
486 LogicalResult
487 matchAndRewrite(ArithOp op, typename ArithOp::Adaptor adaptor,
488 ConversionPatternRewriter &rewriter) const override {
489
490 Type type = this->getTypeConverter()->convertType(op.getType());
491 if (!type || !(isa<IntegerType>(type) || emitc::isPointerWideType(type))) {
492 return rewriter.notifyMatchFailure(
493 op, "expected integer or size_t/ssize_t/ptrdiff_t type");
494 }
495
496 if (type.isInteger(1)) {
497 // arith expects wrap-around arithmethic, which doesn't happen on `bool`.
498 return rewriter.notifyMatchFailure(op, "i1 type is not implemented");
499 }
500
501 Type arithmeticType = type;
502 if ((type.isSignlessInteger() || type.isSignedInteger()) &&
503 !bitEnumContainsAll(op.getOverflowFlags(),
504 arith::IntegerOverflowFlags::nsw)) {
505 // If the C type is signed and the op doesn't guarantee "No Signed Wrap",
506 // we compute in unsigned integers to avoid UB.
507 arithmeticType = rewriter.getIntegerType(type.getIntOrFloatBitWidth(),
508 /*isSigned=*/false);
509 }
510
511 Value lhs = adaptValueType(adaptor.getLhs(), rewriter, arithmeticType);
512 Value rhs = adaptValueType(adaptor.getRhs(), rewriter, arithmeticType);
513
514 Value arithmeticResult =
515 EmitCOp::create(rewriter, op.getLoc(), arithmeticType, lhs, rhs);
516
517 Value result = adaptValueType(arithmeticResult, rewriter, type);
518
519 rewriter.replaceOp(op, result);
520 return success();
521 }
522};
523
524template <typename ArithOp, typename EmitCOp>
525class BitwiseOpConversion : public OpConversionPattern<ArithOp> {
526public:
527 using OpConversionPattern<ArithOp>::OpConversionPattern;
528
529 LogicalResult
530 matchAndRewrite(ArithOp op, typename ArithOp::Adaptor adaptor,
531 ConversionPatternRewriter &rewriter) const override {
532
533 Type type = this->getTypeConverter()->convertType(op.getType());
534 if (!isa_and_nonnull<IntegerType>(type)) {
535 return rewriter.notifyMatchFailure(
536 op,
537 "expected integer type, vector/tensor support not yet implemented");
538 }
539
540 // Bitwise ops can be performed directly on booleans
541 if (type.isInteger(1)) {
542 rewriter.replaceOpWithNewOp<EmitCOp>(op, type, adaptor.getLhs(),
543 adaptor.getRhs());
544 return success();
545 }
546
547 // Bitwise ops are defined by the C standard on unsigned operands.
548 Type arithmeticType =
549 adaptIntegralTypeSignedness(type, /*needsUnsigned=*/true);
550
551 Value lhs = adaptValueType(adaptor.getLhs(), rewriter, arithmeticType);
552 Value rhs = adaptValueType(adaptor.getRhs(), rewriter, arithmeticType);
553
554 Value arithmeticResult =
555 EmitCOp::create(rewriter, op.getLoc(), arithmeticType, lhs, rhs);
556
557 Value result = adaptValueType(arithmeticResult, rewriter, type);
558
559 rewriter.replaceOp(op, result);
560 return success();
561 }
562};
563
564template <typename ArithOp, typename EmitCOp, bool isUnsignedOp>
565class ShiftOpConversion : public OpConversionPattern<ArithOp> {
566public:
567 using OpConversionPattern<ArithOp>::OpConversionPattern;
568
569 LogicalResult
570 matchAndRewrite(ArithOp op, typename ArithOp::Adaptor adaptor,
571 ConversionPatternRewriter &rewriter) const override {
572
573 Type type = this->getTypeConverter()->convertType(op.getType());
574 if (!type || !(isa<IntegerType>(type) || emitc::isPointerWideType(type))) {
575 return rewriter.notifyMatchFailure(
576 op, "expected integer or size_t/ssize_t/ptrdiff_t type");
577 }
578
579 if (type.isInteger(1)) {
580 return rewriter.notifyMatchFailure(op, "i1 type is not implemented");
581 }
582
583 Type arithmeticType = adaptIntegralTypeSignedness(type, isUnsignedOp);
584
585 Value lhs = adaptValueType(adaptor.getLhs(), rewriter, arithmeticType);
586 // Shift amount interpreted as unsigned per Arith dialect spec.
587 Type rhsType = adaptIntegralTypeSignedness(adaptor.getRhs().getType(),
588 /*needsUnsigned=*/true);
589 Value rhs = adaptValueType(adaptor.getRhs(), rewriter, rhsType);
590
591 // Add a runtime check for overflow
592 Value width;
593 if (emitc::isPointerWideType(type)) {
594 Value eight = emitc::ConstantOp::create(rewriter, op.getLoc(), rhsType,
595 rewriter.getIndexAttr(8));
596 emitc::CallOpaqueOp sizeOfCall = emitc::CallOpaqueOp::create(
597 rewriter, op.getLoc(), rhsType, "sizeof", ArrayRef<Value>{eight});
598 width = emitc::MulOp::create(rewriter, op.getLoc(), rhsType, eight,
599 sizeOfCall.getResult(0));
600 } else {
601 width = emitc::ConstantOp::create(
602 rewriter, op.getLoc(), rhsType,
603 rewriter.getIntegerAttr(rhsType, type.getIntOrFloatBitWidth()));
604 }
605
606 Value excessCheck =
607 emitc::CmpOp::create(rewriter, op.getLoc(), rewriter.getI1Type(),
608 emitc::CmpPredicate::lt, rhs, width);
609
610 // Any concrete value is a valid refinement of poison.
611 Value poison = emitc::ConstantOp::create(
612 rewriter, op.getLoc(), arithmeticType,
613 (isa<IntegerType>(arithmeticType)
614 ? rewriter.getIntegerAttr(arithmeticType, 0)
615 : rewriter.getIndexAttr(0)));
616
617 emitc::ExpressionOp ternary =
618 emitc::ExpressionOp::create(rewriter, op.getLoc(), arithmeticType,
619 ValueRange({lhs, rhs, excessCheck, poison}),
620 /*do_not_inline=*/false);
621 Block &bodyBlock = ternary.createBody();
622 auto currentPoint = rewriter.getInsertionPoint();
623 rewriter.setInsertionPointToStart(&bodyBlock);
624 Value arithmeticResult =
625 EmitCOp::create(rewriter, op.getLoc(), arithmeticType,
626 bodyBlock.getArgument(0), bodyBlock.getArgument(1));
627 Value resultOrPoison = emitc::ConditionalOp::create(
628 rewriter, op.getLoc(), arithmeticType, bodyBlock.getArgument(2),
629 arithmeticResult, bodyBlock.getArgument(3));
630 emitc::YieldOp::create(rewriter, op.getLoc(), resultOrPoison);
631 rewriter.setInsertionPoint(op->getBlock(), currentPoint);
632
633 Value result = adaptValueType(ternary, rewriter, type);
634
635 rewriter.replaceOp(op, result);
636 return success();
637 }
638};
639
640template <typename ArithOp, typename EmitCOp>
641class SignedShiftOpConversion final
642 : public ShiftOpConversion<ArithOp, EmitCOp, false> {
643 using ShiftOpConversion<ArithOp, EmitCOp, false>::ShiftOpConversion;
644};
645
646template <typename ArithOp, typename EmitCOp>
647class UnsignedShiftOpConversion final
648 : public ShiftOpConversion<ArithOp, EmitCOp, true> {
649 using ShiftOpConversion<ArithOp, EmitCOp, true>::ShiftOpConversion;
650};
651
652class SelectOpConversion : public OpConversionPattern<arith::SelectOp> {
653public:
654 using Base::Base;
655
656 LogicalResult
657 matchAndRewrite(arith::SelectOp selectOp, OpAdaptor adaptor,
658 ConversionPatternRewriter &rewriter) const override {
659
660 Type dstType = getTypeConverter()->convertType(selectOp.getType());
661 if (!dstType)
662 return rewriter.notifyMatchFailure(selectOp, "type conversion failed");
663
664 if (!adaptor.getCondition().getType().isInteger(1))
665 return rewriter.notifyMatchFailure(
666 selectOp,
667 "can only be converted if condition is a scalar of type i1");
668
669 rewriter.replaceOpWithNewOp<emitc::ConditionalOp>(selectOp, dstType,
670 adaptor.getOperands());
671
672 return success();
673 }
674};
675
676// Floating-point to integer conversions.
677template <typename CastOp>
678class FtoICastOpConversion : public OpConversionPattern<CastOp> {
679public:
680 FtoICastOpConversion(const TypeConverter &typeConverter, MLIRContext *context)
681 : OpConversionPattern<CastOp>(typeConverter, context) {}
682
683 LogicalResult
684 matchAndRewrite(CastOp castOp, typename CastOp::Adaptor adaptor,
685 ConversionPatternRewriter &rewriter) const override {
686
687 Type operandType = adaptor.getIn().getType();
688 if (!emitc::isSupportedFloatType(operandType))
689 return rewriter.notifyMatchFailure(castOp,
690 "unsupported cast source type");
691
692 Type dstType = this->getTypeConverter()->convertType(castOp.getType());
693 if (!dstType)
694 return rewriter.notifyMatchFailure(castOp, "type conversion failed");
695
696 // Float-to-i1 casts are not supported: any value with 0 < value < 1 must be
697 // truncated to 0, whereas a boolean conversion would return true.
698 if (!emitc::isSupportedIntegerType(dstType) || dstType.isInteger(1))
699 return rewriter.notifyMatchFailure(castOp,
700 "unsupported cast destination type");
701
702 // Convert to unsigned if it's the "ui" variant
703 // Signless is interpreted as signed, so no need to cast for "si"
704 Type actualResultType = dstType;
705 if (isa<arith::FPToUIOp>(castOp)) {
706 actualResultType =
707 rewriter.getIntegerType(dstType.getIntOrFloatBitWidth(),
708 /*isSigned=*/false);
709 }
710
711 Value result = emitc::CastOp::create(
712 rewriter, castOp.getLoc(), actualResultType, adaptor.getOperands());
713
714 if (isa<arith::FPToUIOp>(castOp)) {
715 result =
716 emitc::CastOp::create(rewriter, castOp.getLoc(), dstType, result);
717 }
718 rewriter.replaceOp(castOp, result);
719
720 return success();
721 }
722};
723
724// Integer to floating-point conversions.
725template <typename CastOp>
726class ItoFCastOpConversion : public OpConversionPattern<CastOp> {
727public:
728 ItoFCastOpConversion(const TypeConverter &typeConverter, MLIRContext *context)
729 : OpConversionPattern<CastOp>(typeConverter, context) {}
730
731 LogicalResult
732 matchAndRewrite(CastOp castOp, typename CastOp::Adaptor adaptor,
733 ConversionPatternRewriter &rewriter) const override {
734 // Vectors in particular are not supported
735 Type operandType = adaptor.getIn().getType();
736 if (!emitc::isSupportedIntegerType(operandType))
737 return rewriter.notifyMatchFailure(castOp,
738 "unsupported cast source type");
739
740 Type dstType = this->getTypeConverter()->convertType(castOp.getType());
741 if (!dstType)
742 return rewriter.notifyMatchFailure(castOp, "type conversion failed");
743
744 if (!emitc::isSupportedFloatType(dstType))
745 return rewriter.notifyMatchFailure(castOp,
746 "unsupported cast destination type");
747
748 // Convert to unsigned if it's the "ui" variant
749 // Signless is interpreted as signed, so no need to cast for "si"
750 Type actualOperandType = operandType;
751 if (isa<arith::UIToFPOp>(castOp)) {
752 actualOperandType =
753 rewriter.getIntegerType(operandType.getIntOrFloatBitWidth(),
754 /*isSigned=*/false);
755 }
756 Value fpCastOperand = adaptor.getIn();
757 if (actualOperandType != operandType) {
758 fpCastOperand = emitc::CastOp::create(rewriter, castOp.getLoc(),
759 actualOperandType, fpCastOperand);
760 }
761 rewriter.replaceOpWithNewOp<emitc::CastOp>(castOp, dstType, fpCastOperand);
762
763 return success();
764 }
765};
766
767// Floating-point to floating-point conversions.
768template <typename CastOp>
769class FpCastOpConversion : public OpConversionPattern<CastOp> {
770public:
771 FpCastOpConversion(const TypeConverter &typeConverter, MLIRContext *context)
772 : OpConversionPattern<CastOp>(typeConverter, context) {}
773
774 LogicalResult
775 matchAndRewrite(CastOp castOp, typename CastOp::Adaptor adaptor,
776 ConversionPatternRewriter &rewriter) const override {
777 // Vectors in particular are not supported.
778 Type operandType = adaptor.getIn().getType();
779 if (!emitc::isSupportedFloatType(operandType))
780 return rewriter.notifyMatchFailure(castOp,
781 "unsupported cast source type");
782 if (auto roundingModeOp =
783 dyn_cast<arith::ArithRoundingModeInterface>(*castOp)) {
784 // Only supporting default rounding mode as of now.
785 if (roundingModeOp.getRoundingModeAttr())
786 return rewriter.notifyMatchFailure(castOp, "unsupported rounding mode");
787 }
788
789 Type dstType = this->getTypeConverter()->convertType(castOp.getType());
790 if (!dstType)
791 return rewriter.notifyMatchFailure(castOp, "type conversion failed");
792
793 if (!emitc::isSupportedFloatType(dstType))
794 return rewriter.notifyMatchFailure(castOp,
795 "unsupported cast destination type");
796
797 Value fpCastOperand = adaptor.getIn();
798 rewriter.replaceOpWithNewOp<emitc::CastOp>(castOp, dstType, fpCastOperand);
799
800 return success();
801 }
802};
803
804} // namespace
805
806//===----------------------------------------------------------------------===//
807// Pattern population
808//===----------------------------------------------------------------------===//
809
811 RewritePatternSet &patterns) {
812 MLIRContext *ctx = patterns.getContext();
813
815
816 // clang-format off
817 patterns.add<
818 ArithConstantOpConversionPattern,
819 ArithOpConversion<arith::AddFOp, emitc::AddOp>,
820 ArithOpConversion<arith::DivFOp, emitc::DivOp>,
821 ArithOpConversion<arith::DivSIOp, emitc::DivOp>,
822 ArithOpConversion<arith::MulFOp, emitc::MulOp>,
823 ArithOpConversion<arith::RemSIOp, emitc::RemOp>,
824 ArithOpConversion<arith::SubFOp, emitc::SubOp>,
825 BinaryUIOpConversion<arith::DivUIOp, emitc::DivOp>,
826 BinaryUIOpConversion<arith::RemUIOp, emitc::RemOp>,
827 IntegerOpConversion<arith::AddIOp, emitc::AddOp>,
828 IntegerOpConversion<arith::MulIOp, emitc::MulOp>,
829 IntegerOpConversion<arith::SubIOp, emitc::SubOp>,
830 BitwiseOpConversion<arith::AndIOp, emitc::BitwiseAndOp>,
831 BitwiseOpConversion<arith::OrIOp, emitc::BitwiseOrOp>,
832 BitwiseOpConversion<arith::XOrIOp, emitc::BitwiseXorOp>,
833 UnsignedShiftOpConversion<arith::ShLIOp, emitc::BitwiseLeftShiftOp>,
834 SignedShiftOpConversion<arith::ShRSIOp, emitc::BitwiseRightShiftOp>,
835 UnsignedShiftOpConversion<arith::ShRUIOp, emitc::BitwiseRightShiftOp>,
836 CmpFOpConversion,
837 CmpIOpConversion,
838 NegFOpConversion,
839 SelectOpConversion,
840 // Truncation is guaranteed for unsigned types.
841 UnsignedCastConversion<arith::TruncIOp>,
842 SignedCastConversion<arith::ExtSIOp>,
843 UnsignedCastConversion<arith::ExtUIOp>,
844 SignedCastConversion<arith::IndexCastOp>,
845 UnsignedCastConversion<arith::IndexCastUIOp>,
846 ItoFCastOpConversion<arith::SIToFPOp>,
847 ItoFCastOpConversion<arith::UIToFPOp>,
848 FtoICastOpConversion<arith::FPToSIOp>,
849 FtoICastOpConversion<arith::FPToUIOp>,
850 FpCastOpConversion<arith::ExtFOp>,
851 FpCastOpConversion<arith::TruncFOp>
852 >(typeConverter, ctx);
853 // clang-format on
854}
return success()
lhs
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
bool addExtension(TypeID extensionID, std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
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.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
Definition Types.cpp:35
bool isSignedInteger() const
Return true if this is a signed integer type (with the specified width).
Definition Types.cpp:78
bool isSignlessInteger() const
Return true if this is a signless integer type (with the specified width).
Definition Types.cpp:66
bool isUnsignedInteger() const
Return true if this is an unsigned integer type (with the specified width).
Definition Types.cpp:90
bool isInteger() const
Return true if this is an integer type (with the specified width).
Definition Types.cpp:58
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition Types.cpp:124
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Location getLoc() const
Return the location of this value.
Definition Value.cpp:24
bool isSupportedFloatType(mlir::Type type)
Determines whether type is a valid floating-point type in EmitC.
Definition EmitC.cpp:117
bool isPointerWideType(mlir::Type type)
Determines whether type is a emitc.size_t/ssize_t type.
Definition EmitC.cpp:132
bool isSupportedIntegerType(mlir::Type type)
Determines whether type is a valid integer type in EmitC.
Definition EmitC.cpp:96
Include the generated interface declarations.
void registerConvertArithToEmitCInterface(DialectRegistry &registry)
void populateEmitCSizeTTypeConversions(TypeConverter &converter)
void populateArithToEmitCPatterns(TypeConverter &typeConverter, RewritePatternSet &patterns)