https://github.com/Marsian13 created 
https://github.com/llvm/llvm-project/pull/233395

C rejects taking the address of a register variable, but C++ skipped that 
check, so for a global register variable (declared with an asm label) CodeGen 
received an lvalue with no address, which triggered assertions (or invalid IR 
without assertions).

- Diagnose '&sp' for global register variables in C++, as in C.
- Treat global register variables as non-referenceable glvalues, so a 'const 
T&' binds to a temporary holding the register's value, as for bit-fields.
- Diagnose binding a non-const reference to one.

Ordinary 'register' locals are unchanged: their address can still be taken in 
C++ (c++03 7.1.1p3).

Fixes #233283

Testing: added clang/test/SemaCXX/global-register-var-address.cpp and
clang/test/CodeGenCXX/global-register-var-reference.cpp. The Sema, SemaCXX,
CodeGen and CodeGenCXX test directories pass locally (X86 target only).

AI assistance: I used an AI assistant (Claude) to help me understand the code.

>From a9e0800569b2dc99394db2c23629583a1588abe1 Mon Sep 17 00:00:00 2001
From: Marsian13 <[email protected]>
Date: Mon, 19 Oct 2026 03:15:41 +0530
Subject: [PATCH] [clang] Diagnose and handle global register variables in C++

C rejects taking the address of a register variable, but C++ skipped that
check, so for a global register variable (declared with an asm label)
CodeGen received an lvalue with no address, which triggered assertions
(or invalid IR without assertions).

- Diagnose '&sp' for global register variables in C++, as in C.
- Treat global register variables as non-referenceable glvalues, so a
  'const T&' binds to a temporary holding the register's value, as for
  bit-fields.
- Diagnose binding a non-const reference to one.

Ordinary 'register' locals are unchanged: their address can still be
taken in C++ (c++03 7.1.1p3).

