https://github.com/Nemi26 updated 
https://github.com/llvm/llvm-project/pull/231830

>From 8d9036d2eca6528ac473479d8eccaa55a31c48e9 Mon Sep 17 00:00:00 2001
From: Nemi26 <[email protected]>
Date: Wed, 14 Oct 2026 11:28:07 +0200
Subject: [PATCH 1/3] [clang] Load non-simple lvalue operands of
 __builtin_bit_cast

CK_LValueToRValueBitCast reads its operand through the operand's address.
Sema only materialized prvalue operands, so a glvalue without an address
of its own reached CodeGen as is: a vector element, ext_vector swizzle,
matrix element, bit-field or global register variable. CodeGen took the
address of the containing vector, matrix or bit-field storage unit and
read from its start, so `__builtin_bit_cast(unsigned, v.z)` returned the
bits of lane 0, while constant evaluation of the same expression gave
the right answer.

Apply the lvalue-to-rvalue conversion to such operands in Sema and
materialize the result, as is already done for prvalues, and assert in
CodeGen that the operand lvalue is simple.

The constant evaluator now reports an uninitialized vector element read
through a bit_cast as a read of an uninitialized object, rather than as
an invalid subexpression.

Fixes #95673
Fixes #219531
Fixes #226388

This patch was written with the assistance of an AI tool (Claude Code,
Claude Opus); the submitter reviewed it.

