[llvm] 93a5597 - [Verifier][IR] Make sure bitcast doesn't change type size (#230914)

via llvm-commits llvm-commits at lists.llvm.org
Sun Oct 11 02:53:16 PDT 2026


Author: Yingwei Zheng
Date: 2026-10-11T17:53:11+08:00
New Revision: 93a5597d119439a8cc05533fa695db73a63a0cd0

URL: https://github.com/llvm/llvm-project/commit/93a5597d119439a8cc05533fa695db73a63a0cd0
DIFF: https://github.com/llvm/llvm-project/commit/93a5597d119439a8cc05533fa695db73a63a0cd0.diff

LOG: [Verifier][IR] Make sure bitcast doesn't change type size (#230914)

`CastInst::castIsValid` doesn't check whether the source type and
destination type share the same size for bitcast between pointer and
byte types. Since DL is unavailable in this function, we perform the
DL-aware size check in the verifier. `CastInst::castIsValid` also
rejects unsized and target extension types to avoid assertions in
`getTypeSize`.

The tests are generated by DeepSeek-V4.1-Flash. Without this check llubi
will assert in `fromBytes/toBytes`.

Added: 
    llvm/test/Verifier/bitcast-byte-constexpr-neg.ll
    llvm/test/Verifier/bitcast-byte-scalable-neg.ll
    llvm/test/Verifier/bitcast-byte-width-neg.ll
    llvm/test/Verifier/bitcast-byte-width-pos.ll
    llvm/test/Verifier/bitcast-unsupported-type-neg.ll

Modified: 
    llvm/lib/IR/Instructions.cpp
    llvm/lib/IR/Verifier.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp
