Skip to content

Commit ab93bd6

Browse files
HsiangkaiTai78641
andauthored
[mlir][tosa] Change ClampOp's min/max attributes (llvm#125197)
This changes Tosa ClampOp attributes to min_val and max_val which are either integer attributes or float attributes, and adds verify checks that these attribute element types must match element types of input and output Co-authored-by: Tai Ly <[email protected]>
1 parent 9387fd9 commit ab93bd6

File tree

11 files changed

+199
-159
lines changed

11 files changed

+199
-159
lines changed

mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -394,10 +394,8 @@ def Tosa_ClampOp : Tosa_ElementwiseUnaryOp<"clamp"> {
394394

395395
let arguments = (ins
396396
Tosa_Tensor:$input,
397-
I64Attr:$min_int,
398-
I64Attr:$max_int,
399-
Tosa_FloatAttr:$min_fp,
400-
Tosa_FloatAttr:$max_fp,
397+
Tosa_IntOrFloatAttr:$min_val,
398+
Tosa_IntOrFloatAttr:$max_val,
401399
DefaultValuedAttr<Tosa_NanPropagationAttr, "\"PROPAGATE\"">:$nan_mode
402400
);
403401

mlir/include/mlir/Dialect/Tosa/IR/TosaTypesBase.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,14 @@ def Tosa_FloatAttr : Attr<CPred<"::llvm::isa<::mlir::FloatAttr>($_self)">,
202202
let returnType = [{ ::mlir::APFloat }];
203203
}
204204

205+
def Tosa_IntegerAttr : Attr<CPred<"::llvm::isa<::mlir::IntegerAttr>($_self)">,
206+
"arbitrary integer attribute"> {
207+
let storageType = [{ ::mlir::IntegerAttr }];
208+
let returnType = [{ ::llvm::APInt }];
209+
}
210+
211+
def Tosa_IntOrFloatAttr : AnyAttrOf<[Tosa_IntegerAttr, Tosa_FloatAttr]>;
212+
205213
//===----------------------------------------------------------------------===//
206214
// Iterable attributes.
207215
//===----------------------------------------------------------------------===//

