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] [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 0103c29537920..e3594e4993e8e 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 db2b7a4ad1fce..5dccebcf842a8 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 c797bdb11e0cb..7ebb2deb4ad48 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 0000000000000..8818c714d73b8
--- /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 ea5333df0b469..db53076891b47 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 481b80ceb9c1e..c6eb66f6b2995 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, "");
 }

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

Reply via email to