[Mlir-commits] [mlir] [mlir][IR] Add builtin OCP INT8 element type (PR #230479)
Davide Grohmann
llvmlistbot at llvm.org
Fri Oct 9 05:45:32 PDT 2026
https://github.com/davidegrohmann updated https://github.com/llvm/llvm-project/pull/230479
>From 478b33afd9fed5a8d6a18753ad4c6b3693104e3f Mon Sep 17 00:00:00 2001
From: Davide Grohmann <davide.grohmann at arm.com>
Date: Wed, 7 Oct 2026 16:08:05 +0200
Subject: [PATCH] [mlir][IR] Add builtin OCP INT8 element type
Add a builtin ocp_int8 type for the INT8 element of the OCP MXINT8
format. The type uses signed 8-bit storage with six fractional bits
and exposes the corresponding LLVM fixed-point semantics. The shared
E8M0 block scale is separate from this scalar type.
Add text and bytecode support, permit vector and tensor elements, and
encode dense elements through signless i8 attributes. Append the new
bytecode type code so existing codes retain their values.
Test text and bytecode round trips, raw storage endpoints, and the
fixed-point width, scale, and signedness.
See the Open Compute Project (OCP) MX specification:
https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf/
Change-Id: I5677b12fa372cbc1c5031373c9d32ddaf5c4efae
Signed-off-by: Davide Grohmann <davide.grohmann at arm.com>
---
.../include/mlir/IR/BuiltinDialectBytecode.td | 5 +++-
mlir/include/mlir/IR/BuiltinTypes.h | 2 ++
mlir/include/mlir/IR/BuiltinTypes.td | 25 ++++++++++++++++
mlir/include/mlir/IR/CommonTypeConstraints.td | 2 ++
mlir/lib/AsmParser/TokenKinds.def | 1 +
mlir/lib/AsmParser/TypeParser.cpp | 5 ++++
mlir/lib/IR/AsmPrinter.cpp | 1 +
mlir/lib/IR/BuiltinTypes.cpp | 29 ++++++++++++++++++-
mlir/test/Dialect/Builtin/Bytecode/attrs.mlir | 2 ++
mlir/test/Dialect/Builtin/Bytecode/types.mlir | 14 +++++++++
mlir/test/IR/attribute.mlir | 11 +++++++
mlir/unittests/IR/TypeTest.cpp | 10 +++++++
12 files changed, 105 insertions(+), 2 deletions(-)
diff --git a/mlir/include/mlir/IR/BuiltinDialectBytecode.td b/mlir/include/mlir/IR/BuiltinDialectBytecode.td
index d098793989dc4a..67677344dd9608 100644
--- a/mlir/include/mlir/IR/BuiltinDialectBytecode.td
+++ b/mlir/include/mlir/IR/BuiltinDialectBytecode.td
@@ -318,6 +318,8 @@ def Float6E3M2FNType : EnableFloatPrintingJune2026<(type)>;
def Float8E8M0FNUType : EnableFloatPrintingJune2026<(type)>;
+def OCPInt8Type : EnableFloatPrintingJune2026<(type)>;
+
def ComplexType : DialectType<(type
Type:$elementType
)>;
@@ -469,7 +471,8 @@ def BuiltinDialectTypes : DialectTypes<"Builtin"> {
Float6E3M2FNType,
Float8E8M0FNUType,
TokenType,
- GraphType
+ GraphType,
+ OCPInt8Type
];
}
diff --git a/mlir/include/mlir/IR/BuiltinTypes.h b/mlir/include/mlir/IR/BuiltinTypes.h
index d30cba29c98142..e20b7a590ac373 100644
--- a/mlir/include/mlir/IR/BuiltinTypes.h
+++ b/mlir/include/mlir/IR/BuiltinTypes.h
@@ -15,6 +15,8 @@
namespace llvm {
class BitVector;
+// Used by the generated OCPInt8Type declaration in BuiltinTypes.h.inc.
+class FixedPointSemantics;
struct fltSemantics;
} // namespace llvm
diff --git a/mlir/include/mlir/IR/BuiltinTypes.td b/mlir/include/mlir/IR/BuiltinTypes.td
index ffb0e542153ee5..23cb366581b488 100644
--- a/mlir/include/mlir/IR/BuiltinTypes.td
+++ b/mlir/include/mlir/IR/BuiltinTypes.td
@@ -453,6 +453,31 @@ def Builtin_Float8E5M3FNU : Builtin_FloatType<"Float8E5M3FNU", "f8E5M3FNU"> {
}];
}
+//===----------------------------------------------------------------------===//
+// OCPInt8Type
+//===----------------------------------------------------------------------===//
+
+def Builtin_OCPInt8 : Builtin_Type<"OCPInt8", "ocp_int8",
+ [VectorElementTypeInterface,
+ DeclareTypeInterfaceMethods<DenseElementTypeInterface, [
+ "getDenseElementBitSize", "convertToAttribute",
+ "convertFromAttribute"]>]> {
+ let summary = "OCP INT8 element type";
+ let description = [{
+ The INT8 element type of the OCP MXINT8 block format. It is an 8-bit signed
+ two's-complement type with six fractional bits. Its values are multiples of
+ 1/64 in the range [-2, 127/64]. The shared E8M0 scale is not part of this
+ type.
+
+ Open Compute Project (OCP) microscaling formats (MX) specification,
+ Section 5.3.4 (INT8):
+ https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf
+ }];
+ let extraClassDeclaration = [{
+ static llvm::FixedPointSemantics getFixedPointSemantics();
+ }];
+}
+
//===----------------------------------------------------------------------===//
// BFloat16Type
//===----------------------------------------------------------------------===//
diff --git a/mlir/include/mlir/IR/CommonTypeConstraints.td b/mlir/include/mlir/IR/CommonTypeConstraints.td
index 8dbd7ac4b97604..f65b010e90c8cd 100644
--- a/mlir/include/mlir/IR/CommonTypeConstraints.td
+++ b/mlir/include/mlir/IR/CommonTypeConstraints.td
@@ -379,6 +379,8 @@ def F8E8M0FNU : Type<CPred<"::llvm::isa<::mlir::Float8E8M0FNUType>($_self)">, "f
BuildableType<"$_builder.getType<Float8E8M0FNUType>()">;
def F8E5M3FNU : Type<CPred<"::llvm::isa<::mlir::Float8E5M3FNUType>($_self)">, "f8E5M3FNU type">,
BuildableType<"$_builder.getType<Float8E5M3FNUType>()">;
+def OCPInt8 : Type<CPred<"::llvm::isa<::mlir::OCPInt8Type>($_self)">, "ocp_int8 type">,
+ BuildableType<"$_builder.getType<OCPInt8Type>()">;
def AnyComplex : Type<CPred<"::llvm::isa<::mlir::ComplexType>($_self)">,
"complex-type", "::mlir::ComplexType">;
diff --git a/mlir/lib/AsmParser/TokenKinds.def b/mlir/lib/AsmParser/TokenKinds.def
index e9a883bdee92db..6a9191e3cd163f 100644
--- a/mlir/lib/AsmParser/TokenKinds.def
+++ b/mlir/lib/AsmParser/TokenKinds.def
@@ -118,6 +118,7 @@ TOK_KEYWORD(max)
TOK_KEYWORD(memref)
TOK_KEYWORD(min)
TOK_KEYWORD(mod)
+TOK_KEYWORD(ocp_int8)
TOK_KEYWORD(none)
TOK_KEYWORD(offset)
TOK_KEYWORD(size)
diff --git a/mlir/lib/AsmParser/TypeParser.cpp b/mlir/lib/AsmParser/TypeParser.cpp
index 0617e733171841..c4775b69e842ea 100644
--- a/mlir/lib/AsmParser/TypeParser.cpp
+++ b/mlir/lib/AsmParser/TypeParser.cpp
@@ -58,6 +58,7 @@ OptionalParseResult Parser::parseOptionalType(Type &type) {
case Token::kw_f80:
case Token::kw_f128:
case Token::kw_index:
+ case Token::kw_ocp_int8:
case Token::kw_none:
case Token::kw_token:
case Token::exclamation_identifier:
@@ -371,6 +372,10 @@ Type Parser::parseNonFunctionType() {
consumeToken(Token::kw_index);
return builder.getIndexType();
+ case Token::kw_ocp_int8:
+ consumeToken(Token::kw_ocp_int8);
+ return builder.getType<OCPInt8Type>();
+
// none-type
case Token::kw_none:
consumeToken(Token::kw_none);
diff --git a/mlir/lib/IR/AsmPrinter.cpp b/mlir/lib/IR/AsmPrinter.cpp
index f78d33338904b0..0214b1fb72884b 100644
--- a/mlir/lib/IR/AsmPrinter.cpp
+++ b/mlir/lib/IR/AsmPrinter.cpp
@@ -2824,6 +2824,7 @@ void AsmPrinter::Impl::printTypeImpl(Type type) {
.Case<Float64Type>([&](Type) { os << "f64"; })
.Case<Float80Type>([&](Type) { os << "f80"; })
.Case<Float128Type>([&](Type) { os << "f128"; })
+ .Case<OCPInt8Type>([&](Type) { os << "ocp_int8"; })
.Case([&](IntegerType integerTy) {
if (integerTy.isSigned())
os << 's';
diff --git a/mlir/lib/IR/BuiltinTypes.cpp b/mlir/lib/IR/BuiltinTypes.cpp
index 845143f863d943..7903b4c7b05f81 100644
--- a/mlir/lib/IR/BuiltinTypes.cpp
+++ b/mlir/lib/IR/BuiltinTypes.cpp
@@ -18,6 +18,7 @@
#include "mlir/IR/Dialect.h"
#include "mlir/IR/TensorEncoding.h"
#include "mlir/IR/TypeUtilities.h"
+#include "llvm/ADT/APFixedPoint.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/Sequence.h"
@@ -149,6 +150,32 @@ IntegerType::convertFromAttribute(Attribute attr,
return success();
}
+//===----------------------------------------------------------------------===//
+// OCPInt8 Type
+//===----------------------------------------------------------------------===//
+
+llvm::FixedPointSemantics OCPInt8Type::getFixedPointSemantics() {
+ // Saturation is selected by a conversion operation, not by the value type.
+ return {/*Width=*/8, /*Scale=*/6, /*IsSigned=*/true,
+ /*IsSaturated=*/false, /*HasUnsignedPadding=*/false};
+}
+
+size_t OCPInt8Type::getDenseElementBitSize() const { return 8; }
+
+Attribute OCPInt8Type::convertToAttribute(ArrayRef<char> rawData) const {
+ return IntegerType::get(getContext(), 8).convertToAttribute(rawData);
+}
+
+LogicalResult
+OCPInt8Type::convertFromAttribute(Attribute attr,
+ SmallVectorImpl<char> &result) const {
+ auto intAttr = dyn_cast<IntegerAttr>(attr);
+ if (!intAttr || !intAttr.getType().isSignlessInteger(8))
+ return failure();
+ return cast<IntegerType>(intAttr.getType())
+ .convertFromAttribute(attr, result);
+}
+
//===----------------------------------------------------------------------===//
// Index Type
//===----------------------------------------------------------------------===//
@@ -437,7 +464,7 @@ bool TensorType::isValidElementType(Type type) {
// types. Dialects are expected to verify that tensor types have a valid
// element type within that dialect.
return llvm::isa<ComplexType, FloatType, IntegerType, OpaqueType, VectorType,
- IndexType>(type) ||
+ IndexType, OCPInt8Type>(type) ||
!llvm::isa<BuiltinDialect>(type.getDialect());
}
diff --git a/mlir/test/Dialect/Builtin/Bytecode/attrs.mlir b/mlir/test/Dialect/Builtin/Bytecode/attrs.mlir
index ee59abc14f1cdc..7670556b124f28 100644
--- a/mlir/test/Dialect/Builtin/Bytecode/attrs.mlir
+++ b/mlir/test/Dialect/Builtin/Bytecode/attrs.mlir
@@ -36,6 +36,7 @@ module @TestDenseArray attributes {
// CHECK-DAG: bytecode.i64 = dense<1000000> : tensor<4xi64>
// CHECK-DAG: bytecode.f16 = dense<[1.500000e+00, 2.500000e+00]> : tensor<2xf16>
// CHECK-DAG: bytecode.bf16 = dense<[-5.000000e-01, 5.000000e-01]> : tensor<2xbf16>
+// CHECK-DAG: bytecode.ocp_int8 = dense<tensor<4xocp_int8> : [-128 : i8, -1 : i8, 0 : i8, 127 : i8]>
// CHECK-DAG: bytecode.complex_val = dense<(1.000000e+00,2.000000e+00)> : tensor<1xcomplex<f32>>
module @TestDenseTypedElements attributes {
bytecode.test1 = dense<true> : tensor<256xi1>,
@@ -45,6 +46,7 @@ module @TestDenseTypedElements attributes {
bytecode.i64 = dense<1000000> : tensor<4xi64>,
bytecode.f16 = dense<[1.5, 2.5]> : tensor<2xf16>,
bytecode.bf16 = dense<[-0.5, 0.5]> : tensor<2xbf16>,
+ bytecode.ocp_int8 = dense<tensor<4xocp_int8> : [-128 : i8, -1 : i8, 0 : i8, 127 : i8]>,
bytecode.complex_val = dense<(1.0, 2.0)> : tensor<1xcomplex<f32>>
} {}
diff --git a/mlir/test/Dialect/Builtin/Bytecode/types.mlir b/mlir/test/Dialect/Builtin/Bytecode/types.mlir
index e1d4a18a33c739..adf1d3c767a7e7 100644
--- a/mlir/test/Dialect/Builtin/Bytecode/types.mlir
+++ b/mlir/test/Dialect/Builtin/Bytecode/types.mlir
@@ -54,6 +54,20 @@ module @TestFloat attributes {
bytecode.test17 = tf32
} {}
+//===----------------------------------------------------------------------===//
+// OCPInt8Type
+//===----------------------------------------------------------------------===//
+
+// CHECK-LABEL: @TestOCPInt8
+module @TestOCPInt8 attributes {
+ // CHECK: bytecode.scalar = ocp_int8,
+ // CHECK: bytecode.tensor = tensor<4xocp_int8>,
+ // CHECK: bytecode.vector = vector<2xocp_int8>
+ bytecode.scalar = ocp_int8,
+ bytecode.tensor = tensor<4xocp_int8>,
+ bytecode.vector = vector<2xocp_int8>
+} {}
+
//===----------------------------------------------------------------------===//
// IntegerType
//===----------------------------------------------------------------------===//
diff --git a/mlir/test/IR/attribute.mlir b/mlir/test/IR/attribute.mlir
index ea378439c0f3bb..a833e0d2ec6ab3 100644
--- a/mlir/test/IR/attribute.mlir
+++ b/mlir/test/IR/attribute.mlir
@@ -596,6 +596,17 @@ func.func @tf32_elements_attr() {
// -----
+// Test OCP INT8 dense elements at both storage endpoints and around zero.
+
+// CHECK-LABEL: func @ocp_int8_elements_attr
+func.func @ocp_int8_elements_attr() {
+ // CHECK: "foo"() {attr = dense<tensor<4xocp_int8> : [-128 : i8, -1 : i8, 0 : i8, 127 : i8]>} : () -> ()
+ "foo"() {attr = dense<tensor<4xocp_int8> : [-128 : i8, -1 : i8, 0 : i8, 127 : i8]>} : () -> ()
+ return
+}
+
+// -----
+
//===----------------------------------------------------------------------===//
// Test StringElementsAttr
//===----------------------------------------------------------------------===//
diff --git a/mlir/unittests/IR/TypeTest.cpp b/mlir/unittests/IR/TypeTest.cpp
index 30f6642a9ca71d..86f24158e00ee4 100644
--- a/mlir/unittests/IR/TypeTest.cpp
+++ b/mlir/unittests/IR/TypeTest.cpp
@@ -10,6 +10,7 @@
#include "mlir/IR/Dialect.h"
#include "mlir/IR/Types.h"
#include "mlir/IR/Value.h"
+#include "llvm/ADT/APFixedPoint.h"
#include "gtest/gtest.h"
using namespace mlir;
@@ -69,3 +70,12 @@ TEST(Type, Casting) {
EXPECT_EQ(8u, cast<IntegerType>(intTy).getWidth());
}
+
+TEST(Type, OCPInt8FixedPointSemantics) {
+ llvm::FixedPointSemantics semantics = OCPInt8Type::getFixedPointSemantics();
+ EXPECT_EQ(8u, semantics.getWidth());
+ EXPECT_EQ(6u, semantics.getScale());
+ EXPECT_TRUE(semantics.isSigned());
+ EXPECT_FALSE(semantics.isSaturated());
+ EXPECT_FALSE(semantics.hasUnsignedPadding());
+}
More information about the Mlir-commits
mailing list