mlir/lib/Conversion/TosaToLinalg/TosaToLinalg.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,8 @@ static Value createLinalgBodyCalculationForElementwiseOp(
389389
// tosa::ClampOp
390390
if (isa<tosa::ClampOp>(op) && isa<FloatType>(elementTy)) {
391391
bool losesInfo = false;
392-
APFloat minApf = cast<FloatAttr>(op->getAttr("min_fp")).getValue();
393-
APFloat maxApf = cast<FloatAttr>(op->getAttr("max_fp")).getValue();
392+
APFloat minApf = cast<FloatAttr>(op->getAttr("min_val")).getValue();
393+
APFloat maxApf = cast<FloatAttr>(op->getAttr("max_val")).getValue();
394394
minApf.convert(cast<FloatType>(elementTy).getFloatSemantics(),
395395
APFloat::rmNearestTiesToEven, &losesInfo);
396396
maxApf.convert(cast<FloatType>(elementTy).getFloatSemantics(),
@@ -405,9 +405,9 @@ static Value createLinalgBodyCalculationForElementwiseOp(
405405
if (isa<tosa::ClampOp>(op) && isa<IntegerType>(elementTy)) {
406406
auto intTy = cast<IntegerType>(elementTy);
407407
int64_t min =
408-
cast<IntegerAttr>(op->getAttr("min_int")).getValue().getSExtValue();
408+
cast<IntegerAttr>(op->getAttr("min_val")).getValue().getSExtValue();
409409
int64_t max =
410-
cast<IntegerAttr>(op->getAttr("max_int")).getValue().getSExtValue();
410+
cast<IntegerAttr>(op->getAttr("max_val")).getValue().getSExtValue();
411411

412412
int64_t minRepresentable = std::numeric_limits<int64_t>::min();
413413
int64_t maxRepresentable = std::numeric_limits<int64_t>::max();

mlir/lib/Dialect/Tosa/IR/TosaCanonicalizations.cpp

Lines changed: 94 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -287,10 +287,12 @@ struct ClampIsNoOp : public OpRewritePattern<tosa::ClampOp> {
287287

288288
if (isa<FloatType>(inputElementType)) {
289289
// Unlike integer types, floating point types can represent infinity.
290-
auto minClamp = op.getMinFp();
291-
auto maxClamp = op.getMaxFp();
292-
bool isMin = minClamp.isInfinity() && minClamp.isNegative();
293-
bool isMax = maxClamp.isInfinity() && !maxClamp.isNegative();
290+
auto minClamp =
291+
llvm::cast<mlir::FloatAttr>(op.getMinValAttr()).getValue();
292+
auto maxClamp =
293+
llvm::cast<mlir::FloatAttr>(op.getMaxValAttr()).getValue();
294+
bool isMin = minClamp.isNegInfinity();
295+
bool isMax = maxClamp.isInfinity();
294296

295297
if (isMin && isMax) {
296298
rewriter.replaceOp(op, input);
@@ -300,8 +302,10 @@ struct ClampIsNoOp : public OpRewritePattern<tosa::ClampOp> {
300302
}
301303

302304
if (inputElementType.isUnsignedInteger()) {
303-
int64_t minClamp = op.getMinInt();
304-
int64_t maxClamp = op.getMaxInt();
305+
int64_t minClamp =
306+
llvm::cast<mlir::IntegerAttr>(op.getMinValAttr()).getUInt();
307+
int64_t maxClamp =
308+
llvm::cast<mlir::IntegerAttr>(op.getMaxValAttr()).getUInt();
305309

306310
int64_t intMin =
307311
APInt::getMinValue(inputElementType.getIntOrFloatBitWidth())
@@ -318,8 +322,10 @@ struct ClampIsNoOp : public OpRewritePattern<tosa::ClampOp> {
318322
}
319323

320324
if (llvm::isa<IntegerType>(inputElementType)) {
321-
int64_t minClamp = op.getMinInt();
322-
int64_t maxClamp = op.getMaxInt();
325+
int64_t minClamp =
326+
llvm::cast<mlir::IntegerAttr>(op.getMinValAttr()).getInt();
327+
int64_t maxClamp =
328+
llvm::cast<mlir::IntegerAttr>(op.getMaxValAttr()).getInt();
323329

324330
int64_t intMin =
325331
APInt::getSignedMinValue(inputElementType.getIntOrFloatBitWidth())
@@ -374,9 +380,10 @@ struct ClampClampOptimization : public OpRewritePattern<tosa::ClampOp> {
374380

375381
LogicalResult matchAndRewrite(tosa::ClampOp op,
376382
PatternRewriter &rewriter) const override {
383+
Value input = op.getInput();
384+
377385
// Check the input to the CLAMP op is itself a CLAMP.
378-
auto clampOp =
379-
dyn_cast_if_present<tosa::ClampOp>(op.getInput().getDefiningOp());
386+
auto clampOp = dyn_cast_if_present<tosa::ClampOp>(input.getDefiningOp());
380387
if (!clampOp)
381388
return failure();
382389

@@ -386,34 +393,86 @@ struct ClampClampOptimization : public OpRewritePattern<tosa::ClampOp> {
386393
if (opNanMode == "IGNORE" && clampNanMode == "PROPAGATE")
387394
return failure();
388395

389-
// Check we have intersecting ranges.
390-
const auto opMinInt = op.getMinInt();
391-
const auto opMaxInt = op.getMaxInt();
392-
const auto clampOpMinInt = clampOp.getMinInt();
393-
const auto clampOpMaxInt = clampOp.getMaxInt();
394-
ClampRange<std::int64_t> opRangeIntRange(opMinInt, opMaxInt);
395-
ClampRange<std::int64_t> clampRangeIntRange(clampOpMinInt, clampOpMaxInt);
396-
if (!opRangeIntRange.intersects(clampRangeIntRange))
397-
return failure();
396+
auto maxValAttr = op.getMaxValAttr();
397+
auto minValAttr = op.getMinValAttr();
398+
auto clampOpMaxValAttr = clampOp.getMaxValAttr();
399+
auto clampOpMinValAttr = clampOp.getMinValAttr();
398400

399-
const auto opMinFloat = op.getMinFp();
400-
const auto opMaxFloat = op.getMaxFp();
401-
const auto clampOpMinFloat = clampOp.getMinFp();
402-
const auto clampOpMaxFloat = clampOp.getMaxFp();
403-
ClampRange<APFloat> opRangeFloatRange(opMinFloat, opMaxFloat);
404-
ClampRange<APFloat> clampRangeFloatRange(clampOpMinFloat, clampOpMaxFloat);
405-
if (!opRangeFloatRange.intersects(clampRangeFloatRange))
406-
return failure();
401+
auto inputEType = llvm::cast<ShapedType>(input.getType()).getElementType();
402+
if (auto quantType =
403+
llvm::dyn_cast<mlir::quant::UniformQuantizedType>(inputEType)) {
404+
inputEType = quantType.getStorageType();
405+
}
406+
407+
Attribute newMinValAttr, newMaxValAttr;
408+
if (mlir::isa<FloatType>(inputEType)) {
409+
auto floatMaxValAttr = cast<mlir::FloatAttr>(maxValAttr);
410+
auto floatMinValAttr = cast<mlir::FloatAttr>(minValAttr);
411+
auto clampOpFloatMaxValAttr = cast<mlir::FloatAttr>(clampOpMaxValAttr);
412+
auto clampOpFloatMinValAttr = cast<mlir::FloatAttr>(clampOpMinValAttr);
413+
414+
// Check we have intersecting ranges.
415+
const auto opMinFloat = floatMinValAttr.getValue();
416+
const auto opMaxFloat = floatMaxValAttr.getValue();
417+
const auto clampOpMinFloat = clampOpFloatMinValAttr.getValue();
418+
const auto clampOpMaxFloat = clampOpFloatMaxValAttr.getValue();
419+
ClampRange<APFloat> opRangeFloatRange(opMinFloat, opMaxFloat);
420+
ClampRange<APFloat> clampRangeFloatRange(clampOpMinFloat,
421+
clampOpMaxFloat);
422+
if (!opRangeFloatRange.intersects(clampRangeFloatRange))
423+
return failure();
424+
425+
// Run the transformation.
426+
auto newMinVal = std::max(opMinFloat, clampOpMinFloat);
427+
auto newMaxVal = std::min(opMaxFloat, clampOpMaxFloat);
428+
newMinValAttr = rewriter.getFloatAttr(inputEType, newMinVal);
429+
newMaxValAttr = rewriter.getFloatAttr(inputEType, newMaxVal);
430+
} else {
431+
assert(mlir::isa<IntegerType>(inputEType));
432+
auto intMaxValAttr = cast<mlir::IntegerAttr>(maxValAttr);
433+
auto intMinValAttr = cast<mlir::IntegerAttr>(minValAttr);
434+
auto clampOpIntMaxValAttr = cast<mlir::IntegerAttr>(clampOpMaxValAttr);
435+
auto clampOpIntMinValAttr = cast<mlir::IntegerAttr>(clampOpMinValAttr);
436+
437+
if (inputEType.isUnsignedInteger()) {
438+
// Check we have intersecting ranges.
439+
const auto opMinInt = intMinValAttr.getUInt();
440+
const auto opMaxInt = intMaxValAttr.getUInt();
441+
const auto clampOpMinInt = clampOpIntMinValAttr.getUInt();
442+
const auto clampOpMaxInt = clampOpIntMaxValAttr.getUInt();
443+
ClampRange<std::uint64_t> opRangeIntRange(opMinInt, opMaxInt);
444+
ClampRange<std::uint64_t> clampRangeIntRange(clampOpMinInt,
445+
clampOpMaxInt);
446+
if (!opRangeIntRange.intersects(clampRangeIntRange))
447+
return failure();
448+
449+
// Run the transformation.
450+
auto newMinVal = std::max(opMinInt, clampOpMinInt);
451+
auto newMaxVal = std::min(opMaxInt, clampOpMaxInt);
452+
newMinValAttr = rewriter.getIntegerAttr(inputEType, newMinVal);
453+
newMaxValAttr = rewriter.getIntegerAttr(inputEType, newMaxVal);
454+
} else {
455+
// Check we have intersecting ranges.
456+
const auto opMinInt = intMinValAttr.getInt();
457+
const auto opMaxInt = intMaxValAttr.getInt();
458+
const auto clampOpMinInt = clampOpIntMinValAttr.getInt();
459+
const auto clampOpMaxInt = clampOpIntMaxValAttr.getInt();
460+
ClampRange<std::int64_t> opRangeIntRange(opMinInt, opMaxInt);
461+
ClampRange<std::int64_t> clampRangeIntRange(clampOpMinInt,
462+
clampOpMaxInt);
463+
if (!opRangeIntRange.intersects(clampRangeIntRange))
464+
return failure();
465+
466+
// Run the transformation.
467+
auto newMinVal = std::max(opMinInt, clampOpMinInt);
468+
auto newMaxVal = std::min(opMaxInt, clampOpMaxInt);
469+
newMinValAttr = rewriter.getIntegerAttr(inputEType, newMinVal);
470+
newMaxValAttr = rewriter.getIntegerAttr(inputEType, newMaxVal);
471+
}
472+
}
407473

408-
// Run the transformation.
409-
const auto minFp = std::max(opMinFloat, clampOpMinFloat).convertToFloat();
410-
const auto maxFp = std::min(opMaxFloat, clampOpMaxFloat).convertToFloat();
411-
const auto minInt = std::max(opMinInt, clampOpMinInt);
412-
const auto maxInt = std::min(opMaxInt, clampOpMaxInt);
413474
rewriter.replaceOpWithNewOp<tosa::ClampOp>(
414-
op, op.getType(), clampOp.getInput(),
415-
rewriter.getI64IntegerAttr(minInt), rewriter.getI64IntegerAttr(maxInt),
416-
rewriter.getF32FloatAttr(minFp), rewriter.getF32FloatAttr(maxFp),
475+
op, op.getType(), clampOp.getInput(), newMinValAttr, newMaxValAttr,
417476
rewriter.getStringAttr((opNanMode != clampNanMode) ? "IGNORE"
418477
: opNanMode));
419478
return success();

mlir/lib/Dialect/Tosa/IR/TosaOps.cpp

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -476,26 +476,40 @@ LogicalResult tosa::ClampOp::verify() {
476476
llvm::dyn_cast<mlir::quant::UniformQuantizedType>(inputETy)) {
477477
inputETy = quantType.getStorageType();
478478
}
479-
mlir::Type maxFpType = getMaxFpAttr().getType();
480-
mlir::Type minFpType = getMinFpAttr().getType();
481479
mlir::Type outputETy =
482480
llvm::cast<ShapedType>(getOutput().getType()).getElementType();
483481
if (auto quantType =
484482
llvm::dyn_cast<mlir::quant::UniformQuantizedType>(outputETy)) {
485483
outputETy = quantType.getStorageType();
486484
}
487-
unsigned dataTypeBitWidth = inputETy.getIntOrFloatBitWidth();
488-
489485
if (inputETy != outputETy)
490486
return emitOpError("input/output element types are incompatible.");
491487

492-
// If input datatype is float, check that the two min/max_fp attributes
493-
// share the same type and that their type is either the same of the input's
494-
// datatype, or a float type whose bitwidth > input datatype bitwidth.
495-
if (!inputETy.isInteger(dataTypeBitWidth)) {
496-
if (((maxFpType != minFpType) ||
497-
(maxFpType != inputETy && maxFpType.getIntOrFloatBitWidth() <=
498-
inputETy.getIntOrFloatBitWidth())))
488+
auto maxValAttr = getMaxValAttr();
489+
auto minValAttr = getMinValAttr();
490+
491+
unsigned dataTypeBitWidth = inputETy.getIntOrFloatBitWidth();
492+
493+
if (inputETy.isInteger(dataTypeBitWidth)) {
494+
// if input datatype is integer, check that the min_val/max_val attributes
495+
// are integer attributes, and that their type is the same as the input's
496+
// datatype
497+
auto intMaxValAttr = mlir::dyn_cast<mlir::IntegerAttr>(maxValAttr);
498+
auto intMinValAttr = mlir::dyn_cast<mlir::IntegerAttr>(minValAttr);
499+
if (!intMaxValAttr || !intMinValAttr ||
500+
(intMaxValAttr.getType() != intMinValAttr.getType()) ||
501+
(intMaxValAttr.getType() != inputETy))
502+
return emitOpError("min/max attributes types are incompatible with "
503+
"input/output element types.");
504+
} else {
505+
// otherwise, input datatype is float, check that the min_val/max_val
506+
// attributes share the same type and that their type is the same as the
507+
// input's datatype
508+
auto floatMaxValAttr = mlir::dyn_cast<mlir::FloatAttr>(maxValAttr);
509+
auto floatMinValAttr = mlir::dyn_cast<mlir::FloatAttr>(minValAttr);
510+
if (!floatMaxValAttr || !floatMinValAttr ||
511+
(floatMaxValAttr.getType() != floatMinValAttr.getType()) ||
512+
(floatMaxValAttr.getType() != inputETy))
499513
return emitOpError("min/max attributes types are incompatible with "
500514
"input/output element types.");
501515
}

mlir/test/Conversion/TosaToLinalg/tosa-to-linalg.mlir

Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ func.func @test_simple_f32(%arg0: tensor<1xf32>) -> () {
529529
// CHECK: linalg.generic
530530
// CHECK: arith.minimumf
531531
// CHECK: arith.maximumf
532-
%18 = tosa.clamp %0 {min_int = 1 : i64, max_int = 5 : i64, min_fp = 1.0 : f32, max_fp = 5.0 : f32} : (tensor<1xf32>) -> tensor<1xf32>
532+
%18 = tosa.clamp %0 {min_val = 1.0 : f32, max_val = 5.0 : f32} : (tensor<1xf32>) -> tensor<1xf32>
533533

534534
// CHECK: linalg.generic
535535
// CHECK: arith.negf
@@ -729,35 +729,14 @@ func.func @test_simple_i32(%arg0: tensor<1xi32>, %unsigned: tensor<1xui32>, %uns
729729
// CHECK: linalg.generic
730730
// CHECK-DAG: arith.maxsi
731731
// CHECK-DAG: arith.minsi
732-
%19 = tosa.clamp %0 {min_int = 1 : i64, max_int = 5 : i64, min_fp = 1.0 : f32, max_fp = 5.0 : f32} : (tensor<1xi32>) -> tensor<1xi32>
732+
%19 = tosa.clamp %0 {min_val = 1 : i32, max_val = 5 : i32} : (tensor<1xi32>) -> tensor<1xi32>
733733

734734
// CHECK: linalg.generic
735735
// CHECK-DAG: %[[LB:.*]] = arith.constant 4 : i32
736736
// CHECK-DAG: %[[UB:.*]] = arith.constant 32 : i32
737737
// CHECK-DAG: arith.maxui %[[LB]],
738738
// CHECK-DAG: arith.minui %[[UB]],
739-
%u0 = tosa.clamp %unsigned {min_int = 4 : i64, max_int = 32 : i64, min_fp = 1.0 : f32, max_fp = 5.0 : f32} : (tensor<1xui32>) -> tensor<1xui32>
740-
741-
// CHECK: linalg.generic
742-
// CHECK-DAG: %[[LB:.*]] = arith.constant -1 : i32
743-
// CHECK-DAG: %[[UB:.*]] = arith.constant -1 : i32
744-
// CHECK-DAG: arith.maxui %[[LB]],
745-
// CHECK-DAG: arith.minui %[[UB]],
746-
%u1 = tosa.clamp %unsigned {min_int = 9223372036854775807 : i64, max_int = 9223372036854775807 : i64, min_fp = 1.0 : f32, max_fp = 5.0 : f32} : (tensor<1xui32>) -> tensor<1xui32>
747-
748-
// CHECK: linalg.generic
749-
// CHECK-DAG: %[[LB:.*]] = arith.constant 0 : i32
750-
// CHECK-DAG: %[[UB:.*]] = arith.constant 0 : i32
751-
// CHECK-DAG: arith.maxui %[[LB]],
752-
// CHECK-DAG: arith.minui %[[UB]],
753-
%u2 = tosa.clamp %unsigned {min_int = -3 : i64, max_int = -2 : i64, min_fp = 1.0 : f32, max_fp = 5.0 : f32} : (tensor<1xui32>) -> tensor<1xui32>
754-
755-
// CHECK: linalg.generic
756-
// CHECK-DAG: %[[LB:.*]] = arith.constant 0 : i64
757-
// CHECK-DAG: %[[UB:.*]] = arith.constant 9223372036854775807 : i64
758-
// CHECK-DAG: arith.maxui %[[LB]],
759-
// CHECK-DAG: arith.minui %[[UB]],
760-
%u3 = tosa.clamp %unsigned64 {min_int = -3 : i64, max_int = 9223372036854775807 : i64, min_fp = 1.0 : f32, max_fp = 5.0 : f32} : (tensor<1xui64>) -> tensor<1xui64>
739+
%u0 = tosa.clamp %unsigned {min_val = 4 : ui32, max_val = 32 : ui32} : (tensor<1xui32>) -> tensor<1xui32>
761740

762741
// CHECK: linalg.generic
763742
// CHECK: arith.trunci
@@ -807,15 +786,7 @@ func.func @test_i8(%arg0: tensor<1xi8>) -> () {
807786
// CHECK-DAG: %[[C126:.+]] = arith.constant 126
808787
// CHECK-DAG: %[[LOWER:.+]] = arith.maxsi %[[C127]], %[[ARG1]]
809788
// CHECK-DAG: %[[CLAMPED:.+]] = arith.minsi %[[C126]], %[[LOWER]]
810-
%0 = tosa.clamp %arg0 {min_int = -127 : i64, max_int = 126 : i64, min_fp = 0.0 : f32, max_fp = 0.0 : f32} : (tensor<1xi8>) -> tensor<1xi8>
811-
812-
// CHECK: linalg.generic
813-
// CHECK: ^bb0(%[[ARG1:.+]]: i8,
814-
// CHECK-DAG: %[[C128:.+]] = arith.constant -128
815-
// CHECK-DAG: %[[C127:.+]] = arith.constant 127
816-
// CHECK-DAG: %[[LOWER:.+]] = arith.maxsi %[[C128]], %[[ARG1]]
817-
// CHECK-DAG: %[[CLAMPED:.+]] = arith.minsi %[[C127]], %[[LOWER]]
818-
%1 = tosa.clamp %arg0 {min_int = -130 : i64, max_int = 130 : i64, min_fp = 0.0 : f32, max_fp = 0.0 : f32} : (tensor<1xi8>) -> tensor<1xi8>
789+
%0 = tosa.clamp %arg0 {min_val = -127 : i8, max_val = 126 : i8} : (tensor<1xi8>) -> tensor<1xi8>
819790

820791
return
821792
}
@@ -830,7 +801,7 @@ func.func @test_i64(%arg0: tensor<1xi64>) -> () {
830801
// CHECK-DAG: %[[C126:.+]] = arith.constant 9223372036854775807
831802
// CHECK-DAG: %[[LOWER:.+]] = arith.maxsi %[[C127]], %[[ARG1]]
832803
// CHECK-DAG: %[[CLAMPED:.+]] = arith.minsi %[[C126]], %[[LOWER]]
833-
%0 = tosa.clamp %arg0 {min_int = -9223372036854775808 : i64, max_int = 9223372036854775807 : i64, min_fp = 0.0 : f32, max_fp = 0.0 : f32} : (tensor<1xi64>) -> tensor<1xi64>
804+
%0 = tosa.clamp %arg0 {min_val = -9223372036854775808 : i64, max_val = 9223372036854775807 : i64} : (tensor<1xi64>) -> tensor<1xi64>
834805

835806
return
836807
}
@@ -845,7 +816,7 @@ func.func @test_clamp_f16(%arg0: tensor<1xf16>) -> () {
845816
// CHECK-DAG: %[[C6:.+]] = arith.constant 6.0
846817
// CHECK-DAG: %[[MIN:.+]] = arith.minimumf %[[ARG1]], %[[C6]]
847818
// CHECK-DAG: %[[MAX:.+]] = arith.maximumf %[[MIN]], %[[C0]]
848-
%0 = tosa.clamp %arg0 {min_int = 0 : i64, max_int = 0 : i64, min_fp = 0.0 : f32, max_fp = 6.0 : f32} : (tensor<1xf16>) -> tensor<1xf16>
819+
%0 = tosa.clamp %arg0 {min_val = 0.0 : f16, max_val = 6.0 : f16} : (tensor<1xf16>) -> tensor<1xf16>
849820

850821
return
851822
}

0 commit comments

Comments
 (0)