[Mlir-commits] [mlir] 61d6e49 - QuantileType relax quantileType conditions and inheritance issue (#204793)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Wed Jul 29 02:39:16 PDT 2026


Author: vsimion26
Date: 2026-07-29T02:39:11-07:00
New Revision: 61d6e494e97a8fda6946291fd3bc6bb648c86e93

URL: https://github.com/llvm/llvm-project/commit/61d6e494e97a8fda6946291fd3bc6bb648c86e93
DIFF: https://github.com/llvm/llvm-project/commit/61d6e494e97a8fda6946291fd3bc6bb648c86e93.diff

LOG: QuantileType relax quantileType conditions and inheritance issue (#204793)

# Recategorize QuantileType as Storage Format, Not Quantization Scheme

## Summary

Reclassify `QuantileType` from inheriting `QuantizedType` to functioning
as a pure storage type.

## Motivation

### QuantileType is a Storage Format, Not a Quantization Scheme

After careful consideration, there was a conclusion reached that
**QuantileType should not be registered as a subtype of QuantizedType**.
The architectural distinction is critical:

- **QuantizedType** represents different quantization schemes (uniform,
per-axis, etc.), semantic operations that map values into a
lower-precision domain
- **QuantileType** is purely a **storage format** that specifies how to
interpret indices within a lookup table (LUT)

QuantileType was never meant to function as a quantization scheme on its
own. Instead, its purpose is to indicate **how to interpret indices
based on the given LUT**. It is an orthogonal concern to quantization
semantics; it describes data organization, not mathematical
transformation.

### Relaxed Type Conditions

The previous implementation restricted the LUT values to float types
only. However, float types are not the only types that can exist inside
a LUT, integer types are valid as well.

This PR removes the type restriction, allowing **both floating-point and
integer-based LUT interpretations** to coexist, better matching actual
use cases.

## Changes

- **Removed**: Incorrect inheritance of `QuantizedType` by
`QuantileType`
- **Relaxed**: Type constraints to accept both float and integer LUT
value types

Added: 
    

Modified: 
    mlir/include/mlir/Dialect/Quant/IR/QuantTypes.h
    mlir/lib/Dialect/Quant/IR/QuantTypes.cpp
    mlir/test/Dialect/Quant/invalid-quantile-types.mlir
    mlir/test/Dialect/Quant/quantile-types.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Dialect/Quant/IR/QuantTypes.h b/mlir/include/mlir/Dialect/Quant/IR/QuantTypes.h
index 2001178d5939c..82914c631e42d 100644
--- a/mlir/include/mlir/Dialect/Quant/IR/QuantTypes.h
+++ b/mlir/include/mlir/Dialect/Quant/IR/QuantTypes.h
@@ -551,13 +551,14 @@ class CalibratedQuantizedType
 /*Syntax:
 
     ```
-    quantile-type ::= `!quant.quantile` `<` type `:` type `,` `{` float-list `}`
+    quantile-type ::= `!quant.quantile` `<` type `:` type `,` `{` float-list /
+   int-list `}`
    (`,` `<` int `,` int `>`)? `>`
     ```
 
     A quantile type represents a quantile-based floating point encoding, where
-    discrete storage values are totally defined by the floating-point values
-   entries in a quantile lookup table of F8/F16/F32/F64.
+    discrete storage values are totally defined by the floating-point or integer
+   values entries in a quantile lookup table of F8/F16/F32/F64 or integer types.
 
     Optionally, explicit minimum and maximum storage values can be specified
     after the LUT as `<min:max>`.
@@ -570,6 +571,8 @@ class CalibratedQuantizedType
    MLIR:
     !quant.quantile<ui4:f16, {-1.0,-0.696,0.0,0.079,1.0}>
     !quant.quantile<ui4:f16, {-1.0,-0.696,0.0,0.079,1.0}, <-8,7>>
+    !quant.quantile<ui4:si8, {-1.0,-0.696,0.0,0.079,1.0}>
+    !quant.quantile<ui4:ui8, {0.0,0.079,0.696,1.0}>
 
     As an additional explanation for better understanding and readability of the
    above example, the quantile type can be broken down as follows:
@@ -584,11 +587,24 @@ class CalibratedQuantizedType
     - `, <-8,7>`: This optional part specifies the explicit minimum and maximum
    storage values. In this case, the minimum storage value is -8 and the maximum
    storage value is 7.
+    - The number of quantile values in the LUT has to be equal to the number of
+   discrete values that can be represented by the storage type. For example, for
+   `ui4`, there are 16 discrete values, so the LUT must contain
+   exactly 16 quantile values. If the number of quantile values in the LUT does
+   not match the number of discrete values that can be represented by the
+   storage type, it will result in an error during type verification. Otherwise,
+   we can specify a storageMin and storageMax to match the number of quantile
+   values in the LUT. For example, for `ui4`, we can specify a storageMin of 0
+   and a storageMax of 5, which allows us to have 6 discrete values that can be
+   represented by the storage type.
+
+    The above examples showcase how a quantile type would look like in MLIR,
+   with the mention that the LUT size is not correct for the given storage
+   types, but it is just for demonstration purposes.
 */
 
 class QuantileType
-    : public Type::TypeBase<QuantileType, QuantizedType,
-                            detail::QuantileTypeStorage,
+    : public Type::TypeBase<QuantileType, Type, detail::QuantileTypeStorage,
                             mlir::QuantStorageTypeInterface::Trait> {
 public:
   using ImplType = detail::QuantileTypeStorage;

diff  --git a/mlir/lib/Dialect/Quant/IR/QuantTypes.cpp b/mlir/lib/Dialect/Quant/IR/QuantTypes.cpp
index c150c151e1e11..508594c73aefc 100644
--- a/mlir/lib/Dialect/Quant/IR/QuantTypes.cpp
+++ b/mlir/lib/Dialect/Quant/IR/QuantTypes.cpp
@@ -578,8 +578,9 @@ LogicalResult QuantileType::verifyInvariants(
     std::optional<int64_t> storageMin, std::optional<int64_t> storageMax) {
   if (!storageType.isIntOrFloat())
     return emitError() << "storage type must be an integer or float type";
-  if (!llvm::isa<mlir::FloatType>(quantileType))
-    return emitError() << "quantile type must be a float type";
+  if (!llvm::isa<mlir::FloatType>(quantileType) &&
+      !llvm::isa<mlir::IntegerType>(quantileType))
+    return emitError() << "quantile type must be a float or integer type";
   if (quantiles.empty())
     return emitError() << "quantile values must not be empty";
   if (storageMin.has_value() != storageMax.has_value())

diff  --git a/mlir/test/Dialect/Quant/invalid-quantile-types.mlir b/mlir/test/Dialect/Quant/invalid-quantile-types.mlir
index faf16d01a9cd5..08a739d45e120 100644
--- a/mlir/test/Dialect/Quant/invalid-quantile-types.mlir
+++ b/mlir/test/Dialect/Quant/invalid-quantile-types.mlir
@@ -10,9 +10,9 @@ func.func private @invalid_storage_type() -> !quant.quantile<tensor<1xf32>:f32,
 
 // -----
 
-// Quantile (expressed) type must be a float.
-// expected-error @+1 {{quantile type must be a float type}}
-func.func private @invalid_quantile_type() -> !quant.quantile<ui4:i8, {1.0, 0.0, -1.0}>
+// Quantile (expressed) type must be a float or integer type.
+// expected-error @+1 {{quantile type must be a float or integer type}}
+func.func private @invalid_quantile_type() -> !quant.quantile<ui4:tensor<1xf32>, {1.0, 0.0, -1.0}>
 
 // -----
 

diff  --git a/mlir/test/Dialect/Quant/quantile-types.mlir b/mlir/test/Dialect/Quant/quantile-types.mlir
index c8840a7b3d3f2..a879acd7b3126 100644
--- a/mlir/test/Dialect/Quant/quantile-types.mlir
+++ b/mlir/test/Dialect/Quant/quantile-types.mlir
@@ -6,6 +6,20 @@
 // CHECK-SAME: !quant.quantile<ui4:f16, {
 func.func private @quantile_ui4_f16(!quant.quantile<ui4:f16, {-1.0,-0.8667,-0.7333,-0.6,-0.4667,-0.3333,-0.2,-0.0667,0.0667,0.2,0.3333,0.4667,0.6,0.7333,0.8667,1.0}>) -> ()
 
+
+// -----
+// Quantile type: ui4 storage-type with si8 expressed-type, 16 entries (default range 0..15).
+// CHECK-LABEL: func private @quantile_ui4_si8
+// CHECK-SAME: !quant.quantile<ui4:si8, {
+func.func private @quantile_ui4_si8(!quant.quantile<ui4:si8, {-1.0,-0.8667,-0.7333,-0.6,-0.4667,-0.3333,-0.2,-0.0667,0.0667,0.2,0.3333,0.4667,0.6,0.7333,0.8667,1.0}>) -> ()
+
+
+// -----
+// Quantile type: ui4 storage-type with ui8 expressed-type, 16 entries (default range 0..15).
+// CHECK-LABEL: func private @quantile_ui4_ui8
+// CHECK-SAME: !quant.quantile<ui4:ui8, {
+func.func private @quantile_ui4_ui8(!quant.quantile<ui4:ui8, {0.0,0.0667,0.1333,0.2,0.2667,0.3333,0.4,0.4667,0.5333,0.6,0.6667,0.7333,0.8,0.8667,0.9333,1.0}>) -> ()
+
 // -----
 // Quantile type: si8 storage-type with f32 expressed-type, explicit range -2:2 (5 entries).
 // CHECK: func private @quantile_si8_f32(!quant.quantile<si8:f32, {-1.000000e+00,-5.000000e-01,0.000000e+00,5.000000e-01,1.000000e+00}, <-2:2>>)


        


More information about the Mlir-commits mailing list