index fa91cb79a63f9..74831e3afcad2 100644
--- a/llvm/lib/IR/Instructions.cpp
+++ b/llvm/lib/IR/Instructions.cpp
@@ -3498,8 +3498,15 @@ CastInst::castIsValid(Instruction::CastOps op, Type *SrcTy, Type *DstTy) {
       return false;
     return SrcTy->isIntOrIntVectorTy() && DstTy->isPtrOrPtrVectorTy();
   case Instruction::BitCast: {
-    PointerType *SrcPtrTy = dyn_cast<PointerType>(SrcTy->getScalarType());
-    PointerType *DstPtrTy = dyn_cast<PointerType>(DstTy->getScalarType());
+    Type *SrcScalarTy = SrcTy->getScalarType();
+    if (SrcScalarTy->isTargetExtTy() || !SrcScalarTy->isSingleValueType())
+      return false;
+    Type *DstScalarTy = DstTy->getScalarType();
+    if (DstScalarTy->isTargetExtTy() || !DstScalarTy->isSingleValueType())
+      return false;
+
+    PointerType *SrcPtrTy = dyn_cast<PointerType>(SrcScalarTy);
+    PointerType *DstPtrTy = dyn_cast<PointerType>(DstScalarTy);
 
     // BitCast implies a no-op cast of type only. No bits change.
     // However, you can't cast pointers to anything but pointers/bytes.

diff  --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 7785d3c0b78e0..ed7c68b24df1b 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2961,11 +2961,14 @@ void Verifier::visitConstantExprsRecursively(const Constant *EntryC) {
 }
 
 void Verifier::visitConstantExpr(const ConstantExpr *CE) {
-  if (CE->getOpcode() == Instruction::BitCast)
+  if (CE->getOpcode() == Instruction::BitCast) {
     Check(CastInst::castIsValid(Instruction::BitCast, CE->getOperand(0),
                                 CE->getType()),
           "Invalid bitcast", CE);
-  else if (CE->getOpcode() == Instruction::PtrToAddr)
+    Check(DL.getTypeSizeInBits(CE->getOperand(0)->getType()) ==
+              DL.getTypeSizeInBits(CE->getType()),
+          "Invalid bitcast", CE);
+  } else if (CE->getOpcode() == Instruction::PtrToAddr)
     checkPtrToAddr(CE->getOperand(0)->getType(), CE->getType(), *CE);
 }
 
@@ -3990,6 +3993,9 @@ void Verifier::visitBitCastInst(BitCastInst &I) {
   Check(
       CastInst::castIsValid(Instruction::BitCast, I.getOperand(0), I.getType()),
       "Invalid bitcast", &I);
+  Check(DL.getTypeSizeInBits(I.getSrcTy()) ==
+            DL.getTypeSizeInBits(I.getDestTy()),
+        "Invalid bitcast", &I);
   visitInstruction(I);
 }
 

diff  --git a/llvm/test/Verifier/bitcast-byte-constexpr-neg.ll b/llvm/test/Verifier/bitcast-byte-constexpr-neg.ll
new file mode 100644
index 0000000000000..b7351358101c4
--- /dev/null
+++ b/llvm/test/Verifier/bitcast-byte-constexpr-neg.ll
@@ -0,0 +1,10 @@
+; RUN: not llvm-as -disable-output %s 2>&1 | FileCheck %s
+;
+; The same size requirement applies to bitcast constant expressions.
+
+target datalayout = "e-p:64:64:64"
+
+; CHECK: Invalid bitcast
+; CHECK: b1 bitcast (ptr @g to b1)
+ at g = global i8 0
+ at x = global b1 bitcast (ptr @g to b1)

diff  --git a/llvm/test/Verifier/bitcast-byte-scalable-neg.ll b/llvm/test/Verifier/bitcast-byte-scalable-neg.ll
new file mode 100644
index 0000000000000..b252020745700
--- /dev/null
+++ b/llvm/test/Verifier/bitcast-byte-scalable-neg.ll
@@ -0,0 +1,22 @@
+; RUN: not llvm-as -disable-output %s 2>&1 | FileCheck %s
+;
+; With p:64:
+;   <2 x ptr>          = 2*64 = 128 bits (fixed)
+;   <vscale x 4 x b32> = 4*32 = 128*vscale bits (scalable)
+;   <vscale x 4 x ptr> = 4*64 = 256*vscale bits (scalable)
+;
+; A bitcast requires identical sizes, so neither pair is bitcastable: %a has
+; the same minimum size but 
diff erent scalability, %b has 
diff erent minimum
+; sizes.
+
+target datalayout = "e-p:64:64:64"
+
+; CHECK: Invalid bitcast
+; CHECK: %a = bitcast <2 x ptr> %vp to <vscale x 4 x b32>
+; CHECK: Invalid bitcast
+; CHECK: %b = bitcast <vscale x 4 x ptr> %sp to <vscale x 4 x b32>
+define void @f(<2 x ptr> %vp, <vscale x 4 x ptr> %sp) {
+  %a = bitcast <2 x ptr> %vp to <vscale x 4 x b32>
+  %b = bitcast <vscale x 4 x ptr> %sp to <vscale x 4 x b32>
+  ret void
+}

diff  --git a/llvm/test/Verifier/bitcast-byte-width-neg.ll b/llvm/test/Verifier/bitcast-byte-width-neg.ll
new file mode 100644
index 0000000000000..2202b3b27c3b3
--- /dev/null
+++ b/llvm/test/Verifier/bitcast-byte-width-neg.ll
@@ -0,0 +1,16 @@
+; RUN: not llvm-as -disable-output %s 2>&1 | FileCheck %s
+;
+; A pointer may only be bitcast to a byte (vector) of the same size, and vice
+; versa.
+
+target datalayout = "e-p:64:64:64"
+
+; CHECK: Invalid bitcast
+; CHECK: %x = bitcast ptr %p to b1
+; CHECK: Invalid bitcast
+; CHECK: %y = bitcast b1 %b to ptr
+define void @f(ptr %p, b1 %b) {
+  %x = bitcast ptr %p to b1
+  %y = bitcast b1 %b to ptr
+  ret void
+}

diff  --git a/llvm/test/Verifier/bitcast-byte-width-pos.ll b/llvm/test/Verifier/bitcast-byte-width-pos.ll
new file mode 100644
index 0000000000000..dc4192f1fef7b
--- /dev/null
+++ b/llvm/test/Verifier/bitcast-byte-width-pos.ll
@@ -0,0 +1,25 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=verify -S < %s | FileCheck %s
+;
+; Pointer/byte bitcasts must have matching sizes, using the per-address-space
+; pointer size from the DataLayout, including scalable vector types.
+
+target datalayout = "e-p:64:64:64-p1:32:32:32"
+
+define void @f(ptr %p, ptr addrspace(1) %p1, <2 x ptr addrspace(1)> %p1v, <vscale x 4 x ptr> %sp, <vscale x 4 x b8> %sb) {
+; CHECK-LABEL: define void @f(
+; CHECK-SAME: ptr [[P:%.*]], ptr addrspace(1) [[P1:%.*]], <2 x ptr addrspace(1)> [[P1V:%.*]], <vscale x 4 x ptr> [[SP:%.*]], <vscale x 4 x b8> [[SB:%.*]]) {
+; CHECK-NEXT:    [[A:%.*]] = bitcast ptr [[P]] to b64
+; CHECK-NEXT:    [[B:%.*]] = bitcast ptr addrspace(1) [[P1]] to b32
+; CHECK-NEXT:    [[C:%.*]] = bitcast <2 x ptr addrspace(1)> [[P1V]] to b64
+; CHECK-NEXT:    [[D:%.*]] = bitcast <vscale x 4 x ptr> [[SP]] to <vscale x 8 x b32>
+; CHECK-NEXT:    [[E:%.*]] = bitcast <vscale x 4 x b8> [[SB]] to <vscale x 4 x i8>
+; CHECK-NEXT:    ret void
+;
+  %a = bitcast ptr %p to b64
+  %b = bitcast ptr addrspace(1) %p1 to b32
+  %c = bitcast <2 x ptr addrspace(1)> %p1v to b64
+  %d = bitcast <vscale x 4 x ptr> %sp to <vscale x 8 x b32>
+  %e = bitcast <vscale x 4 x b8> %sb to <vscale x 4 x i8>
+  ret void
+}

diff  --git a/llvm/test/Verifier/bitcast-unsupported-type-neg.ll b/llvm/test/Verifier/bitcast-unsupported-type-neg.ll
new file mode 100644
index 0000000000000..a4a86c7ecf703
--- /dev/null
+++ b/llvm/test/Verifier/bitcast-unsupported-type-neg.ll
@@ -0,0 +1,42 @@
+; RUN: split-file %s %t
+; RUN: not llvm-as -disable-output %t/token.ll 2>&1 | FileCheck %t/token.ll
+; RUN: not llvm-as -disable-output %t/label.ll 2>&1 | FileCheck %t/label.ll
+; RUN: not llvm-as -disable-output %t/metadata.ll 2>&1 | FileCheck %t/metadata.ll
+; RUN: not llvm-as -disable-output %t/target-ext.ll 2>&1 | FileCheck %t/target-ext.ll
+; RUN: not llvm-as -disable-output %t/vector-of-target-ext.ll 2>&1 | FileCheck %t/vector-of-target-ext.ll
+
+;--- token.ll
+; CHECK: invalid cast opcode for cast from 'token' to 'token'
+define void @f(token %t) {
+  %x = bitcast token %t to token
+  ret void
+}
+
+;--- label.ll
+; CHECK: invalid cast opcode for cast from 'label' to 'label'
+define void @f() {
+entry:
+  %x = bitcast label %entry to label
+  ret void
+}
+
+;--- metadata.ll
+; CHECK: invalid cast opcode for cast from 'metadata' to 'metadata'
+define void @f(metadata %m) {
+  %x = bitcast metadata %m to metadata
+  ret void
+}
+
+;--- target-ext.ll
+; CHECK: invalid cast opcode for cast from 'target("foo")' to 'target("foo")'
+define void @f(target("foo") %x) {
+  %y = bitcast target("foo") %x to target("foo")
+  ret void
+}
+
+;--- vector-of-target-ext.ll
+; CHECK: invalid cast opcode for cast from '<2 x target("llvm.test.vectorelement")>' to '<2 x target("llvm.test.vectorelement")>'
+define void @f(<2 x target("llvm.test.vectorelement")> %x) {
+  %y = bitcast <2 x target("llvm.test.vectorelement")> %x to <2 x target("llvm.test.vectorelement")>
+  ret void
+}


        


More information about the llvm-commits mailing list