Assisted-by: Claude Code (Claude Opus)
---
 clang/docs/ReleaseNotes.md                    |  4 +
 clang/lib/CodeGen/CGExprScalar.cpp            |  1 +
 clang/lib/Sema/SemaCast.cpp                   |  9 ++
 .../builtin-bit-cast-nonsimple-lvalue.cpp     | 92 +++++++++++++++++++
 .../SemaCXX/constexpr-builtin-bit-cast.cpp    | 14 +++
 .../constexpr-vectors-access-elements.cpp     | 13 ++-
 6 files changed, 129 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/builtin-bit-cast-nonsimple-lvalue.cpp

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 0103c295379207..e3594e4993e8e5 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -756,6 +756,10 @@ features cannot lower the translation-unit ABI level;
 - Fixed an assertion failure when `__builtin_object_size` or
   `__builtin_dynamic_object_size` is called with an out-of-range type argument
   during constant evaluation. (#GH225041)
+- Fixed wrong code generation for `__builtin_bit_cast` of a vector element,
+  matrix element, bit-field or global register variable, which read from the
+  start of the containing object instead of the element or field.
+  (#GH95673), (#GH219531), (#GH226388)
 
 #### Bug Fixes to Attribute Support
 
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp 
b/clang/lib/CodeGen/CGExprScalar.cpp
index db2b7a4ad1fce2..5dccebcf842a87 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -2655,6 +2655,7 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
 
   case CK_LValueToRValueBitCast: {
     LValue SourceLVal = CGF.EmitLValue(E);
+    assert(SourceLVal.isSimple() && "bit_cast operand has no address");
     Address Addr =
         SourceLVal.getAddress().withElementType(CGF.ConvertTypeForMem(DestTy));
     LValue DestLV = CGF.MakeAddrLValue(Addr, DestTy);
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index c797bdb11e0cbd..7ebb2deb4ad48c 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -3404,6 +3404,15 @@ void CastOperation::CheckBuiltinBitCast() {
     return;
   }
 
+  if (SrcExpr.get()->isGLValue() &&
+      (SrcExpr.get()->getObjectKind() != OK_Ordinary ||
+       SrcExpr.get()->refersToGlobalRegisterVar())) {
+    SrcExpr = Self.DefaultLvalueConversion(SrcExpr.get());
+    if (SrcExpr.isInvalid())
+      return;
+    SrcType = SrcExpr.get()->getType();
+  }
+
   if (SrcExpr.get()->isPRValue())
     SrcExpr = Self.CreateMaterializeTemporaryExpr(SrcType, SrcExpr.get(),
                                                   /*IsLValueReference=*/false);
diff --git a/clang/test/CodeGenCXX/builtin-bit-cast-nonsimple-lvalue.cpp 
b/clang/test/CodeGenCXX/builtin-bit-cast-nonsimple-lvalue.cpp
new file mode 100644
index 00000000000000..8818c714d73b84
--- /dev/null
+++ b/clang/test/CodeGenCXX/builtin-bit-cast-nonsimple-lvalue.cpp
@@ -0,0 +1,92 @@
+// RUN: %clang_cc1 -std=c++20 -fenable-matrix -emit-llvm -disable-llvm-passes 
-triple x86_64-unknown-linux-gnu %s -o - | FileCheck %s
+
+// An operand without an address of its own is loaded through the lvalue and
+// materialized, rather than read from the start of the containing object.
+
+typedef float v4f __attribute__((ext_vector_type(4)));
+typedef float gv4f __attribute__((vector_size(16)));
+typedef float m2x2f __attribute__((matrix_type(2, 2)));
+
+// CHECK-LABEL: define{{.*}} i32 @_Z18ext_vector_elementDv4_f(
+// CHECK:         [[TMP:%.*]] = alloca float
+// CHECK:         [[VEC:%.*]] = load <4 x float>, ptr %v.addr
+// CHECK-NEXT:    [[ELT:%.*]] = extractelement <4 x float> [[VEC]], i64 2
+// CHECK-NEXT:    store float [[ELT]], ptr [[TMP]]
+// CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[TMP]]
+// CHECK-NEXT:    ret i32 [[RES]]
+unsigned ext_vector_element(v4f v) {
+  return __builtin_bit_cast(unsigned, v.z);
+}
+
+// CHECK-LABEL: define{{.*}} i32 @_Z20ext_vector_subscriptDv4_f(
+// CHECK:         [[TMP:%.*]] = alloca float
+// CHECK:         [[VEC:%.*]] = load <4 x float>, ptr %v.addr
+// CHECK-NEXT:    [[ELT:%.*]] = extractelement <4 x float> [[VEC]], i32 1
+// CHECK-NEXT:    store float [[ELT]], ptr [[TMP]]
+// CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[TMP]]
+// CHECK-NEXT:    ret i32 [[RES]]
+unsigned ext_vector_subscript(v4f v) {
+  return __builtin_bit_cast(unsigned, v[1]);
+}
+
+// CHECK-LABEL: define{{.*}} i64 @_Z18ext_vector_swizzleDv4_f(
+// CHECK:         [[TMP:%.*]] = alloca <2 x float>
+// CHECK:         [[VEC:%.*]] = load <4 x float>, ptr %v.addr
+// CHECK-NEXT:    [[ELTS:%.*]] = shufflevector <4 x float> [[VEC]], <4 x 
float> poison, <2 x i32> <i32 2, i32 3>
+// CHECK-NEXT:    store <2 x float> [[ELTS]], ptr [[TMP]]
+// CHECK-NEXT:    [[RES:%.*]] = load i64, ptr [[TMP]]
+// CHECK-NEXT:    ret i64 [[RES]]
+unsigned long long ext_vector_swizzle(v4f v) {
+  return __builtin_bit_cast(unsigned long long, v.zw);
+}
+
+// CHECK-LABEL: define{{.*}} i32 @_Z20gnu_vector_subscriptDv4_fi(
+// CHECK:         [[TMP:%.*]] = alloca float
+// CHECK:         [[VEC:%.*]] = load <4 x float>, ptr %v.addr
+// CHECK-NEXT:    [[IDX:%.*]] = load i32, ptr %i.addr
+// CHECK-NEXT:    [[ELT:%.*]] = extractelement <4 x float> [[VEC]], i32 [[IDX]]
+// CHECK-NEXT:    store float [[ELT]], ptr [[TMP]]
+// CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[TMP]]
+// CHECK-NEXT:    ret i32 [[RES]]
+unsigned gnu_vector_subscript(gv4f v, int i) {
+  return __builtin_bit_cast(unsigned, v[i]);
+}
+
+// CHECK-LABEL: define{{.*}} i32 @_Z14matrix_elementu11matrix_typeILm2ELm2EfE(
+// CHECK:         [[TMP:%.*]] = alloca float
+// CHECK:         [[MAT:%.*]] = load <4 x float>, ptr %m.addr
+// CHECK-NEXT:    [[ELT:%.*]] = extractelement <4 x float> [[MAT]], i64 3
+// CHECK-NEXT:    store float [[ELT]], ptr [[TMP]]
+// CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[TMP]]
+// CHECK-NEXT:    ret i32 [[RES]]
+unsigned matrix_element(m2x2f m) {
+  return __builtin_bit_cast(unsigned, m[1][1]);
+}
+
+struct S {
+  unsigned a : 4, b : 12;
+};
+
+// CHECK-LABEL: define{{.*}} i32 @_Z9bit_field1S(
+// CHECK:         [[TMP:%.*]] = alloca i32
+// CHECK:         [[LOAD:%.*]] = load i16, ptr %s
+// CHECK-NEXT:    [[SHR:%.*]] = lshr i16 [[LOAD]], 4
+// CHECK-NEXT:    [[EXT:%.*]] = zext i16 [[SHR]] to i32
+// CHECK-NEXT:    store i32 [[EXT]], ptr [[TMP]]
+// CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[TMP]]
+// CHECK-NEXT:    ret i32 [[RES]]
+unsigned bit_field(S s) {
+  return __builtin_bit_cast(unsigned, s.b);
+}
+
+register unsigned long stack_pointer asm("rsp");
+
+// CHECK-LABEL: define{{.*}} i64 @_Z15global_registerv(
+// CHECK:         [[TMP:%.*]] = alloca i64
+// CHECK:         [[REG:%.*]] = call i64 @llvm.read_register.i64(
+// CHECK-NEXT:    store i64 [[REG]], ptr [[TMP]]
+// CHECK-NEXT:    [[RES:%.*]] = load i64, ptr [[TMP]]
+// CHECK-NEXT:    ret i64 [[RES]]
+long global_register() {
+  return __builtin_bit_cast(long, stack_pointer);
+}
diff --git a/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp 
b/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp
index ea5333df0b4698..db53076891b47b 100644
--- a/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp
+++ b/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp
@@ -491,6 +491,20 @@ static_assert(bit_cast<unsigned long long>(test_vector) == 
(LITTLE_END
 static_assert(round_trip<uint2>(0xCAFEBABE0C05FEFEULL), "");
 static_assert(round_trip<byte8>(0xCAFEBABE0C05FEFEULL), "");
 
+typedef unsigned uint2e __attribute__((ext_vector_type(2)));
+constexpr uint2e test_ext_vector = { 0x0C05FEFE, 0xCAFEBABE };
+
+// Elements are read by value, not from the start of the vector.
+static_assert(__builtin_bit_cast(unsigned, test_vector[1]) == 0xCAFEBABE, "");
+static_assert(__builtin_bit_cast(unsigned, test_ext_vector[1]) == 0xCAFEBABE, 
"");
+static_assert(__builtin_bit_cast(unsigned, test_ext_vector.y) == 0xCAFEBABE, 
"");
+
+struct BitField {
+  unsigned a : 4, b : 12;
+};
+constexpr BitField test_bit_field = { 0xF, 0x123 };
+static_assert(__builtin_bit_cast(unsigned, test_bit_field.b) == 0x123, "");
+
 typedef bool bool8 __attribute__((ext_vector_type(8)));
 typedef bool bool9 __attribute__((ext_vector_type(9)));
 typedef bool bool16 __attribute__((ext_vector_type(16)));
diff --git a/clang/test/SemaCXX/constexpr-vectors-access-elements.cpp 
b/clang/test/SemaCXX/constexpr-vectors-access-elements.cpp
index 481b80ceb9c1e0..c6eb66f6b2995a 100644
--- a/clang/test/SemaCXX/constexpr-vectors-access-elements.cpp
+++ b/clang/test/SemaCXX/constexpr-vectors-access-elements.cpp
@@ -65,17 +65,22 @@ static_assert(t2[15] == -2);
 
 using I = int __attribute__((vector_size(16)));
 
-// expected-error@+1 {{constexpr function never produces a constant 
expression}}
 constexpr unsigned test2() {
-  // cpp17-warning@+1 {{uninitialized variable in a constexpr function is a 
C++20 extension}}
+  // cpp17-warning@+2 {{uninitialized variable in a constexpr function is a 
C++20 extension}}
+  // expected-note@+1 {{declared here}}
   I v;
 
