diff --git a/mlir/docs/LangRef.md b/mlir/docs/LangRef.md --- a/mlir/docs/LangRef.md +++ b/mlir/docs/LangRef.md @@ -745,11 +745,16 @@ ``` // Sized integers like i1, i4, i8, i16, i32. -integer-type ::= `i` [1-9][0-9]* +signed-integer-type ::= `si` [1-9][0-9]* +unsigned-integer-type ::= `ui` [1-9][0-9]* +signless-integer-type ::= `i` [1-9][0-9]* +integer-type ::= signed-integer-type | + unsigned-integer-type | + signless-integer-type ``` -MLIR supports arbitrary precision integer types. Integer types are signless, but -have a designated width. +MLIR supports arbitrary precision integer types. Integer types have a designated +width and may have signedness semantics. **Rationale:** low precision integers (like `i2`, `i4` etc) are useful for low-precision inference chips, and arbitrary precision integers are useful for diff --git a/mlir/docs/Rationale.md b/mlir/docs/Rationale.md --- a/mlir/docs/Rationale.md +++ b/mlir/docs/Rationale.md @@ -244,13 +244,22 @@ The bit width is not defined for dialect-specific types at MLIR level. Dialects are free to define their own quantities for type sizes. -### Signless types +### Integer signedness semantics Integers in the builtin MLIR type system have a bitwidth (note that the `index` -type has a symbolic width equal to the machine word size), but they do not have -an intrinsic sign. This means that the "standard ops" operation set has things -like `addi` and `muli` which do two's complement arithmetic, but some other -operations get a sign, e.g. `divis` vs `diviu`. +type has a symbolic width equal to the machine word size), and they *may* +additionally have signedness semantics. The purpose is to satisfy the needs of +different dialects, which can model different levels of abstractions. Certain +abstraction, especially closer to source language, might want to differentiate +signedness with integer types; while others, especially closer to machine +instruction, might want signless integers. Instead of forcing each abstraction +to adopt the same integer modelling or develop its own one in house, Integer +types provides this as an option to help code reuse and consistency. + +For the standard dialect, the choice is to have signless integer types. An +integer value does not have an intrinsic sign, and it's up to the specific op +for interpretation. For example, ops like `addi` and `muli` do two's complement +arithmetic, but some other operations get a sign, e.g. `divis` vs `diviu`. LLVM uses the [same design](http://llvm.org/docs/LangRef.html#integer-type), which was introduced in a revamp rolled out diff --git a/mlir/include/mlir/Dialect/FxpMathOps/FxpMathOps.td b/mlir/include/mlir/Dialect/FxpMathOps/FxpMathOps.td --- a/mlir/include/mlir/Dialect/FxpMathOps/FxpMathOps.td +++ b/mlir/include/mlir/Dialect/FxpMathOps/FxpMathOps.td @@ -91,10 +91,10 @@ Element-wise equivalent to: r = std::min(clamp_max, std::max(e, clamp_min)) }]; - let arguments = (ins IntegerLike:$operand, + let arguments = (ins SignlessIntegerLike:$operand, APIntAttr:$clamp_min, APIntAttr:$clamp_max); - let results = (outs IntegerLike); + let results = (outs SignlessIntegerLike); } def fxpmath_ConvertISOp : @@ -106,8 +106,8 @@ Similar to an element-wise static_cast in C++, from a one signed integer element type to another. }]; - let arguments = (ins IntegerLike:$operand); - let results = (outs IntegerLike); + let arguments = (ins SignlessIntegerLike:$operand); + let results = (outs SignlessIntegerLike); } def fxpmath_ConvertISToFOp : @@ -120,7 +120,7 @@ element type to a floating point element type, rounding to the nearest floating point value. }]; - let arguments = (ins IntegerLike:$operand); + let arguments = (ins SignlessIntegerLike:$operand); let results = (outs FloatLike); } @@ -134,8 +134,8 @@ See gemmlowp::SaturatingRoundingDoublingHighMul for a reference implementation. }]; - let arguments = (ins IntegerLike:$a, APIntAttr:$b); - let results = (outs IntegerLike); + let arguments = (ins SignlessIntegerLike:$a, APIntAttr:$b); + let results = (outs SignlessIntegerLike); } def fxpmath_RoundingDivideByPotISOp : @@ -148,8 +148,8 @@ Also known as a rounding arithmetic right shift. See gemmlowp::RoundingDivideByPOT for a reference implementation. }]; - let arguments = (ins IntegerLike:$operand, APIntAttr:$exponent); - let results = (outs IntegerLike:$res); + let arguments = (ins SignlessIntegerLike:$operand, APIntAttr:$exponent); + let results = (outs SignlessIntegerLike:$res); let verifier = [{ auto verifyExponent = exponent().getSExtValue(); if (verifyExponent < 0 || verifyExponent > 31) { diff --git a/mlir/include/mlir/Dialect/GPU/GPUOps.td b/mlir/include/mlir/Dialect/GPU/GPUOps.td --- a/mlir/include/mlir/Dialect/GPU/GPUOps.td +++ b/mlir/include/mlir/Dialect/GPU/GPUOps.td @@ -19,7 +19,7 @@ // Type constraint accepting standard integers, indices and wrapped LLVM integer // types. def IntLikeOrLLVMInt : TypeConstraint< - Or<[AnyInteger.predicate, Index.predicate, LLVMInt.predicate]>, + Or<[AnySignlessInteger.predicate, Index.predicate, LLVMInt.predicate]>, "integer, index or LLVM dialect equivalent">; //===----------------------------------------------------------------------===// diff --git a/mlir/include/mlir/Dialect/Linalg/IR/LinalgStructuredOps.td b/mlir/include/mlir/Dialect/Linalg/IR/LinalgStructuredOps.td --- a/mlir/include/mlir/Dialect/Linalg/IR/LinalgStructuredOps.td +++ b/mlir/include/mlir/Dialect/Linalg/IR/LinalgStructuredOps.td @@ -313,7 +313,7 @@ def FillOp : LinalgStructured_Op<"fill", [NInputs<0>, NOutputs<1>]> { let arguments = (ins AnyStridedMemRef:$output, - AnyTypeOf<[AnyFloat, AnyInteger, AnyVector]>:$value); + AnyTypeOf<[AnyFloat, AnySignlessInteger, AnyVector]>:$value); let extraClassDeclaration = libraryCallName # [{ // Defined in C++ for now. // TODO(ntv): auto-generate. diff --git a/mlir/include/mlir/Dialect/QuantOps/QuantPredicates.td b/mlir/include/mlir/Dialect/QuantOps/QuantPredicates.td --- a/mlir/include/mlir/Dialect/QuantOps/QuantPredicates.td +++ b/mlir/include/mlir/Dialect/QuantOps/QuantPredicates.td @@ -36,7 +36,7 @@ // A primitive type that can represent a storage value. This is either an // integer or quantized type. def quant_StoragePrimitiveType : - Type, + Type, "quantized storage primitive (integer or quantized type)">; // A primitive or container of RealPrimitiveType. diff --git a/mlir/include/mlir/Dialect/QuantOps/UniformSupport.h b/mlir/include/mlir/Dialect/QuantOps/UniformSupport.h --- a/mlir/include/mlir/Dialect/QuantOps/UniformSupport.h +++ b/mlir/include/mlir/Dialect/QuantOps/UniformSupport.h @@ -66,7 +66,7 @@ static_cast(uniformType.getStorageTypeMax()), uniformType.getStorageTypeIntegralWidth(), uniformType.isSigned()) { assert(uniformType.getExpressedType().isa()); - assert(uniformType.getStorageType().isa()); + assert(uniformType.getStorageType().isSignlessInteger()); } UniformQuantizedValueConverter(double scale, double zeroPoint, @@ -182,7 +182,7 @@ isSigned(uniformType.isSigned()), quantizationDim(uniformType.getQuantizedDimension()) { assert(uniformType.getExpressedType().isa()); - assert(uniformType.getStorageType().isa()); + assert(uniformType.getStorageType().isSignlessInteger()); assert(scales.size() == zeroPoints.size()); } diff --git a/mlir/include/mlir/Dialect/StandardOps/Ops.td b/mlir/include/mlir/Dialect/StandardOps/Ops.td --- a/mlir/include/mlir/Dialect/StandardOps/Ops.td +++ b/mlir/include/mlir/Dialect/StandardOps/Ops.td @@ -107,7 +107,7 @@ // i %0, %1 : i32 class IntArithmeticOp traits = []> : ArithmeticOp, - Arguments<(ins IntegerLike:$lhs, IntegerLike:$rhs)>; + Arguments<(ins SignlessIntegerLike:$lhs, SignlessIntegerLike:$rhs)>; // Base class for standard arithmetic binary operations on floats, vectors and // tensors thereof. This operation has two operands and returns one result, @@ -466,8 +466,8 @@ let arguments = (ins CmpIPredicateAttr:$predicate, - IntegerLike:$lhs, - IntegerLike:$rhs + SignlessIntegerLike:$lhs, + SignlessIntegerLike:$rhs ); let results = (outs BoolLike:$result); @@ -1070,9 +1070,10 @@ %3 = select %2, %0, %1 : i32 }]; - let arguments = (ins BoolLike:$condition, IntegerOrFloatLike:$true_value, - IntegerOrFloatLike:$false_value); - let results = (outs IntegerOrFloatLike:$result); + let arguments = (ins BoolLike:$condition, + SignlessIntegerOrFloatLike:$true_value, + SignlessIntegerOrFloatLike:$false_value); + let results = (outs SignlessIntegerOrFloatLike:$result); let verifier = ?; let builders = [OpBuilder< @@ -1109,8 +1110,8 @@ %5 = sexti %0 : vector<2 x i32> to vector<2 x i64> }]; - let arguments = (ins IntegerLike:$value); - let results = (outs IntegerLike); + let arguments = (ins SignlessIntegerLike:$value); + let results = (outs SignlessIntegerLike); let builders = [OpBuilder< "Builder *builder, OperationState &result, Value value, Type destType", [{ @@ -1211,7 +1212,7 @@ }]; - let arguments = (ins AnyTypeOf<[AnyInteger, AnyFloat], + let arguments = (ins AnyTypeOf<[AnySignlessInteger, AnyFloat], "integer or float type">:$input); let results = (outs AnyTypeOf<[AnyVector, AnyStaticShapeTensor]>:$aggregate); @@ -1561,8 +1562,8 @@ %5 = trunci %0 : vector<2 x i32> to vector<2 x i16> }]; - let arguments = (ins IntegerLike:$value); - let results = (outs IntegerLike); + let arguments = (ins SignlessIntegerLike:$value); + let results = (outs SignlessIntegerLike); let builders = [OpBuilder< "Builder *builder, OperationState &result, Value value, Type destType", [{ @@ -1661,8 +1662,8 @@ %5 = zexti %0 : vector<2 x i32> to vector<2 x i64> }]; - let arguments = (ins IntegerLike:$value); - let results = (outs IntegerLike); + let arguments = (ins SignlessIntegerLike:$value); + let results = (outs SignlessIntegerLike); let builders = [OpBuilder< "Builder *builder, OperationState &result, Value value, Type destType", [{ diff --git a/mlir/include/mlir/Dialect/VectorOps/VectorOps.td b/mlir/include/mlir/Dialect/VectorOps/VectorOps.td --- a/mlir/include/mlir/Dialect/VectorOps/VectorOps.td +++ b/mlir/include/mlir/Dialect/VectorOps/VectorOps.td @@ -342,7 +342,7 @@ TypesMatchWith<"result type matches element type of vector operand", "vector", "result", "$_self.cast().getElementType()">]>, - Arguments<(ins AnyVector:$vector, AnyInteger:$position)>, + Arguments<(ins AnyVector:$vector, AnySignlessInteger:$position)>, Results<(outs AnyType:$result)> { let summary = "extractelement operation"; let description = [{ @@ -487,7 +487,8 @@ "result", "source", "$_self.cast().getElementType()">, AllTypesMatch<["dest", "result"]>]>, - Arguments<(ins AnyType:$source, AnyVector:$dest, AnyInteger:$position)>, + Arguments<(ins AnyType:$source, AnyVector:$dest, + AnySignlessInteger:$position)>, Results<(outs AnyVector:$result)> { let summary = "insertelement operation"; let description = [{ diff --git a/mlir/include/mlir/IR/Attributes.h b/mlir/include/mlir/IR/Attributes.h --- a/mlir/include/mlir/IR/Attributes.h +++ b/mlir/include/mlir/IR/Attributes.h @@ -351,8 +351,16 @@ static IntegerAttr get(Type type, const APInt &value); APInt getValue() const; + /// Return the integer value as a 64-bit int. The attribute must be a signless + /// integer. // TODO(jpienaar): Change callers to use getValue instead. int64_t getInt() const; + /// Return the integer value as a signed 64-bit int. The attribute must be + /// a signed integer. + int64_t getSInt() const; + /// Return the integer value as a unsigned 64-bit int. The attribute must be + /// an unsigned integer. + uint64_t getUInt() const; /// Methods for support type inquiry through isa, cast, and dyn_cast. static bool kindof(unsigned kind) { @@ -688,7 +696,7 @@ const char *data = reinterpret_cast(values.data()); return getRawIntOrFloat( type, ArrayRef(data, values.size() * sizeof(T)), sizeof(T), - /*isInt=*/std::numeric_limits::is_integer); + std::numeric_limits::is_integer, std::numeric_limits::is_signed); } /// Constructs a dense integer elements attribute from a single element. @@ -863,7 +871,8 @@ std::numeric_limits::is_integer) || llvm::is_one_of::value>::type> llvm::iterator_range> getValues() const { - assert(isValidIntOrFloat(sizeof(T), std::numeric_limits::is_integer)); + assert(isValidIntOrFloat(sizeof(T), std::numeric_limits::is_integer, + std::numeric_limits::is_signed)); auto rawData = getRawData().data(); bool splat = isSplat(); return {ElementIterator(rawData, splat, 0), @@ -976,12 +985,13 @@ /// invariants that the templatized 'get' method cannot. static DenseElementsAttr getRawIntOrFloat(ShapedType type, ArrayRef data, - int64_t dataEltSize, bool isInt); + int64_t dataEltSize, bool isInt, + bool isSigned); - /// Check the information for a c++ data type, check if this type is valid for + /// Check the information for a C++ data type, check if this type is valid for /// the current attribute. This method is used to verify specific type /// invariants that the templatized 'getValues' method cannot. - bool isValidIntOrFloat(int64_t dataEltSize, bool isInt) const; + bool isValidIntOrFloat(int64_t dataEltSize, bool isInt, bool isSigned) const; }; /// An attribute that represents a reference to a dense float vector or tensor diff --git a/mlir/include/mlir/IR/Builders.h b/mlir/include/mlir/IR/Builders.h --- a/mlir/include/mlir/IR/Builders.h +++ b/mlir/include/mlir/IR/Builders.h @@ -70,6 +70,7 @@ IntegerType getI1Type(); IntegerType getIntegerType(unsigned width); + IntegerType getIntegerType(unsigned width, bool isSigned); FunctionType getFunctionType(ArrayRef inputs, ArrayRef results); TupleType getTupleType(ArrayRef elementTypes); NoneType getNoneType(); @@ -111,6 +112,10 @@ IntegerAttr getI32IntegerAttr(int32_t value); IntegerAttr getI64IntegerAttr(int64_t value); + /// Signed and unsigned integer attribute getters. + IntegerAttr getSI32IntegerAttr(int32_t value); + IntegerAttr getUI32IntegerAttr(uint32_t value); + DenseIntElementsAttr getI32VectorAttr(ArrayRef values); ArrayAttr getAffineMapArrayAttr(ArrayRef values); diff --git a/mlir/include/mlir/IR/Matchers.h b/mlir/include/mlir/IR/Matchers.h --- a/mlir/include/mlir/IR/Matchers.h +++ b/mlir/include/mlir/IR/Matchers.h @@ -93,7 +93,7 @@ return false; auto type = op->getResult(0).getType(); - if (type.isIntOrIndex()) { + if (type.isSignlessIntOrIndex()) { return attr_value_binder(bind_value).match(attr); } if (type.isa() || type.isa()) { diff --git a/mlir/include/mlir/IR/OpBase.td b/mlir/include/mlir/IR/OpBase.td --- a/mlir/include/mlir/IR/OpBase.td +++ b/mlir/include/mlir/IR/OpBase.td @@ -310,7 +310,8 @@ // Integer types. // Any integer type irrespective of its width. -def AnyInteger : Type()">, "integer">; +def AnySignlessInteger : Type< + CPred<"$_self.isSignlessInteger()">, "integer">; // Index type. def Index : Type()">, "index">, @@ -318,7 +319,7 @@ // Integer type of a specific width. class I - : Type, + : Type, width # "-bit integer">, BuildableType<"$_builder.getIntegerType(" # width # ")"> { int bitwidth = width; @@ -586,8 +587,10 @@ // Type constraint for integer-like types: integers, indices, vectors of // integers, tensors of integers. -def IntegerLike : TypeConstraint.predicate, TensorOf<[AnyInteger]>.predicate]>, +def SignlessIntegerLike : TypeConstraint.predicate, + TensorOf<[AnySignlessInteger]>.predicate]>, "integer-like">; // Type constraint for float-like types: floats, vectors or tensors thereof. @@ -596,8 +599,8 @@ "floating-point-like">; // Type constraint for integer-like or float-like types. -def IntegerOrFloatLike : TypeConstraint, +def SignlessIntegerOrFloatLike : TypeConstraint, "integer-like or floating-point-like">; @@ -725,7 +728,7 @@ attrValType, "IntegerAttr", And<[CPred<"$_self.isa()">, CPred<"$_self.cast().getType()." - "isInteger(" # attrValType.bitwidth # ")">]>, + "isSignlessInteger(" # attrValType.bitwidth # ")">]>, descr> { let returnType = [{ APInt }]; } @@ -1031,7 +1034,7 @@ class IntElementsAttr : ElementsAttrBase< CPred<"$_self.isa() &&" "$_self.cast().getType()." - "getElementType().isInteger(" # width # ")">, + "getElementType().isSignlessInteger(" # width # ")">, width # "-bit integer elements attribute"> { let storageType = [{ DenseIntElementsAttr }]; diff --git a/mlir/include/mlir/IR/OpDefinition.h b/mlir/include/mlir/IR/OpDefinition.h --- a/mlir/include/mlir/IR/OpDefinition.h +++ b/mlir/include/mlir/IR/OpDefinition.h @@ -365,7 +365,7 @@ LogicalResult verifyNOperands(Operation *op, unsigned numOperands); LogicalResult verifyAtLeastNOperands(Operation *op, unsigned numOperands); LogicalResult verifyOperandsAreFloatLike(Operation *op); -LogicalResult verifyOperandsAreIntegerLike(Operation *op); +LogicalResult verifyOperandsAreSignlessIntegerLike(Operation *op); LogicalResult verifySameTypeOperands(Operation *op); LogicalResult verifyZeroResult(Operation *op); LogicalResult verifyOneResult(Operation *op); @@ -378,7 +378,7 @@ LogicalResult verifySameOperandsAndResultType(Operation *op); LogicalResult verifyResultsAreBoolLike(Operation *op); LogicalResult verifyResultsAreFloatLike(Operation *op); -LogicalResult verifyResultsAreIntegerLike(Operation *op); +LogicalResult verifyResultsAreSignlessIntegerLike(Operation *op); LogicalResult verifyIsTerminator(Operation *op); LogicalResult verifyOperandSizeAttr(Operation *op, StringRef sizeAttrName); LogicalResult verifyResultSizeAttr(Operation *op, StringRef sizeAttrName); @@ -725,14 +725,14 @@ } }; -/// This class verifies that any results of the specified op have an integer or -/// index type, a vector thereof, or a tensor thereof. +/// This class verifies that any results of the specified op have a signless +/// integer or index type, a vector thereof, or a tensor thereof. template -class ResultsAreIntegerLike - : public TraitBase { +class ResultsAreSignlessIntegerLike + : public TraitBase { public: static LogicalResult verifyTrait(Operation *op) { - return impl::verifyResultsAreIntegerLike(op); + return impl::verifyResultsAreSignlessIntegerLike(op); } }; @@ -767,14 +767,14 @@ } }; -/// This class verifies that all operands of the specified op have an integer or -/// index type, a vector thereof, or a tensor thereof. +/// This class verifies that all operands of the specified op have a signless +/// integer or index type, a vector thereof, or a tensor thereof. template -class OperandsAreIntegerLike - : public TraitBase { +class OperandsAreSignlessIntegerLike + : public TraitBase { public: static LogicalResult verifyTrait(Operation *op) { - return impl::verifyOperandsAreIntegerLike(op); + return impl::verifyOperandsAreSignlessIntegerLike(op); } }; diff --git a/mlir/include/mlir/IR/Operation.h b/mlir/include/mlir/IR/Operation.h --- a/mlir/include/mlir/IR/Operation.h +++ b/mlir/include/mlir/IR/Operation.h @@ -503,7 +503,7 @@ SmallVectorImpl &results); /// Returns if the operation was registered with a particular trait, e.g. - /// hasTrait(). + /// hasTrait(). template