MLIR 24.0.0git
TosaNarrowTypes.cpp
Go to the documentation of this file.
1//===- TosaNarrowTypes.cpp ------------------------------------------------===//
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 the TOSA narrowing passes that rewrite tensor element
10// types to narrower equivalents (i64 -> i32, f64 -> f32, ...).
11//
12//===----------------------------------------------------------------------===//
13
15
16#include "llvm/ADT/APFloat.h"
17
18#include <algorithm>
19#include <limits>
20#include <type_traits>
21
28#include "mlir/IR/Verifier.h"
29#include "mlir/Pass/Pass.h"
30
31namespace mlir {
32namespace tosa {
33#define GEN_PASS_DEF_TOSANARROWI64TOI32PASS
34#define GEN_PASS_DEF_TOSANARROWF64TOF32PASS
35#include "mlir/Dialect/Tosa/Transforms/Passes.h.inc"
36} // namespace tosa
37} // namespace mlir
38
39using namespace mlir;
40using namespace mlir::tosa;
41
42namespace {
43
44// Narrowing mode for this pass.
45enum class TosaNarrowKind { Int64ToInt32, Float64ToFloat32 };
46
47// ---------------------------------------------------------------------------
48// Shared helpers
49// ---------------------------------------------------------------------------
50
51template <TosaNarrowKind Kind>
52bool isSourceInteger(IntegerType type) {
53 if constexpr (Kind == TosaNarrowKind::Int64ToInt32)
54 return type.isInteger(64);
55 return false;
56}
57
58template <TosaNarrowKind Kind>
59bool isSourceFloat(FloatType type) {
60 if constexpr (Kind == TosaNarrowKind::Float64ToFloat32)
61 return type.isF64();
62 return false;
63}
64
65template <TosaNarrowKind Kind>
66Type convertInteger(IntegerType type) {
67 if (!isSourceInteger<Kind>(type))
68 return type;
69 if constexpr (Kind == TosaNarrowKind::Int64ToInt32)
70 return IntegerType::get(type.getContext(), 32);
71 return type;
72}
73
74template <TosaNarrowKind Kind>
75Type convertFloat(FloatType type) {
76 if (!isSourceFloat<Kind>(type))
77 return type;
78 if constexpr (Kind == TosaNarrowKind::Float64ToFloat32)
79 return Float32Type::get(type.getContext());
80 return type;
81}
82
83template <TosaNarrowKind Kind>
84bool isSourceElement(Type type) {
85 if (auto intTy = dyn_cast<IntegerType>(type))
86 return isSourceInteger<Kind>(intTy);
87 if (auto floatTy = dyn_cast<FloatType>(type))
88 return isSourceFloat<Kind>(floatTy);
89 return false;
90}
91
92template <TosaNarrowKind Kind>
93Type convertElement(Type type) {
94 if (auto intTy = dyn_cast<IntegerType>(type))
95 return convertInteger<Kind>(intTy);
96 if (auto floatTy = dyn_cast<FloatType>(type))
97 return convertFloat<Kind>(floatTy);
98 return type;
99}
100
101template <TosaNarrowKind Kind>
102bool typeNeedsConversion(Type type) {
103 if (auto shaped = dyn_cast<ShapedType>(type))
104 return isSourceElement<Kind>(shaped.getElementType());
105 return isSourceElement<Kind>(type);
106}
107
108FailureOr<APInt> convertIntegerConstant(IntegerType targetType,
109 const APInt &value,
110 bool allowLossyConversion) {
111 const unsigned targetWidth = targetType.getWidth();
112 if (!allowLossyConversion && !value.isSignedIntN(targetWidth))
113 return failure();
114
115 if (allowLossyConversion)
116 return value.truncSSat(targetWidth);
117 return value.sextOrTrunc(targetWidth);
118}
119
120FailureOr<APFloat> convertFloatConstant(FloatType targetType,
121 const APFloat &value,
122 bool allowLossyConversion) {
123 APFloat converted(value);
124 bool losesInfo = false;
125 converted.convert(targetType.getFloatSemantics(),
126 APFloat::rmNearestTiesToEven, &losesInfo);
127 if (!allowLossyConversion && losesInfo)
128 return failure();
129 return converted;
130}
131
132// Narrows scalar constant attributes so they keep matching the converted
133// element types.
134template <TosaNarrowKind Kind>
135FailureOr<Attribute> tryConvertScalarAttribute(Attribute attribute,
136 bool allowLossyConversion) {
137 if constexpr (Kind == TosaNarrowKind::Int64ToInt32) {
138 if (const auto intAttr = dyn_cast<IntegerAttr>(attribute)) {
139 if (const auto intType = dyn_cast<IntegerType>(intAttr.getType());
140 intType && isSourceInteger<Kind>(intType)) {
141 const auto convertedType =
142 cast<IntegerType>(convertInteger<Kind>(intType));
143 FailureOr<APInt> convertedValue = convertIntegerConstant(
144 convertedType, intAttr.getValue(), allowLossyConversion);
145 if (failed(convertedValue))
146 return failure();
147 return IntegerAttr::get(convertedType, convertedValue.value());
148 }
149 }
150 } else if constexpr (Kind == TosaNarrowKind::Float64ToFloat32) {
151 if (const auto floatAttr = dyn_cast<FloatAttr>(attribute)) {
152 if (const auto floatType = dyn_cast<FloatType>(floatAttr.getType());
153 floatType && isSourceFloat<Kind>(floatType)) {
154 const auto convertedType =
155 cast<FloatType>(convertFloat<Kind>(floatType));
156 FailureOr<APFloat> convertedValue = convertFloatConstant(
157 convertedType, floatAttr.getValue(), allowLossyConversion);
158 if (failed(convertedValue))
159 return failure();
160 return FloatAttr::get(convertedType, convertedValue.value());
161 }
162 }
163 }
164
165 return attribute;
166}
167
168template <TosaNarrowKind Kind>
169FailureOr<Attribute>
170convertDenseIntElementsAttr(ShapedType type, DenseIntElementsAttr attr,
171 const TypeConverter &typeConverter,
172 bool allowLossyConversion) {
173 if constexpr (Kind != TosaNarrowKind::Int64ToInt32)
174 return attr;
175
176 const auto oldElementType = dyn_cast<IntegerType>(type.getElementType());
177 if (!oldElementType || !isSourceInteger<Kind>(oldElementType))
178 return attr;
179
180 const auto newType =
181 dyn_cast_or_null<ShapedType>(typeConverter.convertType(type));
182 if (!newType)
183 return failure();
184
185 const auto newElementType = dyn_cast<IntegerType>(newType.getElementType());
186 if (!newElementType)
187 return failure();
188
189 if (!allowLossyConversion) {
190 for (APInt value : attr.getValues<APInt>())
191 if (failed(convertIntegerConstant(newElementType, value,
192 /*allowLossyConversion=*/false)))
193 return failure();
194 }
195
196 Attribute convertedAttr =
197 attr.mapValues(newElementType, [&](const APInt &value) -> APInt {
198 return convertIntegerConstant(newElementType, value,
199 /*allowLossyConversion=*/true)
200 .value();
201 });
202 return convertedAttr;
203}
204
205template <TosaNarrowKind Kind>
206FailureOr<Attribute>
207convertDenseFPElementsAttr(ShapedType type, DenseFPElementsAttr attr,
208 const TypeConverter &typeConverter,
209 bool allowLossyConversion) {
210 if constexpr (Kind != TosaNarrowKind::Float64ToFloat32)
211 return attr;
212
213 const auto oldElementType = dyn_cast<FloatType>(type.getElementType());
214 if (!oldElementType || !isSourceFloat<Kind>(oldElementType))
215 return attr;
216
217 const auto newType =
218 dyn_cast_or_null<ShapedType>(typeConverter.convertType(type));
219 if (!newType)
220 return failure();
221
222 const auto newElementType = dyn_cast<FloatType>(newType.getElementType());
223 if (!newElementType)
224 return failure();
225
226 if (!allowLossyConversion) {
227 for (APFloat value : attr.getValues<APFloat>())
228 if (failed(convertFloatConstant(newElementType, value,
229 /*allowLossyConversion=*/false)))
230 return failure();
231 }
232
233 Attribute convertedAttr =
234 attr.mapValues(newElementType, [&](const APFloat &value) -> APInt {
235 APFloat converted = convertFloatConstant(newElementType, value,
236 /*allowLossyConversion=*/true)
237 .value();
238 // DenseFPElementsAttr stores each float as raw bits, so emit the APInt
239 // representation that MLIR expects in the underlying buffer.
240 return converted.bitcastToAPInt();
241 });
242 return convertedAttr;
243}
244
245template <TosaNarrowKind Kind>
246FailureOr<Attribute> convertDenseResourceElementsAttr(
247 ShapedType type, DenseResourceElementsAttr attr,
248 const TypeConverter &typeConverter, bool allowLossyConversion) {
249 static_assert(Kind == TosaNarrowKind::Int64ToInt32 ||
250 Kind == TosaNarrowKind::Float64ToFloat32);
251 using From =
252 std::conditional_t<Kind == TosaNarrowKind::Int64ToInt32, int64_t, double>;
253 using To =
254 std::conditional_t<Kind == TosaNarrowKind::Int64ToInt32, int32_t, float>;
255
256 if (Kind == TosaNarrowKind::Int64ToInt32 &&
257 !isa<DenseI64ResourceElementsAttr>(attr)) {
258 return attr;
259 }
260
261 if (Kind == TosaNarrowKind::Float64ToFloat32 &&
262 !isa<DenseF64ResourceElementsAttr>(attr)) {
263 return attr;
264 }
265
266 auto narrow = [](From value) {
267 if constexpr (Kind == TosaNarrowKind::Int64ToInt32) {
268 value = std::clamp<From>(value, std::numeric_limits<To>::min(),
269 std::numeric_limits<To>::max());
270 }
271
272 return static_cast<To>(value);
273 };
274
275 const auto newType =
276 dyn_cast_or_null<ShapedType>(typeConverter.convertType(type));
277 if (!newType) {
278 return failure();
279 }
280
281 const std::optional<ArrayRef<From>> values =
283 if (!values) {
284 return failure();
285 }
286
287 SmallVector<To> newValues;
288 newValues.reserve(values->size());
289 for (From value : *values) {
290 const To convertedValue = narrow(value);
291 if (!allowLossyConversion && convertedValue != value) {
292 return failure();
293 }
294
295 newValues.push_back(convertedValue);
296 }
297
299 ArrayRef<To>(newValues.data(), newValues.size()));
300
301 auto resourceManager =
303 resourceManager.getBlobManager().update(attr.getRawHandle().getKey(),
304 std::move(blob));
305
306 return DenseResourceElementsAttr::get(newType, attr.getRawHandle());
307}
308
309template <TosaNarrowKind Kind, typename AttrT>
310FailureOr<Attribute>
311convertAttributeWithTypeConverter(AttrT attr, Type type,
312 const TypeConverter *typeConverter) {
313 if (!typeNeedsConversion<Kind>(type))
314 return attr;
315
316 const std::optional<Attribute> convertedAttribute =
317 typeConverter->convertTypeAttribute(type, attr);
318 if (!convertedAttribute)
319 return failure();
320
321 return convertedAttribute.value();
322}
323
324// Rejects cast rewrites that would lose precision (unless aggressive mode is
325// enabled).
326template <TosaNarrowKind Kind>
327LogicalResult
328verifyCastDoesNotLosePrecision(Operation *op, ShapedType inputType,
329 ShapedType resultType,
330 ConversionPatternRewriter &rewriter) {
331 if constexpr (Kind == TosaNarrowKind::Int64ToInt32) {
332 const auto elementInputIntType =
333 dyn_cast<IntegerType>(inputType.getElementType());
334 const auto elementResultIntType =
335 dyn_cast<IntegerType>(resultType.getElementType());
336 if (elementInputIntType && elementResultIntType &&
337 elementInputIntType.getWidth() > elementResultIntType.getWidth())
338 return rewriter.notifyMatchFailure(
339 op, "Narrowing cast may lead to data loss.");
340 } else if constexpr (Kind == TosaNarrowKind::Float64ToFloat32) {
341 const auto elementInputFloatType =
342 dyn_cast<FloatType>(inputType.getElementType());
343 const auto elementResultFloatType =
344 dyn_cast<FloatType>(resultType.getElementType());
345 if (elementInputFloatType && elementResultFloatType &&
346 elementInputFloatType.getIntOrFloatBitWidth() >
347 elementResultFloatType.getIntOrFloatBitWidth())
348 return rewriter.notifyMatchFailure(
349 op, "Narrowing cast may lead to data loss.");
350 }
351
352 return success();
353}
354
355// ---------------------------------------------------------------------------
356// Conversion patterns
357// ---------------------------------------------------------------------------
358
359// Applies the narrowing TypeConverter to a single TOSA op, including its
360// attributes and nested regions.
361template <TosaNarrowKind Kind>
362LogicalResult convertGenericOp(Operation *op, ValueRange operands,
363 ConversionPatternRewriter &rewriter,
364 const TypeConverter *typeConverter,
365 bool allowLossyConversion) {
366 SmallVector<Type, 4> newResults;
367 if (failed(typeConverter->convertTypes(op->getResultTypes(), newResults)))
368 return failure();
369
370 OperationState state(op->getLoc(), op->getName().getStringRef(), operands,
371 newResults, {}, op->getSuccessors());
372
373 // Keep attribute payloads consistent with the converted element types.
374 for (const NamedAttribute &namedAttribute : op->getAttrs()) {
375 const Attribute attribute = namedAttribute.getValue();
376
377 if (isa<IntegerAttr>(attribute) || isa<FloatAttr>(attribute)) {
378 FailureOr<Attribute> convertedAttr =
379 tryConvertScalarAttribute<Kind>(attribute, allowLossyConversion);
380 if (failed(convertedAttr))
381 return rewriter.notifyMatchFailure(
382 op, "Scalar attribute narrowing would lose precision; enable "
383 "aggressive rewrite to override.");
384 state.addAttribute(namedAttribute.getName(), convertedAttr.value());
385 continue;
386 }
387
388 if (const auto typeAttr = dyn_cast<TypeAttr>(attribute)) {
389 FailureOr<Attribute> convertedAttr =
390 convertAttributeWithTypeConverter<Kind>(typeAttr, typeAttr.getValue(),
391 typeConverter);
392 if (failed(convertedAttr))
393 return rewriter.notifyMatchFailure(op,
394 "Failed to convert type attribute.");
395 state.addAttribute(namedAttribute.getName(), convertedAttr.value());
396 continue;
397 }
398
399 if (const auto denseElementsAttr = dyn_cast<DenseElementsAttr>(attribute)) {
400 FailureOr<Attribute> convertedAttr =
401 convertAttributeWithTypeConverter<Kind>(
402 denseElementsAttr, denseElementsAttr.getType(), typeConverter);
403 if (failed(convertedAttr))
404 return rewriter.notifyMatchFailure(
405 op, "Failed to convert dense elements attribute without precision "
406 "loss; enable aggressive rewrite to override.");
407 state.addAttribute(namedAttribute.getName(), convertedAttr.value());
408 continue;
409 }
410
411 if (const auto denseResourceElementsAttr =
412 dyn_cast<DenseResourceElementsAttr>(attribute)) {
413 FailureOr<Attribute> convertedAttr =
414 convertAttributeWithTypeConverter<Kind>(
415 denseResourceElementsAttr, denseResourceElementsAttr.getType(),
416 typeConverter);
417 if (failed(convertedAttr))
418 return rewriter.notifyMatchFailure(
419 op, "Failed to convert dense resource elements attribute without "
420 "precision loss; enable aggressive rewrite to override.");
421 state.addAttribute(namedAttribute.getName(), convertedAttr.value());
422 continue;
423 }
424
425 state.addAttribute(namedAttribute.getName(), attribute);
426 }
427
428 for (Region &region : op->getRegions()) {
429 if (failed(rewriter.convertRegionTypes(&region, *typeConverter)))
430 return failure();
431 Region *newRegion = state.addRegion();
432 rewriter.inlineRegionBefore(region, *newRegion, newRegion->begin());
433 }
434
435 Operation *newOp = rewriter.create(state);
436 rewriter.replaceOp(op, newOp->getResults());
437 return success();
438}
439
440template <TosaNarrowKind Kind>
441class ConvertGenericOp : public ConversionPattern {
442public:
443 ConvertGenericOp(TypeConverter &typeConverter, MLIRContext *context,
444 bool allowLossyConversion)
445 : ConversionPattern(typeConverter, MatchAnyOpTypeTag{}, 0, context),
446 allowLossyConversion(allowLossyConversion) {}
447
448 LogicalResult
449 matchAndRewrite(Operation *op, ArrayRef<Value> operands,
450 ConversionPatternRewriter &rewriter) const final {
451 if (!isa<tosa::TosaOp>(op))
452 return rewriter.notifyMatchFailure(
453 op,
454 "Support for operations other than TOSA has not been implemented.");
455
456 return convertGenericOp<Kind>(op, operands, rewriter, typeConverter,
457 allowLossyConversion);
458 }
459
460private:
461 const bool allowLossyConversion;
462};
463
464template <typename OpTy, TosaNarrowKind Kind>
465class ConvertTypedOp : public OpConversionPattern<OpTy> {
466public:
467 ConvertTypedOp(TypeConverter &typeConverter, MLIRContext *context)
468 : OpConversionPattern<OpTy>(typeConverter, context) {}
469
470 LogicalResult
471 matchAndRewrite(OpTy op, typename OpTy::Adaptor adaptor,
472 ConversionPatternRewriter &rewriter) const final {
473 return convertGenericOp<Kind>(op, adaptor.getOperands(), rewriter,
474 this->getTypeConverter(),
475 /*allowLossyConversion=*/false);
476 }
477};
478
479// ---------------------------------------------------------------------------
480// Kind-specific helpers and patterns
481// ---------------------------------------------------------------------------
482
483// Casts get extra checking so we only narrow when it is probably safe.
484template <TosaNarrowKind Kind>
485class ConvertCastOpWithBoundsChecking
486 : public OpConversionPattern<tosa::CastOp> {
487 using OpConversionPattern<tosa::CastOp>::OpConversionPattern;
488
489 LogicalResult
490 matchAndRewrite(tosa::CastOp op, typename tosa::CastOp::Adaptor adaptor,
491 ConversionPatternRewriter &rewriter) const final {
492 const auto inputType = dyn_cast<ShapedType>(adaptor.getInput().getType());
493 const auto resultType = dyn_cast<ShapedType>(op.getResult().getType());
494 if (!inputType || !resultType)
495 return failure();
496
497 const TypeConverter *typeConverter = this->getTypeConverter();
498 if (failed(verifyCastDoesNotLosePrecision<Kind>(op, inputType, resultType,
499 rewriter)))
500 return failure();
501
502 rewriter.replaceOpWithNewOp<tosa::CastOp>(
503 op, typeConverter->convertType(resultType), adaptor.getInput(),
504 op->getAttrs());
505 return success();
506 }
507};
508
509// ArgMax indices must fit the axis dimension, so we guard the integer rewrite.
510class ConvertArgMaxOpWithBoundsChecking
511 : public OpConversionPattern<tosa::ArgMaxOp> {
512 using OpConversionPattern::OpConversionPattern;
513
514 LogicalResult
515 matchAndRewrite(tosa::ArgMaxOp op, typename tosa::ArgMaxOp::Adaptor adaptor,
516 ConversionPatternRewriter &rewriter) const final {
517 const int32_t axis = op.getAxis();
518 const auto inputType = dyn_cast<ShapedType>(adaptor.getInput().getType());
519 if (!inputType || !inputType.isStaticDim(axis))
520 return rewriter.notifyMatchFailure(
521 op, "Requires a static axis dimension for bounds checking.");
522 const int64_t axisDim = inputType.getDimSize(axis);
523 if (axisDim >= std::numeric_limits<int32_t>::max())
524 return rewriter.notifyMatchFailure(
525 op, "Axis dimension is too large to narrow safely.");
526
527 const Type resultType = op.getOutput().getType();
528 const Type newResultType =
529 this->getTypeConverter()->convertType(resultType);
530 rewriter.replaceOpWithNewOp<tosa::ArgMaxOp>(op, newResultType,
531 adaptor.getInput(), axis);
532 return success();
533 }
534};
535
536template <TosaNarrowKind Kind>
537class ConvertClampOpWithBoundsChecking
538 : public OpConversionPattern<tosa::ClampOp> {
539 static_assert(Kind == TosaNarrowKind::Int64ToInt32,
540 "Clamp bounds checking only supported for integer narrowing");
541 using OpConversionPattern<tosa::ClampOp>::OpConversionPattern;
542
543 LogicalResult
544 matchAndRewrite(tosa::ClampOp op, typename tosa::ClampOp::Adaptor adaptor,
545 ConversionPatternRewriter &rewriter) const final {
546 auto minAttr = dyn_cast<IntegerAttr>(op.getMinValAttr());
547 auto maxAttr = dyn_cast<IntegerAttr>(op.getMaxValAttr());
548 if (!minAttr || !maxAttr)
549 return rewriter.notifyMatchFailure(
550 op, "Clamp attributes must be integer constants.");
551
552 const int64_t min = minAttr.getInt();
553 const int64_t max = maxAttr.getInt();
554 if (min < std::numeric_limits<int32_t>::min() ||
555 max > std::numeric_limits<int32_t>::max())
556 return rewriter.notifyMatchFailure(
557 op, "Clamp bounds exceed int32 range. Narrowing may lose data.");
558
559 const Type resultType = op.getOutput().getType();
560 const Type newResultType =
561 this->getTypeConverter()->convertType(resultType);
562 const auto newResultShaped = dyn_cast<ShapedType>(newResultType);
563 if (!newResultShaped)
564 return failure();
565 const auto newElementType =
566 dyn_cast<IntegerType>(newResultShaped.getElementType());
567 if (!newElementType)
568 return failure();
569
570 const IntegerAttr newMinAttr = IntegerAttr::get(newElementType, min);
571 const IntegerAttr newMaxAttr = IntegerAttr::get(newElementType, max);
572
573 rewriter.replaceOpWithNewOp<tosa::ClampOp>(op, newResultType,
574 adaptor.getInput(), newMinAttr,
575 newMaxAttr, op.getNanModeAttr());
576 return success();
577 }
578};
579
580// Shared implementation for both narrowing passes; the mode decides which
581// element types and attribute payloads participate.
582template <TosaNarrowKind Kind>
583LogicalResult runTosaNarrowing(Operation *op, bool aggressiveRewrite,
584 bool convertFunctionBoundaries) {
585 MLIRContext *context = op->getContext();
586 const bool allowLossyConversion = aggressiveRewrite;
587
588 TypeConverter typeConverter;
589 typeConverter.addConversion([](Type type) -> Type { return type; });
590
591 typeConverter.addConversion(
592 [](IntegerType type) -> Type { return convertInteger<Kind>(type); });
593 typeConverter.addConversion(
594 [](FloatType type) -> Type { return convertFloat<Kind>(type); });
595 typeConverter.addConversion([&typeConverter](RankedTensorType type) -> Type {
596 Type elementType = type.getElementType();
597 if (!isSourceElement<Kind>(elementType))
598 return type;
599 Type converted = typeConverter.convertType(elementType);
600 if (!converted || converted == elementType)
601 return type;
602 return RankedTensorType::get(type.getShape(), converted,
603 type.getEncoding());
604 });
605 typeConverter.addConversion(
606 [&typeConverter](UnrankedTensorType type) -> Type {
607 Type elementType = type.getElementType();
608 if (!isSourceElement<Kind>(elementType))
609 return type;
610 Type converted = typeConverter.convertType(elementType);
611 if (!converted || converted == elementType)
612 return type;
613 return UnrankedTensorType::get(converted);
614 });
615
616 const auto materializeCast = [](OpBuilder &builder, Type resultType,
617 ValueRange inputs, Location loc) -> Value {
618 if (inputs.size() != 1)
619 return Value();
620 return tosa::CastOp::create(
621 builder, loc, resultType, inputs.front(),
622 getStorageElementTypeOrSelf(inputs.front().getType())
623 .isUnsignedInteger());
624 };
625 typeConverter.addSourceMaterialization(materializeCast);
626 typeConverter.addTargetMaterialization(materializeCast);
627
628 typeConverter.addTypeAttributeConversion(
629 [&typeConverter, allowLossyConversion](ShapedType type,
630 DenseResourceElementsAttr attr)
631 -> TypeConverter::AttributeConversionResult {
632 FailureOr<Attribute> converted = convertDenseResourceElementsAttr<Kind>(
633 type, attr, typeConverter, allowLossyConversion);
634 if (failed(converted))
635 return TypeConverter::AttributeConversionResult::abort();
636 return TypeConverter::AttributeConversionResult::result(
637 converted.value());
638 });
639
640 if constexpr (Kind == TosaNarrowKind::Int64ToInt32) {
641 typeConverter.addTypeAttributeConversion(
642 [allowLossyConversion](IntegerType /*type*/, IntegerAttr attribute)
643 -> TypeConverter::AttributeConversionResult {
644 FailureOr<Attribute> converted =
645 tryConvertScalarAttribute<Kind>(attribute, allowLossyConversion);
646 if (failed(converted))
647 return TypeConverter::AttributeConversionResult::abort();
648 return TypeConverter::AttributeConversionResult::result(
649 converted.value());
650 });
651 typeConverter.addTypeAttributeConversion(
652 [&typeConverter, allowLossyConversion](ShapedType type,
654 -> TypeConverter::AttributeConversionResult {
655 FailureOr<Attribute> converted = convertDenseIntElementsAttr<Kind>(
656 type, attr, typeConverter, allowLossyConversion);
657 if (failed(converted))
658 return TypeConverter::AttributeConversionResult::abort();
659 return TypeConverter::AttributeConversionResult::result(
660 converted.value());
661 });
662 } else if constexpr (Kind == TosaNarrowKind::Float64ToFloat32) {
663 typeConverter.addTypeAttributeConversion(
664 [allowLossyConversion](FloatType /*type*/, FloatAttr attribute)
665 -> TypeConverter::AttributeConversionResult {
666 FailureOr<Attribute> converted =
667 tryConvertScalarAttribute<Kind>(attribute, allowLossyConversion);
668 if (failed(converted))
669 return TypeConverter::AttributeConversionResult::abort();
670 return TypeConverter::AttributeConversionResult::result(
671 converted.value());
672 });
673 typeConverter.addTypeAttributeConversion(
674 [&typeConverter, allowLossyConversion](ShapedType type,
676 -> TypeConverter::AttributeConversionResult {
677 FailureOr<Attribute> converted = convertDenseFPElementsAttr<Kind>(
678 type, attr, typeConverter, allowLossyConversion);
679 if (failed(converted))
680 return TypeConverter::AttributeConversionResult::abort();
681 return TypeConverter::AttributeConversionResult::result(
682 converted.value());
683 });
684 }
685
686 ConversionTarget target(*context);
687 target.addDynamicallyLegalDialect<tosa::TosaDialect>(
688 [&typeConverter](Operation *op) {
689 return typeConverter.isLegal(op->getResultTypes()) &&
690 typeConverter.isLegal(op->getOperandTypes());
691 });
692 if (convertFunctionBoundaries) {
693 target.addDynamicallyLegalOp<func::FuncOp>(
694 [&typeConverter](func::FuncOp op) {
695 return typeConverter.isSignatureLegal(op.getFunctionType()) &&
696 typeConverter.isLegal(&op.getBody());
697 });
698 target.addDynamicallyLegalOp<func::ReturnOp>([](func::ReturnOp op) {
699 const FunctionType funcType =
700 op->getParentOfType<func::FuncOp>().getFunctionType();
701 return llvm::equal(op.getOperandTypes(), funcType.getResults());
702 });
703 } else {
704 target.addDynamicallyLegalOp<func::FuncOp>(
705 [](func::FuncOp) { return true; });
706 target.addDynamicallyLegalOp<func::ReturnOp>(
707 [](func::ReturnOp) { return true; });
708 }
709
710 RewritePatternSet patterns(context);
711 if (convertFunctionBoundaries) {
712 populateFunctionOpInterfaceTypeConversionPattern<func::FuncOp>(
713 patterns, typeConverter);
714 populateReturnOpTypeConversionPattern(patterns, typeConverter);
715 }
716 if (aggressiveRewrite) {
717 patterns.add<ConvertGenericOp<Kind>>(typeConverter, context,
718 allowLossyConversion);
719 } else {
720 if constexpr (Kind == TosaNarrowKind::Int64ToInt32) {
721 patterns.add<ConvertArgMaxOpWithBoundsChecking>(typeConverter, context);
722 patterns.add<ConvertClampOpWithBoundsChecking<Kind>>(typeConverter,
723 context);
725 patterns.add<ConvertTypedOp<tosa::ConstOp, Kind>>(typeConverter, context);
726 patterns.add<ConvertTypedOp<tosa::ConcatOp, Kind>>(typeConverter, context);
727 patterns.add<ConvertTypedOp<tosa::PadOp, Kind>>(typeConverter, context);
728 patterns.add<ConvertTypedOp<tosa::ReshapeOp, Kind>>(typeConverter, context);
729 patterns.add<ConvertTypedOp<tosa::ReverseOp, Kind>>(typeConverter, context);
730 patterns.add<ConvertTypedOp<tosa::SliceOp, Kind>>(typeConverter, context);
731 patterns.add<ConvertTypedOp<tosa::TileOp, Kind>>(typeConverter, context);
732 patterns.add<ConvertTypedOp<tosa::TransposeOp, Kind>>(typeConverter,
733 context);
734 patterns.add<ConvertTypedOp<tosa::IdentityOp, Kind>>(typeConverter,
735 context);
736 patterns.add<ConvertCastOpWithBoundsChecking<Kind>>(typeConverter, context);
737 patterns.add<ConvertTypedOp<tosa::IfOp, Kind>>(typeConverter, context);
738 patterns.add<ConvertTypedOp<tosa::WhileOp, Kind>>(typeConverter, context);
739 patterns.add<ConvertTypedOp<tosa::YieldOp, Kind>>(typeConverter, context);
740 }
742 if (failed(applyFullConversion(op, target, std::move(patterns))))
743 return failure();
744 return success();
745}
746
747// ---------------------------------------------------------------------------
748// Pass adapters that forward to the shared implementation
749// ---------------------------------------------------------------------------
751struct TosaNarrowI64ToI32
752 : public tosa::impl::TosaNarrowI64ToI32PassBase<TosaNarrowI64ToI32> {
754
755 TosaNarrowI64ToI32() = default;
756
757 explicit TosaNarrowI64ToI32(const TosaNarrowI64ToI32PassOptions &options) {
758 this->aggressiveRewrite = options.aggressiveRewrite;
759 this->convertFunctionBoundaries = options.convertFunctionBoundaries;
761
762 void runOnOperation() override {
763 if (failed(runTosaNarrowing<TosaNarrowKind::Int64ToInt32>(
764 getOperation(), this->aggressiveRewrite,
765 this->convertFunctionBoundaries)))
766 signalPassFailure();
767 }
768};
770struct TosaNarrowF64ToF32
771 : public tosa::impl::TosaNarrowF64ToF32PassBase<TosaNarrowF64ToF32> {
773
774 TosaNarrowF64ToF32() = default;
776 explicit TosaNarrowF64ToF32(const TosaNarrowF64ToF32PassOptions &options) {
777 this->aggressiveRewrite = options.aggressiveRewrite;
778 this->convertFunctionBoundaries = options.convertFunctionBoundaries;
779 }
780
781 void runOnOperation() override {
782 if (failed(runTosaNarrowing<TosaNarrowKind::Float64ToFloat32>(
783 getOperation(), this->aggressiveRewrite,
784 this->convertFunctionBoundaries)))
786 }
787};
789} // namespace
return success()
static llvm::Constant * convertDenseResourceElementsAttr(Location loc, DenseResourceElementsAttr denseResourceAttr, llvm::Type *llvmType, const ModuleTranslation &moduleTranslation)
Convert a dense resource elements attribute to an LLVM IR constant using its raw data storage if poss...
static llvm::ManagedStatic< PassManagerOptions > options
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
This class represents a processed binary blob of data.
Definition AsmState.h:91
Attributes are known-constant values of operations.
Definition Attributes.h:25
An attribute that represents a reference to a dense float vector or tensor object.
DenseElementsAttr mapValues(Type newElementType, function_ref< APInt(const APFloat &)> mapping) const
Generates a new DenseElementsAttr by mapping each value attribute, and constructing the DenseElements...
An attribute that represents a reference to a dense integer vector or tensor object.
DenseElementsAttr mapValues(Type newElementType, function_ref< APInt(const APInt &)> mapping) const
Generates a new DenseElementsAttr by mapping each value attribute, and constructing the DenseElements...
static AsmResourceBlob allocateAndCopyInferAlign(ArrayRef< T > data, bool dataIsMutable=true)
Definition AsmState.h:212
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
NamedAttribute represents a combination of a name and an Attribute value.
Definition Attributes.h:164
This class helps build Operations.
Definition Builders.h:210
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
Definition Operation.h:557
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
OperationName getName()
The name of an operation is the key identifier for it.
Definition Operation.h:115
operand_type_range getOperandTypes()
Definition Operation.h:422
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:722
result_type_range getResultTypes()
Definition Operation.h:453
SuccessorRange getSuccessors()
Definition Operation.h:748
result_range getResults()
Definition Operation.h:440
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
void signalPassFailure()
Signal that some invariant was broken when running.
Definition Pass.h:226
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
iterator begin()
Definition Region.h:55
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
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
Kind
An enumeration of the kinds of predicates.
Definition Predicate.h:44
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
Type getStorageElementTypeOrSelf(Type type)
Definition TosaOps.cpp:583
std::optional< ArrayRef< T > > tryGetDenseResourceValues(ElementsAttr attr)
Include the generated interface declarations.
void populateReturnOpTypeConversionPattern(RewritePatternSet &patterns, const TypeConverter &converter, PatternBenefit benefit=1)
Add a pattern to the given pattern list to rewrite return ops to use operands that have been legalize...
static ManagerInterface & getManagerInterface(MLIRContext *ctx)
This represents an operation in an abstracted form, suitable for use with the builder APIs.