203 return arith::ExtFOp::create(
205 arith::ExtFOp::Properties{});
207 return arith::TruncFOp::create(rewriter, loc, rewriter.
getF32Type(), value);
208 llvm_unreachable(
"The only 32-bit float type is f32");
219 const llvm::fltSemantics &sourceSem =
221 const llvm::fltSemantics &targetSem =
222 cast<FloatType>(outElemType).getFloatSemantics();
224 APFloat
min = APFloat::getLargest(targetSem,
true);
225 APFloat
max = APFloat::getLargest(targetSem,
false);
226 bool ignoredLosesInfo =
false;
230 (
void)
min.convert(sourceSem, APFloat::rmNearestTiesToEven, &ignoredLosesInfo);
231 (
void)
max.convert(sourceSem, APFloat::rmNearestTiesToEven, &ignoredLosesInfo);
237 rewriter, loc, sourceType,
238 APFloat::getInf(sourceSem,
false));
240 rewriter, loc, sourceType, APFloat::getInf(sourceSem,
true));
242 loc, arith::CmpFPredicate::OEQ, source, inf);
244 loc, arith::CmpFPredicate::OEQ, source, negInf);
246 loc, arith::CmpFPredicate::UNO, source, source);
247 Value isNonFinite = arith::OrIOp::create(
248 rewriter, loc, arith::OrIOp::create(rewriter, loc, isInf, isNegInf),
251 Value clampedBelow = arith::MaximumFOp::create(rewriter, loc, source, minCst);
253 arith::MinimumFOp::create(rewriter, loc, clampedBelow, maxCst);
255 arith::SelectOp::create(rewriter, loc, isNonFinite, source, clamped);
260TruncFToFloat8RewritePattern::matchAndRewrite(arith::TruncFOp op,
261 PatternRewriter &rewriter)
const {
263 if (op.getRoundingmodeAttr())
265 Type outType = op.getOut().getType();
266 auto outVecType = dyn_cast<VectorType>(outType);
268 if (outVecType.isScalable())
270 outType = outVecType.getElementType();
273 if (inType && inType.getWidth() <= 8 && saturateFP8)
280 Location loc = op.getLoc();
281 Value in = op.getIn();
284 in =
clampInput(rewriter, loc, outElemType, in);
285 auto inVectorTy = dyn_cast<VectorType>(in.
getType());
286 VectorType truncResType = VectorType::get(4, outElemType);
288 Value asFloat =
castToF32(in, loc, rewriter);
289 Value asF8s = amdgpu::PackedTrunc2xFp8Op::create(
290 rewriter, loc, truncResType, asFloat,
nullptr, 0,
292 Value
result = vector::ExtractOp::create(rewriter, loc, asF8s, 0);
297 int64_t numElements = outVecType.getNumElements();
298 Value zero = arith::ConstantOp::create(
299 rewriter, loc, outElemType, rewriter.
getFloatAttr(outElemType, 0.0));
300 if (outVecType.getShape().empty()) {
302 vector::ExtractOp::create(rewriter, loc, in, ArrayRef<int64_t>{});
305 arith::TruncFOp::create(rewriter, loc, outElemType, scalarIn);
306 Value
result = vector::InsertOp::create(rewriter, loc, scalarTrunc, zero,
307 ArrayRef<int64_t>{});
312 VectorType flatTy = VectorType::get(SmallVector<int64_t>{numElements},
313 outVecType.getElementType());
316 if (inVectorTy.getRank() > 1) {
317 inVectorTy = VectorType::get(SmallVector<int64_t>{numElements},
318 inVectorTy.getElementType());
319 in = vector::ShapeCastOp::create(rewriter, loc, inVectorTy, in);
322 for (int64_t i = 0; i < numElements; i += 4) {
323 int64_t elemsThisOp = std::min(numElements, i + 4) - i;
324 Value thisResult =
nullptr;
325 for (int64_t j = 0; j < elemsThisOp; j += 2) {
326 Value elemA = vector::ExtractOp::create(rewriter, loc, in, i + j);
327 Value asFloatA =
castToF32(elemA, loc, rewriter);
328 Value asFloatB =
nullptr;
329 if (j + 1 < elemsThisOp) {
330 Value elemB = vector::ExtractOp::create(rewriter, loc, in, i + j + 1);
331 asFloatB =
castToF32(elemB, loc, rewriter);
333 thisResult = amdgpu::PackedTrunc2xFp8Op::create(
334 rewriter, loc, truncResType, asFloatA, asFloatB, j / 2, thisResult);
337 thisResult = vector::ExtractStridedSliceOp::create(
338 rewriter, loc, thisResult, 0, elemsThisOp, 1);
339 result = vector::InsertStridedSliceOp::create(rewriter, loc, thisResult,
343 if (inVectorTy.getRank() != outVecType.getRank()) {
344 result = vector::ShapeCastOp::create(rewriter, loc, outVecType,
result);
351LogicalResult TruncfToFloat16RewritePattern::matchAndRewrite(
352 arith::TruncFOp op, PatternRewriter &rewriter)
const {
353 Type outType = op.getOut().getType();
355 auto outVecType = dyn_cast<VectorType>(outType);
357 if (outVecType.isScalable())
359 outType = outVecType.getElementType();
364 Location loc = op.getLoc();
365 Value in = op.getIn();
367 VectorType truncResType = VectorType::get(2, outElemType);
368 auto inVectorTy = dyn_cast<VectorType>(in.
getType());
372 auto sourceB = LLVM::PoisonOp::create(rewriter, loc, rewriter.
getF32Type());
374 ROCDL::CvtPkRtz::create(rewriter, loc, truncResType, in, sourceB);
375 Value
result = vector::ExtractOp::create(rewriter, loc, asF16s, 0);
379 int64_t numElements = outVecType.getNumElements();
381 loc, outElemType, rewriter.
getFloatAttr(outElemType, 0.0));
383 rewriter.
createOrFold<vector::BroadcastOp>(loc, outVecType, zero);
385 if (inVectorTy.getRank() > 1) {
386 inVectorTy = VectorType::get(SmallVector<int64_t>{numElements},
387 inVectorTy.getElementType());
388 in = vector::ShapeCastOp::create(rewriter, loc, inVectorTy, in);
393 for (int64_t i = 0; i < numElements; i += 2) {
394 int64_t elemsThisOp = std::min(numElements, i + 2) - i;
395 Value thisResult =
nullptr;
396 Value elemA = vector::ExtractOp::create(rewriter, loc, in, i);
397 Value elemB = LLVM::PoisonOp::create(rewriter, loc, rewriter.
getF32Type());
399 if (elemsThisOp == 2) {
400 elemB = vector::ExtractOp::create(rewriter, loc, in, i + 1);
404 ROCDL::CvtPkRtz::create(rewriter, loc, truncResType, elemA, elemB);
407 thisResult = vector::ExtractStridedSliceOp::create(
408 rewriter, loc, thisResult, 0, elemsThisOp, 1);
409 result = vector::InsertStridedSliceOp::create(rewriter, loc, thisResult,
413 if (inVectorTy.getRank() != outVecType.getRank()) {
414 result = vector::ShapeCastOp::create(rewriter, loc, outVecType,
result);
423 Value current = value;
426 .Case([¤t](vector::ShapeCastOp op) {
427 current = op.getSource();
430 .Case([¤t](vector::BroadcastOp op) {
431 current = op.getSource();
444ScalingExtFRewritePattern::matchAndRewrite(arith::ScalingExtFOp op,
445 PatternRewriter &rewriter)
const {
446 Location loc = op.getLoc();
447 constexpr int64_t opOutWidth = 2;
449 Value in = op.getIn();
450 Value scale = op.getScale();
451 Value out = op.getOut();
460 VectorType outVecType = dyn_cast<VectorType>(out.
getType());
461 VectorType scaleVecType = dyn_cast<VectorType>(scale.
getType());
463 if (outVecType && outVecType.isScalable())
466 if (isa<RankedTensorType>(out.
getType()) ||
467 isa<RankedTensorType>(in.
getType()) ||
468 isa<RankedTensorType>(scale.
getType()))
472 scaleVecType ? VectorType::get(scaleVecType.getShape(), f32) : f32;
475 arith::ExtFOp::create(rewriter, loc,
TypeRange{scaleF32Type},
476 ValueRange{scale}, arith::ExtFOp::Properties{});
478 scale = arith::TruncFOp::create(rewriter, loc, scaleF32Type, scale);
480 VectorType extScaleResultType = VectorType::get(opOutWidth, outType);
483 Value inCast = vector::BroadcastOp::create(rewriter, loc,
484 VectorType::get(1, inType), in);
486 Value scaleExt = amdgpu::ScaledExtPackedOp::create(
487 rewriter, loc, extScaleResultType, inCast, scale, 0);
492 VectorType inVecType = cast<VectorType>(in.
getType());
494 VectorType origScaleVecType = dyn_cast<VectorType>(origScale.
getType());
496 ArrayRef<int64_t> inShape = inVecType.getShape();
497 SmallVector<int64_t> originalScaleShape;
498 if (origScaleVecType)
499 llvm::append_range(originalScaleShape, origScaleVecType.getShape());
501 originalScaleShape.insert(originalScaleShape.end(),
502 inShape.size() - originalScaleShape.size(), 1);
506 "failed to derive block size from broadcast or splat operation");
508 SmallVector<int64_t> ratio =
509 maybeRatio.value_or(SmallVector<int64_t>(inShape.size(), 1));
513 Value zero = arith::ConstantOp::create(rewriter, loc, outType,
516 rewriter.
createOrFold<vector::BroadcastOp>(loc, outVecType, zero);
518 for (SmallVector<int64_t> offsets : StaticTileOffsetRange(inShape, ratio)) {
519 SmallVector<int64_t> strides(offsets.size(), 1);
520 Value block = vector::ExtractStridedSliceOp::create(
521 rewriter, loc, in, offsets, ratio, strides);
522 VectorType block1DType = VectorType::get(blockSize, inType);
524 vector::ShapeCastOp::create(rewriter, loc, block1DType, block);
526 vector::ExtractOp::create(rewriter, loc, scale, offsets);
528 VectorType blockResultType = VectorType::get(blockSize, outType);
530 rewriter.
createOrFold<vector::BroadcastOp>(loc, blockResultType, zero);
532 for (int64_t i = 0, inSliceWidth = std::min(opInWidth, blockSize - i);
534 i += inSliceWidth, inSliceWidth = std::min(opInWidth, blockSize - i)) {
535 Value inSlice = vector::ExtractStridedSliceOp::create(
536 rewriter, loc, block1D, i, inSliceWidth, 1);
538 outSliceWidth = std::min(opOutWidth, inSliceWidth - j);
539 j < inSliceWidth; j += outSliceWidth,
540 outSliceWidth = std::min(opOutWidth, inSliceWidth - j)) {
542 Value scaleExt = amdgpu::ScaledExtPackedOp::create(
543 rewriter, loc, extScaleResultType, inSlice, uniformScale,
545 if (outSliceWidth < opOutWidth) {
546 scaleExt = vector::ExtractStridedSliceOp::create(
547 rewriter, loc, scaleExt, 0, outSliceWidth, 1);
549 blockResult = vector::InsertStridedSliceOp::create(
550 rewriter, loc, scaleExt, blockResult, i + j, 1);
554 VectorType resultType = VectorType::get(ratio, outType);
556 vector::ShapeCastOp::create(rewriter, loc, resultType, blockResult);
557 result = vector::InsertStridedSliceOp::create(rewriter, loc, cast,
result,
567ScalingTruncFRewritePattern::matchAndRewrite(arith::ScalingTruncFOp op,
568 PatternRewriter &rewriter)
const {
569 Location loc = op.getLoc();
570 constexpr int64_t opInWidth = 2;
572 Value in = op.getIn();
573 Value scale = op.getScale();
574 Value out = op.getOut();
581 VectorType outVecType = dyn_cast<VectorType>(out.
getType());
582 VectorType scaleVecType = dyn_cast<VectorType>(scale.
getType());
583 if (outVecType && outVecType.isScalable())
586 if (isa<RankedTensorType>(out.
getType()) ||
587 isa<RankedTensorType>(in.
getType()) ||
588 isa<RankedTensorType>(scale.
getType()))
592 scaleVecType ? VectorType::get(scaleVecType.getShape(), f32) : f32;
595 arith::ExtFOp::create(rewriter, loc,
TypeRange{scaleF32Type},
596 ValueRange{scale}, arith::ExtFOp::Properties{});
598 scale = arith::TruncFOp::create(rewriter, loc, scaleF32Type, scale);
600 Value zero = arith::ConstantOp::create(rewriter, loc, outType,
603 VectorType truncScaleResultType = VectorType::get(opOutWidth, outType);
606 Type inVecType = VectorType::get(1, inType);
607 Value inCast = vector::BroadcastOp::create(rewriter, loc, inVecType, in);
609 Value scaleTrunc = amdgpu::PackedScaledTruncOp::create(
610 rewriter, loc, truncScaleResultType, inCast, scale, 0,
617 VectorType inVecType = cast<VectorType>(in.
getType());
619 VectorType origScaleVecType = dyn_cast<VectorType>(origScale.
getType());
621 ArrayRef<int64_t> inShape = inVecType.getShape();
622 SmallVector<int64_t> scaleShape;
623 if (origScaleVecType)
624 llvm::append_range(scaleShape, origScaleVecType.getShape());
626 scaleShape.insert(scaleShape.end(), inShape.size() - scaleShape.size(), 1);
630 "failed to derive block size from broadcast or splat operation");
632 SmallVector<int64_t> ratio =
633 maybeRatio.value_or(SmallVector<int64_t>(inShape.size(), 1));
638 rewriter.
createOrFold<vector::BroadcastOp>(loc, outVecType, zero);
640 for (SmallVector<int64_t> offsets : StaticTileOffsetRange(inShape, ratio)) {
641 SmallVector<int64_t> strides(offsets.size(), 1);
642 Value block = vector::ExtractStridedSliceOp::create(
643 rewriter, loc, in, offsets, ratio, strides);
644 VectorType block1DType = VectorType::get(blockSize, inType);
646 vector::ShapeCastOp::create(rewriter, loc, block1DType, block);
648 vector::ExtractOp::create(rewriter, loc, scale, offsets);
650 VectorType blockResultType = VectorType::get(blockSize, outType);
652 rewriter.
createOrFold<vector::BroadcastOp>(loc, blockResultType, zero);
654 for (int64_t i = 0, outSliceWidth = std::min(opOutWidth, blockSize - i);
655 i < blockSize; i += outSliceWidth,
656 outSliceWidth = std::min(opOutWidth, blockSize - i)) {
659 if (outSliceWidth <= opInWidth) {
660 Value
slice = vector::ExtractStridedSliceOp::create(
661 rewriter, loc, block1D, i, outSliceWidth, 1);
663 scaleTrunc = amdgpu::PackedScaledTruncOp::create(
664 rewriter, loc, truncScaleResultType,
slice, uniformScale, 0,
667 scaleTrunc = vector::BroadcastOp::create(rewriter, loc,
668 truncScaleResultType, zero);
670 inSliceWidth = std::min(opInWidth, outSliceWidth - j);
671 j < outSliceWidth; j += opInWidth,
672 inSliceWidth = std::min(opInWidth, outSliceWidth - j)) {
673 Value
slice = vector::ExtractStridedSliceOp::create(
674 rewriter, loc, block1D, i + j, inSliceWidth, 1);
675 scaleTrunc = amdgpu::PackedScaledTruncOp::create(
676 rewriter, loc, truncScaleResultType,
slice, uniformScale,
677 j / opInWidth, scaleTrunc);
680 if (outSliceWidth != opOutWidth) {
681 scaleTrunc = vector::ExtractStridedSliceOp::create(
682 rewriter, loc, scaleTrunc, 0, outSliceWidth, 1);
684 blockResult = vector::InsertStridedSliceOp::create(
685 rewriter, loc, scaleTrunc, blockResult, i, 1);
688 VectorType resultType = VectorType::get(ratio, outType);
690 vector::ShapeCastOp::create(rewriter, loc, resultType, blockResult);
691 result = vector::InsertStridedSliceOp::create(rewriter, loc, cast,
result,
702 bool saturateFP8Truncf,
bool allowPackedF16Rtz,
bool supportsScaledExtTrunc,
705 if (convertFP8Arithmetic) {
708 patterns.
add<TruncFToFloat8RewritePattern>(
711 if (allowPackedF16Rtz)
712 patterns.
add<TruncfToFloat16RewritePattern>(patterns.
getContext(), benefit);
714 if (supportsScaledExtTrunc) {
715 patterns.
add<ScalingExtFRewritePattern>(patterns.
getContext(), benefit);
716 patterns.
add<ScalingTruncFRewritePattern>(patterns.
getContext(), benefit);
720void ArithToAMDGPUConversionPass::runOnOperation() {
726 [&] {
return emitError(UnknownLoc::get(ctx)); });
727 if (failed(targetInfo))
728 return signalPassFailure();
730 bool convertFP8Arithmetic =
731 targetInfo->has(llvm::AMDGPU::FEAT_FP8_CONVERSION_INSTS);
732 bool supportsScaledExtTrunc =
733 targetInfo->has(llvm::AMDGPU::FEAT_GFX950_INSTS);
735 patterns, convertFP8Arithmetic, saturateFP8Truncf, allowPackedF16Rtz,
736 supportsScaledExtTrunc, *targetInfo);
738 return signalPassFailure();