-  // expected-note@+2 {{subexpression not valid in a constant expression}}
-  // expected-note@+1 {{subexpression not valid in a constant expression}}
+  // expected-note@+1 {{read of uninitialized object is not allowed in a 
constant expression}}
   return __builtin_bit_cast(unsigned, v[0]);
 }
 
 // expected-error@+2 {{static assertion expression is not an integral constant 
expression}}
 // expected-note@+1 {{in call to 'test2()'}}
 static_assert(test2(), "");
+
+constexpr unsigned test3() {
+  I v = {1, 2, 3, 4};
+  return __builtin_bit_cast(unsigned, v[1]);
+}
+static_assert(test3() == 2, "");
 }

>From aa3d803912acc60b3490a5fe2514b1f6ebb97e83 Mon Sep 17 00:00:00 2001
From: Nemi26 <[email protected]>
Date: Sun, 18 Oct 2026 21:31:50 +0200
Subject: [PATCH 2/3] [clang] Bind the __builtin_bit_cast operand as a const
 reference

Initialize the operand as std::bit_cast initializes its 'const From &'
parameter, where From keeps the qualifiers of a glvalue operand. Vector
elements, matrix elements and bit-fields are copied into a temporary,
prvalues are materialized as before, and volatile operands are still
accepted; the load from a volatile operand is now volatile.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
---
 clang/docs/ReleaseNotes.md                    |  8 ++--
 clang/lib/AST/ExprConstant.cpp                |  2 +-
 clang/lib/CodeGen/CGExprScalar.cpp            |  3 +-
 clang/lib/Sema/SemaCast.cpp                   | 22 ++++-----
 clang/test/AST/ByteCode/builtin-bit-cast.cpp  |  2 +-
 clang/test/CXX/drs/cwg722.cpp                 | 10 ++--
 .../builtin-bit-cast-nonsimple-lvalue.cpp     | 46 +++++++++++--------
 clang/test/SemaCXX/builtin-bit-cast.cpp       | 11 +++++
 .../SemaCXX/constexpr-builtin-bit-cast.cpp    |  2 +-
 9 files changed, 63 insertions(+), 43 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index e3594e4993e8e5..0ca5c29f0cd0a4 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -756,9 +756,11 @@ features cannot lower the translation-unit ABI level;
 - Fixed an assertion failure when `__builtin_object_size` or
   `__builtin_dynamic_object_size` is called with an out-of-range type argument
   during constant evaluation. (#GH225041)
-- Fixed wrong code generation for `__builtin_bit_cast` of a vector element,
-  matrix element, bit-field or global register variable, which read from the
-  start of the containing object instead of the element or field.
+- `__builtin_bit_cast` now binds its operand as `std::bit_cast` binds its
+  `const From &` parameter. A vector element, matrix element or bit-field is
+  copied into a temporary, which is then read; previously the read started at
+  the containing vector, matrix or bit-field storage unit, which gave the wrong
+  value. A volatile operand is now read with a volatile load.
   (#GH95673), (#GH219531), (#GH226388)
 
 #### Bug Fixes to Attribute Support
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 87909992a46d51..dafdf5a85cf592 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -8552,7 +8552,7 @@ static bool 
checkBitCastConstexprEligibilityType(SourceLocation Loc,
   auto note = [&](int Construct, QualType NoteTy, SourceLocation NoteLoc) {
     if (Info)
       Info->Note(NoteLoc, diag::note_constexpr_bit_cast_invalid_subtype)
-          << NoteTy << Construct << Ty;
+          << NoteTy << Construct << Ty.getUnqualifiedType();
     return false;
   };
 
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp 
b/clang/lib/CodeGen/CGExprScalar.cpp
index 5dccebcf842a87..f56abaa60efd7c 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -2655,10 +2655,11 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
 
   case CK_LValueToRValueBitCast: {
     LValue SourceLVal = CGF.EmitLValue(E);
-    assert(SourceLVal.isSimple() && "bit_cast operand has no address");
     Address Addr =
         SourceLVal.getAddress().withElementType(CGF.ConvertTypeForMem(DestTy));
     LValue DestLV = CGF.MakeAddrLValue(Addr, DestTy);
+    if (SourceLVal.isVolatileQualified())
+      DestLV.getQuals().addVolatile();
     DestLV.setTBAAInfo(TBAAAccessInfo::getMayAliasInfo());
     return EmitLoadOfLValue(DestLV, CE->getExprLoc());
   }
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index 7ebb2deb4ad48c..0539146ff1c856 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -3404,19 +3404,6 @@ void CastOperation::CheckBuiltinBitCast() {
     return;
   }
 
-  if (SrcExpr.get()->isGLValue() &&
-      (SrcExpr.get()->getObjectKind() != OK_Ordinary ||
-       SrcExpr.get()->refersToGlobalRegisterVar())) {
-    SrcExpr = Self.DefaultLvalueConversion(SrcExpr.get());
-    if (SrcExpr.isInvalid())
-      return;
-    SrcType = SrcExpr.get()->getType();
-  }
-
-  if (SrcExpr.get()->isPRValue())
-    SrcExpr = Self.CreateMaterializeTemporaryExpr(SrcType, SrcExpr.get(),
-                                                  /*IsLValueReference=*/false);
-
   CharUnits DestSize = Self.Context.getTypeSizeInChars(DestType);
   CharUnits SourceSize = Self.Context.getTypeSizeInChars(SrcType);
   if (DestSize != SourceSize) {
@@ -3441,6 +3428,15 @@ void CastOperation::CheckBuiltinBitCast() {
     return;
   }
 
+  // The operand binds as it would to the 'const From &' of std::bit_cast.
+  QualType RefType = Self.Context.getLValueReferenceType(SrcType.withConst());
+  SrcExpr = Self.PerformCopyInitialization(
+      InitializedEntity::InitializeParameter(Self.Context, RefType,
+                                             /*Consumed=*/false),
+      SrcExpr.get()->getBeginLoc(), SrcExpr);
+  if (SrcExpr.isInvalid())
+    return;
+
   Kind = CK_LValueToRValueBitCast;
 }
 
diff --git a/clang/test/AST/ByteCode/builtin-bit-cast.cpp 
b/clang/test/AST/ByteCode/builtin-bit-cast.cpp
index 368728d00a8121..b548b5290d25f3 100644
--- a/clang/test/AST/ByteCode/builtin-bit-cast.cpp
+++ b/clang/test/AST/ByteCode/builtin-bit-cast.cpp
@@ -428,7 +428,7 @@ struct vol_mem {
 };
 
 // both-error@+2 {{constexpr variable 'run_vol_mem' must be initialized by a 
constant expression}}
-// both-note@+1 {{non-literal type 'vol_mem' cannot be used in a constant 
expression}}
+// both-note-re@+1 {{non-literal type '{{(const )?}}vol_mem' cannot be used in 
a constant expression}}
 constexpr int run_vol_mem = __builtin_bit_cast(int, vol_mem{43});
 
 struct mem_ptr {
diff --git a/clang/test/CXX/drs/cwg722.cpp b/clang/test/CXX/drs/cwg722.cpp
index 23f90a5601274c..ad323a1f7b8e84 100644
--- a/clang/test/CXX/drs/cwg722.cpp
+++ b/clang/test/CXX/drs/cwg722.cpp
@@ -47,10 +47,10 @@ void h() {
 // CHECK-NEXT:  |     `-DeclRefExpr {{.+}} 'std::nullptr_t ()' lvalue Function 
{{.+}} 'g' 'std::nullptr_t ()'
 // CHECK-NEXT:  `-ImplicitCastExpr {{.+}} 'void *' <NullToPointer>
 // CHECK-NEXT:    `-BuiltinBitCastExpr {{.+}} 'std::nullptr_t' 
<LValueToRValueBitCast>
-// CHECK-NEXT:      `-MaterializeTemporaryExpr {{.+}} 'void *' xvalue
-// CHECK-NEXT:        `-CXXStaticCastExpr {{.+}} 'void *' static_cast<void *> 
<NoOp>
-// CHECK-NEXT:          `-ImplicitCastExpr {{.+}} 'void *' <BitCast> 
part_of_explicit_cast
-// CHECK-NEXT:            `-UnaryOperator {{.+}} 'int *' prefix '&' cannot 
overflow
-// CHECK-NEXT:              `-DeclRefExpr {{.+}} 'int' lvalue Var {{.+}} 'i' 
'int'
+// CHECK-NEXT:      `-MaterializeTemporaryExpr {{.+}} 'void *const' lvalue
+// CHECK:               `-CXXStaticCastExpr {{.+}} 'void *' static_cast<void 
*> <NoOp>
+// CHECK-NEXT:            `-ImplicitCastExpr {{.+}} 'void *' <BitCast> 
part_of_explicit_cast
+// CHECK-NEXT:              `-UnaryOperator {{.+}} 'int *' prefix '&' cannot 
overflow
+// CHECK-NEXT:                `-DeclRefExpr {{.+}} 'int' lvalue Var {{.+}} 'i' 
'int'
 }
 #endif
diff --git a/clang/test/CodeGenCXX/builtin-bit-cast-nonsimple-lvalue.cpp 
b/clang/test/CodeGenCXX/builtin-bit-cast-nonsimple-lvalue.cpp
index 8818c714d73b84..f58d25412f686b 100644
--- a/clang/test/CodeGenCXX/builtin-bit-cast-nonsimple-lvalue.cpp
+++ b/clang/test/CodeGenCXX/builtin-bit-cast-nonsimple-lvalue.cpp
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -std=c++20 -fenable-matrix -emit-llvm -disable-llvm-passes 
-triple x86_64-unknown-linux-gnu %s -o - | FileCheck %s
+// RUN: %clang_cc1 -std=c++20 -fenable-matrix -O -emit-llvm 
-disable-llvm-passes -triple x86_64-unknown-linux-gnu %s -o - | FileCheck %s
 
-// An operand without an address of its own is loaded through the lvalue and
-// materialized, rather than read from the start of the containing object.
+// An operand without an address of its own is copied into a temporary, which
+// is then read.
 
 typedef float v4f __attribute__((ext_vector_type(4)));
 typedef float gv4f __attribute__((vector_size(16)));
@@ -9,10 +9,12 @@ typedef float m2x2f __attribute__((matrix_type(2, 2)));
 
 // CHECK-LABEL: define{{.*}} i32 @_Z18ext_vector_elementDv4_f(
 // CHECK:         [[TMP:%.*]] = alloca float
-// CHECK:         [[VEC:%.*]] = load <4 x float>, ptr %v.addr
+// CHECK:         call void @llvm.lifetime.start.p0(ptr [[TMP]])
+// CHECK-NEXT:    [[VEC:%.*]] = load <4 x float>, ptr %v.addr
 // CHECK-NEXT:    [[ELT:%.*]] = extractelement <4 x float> [[VEC]], i64 2
 // CHECK-NEXT:    store float [[ELT]], ptr [[TMP]]
 // CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[TMP]]
+// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[TMP]])
 // CHECK-NEXT:    ret i32 [[RES]]
 unsigned ext_vector_element(v4f v) {
   return __builtin_bit_cast(unsigned, v.z);
@@ -20,10 +22,12 @@ unsigned ext_vector_element(v4f v) {
 
 // CHECK-LABEL: define{{.*}} i32 @_Z20ext_vector_subscriptDv4_f(
 // CHECK:         [[TMP:%.*]] = alloca float
-// CHECK:         [[VEC:%.*]] = load <4 x float>, ptr %v.addr
+// CHECK:         call void @llvm.lifetime.start.p0(ptr [[TMP]])
+// CHECK-NEXT:    [[VEC:%.*]] = load <4 x float>, ptr %v.addr
 // CHECK-NEXT:    [[ELT:%.*]] = extractelement <4 x float> [[VEC]], i32 1
 // CHECK-NEXT:    store float [[ELT]], ptr [[TMP]]
 // CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[TMP]]
+// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[TMP]])
 // CHECK-NEXT:    ret i32 [[RES]]
 unsigned ext_vector_subscript(v4f v) {
   return __builtin_bit_cast(unsigned, v[1]);
@@ -31,10 +35,12 @@ unsigned ext_vector_subscript(v4f v) {
 
 // CHECK-LABEL: define{{.*}} i64 @_Z18ext_vector_swizzleDv4_f(
 // CHECK:         [[TMP:%.*]] = alloca <2 x float>
-// CHECK:         [[VEC:%.*]] = load <4 x float>, ptr %v.addr
+// CHECK:         call void @llvm.lifetime.start.p0(ptr [[TMP]])
+// CHECK-NEXT:    [[VEC:%.*]] = load <4 x float>, ptr %v.addr
 // CHECK-NEXT:    [[ELTS:%.*]] = shufflevector <4 x float> [[VEC]], <4 x 
float> poison, <2 x i32> <i32 2, i32 3>
 // CHECK-NEXT:    store <2 x float> [[ELTS]], ptr [[TMP]]
 // CHECK-NEXT:    [[RES:%.*]] = load i64, ptr [[TMP]]
+// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[TMP]])
 // CHECK-NEXT:    ret i64 [[RES]]
 unsigned long long ext_vector_swizzle(v4f v) {
   return __builtin_bit_cast(unsigned long long, v.zw);
@@ -42,11 +48,13 @@ unsigned long long ext_vector_swizzle(v4f v) {
 
 // CHECK-LABEL: define{{.*}} i32 @_Z20gnu_vector_subscriptDv4_fi(
 // CHECK:         [[TMP:%.*]] = alloca float
-// CHECK:         [[VEC:%.*]] = load <4 x float>, ptr %v.addr
+// CHECK:         call void @llvm.lifetime.start.p0(ptr [[TMP]])
+// CHECK-NEXT:    [[VEC:%.*]] = load <4 x float>, ptr %v.addr
 // CHECK-NEXT:    [[IDX:%.*]] = load i32, ptr %i.addr
 // CHECK-NEXT:    [[ELT:%.*]] = extractelement <4 x float> [[VEC]], i32 [[IDX]]
 // CHECK-NEXT:    store float [[ELT]], ptr [[TMP]]
 // CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[TMP]]
+// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[TMP]])
 // CHECK-NEXT:    ret i32 [[RES]]
 unsigned gnu_vector_subscript(gv4f v, int i) {
   return __builtin_bit_cast(unsigned, v[i]);
@@ -54,10 +62,12 @@ unsigned gnu_vector_subscript(gv4f v, int i) {
 
 // CHECK-LABEL: define{{.*}} i32 @_Z14matrix_elementu11matrix_typeILm2ELm2EfE(
 // CHECK:         [[TMP:%.*]] = alloca float
-// CHECK:         [[MAT:%.*]] = load <4 x float>, ptr %m.addr
+// CHECK:         call void @llvm.lifetime.start.p0(ptr [[TMP]])
+// CHECK-NEXT:    [[MAT:%.*]] = load <4 x float>, ptr %m.addr
 // CHECK-NEXT:    [[ELT:%.*]] = extractelement <4 x float> [[MAT]], i64 3
 // CHECK-NEXT:    store float [[ELT]], ptr [[TMP]]
 // CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[TMP]]
+// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[TMP]])
 // CHECK-NEXT:    ret i32 [[RES]]
 unsigned matrix_element(m2x2f m) {
   return __builtin_bit_cast(unsigned, m[1][1]);
@@ -69,24 +79,24 @@ struct S {
 
 // CHECK-LABEL: define{{.*}} i32 @_Z9bit_field1S(
 // CHECK:         [[TMP:%.*]] = alloca i32
-// CHECK:         [[LOAD:%.*]] = load i16, ptr %s
+// CHECK:         call void @llvm.lifetime.start.p0(ptr [[TMP]])
+// CHECK-NEXT:    [[LOAD:%.*]] = load i16, ptr %s
 // CHECK-NEXT:    [[SHR:%.*]] = lshr i16 [[LOAD]], 4
 // CHECK-NEXT:    [[EXT:%.*]] = zext i16 [[SHR]] to i32
 // CHECK-NEXT:    store i32 [[EXT]], ptr [[TMP]]
 // CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[TMP]]
+// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[TMP]])
 // CHECK-NEXT:    ret i32 [[RES]]
 unsigned bit_field(S s) {
   return __builtin_bit_cast(unsigned, s.b);
 }
 
-register unsigned long stack_pointer asm("rsp");
+volatile float vf;
 
-// CHECK-LABEL: define{{.*}} i64 @_Z15global_registerv(
-// CHECK:         [[TMP:%.*]] = alloca i64
-// CHECK:         [[REG:%.*]] = call i64 @llvm.read_register.i64(
-// CHECK-NEXT:    store i64 [[REG]], ptr [[TMP]]
-// CHECK-NEXT:    [[RES:%.*]] = load i64, ptr [[TMP]]
-// CHECK-NEXT:    ret i64 [[RES]]
-long global_register() {
-  return __builtin_bit_cast(long, stack_pointer);
+// CHECK-LABEL: define{{.*}} i32 @_Z15volatile_lvaluev(
+// CHECK-NOT:     alloca
+// CHECK:         [[RES:%.*]] = load volatile i32, ptr @vf
+// CHECK-NEXT:    ret i32 [[RES]]
+unsigned volatile_lvalue() {
+  return __builtin_bit_cast(unsigned, vf);
 }
diff --git a/clang/test/SemaCXX/builtin-bit-cast.cpp 
b/clang/test/SemaCXX/builtin-bit-cast.cpp
index 626065d5bdc000..57ae691263db63 100644
--- a/clang/test/SemaCXX/builtin-bit-cast.cpp
+++ b/clang/test/SemaCXX/builtin-bit-cast.cpp
@@ -56,3 +56,14 @@ constexpr To bit_cast(const From &from) {
 constexpr __int128_t foo = bit_cast<__int128_t>((long double)0);
 // expected-error@-1{{constexpr variable 'foo' must be initialized by a 
constant expression}}
 // expected-note@-2{{in call to 'bit_cast<__int128, long double>((long 
double)0)'}}
+
+volatile int volatile_int;
+int from_volatile = __builtin_bit_cast(int, volatile_int);
+
+struct volatile_bit_field {
+  volatile unsigned b : 8; // expected-note {{bit-field is declared here}}
+};
+unsigned from_volatile_bit_field(volatile_bit_field &v) {
+  // expected-error@+1 {{non-const reference cannot bind to bit-field 'b'}}
+  return __builtin_bit_cast(unsigned, v.b);
+}
diff --git a/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp 
b/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp
index db53076891b47b..c2cefb7ef45bb4 100644
--- a/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp
+++ b/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp
@@ -332,7 +332,7 @@ struct vol_mem {
 };
 
 // expected-error@+2 {{constexpr variable 'run_vol_mem' must be initialized by 
a constant expression}}
-// expected-note@+1 {{non-literal type 'vol_mem' cannot be used in a constant 
expression}}
+// expected-note-re@+1 {{non-literal type '{{(const )?}}vol_mem' cannot be 
used in a constant expression}}
 constexpr int run_vol_mem = __builtin_bit_cast(int, vol_mem{43});
 
 struct mem_ptr {

>From 1fd0dae1e8f9f7014032e08d492486cf37bf4e2a Mon Sep 17 00:00:00 2001
From: Nemi26 <[email protected]>
Date: Mon, 19 Oct 2026 00:50:02 +0200
Subject: [PATCH 3/3] [clang] Name the unqualified type in the bytecode
 non-literal note; test C

The bytecode interpreter now names the unqualified type in
note_constexpr_nonliteral, as the current interpreter does, so the
const that binding the bit_cast operand adds does not show.

cwg722 checks the NoOp cast to 'void *const' that the binding adds from
C++17 on, and a new CodeGen test covers __builtin_bit_cast in C.

Assisted-by: Claude Code (Claude Opus)
---
 clang/lib/AST/ByteCode/Interp.cpp             |  3 +-
 clang/test/AST/ByteCode/builtin-bit-cast.cpp  |  2 +-
 clang/test/CXX/drs/cwg722.cpp                 | 11 ++--
 .../builtin-bit-cast-nonsimple-lvalue.c       | 57 +++++++++++++++++++
 .../SemaCXX/constexpr-builtin-bit-cast.cpp    |  2 +-
 5 files changed, 67 insertions(+), 8 deletions(-)
 create mode 100644 clang/test/CodeGen/builtin-bit-cast-nonsimple-lvalue.c

diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index b217c14988f9cf..fc07c528863944 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1654,7 +1654,8 @@ bool CheckLiteralType(InterpState &S, CodePtr OpPC, const 
Type *T) {
 
   const Expr *E = S.Current->getExpr(OpPC);
   if (S.getLangOpts().CPlusPlus11)
-    S.FFDiag(E, diag::note_constexpr_nonliteral) << E->getType();
+    S.FFDiag(E, diag::note_constexpr_nonliteral)
+        << E->getType().getUnqualifiedType();
   else
     S.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
   return false;
diff --git a/clang/test/AST/ByteCode/builtin-bit-cast.cpp 
b/clang/test/AST/ByteCode/builtin-bit-cast.cpp
index b548b5290d25f3..368728d00a8121 100644
--- a/clang/test/AST/ByteCode/builtin-bit-cast.cpp
+++ b/clang/test/AST/ByteCode/builtin-bit-cast.cpp
@@ -428,7 +428,7 @@ struct vol_mem {
 };
 
 // both-error@+2 {{constexpr variable 'run_vol_mem' must be initialized by a 
constant expression}}
-// both-note-re@+1 {{non-literal type '{{(const )?}}vol_mem' cannot be used in 
a constant expression}}
+// both-note@+1 {{non-literal type 'vol_mem' cannot be used in a constant 
expression}}
 constexpr int run_vol_mem = __builtin_bit_cast(int, vol_mem{43});
 
 struct mem_ptr {
diff --git a/clang/test/CXX/drs/cwg722.cpp b/clang/test/CXX/drs/cwg722.cpp
index ad323a1f7b8e84..c413fe2a28ef0b 100644
--- a/clang/test/CXX/drs/cwg722.cpp
+++ b/clang/test/CXX/drs/cwg722.cpp
@@ -1,10 +1,10 @@
 // RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions 
-pedantic-errors
 // RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions 
-pedantic-errors -ast-dump | FileCheck %s
 // RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions 
-pedantic-errors -ast-dump | FileCheck %s
-// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions 
-pedantic-errors -ast-dump | FileCheck %s
-// RUN: %clang_cc1 -std=c++20 %s -verify -fexceptions -fcxx-exceptions 
-pedantic-errors -ast-dump | FileCheck %s
-// RUN: %clang_cc1 -std=c++23 %s -verify -fexceptions -fcxx-exceptions 
-pedantic-errors -ast-dump | FileCheck %s
-// RUN: %clang_cc1 -std=c++26 %s -verify -fexceptions -fcxx-exceptions 
-pedantic-errors -ast-dump | FileCheck %s
+// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions 
-pedantic-errors -ast-dump | FileCheck %s --check-prefixes CHECK,SINCE-CXX17
+// RUN: %clang_cc1 -std=c++20 %s -verify -fexceptions -fcxx-exceptions 
-pedantic-errors -ast-dump | FileCheck %s --check-prefixes CHECK,SINCE-CXX17
+// RUN: %clang_cc1 -std=c++23 %s -verify -fexceptions -fcxx-exceptions 
-pedantic-errors -ast-dump | FileCheck %s --check-prefixes CHECK,SINCE-CXX17
+// RUN: %clang_cc1 -std=c++26 %s -verify -fexceptions -fcxx-exceptions 
-pedantic-errors -ast-dump | FileCheck %s --check-prefixes CHECK,SINCE-CXX17
 
 // expected-no-diagnostics
 // cwg722: 20
@@ -48,7 +48,8 @@ void h() {
 // CHECK-NEXT:  `-ImplicitCastExpr {{.+}} 'void *' <NullToPointer>
 // CHECK-NEXT:    `-BuiltinBitCastExpr {{.+}} 'std::nullptr_t' 
<LValueToRValueBitCast>
 // CHECK-NEXT:      `-MaterializeTemporaryExpr {{.+}} 'void *const' lvalue
-// CHECK:               `-CXXStaticCastExpr {{.+}} 'void *' static_cast<void 
*> <NoOp>
+// SINCE-CXX17-NEXT:  `-ImplicitCastExpr {{.+}} 'void *const' <NoOp>
+// CHECK-NEXT:          `-CXXStaticCastExpr {{.+}} 'void *' static_cast<void 
*> <NoOp>
 // CHECK-NEXT:            `-ImplicitCastExpr {{.+}} 'void *' <BitCast> 
part_of_explicit_cast
 // CHECK-NEXT:              `-UnaryOperator {{.+}} 'int *' prefix '&' cannot 
overflow
 // CHECK-NEXT:                `-DeclRefExpr {{.+}} 'int' lvalue Var {{.+}} 'i' 
'int'
diff --git a/clang/test/CodeGen/builtin-bit-cast-nonsimple-lvalue.c 
b/clang/test/CodeGen/builtin-bit-cast-nonsimple-lvalue.c
new file mode 100644
index 00000000000000..fdd365a1bc6ad7
--- /dev/null
+++ b/clang/test/CodeGen/builtin-bit-cast-nonsimple-lvalue.c
@@ -0,0 +1,57 @@
+// RUN: %clang_cc1 -O -emit-llvm -disable-llvm-passes -triple 
x86_64-unknown-linux-gnu %s -o - | FileCheck %s
+
+typedef float v4f __attribute__((ext_vector_type(4)));
+
+// CHECK-LABEL: define{{.*}} i32 @ext_vector_element(
+// CHECK:         [[TMP:%ref.tmp.*]] = alloca float
+// CHECK:         call void @llvm.lifetime.start.p0(ptr [[TMP]])
+// CHECK-NEXT:    [[VEC:%.*]] = load <4 x float>, ptr %v.addr
+// CHECK-NEXT:    [[ELT:%.*]] = extractelement <4 x float> [[VEC]], i64 2
+// CHECK-NEXT:    store float [[ELT]], ptr [[TMP]]
+// CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[TMP]]
+// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[TMP]])
+// CHECK-NEXT:    ret i32 [[RES]]
+unsigned ext_vector_element(v4f v) {
+  return __builtin_bit_cast(unsigned, v.z);
+}
+
+struct S {
+  unsigned a : 4, b : 12;
+};
+
+// CHECK-LABEL: define{{.*}} i32 @bit_field(
+// CHECK:         [[TMP:%ref.tmp.*]] = alloca i32
+// CHECK:         call void @llvm.lifetime.start.p0(ptr [[TMP]])
+// CHECK-NEXT:    [[LOAD:%.*]] = load i16, ptr %s
+// CHECK-NEXT:    [[SHR:%.*]] = lshr i16 [[LOAD]], 4
+// CHECK-NEXT:    [[EXT:%.*]] = zext i16 [[SHR]] to i32
+// CHECK-NEXT:    store i32 [[EXT]], ptr [[TMP]]
+// CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[TMP]]
+// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[TMP]])
+// CHECK-NEXT:    ret i32 [[RES]]
+unsigned bit_field(struct S s) {
+  return __builtin_bit_cast(unsigned, s.b);
+}
+
+// CHECK-LABEL: define{{.*}} i32 @prvalue(
+// CHECK:         [[TMP:%ref.tmp.*]] = alloca float
+// CHECK:         call void @llvm.lifetime.start.p0(ptr [[TMP]])
+// CHECK-NEXT:    [[F:%.*]] = load float, ptr %f.addr
+// CHECK-NEXT:    [[ADD:%.*]] = fadd float [[F]], 1.000000e+00
+// CHECK-NEXT:    store float [[ADD]], ptr [[TMP]]
+// CHECK-NEXT:    [[RES:%.*]] = load i32, ptr [[TMP]]
+// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[TMP]])
+// CHECK-NEXT:    ret i32 [[RES]]
+unsigned prvalue(float f) {
+  return __builtin_bit_cast(unsigned, f + 1.0f);
+}
+
+volatile float vf;
+
+// CHECK-LABEL: define{{.*}} i32 @volatile_lvalue(
+// CHECK-NOT:     alloca
+// CHECK:         [[RES:%.*]] = load volatile i32, ptr @vf
+// CHECK-NEXT:    ret i32 [[RES]]
+unsigned volatile_lvalue(void) {
+  return __builtin_bit_cast(unsigned, vf);
+}
diff --git a/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp 
b/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp
index c2cefb7ef45bb4..db53076891b47b 100644
--- a/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp
+++ b/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp
@@ -332,7 +332,7 @@ struct vol_mem {
 };
 
 // expected-error@+2 {{constexpr variable 'run_vol_mem' must be initialized by 
a constant expression}}
-// expected-note-re@+1 {{non-literal type '{{(const )?}}vol_mem' cannot be 
used in a constant expression}}
+// expected-note@+1 {{non-literal type 'vol_mem' cannot be used in a constant 
expression}}
 constexpr int run_vol_mem = __builtin_bit_cast(int, vol_mem{43});
 
 struct mem_ptr {

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to