Fixes #233283
---
 clang/docs/ReleaseNotes.md                    |  5 ++
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 +
 clang/include/clang/Sema/Initialization.h     |  3 +
 clang/lib/Sema/SemaExpr.cpp                   |  2 +-
 clang/lib/Sema/SemaInit.cpp                   | 25 ++++++++-
 .../global-register-var-reference.cpp         | 16 ++++++
 .../SemaCXX/global-register-var-address.cpp   | 55 +++++++++++++++++++
 7 files changed, 106 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/global-register-var-reference.cpp
 create mode 100644 clang/test/SemaCXX/global-register-var-address.cpp

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 7705b0e61bf4bf..287c2e49ecb691 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -1030,6 +1030,11 @@ features cannot lower the translation-unit ABI level;
   whose parameter type uses pack indexing, and makes such valid matches
   succeed instead of being rejected. (#GH232220)
 
+- Clang no longer crashes when taking the address of, or binding a reference
+  to, a global register variable. Taking the address is now diagnosed as in C,
+  binding a `const` reference uses a temporary holding the register's value,
+  and binding a non-const reference is diagnosed. (#GH233283)
+
 #### Bug Fixes to AST Handling
 
 - Fixed a non-deterministic ordering of unused local typedefs that made
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 3e8ba5bf88a42b..8ed766294cc5bf 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2562,6 +2562,8 @@ def err_reference_bind_to_vector_element : Error<
   "%select{non-const|volatile}0 reference cannot bind to vector element">;
 def err_reference_bind_to_matrix_element : Error<
   "%select{non-const|volatile}0 reference cannot bind to matrix element">;
+def err_reference_bind_to_global_register_variable : Error<
+  "%select{non-const|volatile}0 reference cannot bind to global register 
variable">;
 def err_reference_var_requires_init : Error<
   "declaration of reference variable %0 requires an initializer">;
 def err_reference_without_init : Error<
diff --git a/clang/include/clang/Sema/Initialization.h 
b/clang/include/clang/Sema/Initialization.h
index 2c2df1f03747b3..31adaad1d65184 100644
--- a/clang/include/clang/Sema/Initialization.h
+++ b/clang/include/clang/Sema/Initialization.h
@@ -1091,6 +1091,9 @@ class InitializationSequence {
     /// Non-const lvalue reference binding to a matrix element.
     FK_NonConstLValueReferenceBindingToMatrixElement,
 
+    /// Non-const lvalue reference binding to a global register variable.
+    FK_NonConstLValueReferenceBindingToRegisterVariable,
+
     /// Non-const lvalue reference binding to an lvalue of unrelated
     /// type.
     FK_NonConstLValueReferenceBindingToUnrelated,
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 2e9a3b73e5c658..0035488909459f 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -15374,7 +15374,7 @@ QualType Sema::CheckAddressOfOperand(ExprResult 
&OrigOp, SourceLocation OpLoc) {
       // in C++ it is not error to take address of a register
       // variable (c++03 7.1.1P3)
       if (vd->getStorageClass() == SC_Register &&
-          !getLangOpts().CPlusPlus) {
+          (!getLangOpts().CPlusPlus || vd->hasGlobalStorage())) {
         AddressOfError = AO_Register_Variable;
       }
     } else if (isa<MSPropertyDecl>(dcl)) {
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 1cea790da1d27f..9249f024178a0a 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -4024,6 +4024,7 @@ bool InitializationSequence::isAmbiguous() const {
   case FK_NonConstLValueReferenceBindingToBitfield:
   case FK_NonConstLValueReferenceBindingToVectorElement:
   case FK_NonConstLValueReferenceBindingToMatrixElement:
+  case FK_NonConstLValueReferenceBindingToRegisterVariable:
   case FK_NonConstLValueReferenceBindingToUnrelated:
   case FK_RValueReferenceBindingToLValue:
   case FK_ReferenceAddrspaceMismatchTemporary:
@@ -5490,9 +5491,18 @@ static void TryReferenceInitialization(Sema &S, const 
InitializedEntity &Entity,
 /// Determine whether an expression is a non-referenceable glvalue (one to
 /// which a reference can never bind). Attempting to bind a reference to
 /// such a glvalue will always create a temporary.
+/// Determine whether an expression refers to a global register variable,
+/// which lives in a hardware register and has no address to bind to.
+static bool refersToGlobalRegisterVar(Expr *E) {
+  if (const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParens()))
+    if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()))
+      return VD->getStorageClass() == SC_Register && VD->hasGlobalStorage();
+  return false;
+}
+
 static bool isNonReferenceableGLValue(Expr *E) {
   return E->refersToBitField() || E->refersToVectorElement() ||
-         E->refersToMatrixElement();
+         E->refersToMatrixElement() || refersToGlobalRegisterVar(E);
 }
 
 /// Reference initialization without resolving overloaded functions.
@@ -5626,6 +5636,9 @@ static void TryReferenceInitializationCore(Sema &S,
         else if (Initializer->refersToMatrixElement())
           FK = InitializationSequence::
               FK_NonConstLValueReferenceBindingToMatrixElement;
+        else if (refersToGlobalRegisterVar(Initializer))
+          FK = InitializationSequence::
+              FK_NonConstLValueReferenceBindingToRegisterVariable;
         else
           llvm_unreachable("unexpected kind of compatible initializer");
         break;
@@ -9244,6 +9257,12 @@ bool InitializationSequence::Diagnose(Sema &S,
         << DestType.isVolatileQualified() << Args[0]->getSourceRange();
     break;
 
+  case FK_NonConstLValueReferenceBindingToRegisterVariable:
+    S.Diag(Kind.getLocation(),
+           diag::err_reference_bind_to_global_register_variable)
+        << DestType.isVolatileQualified() << Args[0]->getSourceRange();
+    break;
+
   case FK_RValueReferenceBindingToLValue:
     S.Diag(Kind.getLocation(), diag::err_lvalue_to_rvalue_ref)
       << DestType.getNonReferenceType() << OnlyArg->getType()
@@ -9637,6 +9656,10 @@ void InitializationSequence::dump(raw_ostream &OS) const 
{
       OS << "non-const lvalue reference bound to matrix element";
       break;
 
+    case FK_NonConstLValueReferenceBindingToRegisterVariable:
+      OS << "non-const lvalue reference bound to global register variable";
+      break;
+
     case FK_NonConstLValueReferenceBindingToUnrelated:
       OS << "non-const lvalue reference bound to unrelated type";
       break;
diff --git a/clang/test/CodeGenCXX/global-register-var-reference.cpp 
b/clang/test/CodeGenCXX/global-register-var-reference.cpp
new file mode 100644
index 00000000000000..8d17194f1c3be5
--- /dev/null
+++ b/clang/test/CodeGenCXX/global-register-var-reference.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm -o - 
%s | FileCheck %s
+
+register unsigned long sp asm("rsp");
+
+template <class T> unsigned long f(const T &x) { return x; }
+
+// A const reference to a global register variable binds to a temporary that
+// holds the register's value.
+// CHECK-LABEL: define {{.*}} @_Z1gv(
+// CHECK:         [[TMP:%.*]] = alloca i64
+// CHECK:         [[VAL:%.*]] = call i64 @llvm.read_register.i64(metadata 
![[REG:[0-9]+]])
+// CHECK-NEXT:    store i64 [[VAL]], ptr [[TMP]]
+// CHECK:         call {{.*}} @_Z1fImEmRKT_(ptr {{.*}} [[TMP]])
+unsigned long g() { return f(sp); }
+
+// CHECK: ![[REG]] = !{!"rsp"}
diff --git a/clang/test/SemaCXX/global-register-var-address.cpp 
b/clang/test/SemaCXX/global-register-var-address.cpp
new file mode 100644
index 00000000000000..5171a7ca1003e9
--- /dev/null
+++ b/clang/test/SemaCXX/global-register-var-address.cpp
@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -std=c++20 -Wno-register -triple x86_64-unknown-linux-gnu 
-fsyntax-only -verify %s
+
+// A global register variable (GNU extension) has no memory address, so C++
+// should reject taking its address, as C does.
+register unsigned long sp asm("rsp");
+
+unsigned long *take_address() {
+  return &sp; // expected-error {{address of register variable requested}}
+}
+
+// Reading the value is fine, only taking the address is rejected.
+unsigned long read_value() {
+  return sp;
+}
+
+void bind_reference() {
+  unsigned long &ref = sp; // expected-error {{non-const reference cannot bind 
to global register variable}}
+  (void)ref;
+}
+
+// Taking the address of an ordinary register variable stays valid in C++
+// (c++03 7.1.1p3), so the check must not apply to it.
+void local_register_ok() {
+  register int x = 0;
+  int *q = &x;
+  (void)q;
+}
+
+// A non-const reference can't bind to a global register variable: there is
+// no object to refer to.
+unsigned long &bad_return() {
+  return sp; // expected-error {{non-const reference cannot bind to global 
register variable}}
+}
+
+void takes_ref(unsigned long &x); // expected-note {{passing argument to 
parameter 'x' here}}
+void bad_call() {
+  takes_ref(sp); // expected-error {{non-const reference cannot bind to global 
register variable}}
+}
+
+// A const reference binds to a temporary copy of the register's value.
+void const_ref_ok() {
+  const unsigned long &r = sp;
+  (void)r;
+}
+
+// Parentheses around the variable don't change the result.
+void paren_ref() {
+  unsigned long &r = (sp); // expected-error {{non-const reference cannot bind 
to global register variable}}
+  (void)r;
+}
+
+void paren_const_ref_ok() {
+  const unsigned long &r = (sp);
+  (void)r;
+}

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

Reply via email to