[Mlir-commits] [mlir] [mlir][LLVM] Add the `byte` type to the LLVM dialect (PR #203795)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Sun Jun 14 14:18:41 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Markus Böck (zero9178)

<details>
<summary>Changes</summary>

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

---
Full diff: https://github.com/llvm/llvm-project/pull/203795.diff


11 Files Affected:

- (modified) mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td (+25) 
- (modified) mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp (+9-4) 
- (modified) mlir/lib/Dialect/LLVMIR/IR/LLVMTypeSyntax.cpp (+3-1) 
- (modified) mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp (+23-1) 
- (modified) mlir/lib/Target/LLVMIR/TypeFromLLVM.cpp (+8-2) 
- (modified) mlir/lib/Target/LLVMIR/TypeToLLVM.cpp (+9-3) 
- (modified) mlir/test/Dialect/LLVMIR/layout.mlir (+57) 
- (modified) mlir/test/Dialect/LLVMIR/roundtrip.mlir (+14) 
- (modified) mlir/test/Dialect/LLVMIR/types.mlir (+11) 
- (modified) mlir/test/Target/LLVMIR/Import/basic.ll (+6) 
- (modified) mlir/test/Target/LLVMIR/llvmir.mlir (+7) 


``````````diff
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td
index 072fc7be07dd4..dd3eaef87885c 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td
@@ -60,6 +60,31 @@ 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 `>`
+  }];
+}
+
 //===----------------------------------------------------------------------===//
 // LLVMFunctionType
 //===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index ac6cf03e057dc..ff9b13b00df78 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -3867,10 +3867,15 @@ 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());
+
+  // 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..46abad337acfd 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,7 +103,7 @@ void mlir::LLVM::detail::printType(Type type, AsmPrinter &printer) {
   printer << getTypeKeyword(type);
 
   llvm::TypeSwitch<Type>(type)
-      .Case<LLVMPointerType, LLVMArrayType, LLVMFunctionType, LLVMTargetExtType,
+      .Case<LLVMPointerType, LLVMArrayType, LLVMByteType, LLVMFunctionType, LLVMTargetExtType,
             LLVMStructType>([&](auto type) { type.print(printer); });
 }
 
@@ -270,6 +271,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..f8bb2a0e39f22 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp
@@ -203,6 +203,21 @@ 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(), 8));
+}
+
 //===----------------------------------------------------------------------===//
 // Function type.
 //===----------------------------------------------------------------------===//
@@ -737,6 +752,7 @@ bool mlir::LLVM::isCompatibleOuterType(Type type) {
       Float80Type,
       Float128Type,
       LLVMArrayType,
+      LLVMByteType,
       LLVMFunctionType,
       LLVMLabelType,
       LLVMMetadataType,
@@ -800,6 +816,7 @@ static bool isCompatibleImpl(Type type, DenseSet<Type> &compatibleTypes) {
             Float64Type,
             Float80Type,
             Float128Type,
+            LLVMByteType,
             LLVMLabelType,
             LLVMMetadataType,
             LLVMPPCFP128Type,
@@ -856,7 +873,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 +924,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 +955,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.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>
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/203795


More information about the Mlir-commits mailing list