--- /dev/null
+//===- FxpMathOps/FxpMathOps.h - Fixed point ops ----------------*- C++ -*-===//
+//
+// Copyright 2019 The MLIR Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// =============================================================================
+
+#ifndef MLIR_FXPMATH_QUANTOPS_H_
+#define MLIR_FXPMATH_QUANTOPS_H_
+
+#include "mlir/IR/Dialect.h"
+#include "mlir/IR/OpDefinition.h"
+
+namespace mlir {
+namespace fxpmath {
+
+/// Defines the 'FxpMathOps' dialect.
+class FxpMathOpsDialect : public Dialect {
+public:
+ FxpMathOpsDialect(MLIRContext *context);
+};
+
+#define GET_OP_CLASSES
+#include "mlir/FxpMathOps/FxpMathOps.h.inc"
+
+} // namespace fxpmath
+} // namespace mlir
+
+#endif // MLIR_FXPMATH_QUANTOPS_H_
--- /dev/null
+//===- FxpMathOps.td - Fixed point ops --------------------*- tablegen -*-===//
+//
+// Copyright 2019 The MLIR Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// =============================================================================
+//
+// This is the operation definition file for fixed point ops (and real
+// equivalents).
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef FXPMATH_OPS
+#else
+
+#ifdef OP_BASE
+#else
+include "mlir/IR/OpBase.td"
+include "mlir/Quantization/QuantPredicates.td"
+#endif // OP_BASE
+
+//===----------------------------------------------------------------------===//
+// Attributes
+//===----------------------------------------------------------------------===//
+
+// Real value for an (inclusive) min/max clamp limit.
+def fxpmath_ClampValueAttr : OptionalAttr<F64Attr>;
+
+// Element-wise activation function to apply.
+// Note that RELU activations are not here: they are expressed as clamps.
+def fxpmath_EwUnaryFnAttr :
+ StringBasedAttr<CPred<"true">, "element-wise unary function"> {
+ let returnType = [{ StringRef }];
+ let defaultValue = "IDENTITY";
+}
+
+class fxpmath_ConstEwUnaryFn<string val> : ConstantAttr<fxpmath_EwUnaryFnAttr, val>;
+def fxpmath_EwUnaryFn_Identity: fxpmath_ConstEwUnaryFn<"IDENTITY">;
+def fxpmath_EwUnaryFn_Tanh : fxpmath_ConstEwUnaryFn<"TANH">;
+def fxpmath_EwUnaryFn_Sigmoid : fxpmath_ConstEwUnaryFn<"SIGMOID">;
+def fxpmath_EwUnaryFn_Exp : fxpmath_ConstEwUnaryFn<"EXP">;
+def fxpmath_EwUnaryFn_Log : fxpmath_ConstEwUnaryFn<"LOG">;
+def fxpmath_EwUnaryFn_Neg : fxpmath_ConstEwUnaryFn<"NEG">;
+def fxpmath_EwUnaryFn_Rsqrt : fxpmath_ConstEwUnaryFn<"RSQRT">;
+def fxpmath_EwUnaryFn_Sin : fxpmath_ConstEwUnaryFn<"SIN">;
+def fxpmath_EwUnaryFn_Square : fxpmath_ConstEwUnaryFn<"SQUARE">;
+def fxpmath_EwUnaryFn_Sqrt : fxpmath_ConstEwUnaryFn<"SQRT">;
+def fxpmath_EwUnaryFn_CmpZ : fxpmath_ConstEwUnaryFn<"CMPZ">;
+def fxpmath_EwUnaryFn_CmpNZ : fxpmath_ConstEwUnaryFn<"CMPNZ">;
+def fxpmath_EwUnaryFn_CmpLZ : fxpmath_ConstEwUnaryFn<"CMPLZ">;
+def fxpmath_EwUnaryFn_CmpGZ : fxpmath_ConstEwUnaryFn<"CMPGZ">;
+
+//===----------------------------------------------------------------------===//
+// Base classes
+//===----------------------------------------------------------------------===//
+
+class fxpmath_Op<string mnemonic, list<OpTrait> traits> :
+ Op<!strconcat("fxpmath.", mnemonic), traits>;
+
+//===----------------------------------------------------------------------===//
+// Fixed-point (fxp) arithmetic ops used by kernels.
+//===----------------------------------------------------------------------===//
+
+def fxpmath_RoundingDivideByPotFxpOp :
+ fxpmath_Op<"rounding_divide_by_poti", [NoSideEffect, SameValueType]>,
+ Arguments<(ins quant_StorageValueType:$x, I32Attr:$exponent)>,
+ Results<(outs quant_StorageValueType:$y)> {
+ let description = [{
+ Computes integer division by a power-of-two, correctly rounded-to-nearest.
+ Also known as a rounding arithmetic right shift. See
+ gemmlowp::RoundingDivideByPOT for a reference implementation.
+ }];
+
+ let verifier = [{
+ auto verifyExponent = exponent().getSExtValue();
+ if (verifyExponent < 0 || verifyExponent > 31) {
+ return emitOpError("exponent must be in range [0..31]");
+ }
+ return success();
+ }];
+}
+
+def fxpmath_SaturatingAddFxpOp :
+ fxpmath_Op<"saturating_addi", [NoSideEffect, SameValueType]>,
+ Arguments<(ins quant_StorageValueType:$x,
+ quant_StorageValueType:$y,
+ I32Attr:$clamp_min,
+ I32Attr:$clamp_max)>,
+ Results<(outs quant_StorageValueType:$sum)> {
+ let description = [{
+ Computes saturating addition of two operands, saturating to the given min
+ and max value. The implementation is responsible for choosing an
+ intermediate register size appropriate to carry out the operation without
+ overflow. See gemmlowp::SaturatingAdd for a reference implementation.
+ }];
+}
+
+//===----------------------------------------------------------------------===//
+// Real math ops.
+//
+// Math ops on real numbers which may have a representation in quantized
+// arithmetic. It is expected that eligible ops are lowered from a source
+// dialect to this set of ops prior to the process of converting a compuation
+// to a quantized form. It is a non-goal of these ops to preserve enough
+// information to convert back to the higher level, source dialect.
+//
+// These ops support either real/floating point or QuantizedTypes as operands
+// and results. Since not all transformations are supported (globally or
+// sometimes for specific targets), a computation may end up with
+// untransformable RealMathOps, in which case they need to be lowered as is
+// (using floating point math).
+//
+// This op set takes advantage of the fact that it is typically trivial to
+// combine a math function with a compatible bias addition and real-valued
+// clamp (which can be done at a higher accumulation bit depth).
+//
+// In addition, all element-wise unary functions are collapsed into a single
+// fxpmath_RealUnaryEwOp and selected via an enum-like attribute. Especially at
+// low bit depths, this makes matching simpler and allows the construction of
+// generic LUT-based implementations. It also allows specific lowering rules
+// to consolidate runs of chained unary ops and fuse them to preceding math
+// ops, potentially allowing them to operate directly on higher precision
+// intermediates without resorting to lots of custom kernels for common
+// formulas that can suffer from insufficient precision at low bit depths.
+//
+// Comparison operators are modeled as element-wise unary functions (i.e.
+// CMPZ, CMPNZ, CMPLZ, CMPGZ) intended to follow a sub and output a 1bit
+// quantized value. It is expected that lowering rules can fuse them with
+// the preceding sub.
+//===----------------------------------------------------------------------===//
+
+class fxpmath_RealMathOp<string mnemonic, list<OpTrait> traits = [], dag args> :
+ fxpmath_Op<mnemonic, traits>,
+ Arguments<!con(args, (ins
+ fxpmath_ClampValueAttr:$clamp_min, fxpmath_ClampValueAttr:$clamp_max))>;
+
+//===----------------------------------------------------------------------===//
+// Element wise binary real math ops.
+//===----------------------------------------------------------------------===//
+
+class fxpmath_RealBinaryOp<string mnemonic, list<OpTrait> traits = []> :
+ fxpmath_RealMathOp<mnemonic, traits,
+ (ins quant_RealValueType:$x, quant_RealValueType:$y)>,
+ Results<(outs quant_RealValueType:$r)>;
+
+class fxpmath_RealBinaryBiasOp<string mnemonic, list<OpTrait> traits = []> :
+ fxpmath_RealMathOp<mnemonic, traits,
+ (ins quant_RealValueType:$x, quant_RealValueType:$y,
+ quant_RealValueType:$bias)>,
+ Results<(outs quant_RealValueType:$r)>;
+
+def fxpmath_RealAddEwOp :
+ fxpmath_RealBinaryOp<"real_add_ew", [NoSideEffect]>;
+
+def fxpmath_RealSubEwOp :
+ fxpmath_RealBinaryOp<"real_sub_ew", [NoSideEffect]>;
+
+def fxpmath_RealMulEwOp :
+ fxpmath_RealBinaryOp<"real_mul_ew", [NoSideEffect]>;
+
+def fxpmath_RealDivEwOp :
+ fxpmath_RealBinaryOp<"real_div_ew", [NoSideEffect]>;
+
+//===----------------------------------------------------------------------===//
+// Element wise unary real math op.
+//===----------------------------------------------------------------------===//
+
+def fxpmath_RealUnaryEwOp :
+ fxpmath_RealMathOp<"real_unary_ew", [NoSideEffect],
+ (ins quant_RealValueType:$x, fxpmath_EwUnaryFnAttr:$fn)>,
+ Results<(outs quant_RealValueType:$r)>;
+
+#endif // FXPMATH_OPS
--- /dev/null
+//===- Passes.h - Fixed point math passes -----------------------*- C++ -*-===//
+//
+// Copyright 2019 The MLIR Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// =============================================================================
+//
+// This file defines all of the passes owned by the FxpMathOps dialect.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_FXPMATH_PASSES_H
+#define MLIR_FXPMATH_PASSES_H
+
+namespace mlir {
+class FunctionPassBase;
+
+namespace fxpmath {
+
+/// Creates a pass that lowers uniform-quantized real math ops to integer
+/// arithmetic. This will leave unrecognized real math ops as-is and is
+/// typically followed by a pass that lowers any unrecognized ops to a pure
+/// floating point form.
+FunctionPassBase *createLowerUniformRealMathPass();
+
+} // namespace fxpmath
+} // namespace mlir
+
+#endif // MLIR_FXPMATH_PASSES_H
namespace quant {
-/// Creates a pass that lowers quantization related TensorFlow ops into
-/// the quantization dialect so that express and implied constraints expressed
-/// at the TensorFlow source level can be represented to the quantization
-/// system. This will specially handle any TensorFlow op that is useful for
-/// guiding quantization.
-///
-/// Note that if your intent is to compile a TensorFlow graph for floating
-/// point inference, you should probably not use this pass.
-FunctionPassBase *createLowerTFPass();
+/// Creates a pass that converts quantization simulation operations (i.e.
+/// FakeQuant and those like it) to casts into/out of supported QuantizedTypes.
+FunctionPassBase *createConvertSimulatedQuantPass();
/// Creates a pass that converts constants followed by a qbarrier to a
/// constant whose value is quantized. This is typically one of the last
/// destructive and cannot be undone.
FunctionPassBase *createConvertConstPass();
-/// Creates a pass that lowers uniform-quantized real math ops to integer
-/// arithmetic. This will leave unrecognized real math ops as-is and is
-/// typically followed by a pass that lowers any unrecognized ops to a pure
-/// floating point form.
-FunctionPassBase *createLowerUniformRealMathPass();
-
} // namespace quant
} // namespace mlir
#ifdef OP_BASE
#else
include "mlir/IR/OpBase.td"
+include "mlir/Quantization/QuantPredicates.td"
#endif // OP_BASE
//===----------------------------------------------------------------------===//
-// Quantization type definitions
-//===----------------------------------------------------------------------===//
-
-class quant_TypedPrimitiveOrContainer<Type etype> :
- Type<AnyOf<[etype.predicate,
- TypedTensor<etype>.predicate,
- TypedVector<etype>.predicate]>,
- "primitive/tensor/vector of " # etype.description>;
-
-// An implementation of QuantizedType.
-def quant_QuantizedType :
- Type<CPred<"{0}.isa<QuantizedType>()">, "QuantizedType">;
-
-// A primitive type that can represent a real value. This is either a
-// floating point value or a quantized type.
-def quant_RealPrimitiveType :
- Type<AnyOf<[Float.predicate, quant_QuantizedType.predicate]>,
- "real valued primitive (float or quantized type)">;
-
-// A primitive type that can represent a storage value. This is either an
-// integer or quantized type.
-def quant_StoragePrimitiveType :
- Type<AnyOf<[Integer.predicate, quant_QuantizedType.predicate]>,
- "quantized storage primitive (integer or quantized type)">;
-
-// A primitive or container of RealPrimitiveType.
-def quant_RealValueType :
- quant_TypedPrimitiveOrContainer<quant_RealPrimitiveType>;
-
-// A primitive or container of StoragePrimitiveType.
-def quant_StorageValueType :
- quant_TypedPrimitiveOrContainer<quant_StoragePrimitiveType>;
-
-// Either a real valued or storage primitive or container type.
-def quant_RealOrStorageValueType :
- Type<AnyOf<[quant_RealValueType.predicate,
- quant_StorageValueType.predicate]>>;
-
-// An implementation of UniformQuantizedType.
-def quant_UniformQuantizedType :
- Type<CPred<"{0}.isa<UniformQuantizedType>()">, "UniformQuantizedType">;
-
-// Predicate for detecting a container or primitive of UniformQuantizedType.
-def quant_UniformQuantizedValueType :
- quant_TypedPrimitiveOrContainer<quant_UniformQuantizedType>;
-
-//===----------------------------------------------------------------------===//
-// Attributes
-//===----------------------------------------------------------------------===//
-
-// Real value for an (inclusive) min/max clamp limit.
-def quant_ClampValueAttr : OptionalAttr<F64Attr>;
-
-// Element-wise activation function to apply.
-// Note that RELU activations are not here: they are expressed as clamps.
-def quant_EwUnaryFnAttr :
- StringBasedAttr<CPred<"true">, "element-wise unary function"> {
- let returnType = [{ StringRef }];
- let defaultValue = "IDENTITY";
-}
-
-class quant_ConstEwUnaryFn<string val> : ConstantAttr<quant_EwUnaryFnAttr, val>;
-def quant_EwUnaryFn_Identity: quant_ConstEwUnaryFn<"IDENTITY">;
-def quant_EwUnaryFn_Tanh : quant_ConstEwUnaryFn<"TANH">;
-def quant_EwUnaryFn_Sigmoid : quant_ConstEwUnaryFn<"SIGMOID">;
-def quant_EwUnaryFn_Exp : quant_ConstEwUnaryFn<"EXP">;
-def quant_EwUnaryFn_Log : quant_ConstEwUnaryFn<"LOG">;
-def quant_EwUnaryFn_Neg : quant_ConstEwUnaryFn<"NEG">;
-def quant_EwUnaryFn_Rsqrt : quant_ConstEwUnaryFn<"RSQRT">;
-def quant_EwUnaryFn_Sin : quant_ConstEwUnaryFn<"SIN">;
-def quant_EwUnaryFn_Square : quant_ConstEwUnaryFn<"SQUARE">;
-def quant_EwUnaryFn_Sqrt : quant_ConstEwUnaryFn<"SQRT">;
-def quant_EwUnaryFn_CmpZ : quant_ConstEwUnaryFn<"CMPZ">;
-def quant_EwUnaryFn_CmpNZ : quant_ConstEwUnaryFn<"CMPNZ">;
-def quant_EwUnaryFn_CmpLZ : quant_ConstEwUnaryFn<"CMPLZ">;
-def quant_EwUnaryFn_CmpGZ : quant_ConstEwUnaryFn<"CMPGZ">;
-
-//===----------------------------------------------------------------------===//
// Base classes
//===----------------------------------------------------------------------===//
Results<(outs quant_RealOrStorageValueType)>;
//===----------------------------------------------------------------------===//
-// Integral arithmetic ops used by kernels.
+// Training integration ops
//===----------------------------------------------------------------------===//
-def quant_RoundingDivideByPotIOp :
- quant_Op<"rounding_divide_by_poti", [NoSideEffect, SameValueType]>,
- Arguments<(ins quant_StorageValueType:$x, I32Attr:$exponent)>,
- Results<(outs quant_StorageValueType:$y)> {
- let description = [{
- Computes integer division by a power-of-two, correctly rounded-to-nearest.
- Also known as a rounding arithmetic right shift. See
- gemmlowp::RoundingDivideByPOT for a reference implementation.
- }];
-
- let verifier = [{
- auto verifyExponent = exponent().getSExtValue();
- if (verifyExponent < 0 || verifyExponent > 31) {
- return emitOpError("exponent must be in range [0..31]");
- }
- return success();
- }];
-}
+def quant_ConstFakeQuant : quant_Op<"const_fake_quant",
+ [NoSideEffect]> {
+ let summary =
+ "Simulates the effect of uniform quantization with const range.";
-def quant_SaturatingAddIOp :
- quant_Op<"saturating_addi", [NoSideEffect, SameValueType]>,
- Arguments<(ins quant_StorageValueType:$x,
- quant_StorageValueType:$y,
- I32Attr:$clamp_min,
- I32Attr:$clamp_max)>,
- Results<(outs quant_StorageValueType:$sum)> {
let description = [{
- Computes saturating addition of two operands, saturating to the given min
- and max value. The implementation is responsible for choosing an
- intermediate register size appropriate to carry out the operation without
- overflow. See gemmlowp::SaturatingAdd for a reference implementation.
- }];
+Given a const min, max, num_bits and narrow_range attribute, applies the same
+uniform quantization simulation as is done by the TensorFlow
+fake_quant_with_min_max_args op. See the fakeQuantAttrsToType() utility
+method and the quant-convert-simulated-quantization pass for futher details.
+}];
+
+ let arguments = (ins
+ F32Tensor:$inputs,
+ F32Attr:$min,
+ F32Attr:$max,
+ // The bitwidth of the quantization; between 2 and 16, inclusive.
+ I64Attr:$num_bits,
+ // Quantization range starts from 0 or 1; starts from 1 if true.
+ DefaultValuedAttr<BoolAttr, "false">:$narrow_range
+ );
+
+ let results = (outs
+ F32Tensor:$outputs
+ );
}
-//===----------------------------------------------------------------------===//
-// Real math ops.
-//
-// Math ops on real numbers which may have a representation in quantized
-// arithmetic. It is expected that eligible ops are lowered from a source
-// dialect to this set of ops prior to the process of converting a compuation
-// to a quantized form. It is a non-goal of these ops to preserve enough
-// information to convert back to the higher level, source dialect.
-//
-// These ops support either real/floating point or QuantizedTypes as operands
-// and results. Since not all transformations are supported (globally or
-// sometimes for specific targets), a computation may end up with
-// untransformable RealMathOps, in which case they need to be lowered as is
-// (using floating point math).
-//
-// This op set takes advantage of the fact that it is typically trivial to
-// combine a math function with a compatible bias addition and real-valued
-// clamp (which can be done at a higher accumulation bit depth).
-//
-// In addition, all element-wise unary functions are collapsed into a single
-// quant_RealUnaryEwOp and selected via an enum-like attribute. Especially at
-// low bit depths, this makes matching simpler and allows the construction of
-// generic LUT-based implementations. It also allows specific lowering rules
-// to consolidate runs of chained unary ops and fuse them to preceding math
-// ops, potentially allowing them to operate directly on higher precision
-// intermediates without resorting to lots of custom kernels for common
-// formulas that can suffer from insufficient precision at low bit depths.
-//
-// Comparison operators are modeled as element-wise unary functions (i.e.
-// CMPZ, CMPNZ, CMPLZ, CMPGZ) intended to follow a sub and output a 1bit
-// quantized value. It is expected that lowering rules can fuse them with
-// the preceding sub.
-//===----------------------------------------------------------------------===//
-
-class quant_RealMathOp<string mnemonic, list<OpTrait> traits = [], dag args> :
- quant_Op<mnemonic, traits>,
- Arguments<!con(args, (ins
- quant_ClampValueAttr:$clamp_min, quant_ClampValueAttr:$clamp_max))>;
-
-//===----------------------------------------------------------------------===//
-// Element wise binary real math ops.
-//===----------------------------------------------------------------------===//
-
-class quant_RealBinaryOp<string mnemonic, list<OpTrait> traits = []> :
- quant_RealMathOp<mnemonic, traits,
- (ins quant_RealValueType:$x, quant_RealValueType:$y)>,
- Results<(outs quant_RealValueType:$r)>;
-
-class quant_RealBinaryBiasOp<string mnemonic, list<OpTrait> traits = []> :
- quant_RealMathOp<mnemonic, traits,
- (ins quant_RealValueType:$x, quant_RealValueType:$y,
- quant_RealValueType:$bias)>,
- Results<(outs quant_RealValueType:$r)>;
-
-def quant_RealAddEwOp :
- quant_RealBinaryOp<"real_add_ew", [NoSideEffect]>;
-
-def quant_RealSubEwOp :
- quant_RealBinaryOp<"real_sub_ew", [NoSideEffect]>;
-
-def quant_RealMulEwOp :
- quant_RealBinaryOp<"real_mul_ew", [NoSideEffect]>;
-
-def quant_RealDivEwOp :
- quant_RealBinaryOp<"real_div_ew", [NoSideEffect]>;
-
-//===----------------------------------------------------------------------===//
-// Element wise unary real math op.
-//===----------------------------------------------------------------------===//
-
-def quant_RealUnaryEwOp :
- quant_RealMathOp<"real_unary_ew", [NoSideEffect],
- (ins quant_RealValueType:$x, quant_EwUnaryFnAttr:$fn)>,
- Results<(outs quant_RealValueType:$r)>;
-
-#endif // QUANTIZATION_OPS
+#endif // QUANT_OPS
--- /dev/null
+//===- QuantPredicates.td - Predicates for dialect types ---*- tablegen -*-===//
+//
+// Copyright 2019 The MLIR Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// =============================================================================
+//
+// Predicates for types in the Quantization dialect.
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef QUANTIZATION_PREDICATES_
+#else
+
+//===----------------------------------------------------------------------===//
+// Quantization type definitions
+//===----------------------------------------------------------------------===//
+
+class quant_TypedPrimitiveOrContainer<Type etype> :
+ Type<AnyOf<[etype.predicate,
+ TypedTensor<etype>.predicate,
+ TypedVector<etype>.predicate]>,
+ "primitive/tensor/vector of " # etype.description>;
+
+// An implementation of QuantizedType.
+def quant_QuantizedType :
+ Type<CPred<"{0}.isa<mlir::quant::QuantizedType>()">, "QuantizedType">;
+
+// A primitive type that can represent a real value. This is either a
+// floating point value or a quantized type.
+def quant_RealPrimitiveType :
+ Type<AnyOf<[Float.predicate, quant_QuantizedType.predicate]>,
+ "real valued primitive (float or quantized type)">;
+
+// A primitive type that can represent a storage value. This is either an
+// integer or quantized type.
+def quant_StoragePrimitiveType :
+ Type<AnyOf<[Integer.predicate, quant_QuantizedType.predicate]>,
+ "quantized storage primitive (integer or quantized type)">;
+
+// A primitive or container of RealPrimitiveType.
+def quant_RealValueType :
+ quant_TypedPrimitiveOrContainer<quant_RealPrimitiveType>;
+
+// A primitive or container of StoragePrimitiveType.
+def quant_StorageValueType :
+ quant_TypedPrimitiveOrContainer<quant_StoragePrimitiveType>;
+
+// Either a real valued or storage primitive or container type.
+def quant_RealOrStorageValueType :
+ Type<AnyOf<[quant_RealValueType.predicate,
+ quant_StorageValueType.predicate]>>;
+
+// An implementation of UniformQuantizedType.
+def quant_UniformQuantizedType :
+ Type<CPred<"{0}.isa<UniformQuantizedType>()">, "UniformQuantizedType">;
+
+// Predicate for detecting a container or primitive of UniformQuantizedType.
+def quant_UniformQuantizedValueType :
+ quant_TypedPrimitiveOrContainer<quant_UniformQuantizedType>;
+
+#endif // QUANTIZATION_PREDICATES_
\ No newline at end of file
--- /dev/null
+//===- DialectRegistration.cpp - Register FxpMathOps dialect --------------===//
+//
+// Copyright 2019 The MLIR Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// =============================================================================
+
+#include "mlir/FxpMathOps/FxpMathOps.h"
+
+using namespace mlir;
+using namespace mlir::fxpmath;
+
+// Static initialization for the fxpmath ops dialect registration.
+static mlir::DialectRegistration<FxpMathOpsDialect> FxpMathOps;
--- /dev/null
+//===- FxpMathOps.cpp - Op implementation for FxpMathOps ------------------===//
+//
+// Copyright 2019 The MLIR Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// =============================================================================
+
+#include "mlir/FxpMathOps/FxpMathOps.h"
+#include "mlir/IR/MLIRContext.h"
+#include "mlir/IR/StandardTypes.h"
+#include "mlir/Quantization/QuantOps.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/MathExtras.h"
+
+using namespace mlir;
+using namespace mlir::fxpmath;
+
+#define GET_OP_CLASSES
+#include "mlir/FxpMathOps/FxpMathOps.cpp.inc"
+
+FxpMathOpsDialect::FxpMathOpsDialect(MLIRContext *context)
+ : Dialect(/*name=*/"fxpmath", context) {
+ addOperations<
+#define GET_OP_LIST
+#include "mlir/FxpMathOps/FxpMathOps.cpp.inc"
+ >();
+}
// limitations under the License.
// =============================================================================
+#include "mlir/FxpMathOps/FxpMathOps.h"
+#include "mlir/FxpMathOps/Passes.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/Pass/Pass.h"
-#include "mlir/Quantization/Passes.h"
-#include "mlir/Quantization/QuantOps.h"
#include "mlir/Quantization/UniformSupport.h"
#include <functional>
using namespace mlir;
+using namespace mlir::fxpmath;
using namespace mlir::quant;
namespace {
if (rhsRightShift != 0) {
rhsStorageValue =
rewriter
- .create<RoundingDivideByPotIOp>(
+ .create<RoundingDivideByPotFxpOp>(
mathOp->getLoc(), rhsStorageValue,
IntegerAttr::get(IntegerType::get(32, rewriter.getContext()),
rhsRightShift))
}
// Add.
- Value *sumValue = rewriter.create<SaturatingAddIOp>(
+ Value *sumValue = rewriter.create<SaturatingAddFxpOp>(
mathOp->getLoc(), lhsStorageValue, rhsStorageValue, clampMinMax.first,
clampMinMax.second);
}
static PassRegistration<LowerUniformRealMathPass>
- pass("quant-lower-uniform-real-math",
+ pass("fxpmath-lower-uniform-real-math",
"Lowers uniform-quantized real math ops to integer arithmetic.");
-//===- LowerTF.cpp - Passes for lowering from TensorFlow ------------------===//
+//===- ConvertSimQuant.cpp - Converts simulated quant ops------------------===//
//
// Copyright 2019 The MLIR Authors.
//
#include "mlir/Quantization/Passes.h"
#include "mlir/Quantization/QuantOps.h"
#include "mlir/Quantization/UniformSupport.h"
-#include "mlir/TensorFlow/TFOps.h"
using namespace mlir;
using namespace mlir::quant;
namespace {
-class LowerTFPass : public FunctionPass<LowerTFPass> {
+class ConvertSimulatedQuantPass
+ : public FunctionPass<ConvertSimulatedQuantPass> {
public:
void runOnFunction() override;
};
} // end anonymous namespace
-/// Rewrites TensorFlow FakeQuantWithMinMaxArgs into a qbarrier/dbarrier pair.
-class FakeQuantWithMinMaxArgsRewrite : public RewritePattern {
+/// Rewrites ConstFakeQuant into a qbarrier/dbarrier pair.
+class ConstFakeQuantRewrite : public RewritePattern {
public:
bool *hadFailure;
- FakeQuantWithMinMaxArgsRewrite(MLIRContext *context, bool *hadFailure)
- : RewritePattern(TF::FakeQuantWithMinMaxArgsOp::getOperationName(), 1,
- context),
+ ConstFakeQuantRewrite(MLIRContext *context, bool *hadFailure)
+ : RewritePattern(ConstFakeQuant::getOperationName(), 1, context),
hadFailure(hadFailure) {}
- PatternMatchResult match(Operation *op) const override {
- return matchSuccess();
- }
-
- void rewrite(Operation *op, PatternRewriter &rewriter) const override {
+ PatternMatchResult matchAndRewrite(Operation *op,
+ PatternRewriter &rewriter) const override {
// TODO: If this pattern comes up more frequently, consider adding core
// support for failable rewrites.
if (failableRewrite(op, rewriter)) {
*hadFailure = true;
}
+
+ return matchSuccess();
}
bool failableRewrite(Operation *op, PatternRewriter &rewriter) const {
- auto fqOp = op->template cast<TF::FakeQuantWithMinMaxArgsOp>();
+ auto fqOp = op->cast<ConstFakeQuant>();
auto converter =
ExpressedToUniformQuantizedConverter::forInputType(fqOp.getType());
}
};
-void LowerTFPass::runOnFunction() {
+void ConvertSimulatedQuantPass::runOnFunction() {
bool hadFailure = false;
OwningRewritePatternList patterns;
auto &func = getFunction();
auto *context = &getContext();
patterns.push_back(
- llvm::make_unique<FakeQuantWithMinMaxArgsRewrite>(context, &hadFailure));
+ llvm::make_unique<ConstFakeQuantRewrite>(context, &hadFailure));
applyPatternsGreedily(func, std::move(patterns));
if (hadFailure)
signalPassFailure();
}
-FunctionPassBase *createLowerTFPass() { return new LowerTFPass(); }
+FunctionPassBase *createConvertSimulatedQuantPass() {
+ return new ConvertSimulatedQuantPass();
+}
-static PassRegistration<LowerTFPass>
- pass("quant-lower-tf",
- "Lowers TensorFlow constraint ops to the quantization dialect");
+static PassRegistration<ConvertSimulatedQuantPass>
+ pass("quant-convert-simulated-quantization",
+ "Converts training-time simulated quantization ops to corresponding "
+ "quantize/dequantize casts.");
-// RUN: mlir-opt %s -split-input-file -quant-lower-uniform-real-math | FileCheck %s --dump-input=fail
+// RUN: mlir-opt %s -split-input-file -fxpmath-lower-uniform-real-math | FileCheck %s --dump-input=fail
// -----
// Verify lowering when operands and result have the same fixedpoint pot scale.
// CHECK-LABEL: real_addew_fixedpoint_same_scale
// CHECK: %0 = "quant.scast"(%arg0) : (tensor<4x!quant<"uniform[i8:f32]{6.250000e-02}">>) -> tensor<4xi8>
// CHECK-NEXT: %1 = "quant.scast"(%arg1) : (tensor<4x!quant<"uniform[i8:f32]{6.250000e-02}">>) -> tensor<4xi8>
-// CHECK-NEXT: %2 = "quant.saturating_addi"(%0, %1) {clamp_max: 127 : i32, clamp_min: -128 : i32} : (tensor<4xi8>, tensor<4xi8>) -> tensor<4xi8>
+// CHECK-NEXT: %2 = "fxpmath.saturating_addi"(%0, %1) {clamp_max: 127 : i32, clamp_min: -128 : i32} : (tensor<4xi8>, tensor<4xi8>) -> tensor<4xi8>
// CHECK-NEXT: %3 = "quant.scast"(%2) : (tensor<4xi8>) -> tensor<4x!quant<"uniform[i8:f32]{6.250000e-02}">>
// CHECK-NEXT: return %3 : tensor<4x!quant<"uniform[i8:f32]{6.250000e-02}">>
!type_lhs = type tensor<4x!quant<"uniform[i8:f32]{6.25e-2}">>
!type_rhs = type tensor<4x!quant<"uniform[i8:f32]{6.25e-2}">>
!type_result = type tensor<4x!quant<"uniform[i8:f32]{6.25e-2}">>
func @real_addew_fixedpoint_same_scale(%arg0 : !type_lhs, %arg1: !type_rhs) -> !type_result {
- %0 = "quant.real_add_ew"(%arg0, %arg1) : (!type_lhs, !type_rhs) -> (!type_result)
+ %0 = "fxpmath.real_add_ew"(%arg0, %arg1) : (!type_lhs, !type_rhs) -> (!type_result)
return %0 : !type_result
}
// CHECK-LABEL: real_addew_fixedpoint_rhs_shift
// CHECK: %0 = "quant.scast"(%arg0) : (tensor<4x!quant<"uniform[i8:f32]{6.250000e-02}">>) -> tensor<4xi8>
// CHECK-NEXT: %1 = "quant.scast"(%arg1) : (tensor<4x!quant<"uniform[i8:f32]{7.812500e-03}">>) -> tensor<4xi8>
-// CHECK-NEXT: %2 = "quant.rounding_divide_by_poti"(%1) {exponent: 3 : i32} : (tensor<4xi8>) -> tensor<4xi8>
-// CHECK-NEXT: %3 = "quant.saturating_addi"(%0, %2) {clamp_max: 127 : i32, clamp_min: -128 : i32} : (tensor<4xi8>, tensor<4xi8>) -> tensor<4xi8>
+// CHECK-NEXT: %2 = "fxpmath.rounding_divide_by_poti"(%1) {exponent: 3 : i32} : (tensor<4xi8>) -> tensor<4xi8>
+// CHECK-NEXT: %3 = "fxpmath.saturating_addi"(%0, %2) {clamp_max: 127 : i32, clamp_min: -128 : i32} : (tensor<4xi8>, tensor<4xi8>) -> tensor<4xi8>
// CHECK-NEXT: %4 = "quant.scast"(%3) : (tensor<4xi8>) -> tensor<4x!quant<"uniform[i8:f32]{6.250000e-02}">>
// CHECK-NEXT: return %4 : tensor<4x!quant<"uniform[i8:f32]{6.250000e-02}">>
!type_lhs = type tensor<4x!quant<"uniform[i8:f32]{6.25e-2}">>
!type_rhs = type tensor<4x!quant<"uniform[i8:f32]{7.8125e-03}">>
!type_result = type tensor<4x!quant<"uniform[i8:f32]{6.25e-2}">>
func @real_addew_fixedpoint_rhs_shift(%arg0 : !type_lhs, %arg1: !type_rhs) -> !type_result {
- %0 = "quant.real_add_ew"(%arg0, %arg1) : (!type_lhs, !type_rhs) -> (!type_result)
+ %0 = "fxpmath.real_add_ew"(%arg0, %arg1) : (!type_lhs, !type_rhs) -> (!type_result)
return %0 : !type_result
}
// CHECK-LABEL: real_addew_fixedpoint_lhs_shift
// CHECK: %0 = "quant.scast"(%arg1) : (tensor<4x!quant<"uniform[i8:f32]{6.250000e-02}">>) -> tensor<4xi8>
// CHECK-NEXT: %1 = "quant.scast"(%arg0) : (tensor<4x!quant<"uniform[i8:f32]{7.812500e-03}">>) -> tensor<4xi8>
-// CHECK-NEXT: %2 = "quant.rounding_divide_by_poti"(%1) {exponent: 3 : i32} : (tensor<4xi8>) -> tensor<4xi8>
-// CHECK-NEXT: %3 = "quant.saturating_addi"(%0, %2) {clamp_max: 127 : i32, clamp_min: -128 : i32} : (tensor<4xi8>, tensor<4xi8>) -> tensor<4xi8>
+// CHECK-NEXT: %2 = "fxpmath.rounding_divide_by_poti"(%1) {exponent: 3 : i32} : (tensor<4xi8>) -> tensor<4xi8>
+// CHECK-NEXT: %3 = "fxpmath.saturating_addi"(%0, %2) {clamp_max: 127 : i32, clamp_min: -128 : i32} : (tensor<4xi8>, tensor<4xi8>) -> tensor<4xi8>
// CHECK-NEXT: %4 = "quant.scast"(%3) : (tensor<4xi8>) -> tensor<4x!quant<"uniform[i8:f32]{6.250000e-02}">>
// CHECK-NEXT: return %4 : tensor<4x!quant<"uniform[i8:f32]{6.250000e-02}">>
!type_lhs = type tensor<4x!quant<"uniform[i8:f32]{7.8125e-03}">>
!type_rhs = type tensor<4x!quant<"uniform[i8:f32]{6.25e-2}">>
!type_result = type tensor<4x!quant<"uniform[i8:f32]{6.25e-2}">>
func @real_addew_fixedpoint_lhs_shift(%arg0 : !type_lhs, %arg1: !type_rhs) -> !type_result {
- %0 = "quant.real_add_ew"(%arg0, %arg1) : (!type_lhs, !type_rhs) -> (!type_result)
+ %0 = "fxpmath.real_add_ew"(%arg0, %arg1) : (!type_lhs, !type_rhs) -> (!type_result)
return %0 : !type_result
}
// CHECK-LABEL: real_addew_fixedpoint_clamp
// CHECK: %0 = "quant.scast"(%arg1) : (tensor<4x!quant<"uniform[i8:f32]{6.250000e-02}">>) -> tensor<4xi8>
// CHECK-NEXT: %1 = "quant.scast"(%arg0) : (tensor<4x!quant<"uniform[i8:f32]{7.812500e-03}">>) -> tensor<4xi8>
-// CHECK-NEXT: %2 = "quant.rounding_divide_by_poti"(%1) {exponent: 3 : i32} : (tensor<4xi8>) -> tensor<4xi8>
-// CHECK-NEXT: %3 = "quant.saturating_addi"(%0, %2) {clamp_max: 64 : i32, clamp_min: -64 : i32} : (tensor<4xi8>, tensor<4xi8>) -> tensor<4xi8>
+// CHECK-NEXT: %2 = "fxpmath.rounding_divide_by_poti"(%1) {exponent: 3 : i32} : (tensor<4xi8>) -> tensor<4xi8>
+// CHECK-NEXT: %3 = "fxpmath.saturating_addi"(%0, %2) {clamp_max: 64 : i32, clamp_min: -64 : i32} : (tensor<4xi8>, tensor<4xi8>) -> tensor<4xi8>
// CHECK-NEXT: %4 = "quant.scast"(%3) : (tensor<4xi8>) -> tensor<4x!quant<"uniform[i8:f32]{6.250000e-02}">>
// CHECK-NEXT: return %4 : tensor<4x!quant<"uniform[i8:f32]{6.250000e-02}">>
!type_lhs = type tensor<4x!quant<"uniform[i8:f32]{7.8125e-03}">>
!type_rhs = type tensor<4x!quant<"uniform[i8:f32]{6.25e-2}">>
!type_result = type tensor<4x!quant<"uniform[i8:f32]{6.25e-2}">>
func @real_addew_fixedpoint_clamp(%arg0 : !type_lhs, %arg1: !type_rhs) -> !type_result {
- %0 = "quant.real_add_ew"(%arg0, %arg1) { clamp_min:-4.0, clamp_max:4.0 }
+ %0 = "fxpmath.real_add_ew"(%arg0, %arg1) { clamp_min:-4.0, clamp_max:4.0 }
: (!type_lhs, !type_rhs) -> (!type_result)
return %0 : !type_result
}
!type_rhs = type tensor<4x!quant<"uniform[i8:f32]{6.25e-2}">>
!type_result = type tensor<4x!quant<"uniform[i8:f32]{6.25e-2}">>
func @real_addew_unquantized_lhs(%arg0 : !type_lhs, %arg1: !type_rhs) -> !type_result {
- // CHECK: %0 = "quant.real_add_ew"(%arg0, %arg1)
- %0 = "quant.real_add_ew"(%arg0, %arg1) : (!type_lhs, !type_rhs) -> (!type_result)
+ // CHECK: %0 = "fxpmath.real_add_ew"(%arg0, %arg1)
+ %0 = "fxpmath.real_add_ew"(%arg0, %arg1) : (!type_lhs, !type_rhs) -> (!type_result)
return %0 : !type_result
}
!type_rhs = type tensor<4xf32>
!type_result = type tensor<4x!quant<"uniform[i8:f32]{6.25e-2}">>
func @real_addew_unquantized_rhs(%arg0 : !type_lhs, %arg1: !type_rhs) -> !type_result {
- // CHECK: %0 = "quant.real_add_ew"(%arg0, %arg1)
- %0 = "quant.real_add_ew"(%arg0, %arg1) : (!type_lhs, !type_rhs) -> (!type_result)
+ // CHECK: %0 = "fxpmath.real_add_ew"(%arg0, %arg1)
+ %0 = "fxpmath.real_add_ew"(%arg0, %arg1) : (!type_lhs, !type_rhs) -> (!type_result)
return %0 : !type_result
}
!type_rhs = type tensor<4x!quant<"uniform[i8:f32]{6.25e-2}">>
!type_result = type tensor<4xf32>
func @real_addew_unquantized_result(%arg0 : !type_lhs, %arg1: !type_rhs) -> !type_result {
- // CHECK: %0 = "quant.real_add_ew"(%arg0, %arg1)
- %0 = "quant.real_add_ew"(%arg0, %arg1) : (!type_lhs, !type_rhs) -> (!type_result)
+ // CHECK: %0 = "fxpmath.real_add_ew"(%arg0, %arg1)
+ %0 = "fxpmath.real_add_ew"(%arg0, %arg1) : (!type_lhs, !type_rhs) -> (!type_result)
return %0 : !type_result
}
-// RUN: mlir-opt %s -split-input-file -verify -quant-lower-tf
+// RUN: mlir-opt %s -split-input-file -verify -quant-convert-simulated-quantization
// -----
-// TODO(laurenzo): move this test to the TensorFlow/tf-ops-invalid.mlir
// Verify that a mismatched range errors.
func @fakeQuantArgs(tensor<8x4x3xf32>) -> tensor<8x4x3xf32> {
^bb0(%arg0: tensor<8x4x3xf32>):
- // expected-error@+1 {{op range failed to straddle zero: [1.100000,1.500000]}}
- %0 = "tf.FakeQuantWithMinMaxArgs"(%arg0) {
+ // expected-error@+1 {{FakeQuant range must straddle zero: [1.100000,1.500000]}}
+ %0 = "quant.const_fake_quant"(%arg0) {
min: 1.1, max: 1.5, num_bits: 8
} : (tensor<8x4x3xf32>) -> tensor<8x4x3xf32>
return %0 : tensor<8x4x3xf32>
// Verify that a valid range errors.
func @fakeQuantArgs(tensor<8x4x3xf32>) -> tensor<8x4x3xf32> {
^bb0(%arg0: tensor<8x4x3xf32>):
- // expected-error@+1 {{op range is invalid: [1.100000,1.000000}}
- %0 = "tf.FakeQuantWithMinMaxArgs"(%arg0) {
+ // expected-error@+1 {{FakeQuant range must straddle zero: [1.100000,1.000000}}
+ %0 = "quant.const_fake_quant"(%arg0) {
min: 1.1, max: 1.0, num_bits: 8
} : (tensor<8x4x3xf32>) -> tensor<8x4x3xf32>
return %0 : tensor<8x4x3xf32>
}
// -----
-// TODO(laurenzo): move this test to the TensorFlow/tf-ops-invalid.mlir
// Unsupported quantizable type (i1 is currently not a supported element type).
func @fakeQuantArgs(tensor<8x4x3xi1>) -> tensor<8x4x3xi1> {
^bb0(%arg0: tensor<8x4x3xi1>):
// expected-error@+1 {{op operand #0 must be tensor of 32-bit float values}}
- %0 = "tf.FakeQuantWithMinMaxArgs"(%arg0) {
+ %0 = "quant.const_fake_quant"(%arg0) {
min: 1.1, max: 1.0, num_bits: 8
} : (tensor<8x4x3xi1>) -> tensor<8x4x3xi1>
return %0 : tensor<8x4x3xi1>
-// RUN: mlir-opt %s -split-input-file -quant-lower-tf | FileCheck %s --dump-input=fail
+// RUN: mlir-opt %s -split-input-file -quant-convert-simulated-quantization | FileCheck %s --dump-input=fail
// -----
// Verifies a quint8 asymmetric 0..1 range.
// CHECK-SAME: -> tensor<8x4x3x!quant<"uniform[u8:f32]{0.0039215686274509803}">>
// CHECK-NEXT: %1 = "quant.dbarrier"(%0) : (tensor<8x4x3x!quant<"uniform[u8:f32]{0.0039215686274509803}">>)
// CHECK-SAME: -> tensor<8x4x3xf32>
- %0 = "tf.FakeQuantWithMinMaxArgs"(%arg0) {
+ %0 = "quant.const_fake_quant"(%arg0) {
min: 0.0, max: 1.0, num_bits: 8
} : (tensor<8x4x3xf32>) -> tensor<8x4x3xf32>
return %0 : tensor<8x4x3xf32>
// CHECK-SAME: -> tensor<8x4x3x!quant<"uniform[u8(1:255):f32]{0.003937007874015748:1}">>
// CHECK-NEXT: %1 = "quant.dbarrier"(%0) : (tensor<8x4x3x!quant<"uniform[u8(1:255):f32]{0.003937007874015748:1}">>)
// CHECK-SAME: -> tensor<8x4x3xf32>
- %0 = "tf.FakeQuantWithMinMaxArgs"(%arg0) {
+ %0 = "quant.const_fake_quant"(%arg0) {
min: 0.0, max: 1.0, num_bits: 8, narrow_range: true
} : (tensor<8x4x3xf32>) -> tensor<8x4x3xf32>
return %0 : tensor<8x4x3xf32>
// CHECK-SAME: -> tensor<8x4x3x!quant<"uniform[u8:f32]{7.812500e-03:128}">>
// CHECK-NEXT: %1 = "quant.dbarrier"(%0) : (tensor<8x4x3x!quant<"uniform[u8:f32]{7.812500e-03:128}">>)
// CHECK-SAME: -> tensor<8x4x3xf32>
- %0 = "tf.FakeQuantWithMinMaxArgs"(%arg0) {
+ %0 = "quant.const_fake_quant"(%arg0) {
min: -1.0, max: 0.9921875, num_bits: 8, narrow_range: false
} : (tensor<8x4x3xf32>) -> tensor<8x4x3xf32>
return %0 : tensor<8x4x3xf32>
// CHECK-SAME: -> tensor<8x4x3x!quant<"uniform[i16:f32]{3.05175781185626E-5}">>
// CHECK-NEXT: %1 = "quant.dbarrier"(%0) : (tensor<8x4x3x!quant<"uniform[i16:f32]{3.05175781185626E-5}">>)
// CHECK-SAME: -> tensor<8x4x3xf32>
- %0 = "tf.FakeQuantWithMinMaxArgs"(%arg0) {
+ %0 = "quant.const_fake_quant"(%arg0) {
min: -1.0, max: 0.999969482, num_bits: 16
} : (tensor<8x4x3xf32>) -> tensor<8x4x3xf32>
return %0 : tensor<8x4x3xf32>
// CHECK-SAME: -> tensor<!quant<"uniform[u8:f32]{0.0039215686274509803}">>
// CHECK-NEXT: %1 = "quant.dbarrier"(%0) : (tensor<!quant<"uniform[u8:f32]{0.0039215686274509803}">>)
// CHECK-SAME: -> tensor<f32>
- %0 = "tf.FakeQuantWithMinMaxArgs"(%arg0) {
+ %0 = "quant.const_fake_quant"(%arg0) {
min: 0.0, max: 1.0, num_bits: 8
} : (tensor<f32>) -> tensor<f32>
return %0 : tensor<f32>