@@ -565,6 +565,268 @@ class ResizeToLinalg : public OpConversionPattern<ResizeOp> {
565565 }
566566};
567567
568+ // Lower grid_sample to an output-indexed linalg.generic. Each output element
569+ // extracts the normalized x/y grid coordinate, converts it to input-space pixel
570+ // coordinates, samples the input with the requested interpolation rule, and
571+ // applies zeros or border padding while materializing the sample.
572+ class GridSampleToLinalg : public OpConversionPattern <GridSampleOp> {
573+ public:
574+ using OpConversionPattern<GridSampleOp>::OpConversionPattern;
575+
576+ struct GridSampleParams {
577+ Value input;
578+ Value grid;
579+ RankedTensorType inputTy;
580+ RankedTensorType gridTy;
581+ RankedTensorType resultTy;
582+ uint64_t alignCorners;
583+ uint64_t mode;
584+ uint64_t paddingMode;
585+ };
586+
587+ static Value gridCoordinateToInput (const GridSampleParams ¶ms,
588+ OpBuilder &b, Location loc,
589+ Value gridValue, int64_t dim,
590+ Type calcType) {
591+ gridValue = convertValue (b, loc, gridValue, calcType);
592+ const double inputSize = params.inputTy .getDimSize (dim);
593+ const Value one = createFloatConstant (b, loc, calcType, 1.0 );
594+ const Value half = createFloatConstant (b, loc, calcType, 0.5 );
595+ const Value shifted = b.create <arith::AddFOp>(loc, gridValue, one);
596+
597+ if (params.alignCorners ) {
598+ const Value sizeMinusOne =
599+ createFloatConstant (b, loc, calcType, inputSize - 1.0 );
600+ const Value scaled = b.create <arith::MulFOp>(loc, shifted, sizeMinusOne);
601+ return b.create <arith::MulFOp>(loc, scaled, half);
602+ }
603+
604+ const Value size = createFloatConstant (b, loc, calcType, inputSize);
605+ const Value scaled = b.create <arith::MulFOp>(loc, shifted, size);
606+ const Value shiftedBack = b.create <arith::SubFOp>(loc, scaled, one);
607+ return b.create <arith::MulFOp>(loc, shiftedBack, half);
608+ }
609+
610+ static Value isIndexInBounds (OpBuilder &b, Location loc, Value index,
611+ int64_t size) {
612+ const Value zero = createIndexConstant (b, loc, 0 );
613+ const Value upper = createIndexConstant (b, loc, size - 1 );
614+ const Value aboveLower =
615+ b.create <arith::CmpIOp>(loc, arith::CmpIPredicate::sge, index, zero);
616+ const Value belowUpper =
617+ b.create <arith::CmpIOp>(loc, arith::CmpIPredicate::sle, index, upper);
618+ return b.create <arith::AndIOp>(loc, aboveLower, belowUpper);
619+ }
620+
621+ static Value sampleInput (const GridSampleParams ¶ms, OpBuilder &b,
622+ Location loc, Value n, Value c, Value h, Value w,
623+ Type resultElementTy) {
624+ Value sampleH = h;
625+ Value sampleW = w;
626+ Value inBounds;
627+ if (params.paddingMode == 0 ) {
628+ Value hInBounds =
629+ isIndexInBounds (b, loc, h, params.inputTy .getDimSize (2 ));
630+ Value wInBounds =
631+ isIndexInBounds (b, loc, w, params.inputTy .getDimSize (3 ));
632+ inBounds = b.create <arith::AndIOp>(loc, hInBounds, wInBounds);
633+ sampleH = clampIndex (b, loc, h, params.inputTy .getDimSize (2 ) - 1 );
634+ sampleW = clampIndex (b, loc, w, params.inputTy .getDimSize (3 ) - 1 );
635+ } else {
636+ sampleH = clampIndex (b, loc, h, params.inputTy .getDimSize (2 ) - 1 );
637+ sampleW = clampIndex (b, loc, w, params.inputTy .getDimSize (3 ) - 1 );
638+ }
639+
640+ Value sample =
641+ b.create <tensor::ExtractOp>(loc, params.input ,
642+ ValueRange{n, c, sampleH, sampleW});
643+ if (params.paddingMode == 0 ) {
644+ const Value zero = createFloatConstant (b, loc, resultElementTy, 0.0 );
645+ sample = b.create <arith::SelectOp>(loc, inBounds, sample, zero);
646+ }
647+ return sample;
648+ }
649+
650+ static Value getNearestIndex (OpBuilder &b, Location loc, Value coord,
651+ Type calcType) {
652+ const Value floorCoord = b.create <math::FloorOp>(loc, coord);
653+ const Value floorIndex = castFloatToIndex (b, loc, floorCoord);
654+ const Value one = createIndexConstant (b, loc, 1 );
655+ const Value upperIndex = b.create <arith::AddIOp>(loc, floorIndex, one);
656+
657+ const Value fraction = b.create <arith::SubFOp>(loc, coord, floorCoord);
658+ const Value half = createFloatConstant (b, loc, calcType, 0.5 );
659+ const Value takeUpper =
660+ b.create <arith::CmpFOp>(loc, arith::CmpFPredicate::OGT , fraction, half);
661+ const Value isTie =
662+ b.create <arith::CmpFOp>(loc, arith::CmpFPredicate::OEQ , fraction, half);
663+
664+ const Value floorInt =
665+ b.create <arith::IndexCastOp>(loc, b.getI64Type (), floorIndex);
666+ const Value two = b.create <arith::ConstantIntOp>(loc, 2 , 64 );
667+ const Value remainder = b.create <arith::RemSIOp>(loc, floorInt, two);
668+ const Value zero = b.create <arith::ConstantIntOp>(loc, 0 , 64 );
669+ const Value floorIsOdd =
670+ b.create <arith::CmpIOp>(loc, arith::CmpIPredicate::ne, remainder, zero);
671+ const Value tieTakesUpper =
672+ b.create <arith::AndIOp>(loc, isTie, floorIsOdd);
673+ const Value selectUpper =
674+ b.create <arith::OrIOp>(loc, takeUpper, tieTakesUpper);
675+ return b.create <arith::SelectOp>(loc, selectUpper, upperIndex, floorIndex);
676+ }
677+
678+ static Value buildNearest (const GridSampleParams ¶ms, OpBuilder &b,
679+ Location loc, Type calcType,
680+ Type resultElementTy) {
681+ const Value n = b.create <linalg::IndexOp>(loc, 0 );
682+ const Value c = b.create <linalg::IndexOp>(loc, 1 );
683+ const Value outH = b.create <linalg::IndexOp>(loc, 2 );
684+ const Value outW = b.create <linalg::IndexOp>(loc, 3 );
685+ const Value zero = createIndexConstant (b, loc, 0 );
686+ const Value one = createIndexConstant (b, loc, 1 );
687+ const Value gridX =
688+ b.create <tensor::ExtractOp>(loc, params.grid ,
689+ ValueRange{n, outH, outW, zero});
690+ const Value gridY =
691+ b.create <tensor::ExtractOp>(loc, params.grid ,
692+ ValueRange{n, outH, outW, one});
693+ const Value inputX =
694+ gridCoordinateToInput (params, b, loc, gridX, 3 , calcType);
695+ const Value inputY =
696+ gridCoordinateToInput (params, b, loc, gridY, 2 , calcType);
697+ const Value x = getNearestIndex (b, loc, inputX, calcType);
698+ const Value y = getNearestIndex (b, loc, inputY, calcType);
699+ return sampleInput (params, b, loc, n, c, y, x, resultElementTy);
700+ }
701+
702+ static Value buildBilinear (const GridSampleParams ¶ms, OpBuilder &b,
703+ Location loc, Type calcType,
704+ Type resultElementTy) {
705+ const Value n = b.create <linalg::IndexOp>(loc, 0 );
706+ const Value c = b.create <linalg::IndexOp>(loc, 1 );
707+ const Value outH = b.create <linalg::IndexOp>(loc, 2 );
708+ const Value outW = b.create <linalg::IndexOp>(loc, 3 );
709+ const Value zeroIndex = createIndexConstant (b, loc, 0 );
710+ const Value oneIndex = createIndexConstant (b, loc, 1 );
711+ const Value gridX =
712+ b.create <tensor::ExtractOp>(loc, params.grid ,
713+ ValueRange{n, outH, outW, zeroIndex});
714+ const Value gridY =
715+ b.create <tensor::ExtractOp>(loc, params.grid ,
716+ ValueRange{n, outH, outW, oneIndex});
717+ const Value inputX =
718+ gridCoordinateToInput (params, b, loc, gridX, 3 , calcType);
719+ const Value inputY =
720+ gridCoordinateToInput (params, b, loc, gridY, 2 , calcType);
721+
722+ const Value x0Float = b.create <math::FloorOp>(loc, inputX);
723+ const Value y0Float = b.create <math::FloorOp>(loc, inputY);
724+ const Value x0 = castFloatToIndex (b, loc, x0Float);
725+ const Value y0 = castFloatToIndex (b, loc, y0Float);
726+ const Value x1 = b.create <arith::AddIOp>(loc, x0, oneIndex);
727+ const Value y1 = b.create <arith::AddIOp>(loc, y0, oneIndex);
728+
729+ const Value xLerp = b.create <arith::SubFOp>(loc, inputX, x0Float);
730+ const Value yLerp = b.create <arith::SubFOp>(loc, inputY, y0Float);
731+ const Value oneFloat = createFloatConstant (b, loc, calcType, 1.0 );
732+ const Value x0Weight = b.create <arith::SubFOp>(loc, oneFloat, xLerp);
733+ const Value y0Weight = b.create <arith::SubFOp>(loc, oneFloat, yLerp);
734+
735+ auto weightedSample = [&](Value h, Value w, Value hWeight,
736+ Value wWeight) -> Value {
737+ Value sample = sampleInput (params, b, loc, n, c, h, w, resultElementTy);
738+ sample = convertValue (b, loc, sample, calcType);
739+ const Value weight = b.create <arith::MulFOp>(loc, hWeight, wWeight);
740+ return b.create <arith::MulFOp>(loc, sample, weight);
741+ };
742+
743+ Value acc = weightedSample (y0, x0, y0Weight, x0Weight);
744+ acc = b.create <arith::AddFOp>(
745+ loc, acc, weightedSample (y0, x1, y0Weight, xLerp));
746+ acc = b.create <arith::AddFOp>(
747+ loc, acc, weightedSample (y1, x0, yLerp, x0Weight));
748+ acc = b.create <arith::AddFOp>(
749+ loc, acc, weightedSample (y1, x1, yLerp, xLerp));
750+ return convertValue (b, loc, acc, resultElementTy);
751+ }
752+
753+ LogicalResult
754+ matchAndRewrite (GridSampleOp op, OpAdaptor adaptor,
755+ ConversionPatternRewriter &rewriter) const override {
756+ const Location loc = op->getLoc ();
757+ const auto inputTy = cast<RankedTensorType>(adaptor.getX ().getType ());
758+ const auto gridTy = cast<RankedTensorType>(adaptor.getGrid ().getType ());
759+ const auto resultTy = cast<RankedTensorType>(op->getResult (0 ).getType ());
760+ const Type resultElementTy = resultTy.getElementType ();
761+
762+ if (!inputTy.hasStaticShape () || !gridTy.hasStaticShape () ||
763+ !resultTy.hasStaticShape ())
764+ return rewriter.notifyMatchFailure (
765+ op, " grid_sample lowering requires static shapes" );
766+ if (inputTy.getRank () != 4 || gridTy.getRank () != 4 ||
767+ resultTy.getRank () != 4 )
768+ return rewriter.notifyMatchFailure (
769+ op, " grid_sample lowering supports rank-4 tensors" );
770+ if (!isa<FloatType>(inputTy.getElementType ()) ||
771+ !isa<FloatType>(gridTy.getElementType ()) ||
772+ !isa<FloatType>(resultElementTy))
773+ return rewriter.notifyMatchFailure (
774+ op, " grid_sample lowering only supports floating point tensors" );
775+ // align_corners encoding: 0=false, 1=true.
776+ if (adaptor.getAlignCorners () > 1 )
777+ return rewriter.notifyMatchFailure (
778+ op, " grid_sample align_corners must be 0 or 1" );
779+
780+ // mode encoding: 0=bilinear, 1=nearest, 2=cubic.
781+ if (adaptor.getMode () > 2 )
782+ return rewriter.notifyMatchFailure (op, " invalid grid_sample mode" );
783+ if (adaptor.getMode () == 2 )
784+ return rewriter.notifyMatchFailure (
785+ op, " grid_sample cubic mode is not supported" );
786+
787+ // padding_mode encoding: 0=zeros, 1=border, 2=reflection.
788+ if (adaptor.getPaddingMode () > 2 )
789+ return rewriter.notifyMatchFailure (
790+ op, " invalid grid_sample padding mode" );
791+ if (adaptor.getPaddingMode () == 2 )
792+ return rewriter.notifyMatchFailure (
793+ op, " grid_sample reflection padding is not supported" );
794+
795+ Type calcType = resultElementTy;
796+ if (auto floatTy = dyn_cast<FloatType>(calcType)) {
797+ if (floatTy.getWidth () < 32 )
798+ calcType = rewriter.getF32Type ();
799+ }
800+
801+ const GridSampleParams params{adaptor.getX (),
802+ adaptor.getGrid (),
803+ inputTy,
804+ gridTy,
805+ resultTy,
806+ adaptor.getAlignCorners (),
807+ adaptor.getMode (),
808+ adaptor.getPaddingMode ()};
809+ const Value output = getEmptyTensor (rewriter, loc, resultTy, {});
810+ const AffineMap outputMap =
811+ rewriter.getMultiDimIdentityMap (resultTy.getRank ());
812+ auto generic = rewriter.create <linalg::GenericOp>(
813+ loc, TypeRange{resultTy}, ValueRange{}, output,
814+ SmallVector<AffineMap>{outputMap},
815+ mlir::tosa::getNParallelLoopsAttrs (resultTy.getRank ()),
816+ [&](OpBuilder &nestedBuilder, Location nestedLoc, ValueRange) {
817+ Value result = params.mode == 0
818+ ? buildBilinear (params, nestedBuilder, nestedLoc,
819+ calcType, resultElementTy)
820+ : buildNearest (params, nestedBuilder, nestedLoc,
821+ calcType, resultElementTy);
822+ nestedBuilder.create <linalg::YieldOp>(nestedLoc, result);
823+ });
824+
825+ rewriter.replaceOp (op, generic.getResults ());
826+ return success ();
827+ }
828+ };
829+
568830struct ConvertXtenNNtoLinalg
569831 : public xilinx::xten::impl::ConvertXTenNNToLinalgBase<
570832 ConvertXtenNNtoLinalg> {
@@ -582,14 +844,15 @@ struct ConvertXtenNNtoLinalg
582844 auto funcOp = getOperation ();
583845
584846 ConversionTarget target (*context);
585- target.addIllegalOp <EluOp, ReduceMeanOp, ResizeOp, SignOp>();
847+ target.addIllegalOp <EluOp, GridSampleOp, ReduceMeanOp, ResizeOp, SignOp>();
586848 target.addLegalDialect <linalg::LinalgDialect, scf::SCFDialect,
587849 complex ::ComplexDialect, math::MathDialect,
588850 shape::ShapeDialect, tensor::TensorDialect,
589851 arith::ArithDialect>();
590852
591853 RewritePatternSet patterns (context);
592- patterns.add <EluToLinalg, ReduceMeanToLinalg, ResizeToLinalg>(context);
854+ patterns.add <EluToLinalg, GridSampleToLinalg, ReduceMeanToLinalg,
855+ ResizeToLinalg>(context);
593856
594857 if (failed (applyPartialConversion (funcOp, target, std::move (patterns))))
595858 signalPassFailure ();
0 commit comments