[Mlir-commits] [mlir] e559d43 - [mlir][LLVM] Add the `byte` type to the LLVM dialect (#203795)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Tue Jun 16 01:58:04 PDT 2026
Author: Markus Böck
Date: 2026-06-16T10:58:00+02:00
New Revision: e559d43e47e1264962831f936d1ea56d0fd3465d
URL: https://github.com/llvm/llvm-project/commit/e559d43e47e1264962831f936d1ea56d0fd3465d
DIFF: https://github.com/llvm/llvm-project/commit/e559d43e47e1264962831f936d1ea56d0fd3465d.diff
LOG: [mlir][LLVM] Add the `byte` type to the LLVM dialect (#203795)
This PR ports the newly added `byte` type from LLVM IR to mlir's LLVM
dialect. The simplest motivation for the byte type is being able to
implement `memcpy` in LLVM IR. This was previously not possible: Due to
rules around conversions between integers and pointers (which e.g.
implicitly happen during loads), partial-poisons and pointer provenance
were not preserved. No alterantive types to integers existed that one
could use to have poison and provenance preserving SSA-values. The byte
type solves exactly this issue. Frontends are encouraged to use it when
needed for better optimization capabilities.
Currently, the only operation that has changed semantics around `byte`
is `bitcast`. Is now allows casting between `byte` and `ptr` (unlike
integers and pointers).
Corresponding LLVM commit:
https://github.com/llvm/llvm-project/commit/80f2ef70f592
Assisted by Claude & Gemini
---------
Co-authored-by: Fabian Mora <fmora.dev at gmail.com>
Added:
Modified:
mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
mlir/lib/Dialect/LLVMIR/IR/LLVMTypeSyntax.cpp
mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp
mlir/lib/Target/LLVMIR/TypeFromLLVM.cpp
mlir/lib/Target/LLVMIR/TypeToLLVM.cpp
mlir/test/Dialect/LLVMIR/layout.mlir
mlir/test/Dialect/LLVMIR/roundtrip.mlir
mlir/test/Dialect/LLVMIR/types-invalid.mlir
mlir/test/Dialect/LLVMIR/types.mlir
mlir/test/Target/LLVMIR/Import/basic.ll
mlir/test/Target/LLVMIR/llvmir.mlir
Removed:
################################################################################
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td
index 072fc7be07dd4..910256aa4617b 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td
@@ -60,6 +60,32 @@ def LLVMArrayType : LLVMType<"LLVMArray", "array", [
}];
}
+//===----------------------------------------------------------------------===//
+// LLVMByteType
+//===----------------------------------------------------------------------===//
+
+def LLVMByteType : LLVMType<"LLVMByte", "byte", [
+ DeclareTypeInterfaceMethods<DataLayoutTypeInterface,
+ ["getTypeSizeInBits", "getABIAlignment"]>,
+ VectorElementTypeInterface]> {
+ let summary = "LLVM byte type";
+ let description = [{
+ The `!llvm.byte` type represents raw memory data in SSA registers, mirroring
+ LLVM's builtin byte type.
+ It should be used when it cannot be determined whether a value holds a
+ pointer or another type at run time, or if the value contains uninitialized
+ or poison data.
+ Unlike integer types, copying byte types preserves all poison bits and the
+ provenance of pointers.
+ }];
+
+ let parameters = (ins "unsigned":$bitWidth);
+ let assemblyFormat = [{
+ `<` $bitWidth `>`
+ }];
+ let genVerifyDecl = 1;
+}
+
//===----------------------------------------------------------------------===//
// LLVMFunctionType
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 3111d5bed3a64..58f569abff8ea 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -3868,10 +3868,16 @@ OpFoldResult LLVM::BitcastOp::fold(FoldAdaptor adaptor) {
}
LogicalResult LLVM::BitcastOp::verify() {
- auto resultType = llvm::dyn_cast<LLVMPointerType>(
- extractVectorElementType(getResult().getType()));
- auto sourceType = llvm::dyn_cast<LLVMPointerType>(
- extractVectorElementType(getArg().getType()));
+ Type srcElemType = extractVectorElementType(getArg().getType());
+ Type dstElemType = extractVectorElementType(getResult().getType());
+
+ // TODO: 'bitcast' requires result and operand type to be identical in size.
+ // Byte types may be cast from/to any type pointer constraints.
+ if (isa<LLVMByteType>(srcElemType) || isa<LLVMByteType>(dstElemType))
+ return success();
+
+ auto resultType = llvm::dyn_cast<LLVMPointerType>(dstElemType);
+ auto sourceType = llvm::dyn_cast<LLVMPointerType>(srcElemType);
// If one of the types is a pointer (or vector of pointers), then
// both source and result type have to be pointers.
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMTypeSyntax.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMTypeSyntax.cpp
index c498f0746169d..8ad623763bb92 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMTypeSyntax.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMTypeSyntax.cpp
@@ -41,6 +41,7 @@ static StringRef getTypeKeyword(Type type) {
.Case<LLVMFunctionType>([&](Type) { return "func"; })
.Case<LLVMPointerType>([&](Type) { return "ptr"; })
.Case<LLVMArrayType>([&](Type) { return "array"; })
+ .Case<LLVMByteType>([&](Type) { return "byte"; })
.Case<LLVMStructType>([&](Type) { return "struct"; })
.Case<LLVMTargetExtType>([&](Type) { return "target"; })
.Case<LLVMX86AMXType>([&](Type) { return "x86_amx"; })
@@ -102,8 +103,9 @@ void mlir::LLVM::detail::printType(Type type, AsmPrinter &printer) {
printer << getTypeKeyword(type);
llvm::TypeSwitch<Type>(type)
- .Case<LLVMPointerType, LLVMArrayType, LLVMFunctionType, LLVMTargetExtType,
- LLVMStructType>([&](auto type) { type.print(printer); });
+ .Case<LLVMPointerType, LLVMArrayType, LLVMByteType, LLVMFunctionType,
+ LLVMTargetExtType, LLVMStructType>(
+ [&](auto type) { type.print(printer); });
}
//===----------------------------------------------------------------------===//
@@ -270,6 +272,7 @@ static Type dispatchParse(AsmParser &parser, bool allowAny = true) {
.Case("func", [&] { return LLVMFunctionType::parse(parser); })
.Case("ptr", [&] { return LLVMPointerType::parse(parser); })
.Case("array", [&] { return LLVMArrayType::parse(parser); })
+ .Case("byte", [&] { return LLVMByteType::parse(parser); })
.Case("struct", [&] { return LLVMStructType::parse(parser); })
.Case("target", [&] { return LLVMTargetExtType::parse(parser); })
.Case("x86_amx", [&] { return LLVMX86AMXType::get(ctx); })
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp
index 2c29b38c26f08..ba2b8de4d7d82 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp
@@ -203,6 +203,34 @@ LLVMArrayType::getPreferredAlignment(const DataLayout &dataLayout,
return dataLayout.getTypePreferredAlignment(getElementType());
}
+//===----------------------------------------------------------------------===//
+// LLVMByteType
+//===----------------------------------------------------------------------===//
+
+llvm::TypeSize
+LLVMByteType::getTypeSizeInBits(const DataLayout &dataLayout,
+ DataLayoutEntryListRef params) const {
+ return llvm::TypeSize::getFixed(getBitWidth());
+}
+
+uint64_t LLVMByteType::getABIAlignment(const DataLayout &dataLayout,
+ DataLayoutEntryListRef params) const {
+ return llvm::PowerOf2Ceil(llvm::divideCeil(getBitWidth(), kBitsInByte));
+}
+
+LogicalResult LLVMByteType::verify(function_ref<InFlightDiagnostic()> emitError,
+ unsigned bitWidth) {
+ if (bitWidth == 0)
+ return emitError() << "bitwidth must be greater than 0";
+
+ // Mirror LLVM IR, which limits the bit width to fit in 23 bits.
+ constexpr unsigned kMaxBitWidth = 1 << 23;
+ if (bitWidth >= kMaxBitWidth)
+ return emitError() << "bitwidth must be less than " << kMaxBitWidth
+ << ", but got " << bitWidth;
+ return success();
+}
+
//===----------------------------------------------------------------------===//
// Function type.
//===----------------------------------------------------------------------===//
@@ -737,6 +765,7 @@ bool mlir::LLVM::isCompatibleOuterType(Type type) {
Float80Type,
Float128Type,
LLVMArrayType,
+ LLVMByteType,
LLVMFunctionType,
LLVMLabelType,
LLVMMetadataType,
@@ -800,6 +829,7 @@ static bool isCompatibleImpl(Type type, DenseSet<Type> &compatibleTypes) {
Float64Type,
Float80Type,
Float128Type,
+ LLVMByteType,
LLVMLabelType,
LLVMMetadataType,
LLVMPPCFP128Type,
@@ -856,7 +886,8 @@ bool mlir::LLVM::isCompatibleVectorType(Type type) {
if (auto intType = llvm::dyn_cast<IntegerType>(elementType))
return intType.isSignless();
return llvm::isa<BFloat16Type, Float16Type, Float32Type, Float64Type,
- Float80Type, Float128Type, LLVMPointerType>(elementType) ||
+ Float80Type, Float128Type, LLVMByteType, LLVMPointerType>(
+ elementType) ||
isCompatiblePtrType(elementType);
}
return false;
@@ -906,6 +937,9 @@ llvm::TypeSize mlir::LLVM::getPrimitiveTypeSizeInBits(Type type) {
.Case([](IntegerType intTy) {
return llvm::TypeSize::getFixed(intTy.getWidth());
})
+ .Case([](LLVMByteType byteTy) {
+ return llvm::TypeSize::getFixed(byteTy.getBitWidth());
+ })
.Case<LLVMPPCFP128Type>(
[](Type) { return llvm::TypeSize::getFixed(128); })
.Case([](VectorType t) {
@@ -934,6 +968,7 @@ void LLVMDialect::registerTypes() {
addTypes<
#define GET_TYPEDEF_LIST
#include "mlir/Dialect/LLVMIR/LLVMTypes.cpp.inc"
+
>();
}
diff --git a/mlir/lib/Target/LLVMIR/TypeFromLLVM.cpp b/mlir/lib/Target/LLVMIR/TypeFromLLVM.cpp
index 018f66802fc47..c2dd19e01ee8e 100644
--- a/mlir/lib/Target/LLVMIR/TypeFromLLVM.cpp
+++ b/mlir/lib/Target/LLVMIR/TypeFromLLVM.cpp
@@ -36,8 +36,9 @@ class TypeFromLLVMIRTranslatorImpl {
Type translated =
llvm::TypeSwitch<llvm::Type *, Type>(type)
.Case<llvm::ArrayType, llvm::FunctionType, llvm::IntegerType,
- llvm::PointerType, llvm::StructType, llvm::FixedVectorType,
- llvm::ScalableVectorType, llvm::TargetExtType>(
+ llvm::ByteType, llvm::PointerType, llvm::StructType,
+ llvm::FixedVectorType, llvm::ScalableVectorType,
+ llvm::TargetExtType>(
[this](auto *type) { return this->translate(type); })
.Default([this](llvm::Type *type) {
return translatePrimitiveType(type);
@@ -96,6 +97,11 @@ class TypeFromLLVMIRTranslatorImpl {
return IntegerType::get(&context, type->getBitWidth());
}
+ /// Translates the given byte type.
+ Type translate(llvm::ByteType *type) {
+ return LLVM::LLVMByteType::get(&context, type->getBitWidth());
+ }
+
/// Translates the given pointer type.
Type translate(llvm::PointerType *type) {
return LLVM::LLVMPointerType::get(&context, type->getAddressSpace());
diff --git a/mlir/lib/Target/LLVMIR/TypeToLLVM.cpp b/mlir/lib/Target/LLVMIR/TypeToLLVM.cpp
index 61997c691e35d..179a5459da4e1 100644
--- a/mlir/lib/Target/LLVMIR/TypeToLLVM.cpp
+++ b/mlir/lib/Target/LLVMIR/TypeToLLVM.cpp
@@ -68,9 +68,10 @@ class TypeToLLVMIRTranslatorImpl {
.Case([this](LLVM::LLVMX86AMXType) {
return llvm::Type::getX86_AMXTy(context);
})
- .Case<LLVM::LLVMArrayType, IntegerType, LLVM::LLVMFunctionType,
- LLVM::LLVMPointerType, LLVM::LLVMStructType, VectorType,
- LLVM::LLVMTargetExtType, PtrLikeTypeInterface>(
+ .Case<LLVM::LLVMArrayType, LLVM::LLVMByteType, IntegerType,
+ LLVM::LLVMFunctionType, LLVM::LLVMPointerType,
+ LLVM::LLVMStructType, VectorType, LLVM::LLVMTargetExtType,
+ PtrLikeTypeInterface>(
[this](auto type) { return this->translate(type); })
.DefaultUnreachable("unknown LLVM dialect type");
@@ -94,6 +95,11 @@ class TypeToLLVMIRTranslatorImpl {
paramTypes, type.isVarArg());
}
+ /// Translates the given byte type.
+ llvm::Type *translate(LLVM::LLVMByteType type) {
+ return llvm::Type::getByteNTy(context, type.getBitWidth());
+ }
+
/// Translates the given integer type.
llvm::Type *translate(IntegerType type) {
return llvm::IntegerType::get(context, type.getWidth());
diff --git a/mlir/test/Dialect/LLVMIR/layout.mlir b/mlir/test/Dialect/LLVMIR/layout.mlir
index aa1744b6d7720..c650ca3bc1a88 100644
--- a/mlir/test/Dialect/LLVMIR/layout.mlir
+++ b/mlir/test/Dialect/LLVMIR/layout.mlir
@@ -367,3 +367,60 @@ module attributes { dlti.dl_spec = #dlti.dl_spec<
#dlti.dl_entry<!llvm.struct<()>, dense<[64]> : vector<1xi32>>
>} {
}
+
+// -----
+
+module {
+ // CHECK: @byte_types
+ func.func @byte_types() {
+ // 8-bit byte type: same width as i8
+ // CHECK: alignment = 1
+ // CHECK: bitsize = 8
+ // CHECK: index = 0
+ // CHECK: preferred = 1
+ // CHECK: size = 1
+ "test.data_layout_query"() : () -> !llvm.byte<8>
+
+ // 16-bit byte type
+ // CHECK: alignment = 2
+ // CHECK: bitsize = 16
+ // CHECK: index = 0
+ // CHECK: preferred = 2
+ // CHECK: size = 2
+ "test.data_layout_query"() : () -> !llvm.byte<16>
+
+ // 32-bit byte type: same width as i32/f32
+ // CHECK: alignment = 4
+ // CHECK: bitsize = 32
+ // CHECK: index = 0
+ // CHECK: preferred = 4
+ // CHECK: size = 4
+ "test.data_layout_query"() : () -> !llvm.byte<32>
+
+ // 64-bit byte type: same width as i64/f64
+ // CHECK: alignment = 8
+ // CHECK: bitsize = 64
+ // CHECK: index = 0
+ // CHECK: preferred = 8
+ // CHECK: size = 8
+ "test.data_layout_query"() : () -> !llvm.byte<64>
+
+ // 24-bit byte type: non-power-of-2 byte count (3 bytes), alignment rounds up to 4
+ // CHECK: alignment = 4
+ // CHECK: bitsize = 24
+ // CHECK: index = 0
+ // CHECK: preferred = 4
+ // CHECK: size = 3
+ "test.data_layout_query"() : () -> !llvm.byte<24>
+
+ // 1-bit byte type: sub-byte, still takes up 1 byte in memory
+ // CHECK: alignment = 1
+ // CHECK: bitsize = 1
+ // CHECK: index = 0
+ // CHECK: preferred = 1
+ // CHECK: size = 1
+ "test.data_layout_query"() : () -> !llvm.byte<1>
+
+ return
+ }
+}
diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
index d0acdfec6c8de..e5214ecebad23 100644
--- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir
+++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
@@ -402,6 +402,20 @@ func.func @casts(%arg0: i32, %arg1: i64, %arg2: vector<4xi32>,
%10 = llvm.addrspacecast %arg4 : !llvm.ptr to !llvm.ptr<2>
// CHECK: = llvm.bitcast %[[I64]] : i64 to f64
%11 = llvm.bitcast %arg1 : i64 to f64
+// CHECK: = llvm.bitcast %[[I64]] : i64 to !llvm.byte<64>
+ %12 = llvm.bitcast %arg1 : i64 to !llvm.byte<64>
+// CHECK: = llvm.bitcast %12 : !llvm.byte<64> to i64
+ %13 = llvm.bitcast %12 : !llvm.byte<64> to i64
+// CHECK: = llvm.bitcast %[[I64]] : i64 to !llvm.byte<8>
+ %14 = llvm.bitcast %arg1 : i64 to !llvm.byte<8>
+// CHECK: = llvm.bitcast %12 : !llvm.byte<64> to i32
+ %15 = llvm.bitcast %12 : !llvm.byte<64> to i32
+// CHECK: = llvm.bitcast %12 : !llvm.byte<64> to !llvm.byte<8>
+ %16 = llvm.bitcast %12 : !llvm.byte<64> to !llvm.byte<8>
+// CHECK: = llvm.bitcast %12 : !llvm.byte<64> to !llvm.ptr
+ %17 = llvm.bitcast %12 : !llvm.byte<64> to !llvm.ptr
+// CHECK: = llvm.bitcast %[[PTR]] : !llvm.ptr to !llvm.byte<8>
+ %18 = llvm.bitcast %arg4 : !llvm.ptr to !llvm.byte<8>
llvm.return
}
diff --git a/mlir/test/Dialect/LLVMIR/types-invalid.mlir b/mlir/test/Dialect/LLVMIR/types-invalid.mlir
index 018a82c7b259c..76a7259883e8f 100644
--- a/mlir/test/Dialect/LLVMIR/types-invalid.mlir
+++ b/mlir/test/Dialect/LLVMIR/types-invalid.mlir
@@ -154,3 +154,19 @@ func.func private @target_ext_no_name() {
// expected-error at below {{failed to parse LLVMTargetExtType parameter 'extTypeName' which is to be a `::llvm::StringRef`}}
"some.op"() : () -> !llvm.target<i32, 42>
}
+
+// -----
+
+func.func @byte_invalid_bitwidth() {
+ // expected-error at below {{bitwidth must be less than 8388608, but got 8388608}}
+ %0 = "some.op"() : () -> !llvm.byte<8388608>
+ llvm.return
+}
+
+// -----
+
+llvm.func @byte_zero_bitwidth() {
+ // expected-error at below {{bitwidth must be greater than 0}}
+ %0 = "some.op"() : () -> !llvm.byte<0>
+ llvm.return
+}
diff --git a/mlir/test/Dialect/LLVMIR/types.mlir b/mlir/test/Dialect/LLVMIR/types.mlir
index 69582546cb429..ddea95b3979bd 100644
--- a/mlir/test/Dialect/LLVMIR/types.mlir
+++ b/mlir/test/Dialect/LLVMIR/types.mlir
@@ -197,3 +197,14 @@ llvm.func @ext_target() {
%4 = "some.op"() : () -> !llvm.target<"target5", i32, f64, 0, 5>
llvm.return
}
+
+// -----
+
+// CHECK-LABEL: byte
+llvm.func @byte() {
+ // CHECK: !llvm.byte<8>
+ %0 = "some.op"() : () -> !llvm.byte<8>
+ // CHECK: !llvm.byte<1>
+ %1 = "some.op"() : () -> !llvm.byte<1>
+ llvm.return
+}
diff --git a/mlir/test/Target/LLVMIR/Import/basic.ll b/mlir/test/Target/LLVMIR/Import/basic.ll
index 332b583b5ce3b..8ee797c49f519 100644
--- a/mlir/test/Target/LLVMIR/Import/basic.ll
+++ b/mlir/test/Target/LLVMIR/Import/basic.ll
@@ -97,3 +97,9 @@ define i32 @variadic_function(i32 %X, ...) {
; CHECK: llvm.return
ret i32 %ret
}
+
+; CHECK-LABEL: llvm.func @byte_type(%arg0: !llvm.byte<8>) -> !llvm.byte<8>
+define b8 @byte_type(b8 %0) {
+ ; CHECK: llvm.return %arg0 : !llvm.byte<8>
+ ret b8 %0
+}
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index 58c08fed772b0..5eca8f19154a1 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -3557,3 +3557,10 @@ llvm.mlir.global external @target_specific_attrs_only() {target_specific_attrs =
// CHECK: @target_specific_attrs_combined = global i32 2, section "mysection", align 4 #[[ATTRS:[0-9]+]]
// CHECK: attributes #[[ATTRS]] = { norecurse "bss-section"="my_bss.1" }
llvm.mlir.global external @target_specific_attrs_combined(2 : i32) {alignment = 4 : i64, section = "mysection", target_specific_attrs = ["norecurse", ["bss-section", "my_bss.1"]]} : i32
+
+// -----
+
+// CHECK-LABEL: define b8 @byte_type(b8 %0)
+llvm.func @byte_type(%arg0: !llvm.byte<8>) -> !llvm.byte<8> {
+ llvm.return %arg0 : !llvm.byte<8>
+}
More information about the Mlir-commits
mailing list