[clang] [llvm] [clang][X86] Reject register variables bound to status registers (PR #227576)

via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 30 00:14:30 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-selectiondag

Author: Akash Manna (akash-manna-sky)

<details>
<summary>Changes</summary>

Fixes #<!-- -->225033

`register long double t __asm("17");` binds `t` to GCC register number 17, which on x86 is the `flags` register. Sema only checks that the name exists, so the declaration is accepted and the asm operand comes out as `={flags}`. The X86 backend maps that to EFLAGS regardless of the value type, and `getRegistersForValue` then tries to retype the `long double` as an integer of the same width — there is no i80, so it hands an invalid MVT to `getNumRegisters` and crashes. With an `int` it gets a bit further and dies in the EFLAGS copy instead. The number isn't the problem, by the way: `__asm("flags")` crashes exactly the same way.

Sema now rejects a local register variable bound to a register that can't hold a value (`argp`, `flags`, `fpcr`, `fpsr`, `dirflag` and `frame` on x86), whatever the spelling, the same way GCC does ("not general enough to be used as a register variable"). On the LLVM side, `{flags}` is only accepted as a clobber, like `dirflag` and `fpsr` already were, and `getRegistersForValue` fails cleanly when no integer type of the operand's size exists instead of fabricating an invalid one — that path was still reachable through real registers like `cr0`.

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


10 Files Affected:

- (modified) clang/docs/ReleaseNotes.md (+3) 
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+2) 
- (modified) clang/include/clang/Basic/TargetInfo.h (+7) 
- (modified) clang/lib/Basic/Targets/X86.h (+6) 
- (modified) clang/lib/Sema/SemaDecl.cpp (+9-3) 
- (modified) clang/test/Sema/asm.c (+23) 
- (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp (+3) 
- (modified) llvm/lib/Target/X86/X86ISelLowering.cpp (+2-1) 
- (added) llvm/test/CodeGen/X86/asm-reject-flags.ll (+21) 
- (modified) llvm/test/CodeGen/X86/asm-reject-reg-type-mismatch.ll (+13) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3c6acf353f93f..702bf9c73730b 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -555,6 +555,9 @@ features cannot lower the translation-unit ABI level;
 - Fixed a bug where repeated #imports of modular headers in non-modular compilation were translated to #pragma clang module import. (#GH216924)
 - Fixed an assertion when `#pragma omp declare simd` or `#pragma omp declare variant` is followed by another OpenMP declarative directive containing a qualified identifier. (#GH217204)
 - Fixed a crash when an `asm` label names the register for a global variable of incomplete type. (#GH219746)
+- Fixed a crash when a local register variable bound to a pseudo or status register by an `asm` label,
+  such as `register long double t __asm("17");` (the flags register), was used as an inline asm operand.
+  Such declarations are now rejected, matching GCC. (#GH225033)
 - Fixed an ICE hat occurred when using `__imag int/float` as lvalue in assignment. (#GH119498)
 - Fixed an assertion failure in `-Wsign-compare` when a negated or complemented vector of unsigned integers was compared against a signed constant. (#GH203575)
 - Fixed an assertion failure when a constant statement expression that declares a variable is used as a bound of an OpenMP loop. A statement expression in a bound of a non-rectangular loop is now diagnosed. (#GH153987)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d293a9798da6a..7004113d72799 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10132,6 +10132,8 @@ let CategoryName = "Inline Assembly Issue" in {
   def err_asm_unwind_and_goto : Error<"unwind clobber cannot be used with asm goto">;
   def err_asm_invalid_global_var_reg : Error<"register '%0' unsuitable for "
     "global register variables on this target">;
+  def err_asm_invalid_local_var_reg : Error<"register '%0' unsuitable for "
+    "register variables">;
   def err_asm_register_size_mismatch : Error<"size of register '%0' does not "
     "match variable size">;
   def err_asm_unsupported_register_type : Error<
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index ebf065c0934fa..09f3e552dcb06 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1248,6 +1248,13 @@ class TargetInfo : public TransferrableTargetInfo,
     return true;
   }
 
+  /// Returns whether the register RegName (a canonical GCC register name) can
+  /// hold a local register variable, i.e. it is not a status or pseudo register
+  /// that is only valid in a clobber list.
+  virtual bool validateLocalRegisterVariable(StringRef RegName) const {
+    return true;
+  }
+
   // validateOutputConstraint, validateInputConstraint - Checks that
   // a constraint is valid and provides information about it.
   // FIXME: These should return a real error instead of just true/false.
diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h
index 86559a83c69df..536168212a8cb 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -17,6 +17,7 @@
 #include "clang/Basic/BitmaskEnum.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Basic/TargetOptions.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/TargetParser/Triple.h"
@@ -248,6 +249,11 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo {
     return false;
   }
 
+  bool validateLocalRegisterVariable(StringRef RegName) const override {
+    return !llvm::is_contained(
+        {"argp", "flags", "fpcr", "fpsr", "dirflag", "frame"}, RegName);
+  }
+
   bool validateOutputSize(const llvm::StringMap<bool> &FeatureMap,
                           StringRef Constraint, unsigned Size) const override;
 
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index db5e66cb96c3e..ac031412ceb96 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7782,9 +7782,15 @@ void Sema::CheckAsmLabel(Scope *S, Expr *E, StorageClass SC,
       break;
     case SC_Register:
       // Local Named register
-      if (!Context.getTargetInfo().isValidGCCRegisterName(Label) &&
-          DeclAttrsMatchCUDAMode(getLangOpts(), getCurFunctionDecl()))
-        Diag(E->getExprLoc(), diag::err_asm_unknown_register_name) << Label;
+      if (DeclAttrsMatchCUDAMode(getLangOpts(), getCurFunctionDecl())) {
+        const auto &TI = Context.getTargetInfo();
+        if (!TI.isValidGCCRegisterName(Label))
+          Diag(E->getExprLoc(), diag::err_asm_unknown_register_name) << Label;
+        else if (!TI.validateLocalRegisterVariable(
+                     TI.getNormalizedGCCRegisterName(Label,
+                                                     /*ReturnCanonical=*/true)))
+          Diag(E->getExprLoc(), diag::err_asm_invalid_local_var_reg) << Label;
+      }
       break;
     case SC_Static:
     case SC_Extern:
diff --git a/clang/test/Sema/asm.c b/clang/test/Sema/asm.c
index cc9acac1e169d..101aa019a4db4 100644
--- a/clang/test/Sema/asm.c
+++ b/clang/test/Sema/asm.c
@@ -404,3 +404,26 @@ void test20(char x) {
   asm ("fabs" : "=t" (d): "0" (v)); // expected-error {{unsupported inline asm: input with type 'int2' (vector of 2 'int' values) matching output with type 'double'}}
   asm ("fabs" : "=t" (v): "0" (d)); // expected-error {{unsupported inline asm: input with type 'double' matching output with type 'int2' (vector of 2 'int' values)}}
 }
+
+// GH225033
+void test21(long double x, int y) {
+  register long double t __asm("17"); // expected-error {{register '17' unsuitable for register variables}}
+  asm ("fabs" : "=t" (t) : "0" (x));
+  register int r __asm("17"); // expected-error {{register '17' unsuitable for register variables}}
+  asm ("mov %1, %0" : "=r" (r) : "r" (y));
+
+  register int flags __asm("flags"); // expected-error {{register 'flags' unsuitable for register variables}}
+  register int flags_prefixed __asm("%flags"); // expected-error {{register '%flags' unsuitable for register variables}}
+  register int argp __asm("argp"); // expected-error {{register 'argp' unsuitable for register variables}}
+  register int fpcr __asm("fpcr"); // expected-error {{register 'fpcr' unsuitable for register variables}}
+  register int fpsr __asm("19"); // expected-error {{register '19' unsuitable for register variables}}
+  register int dirflag __asm("dirflag"); // expected-error {{register 'dirflag' unsuitable for register variables}}
+  register int frame __asm("frame"); // expected-error {{register 'frame' unsuitable for register variables}}
+
+  // Still valid: real registers by number or name, and clobbers.
+  register int ax __asm("0");
+  register int ymm29 __asm("99");
+  register long double st __asm("st");
+  asm ("fabs" : "=t" (st) : "0" (x));
+  asm ("nop" : : : "17", "flags", "fpsr", "dirflag");
+}
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index b47f2c541e042..9e8600b13d8cd 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -10244,6 +10244,9 @@ getRegistersForValue(SelectionDAG &DAG, const SDLoc &DL,
         // i64, which can be passed with two i32 values on a 32-bit machine.
       } else if (RegVT.isInteger() && OpInfo.ConstraintVT.isFloatingPoint()) {
         MVT VT = MVT::getIntegerVT(OpInfo.ConstraintVT.getSizeInBits());
+        // No integer type of that size to bitcast to (e.g. f80).
+        if (!VT.isValid())
+          return std::nullopt;
         if (OpInfo.Type == InlineAsm::isInput)
           OpInfo.CallOperand =
               DAG.getNode(ISD::BITCAST, DL, VT, OpInfo.CallOperand);
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index e2f3b5f3cd3d5..7cc1ae793bf5b 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -65253,7 +65253,8 @@ X86TargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
     }
 
     // flags -> EFLAGS
-    if (StringRef("{flags}").equals_insensitive(Constraint))
+    // Only allow for clobber.
+    if (StringRef("{flags}").equals_insensitive(Constraint) && VT == MVT::Other)
       return std::make_pair(X86::EFLAGS, &X86::CCRRegClass);
 
     // dirflag -> DF
diff --git a/llvm/test/CodeGen/X86/asm-reject-flags.ll b/llvm/test/CodeGen/X86/asm-reject-flags.ll
new file mode 100644
index 0000000000000..c840bfac34279
--- /dev/null
+++ b/llvm/test/CodeGen/X86/asm-reject-flags.ll
@@ -0,0 +1,21 @@
+; RUN: not llc -o /dev/null %s -mtriple=x86_64-unknown-unknown 2>&1 | FileCheck %s
+
+; GH225033: {flags} is only valid as a clobber.
+
+; CHECK: error: could not allocate output register for constraint '{flags}'
+define x86_fp80 @flags_f80_output(x86_fp80 %x) {
+  %r = call x86_fp80 asm "fabs", "={flags},0,~{dirflag},~{fpsr},~{flags}"(x86_fp80 %x)
+  ret x86_fp80 %r
+}
+
+; CHECK: error: could not allocate output register for constraint '{flags}'
+define i32 @flags_i32_output(i32 %x) {
+  %r = call i32 asm "mov $1, $0", "={flags},r,~{dirflag},~{fpsr},~{flags}"(i32 %x)
+  ret i32 %r
+}
+
+; CHECK: error: could not allocate input reg for constraint '{flags}'
+define void @flags_i32_input(i32 %x) {
+  call void asm sideeffect "", "{flags},~{dirflag},~{fpsr},~{flags}"(i32 %x)
+  ret void
+}
diff --git a/llvm/test/CodeGen/X86/asm-reject-reg-type-mismatch.ll b/llvm/test/CodeGen/X86/asm-reject-reg-type-mismatch.ll
index a71f795365ccc..fb808f32ae71a 100644
--- a/llvm/test/CodeGen/X86/asm-reject-reg-type-mismatch.ll
+++ b/llvm/test/CodeGen/X86/asm-reject-reg-type-mismatch.ll
@@ -27,3 +27,16 @@ define void @r_constraint_v4i128(ptr %0) {
   store <4 x i128> %3, ptr %0, align 64
   ret void
 }
+
+; There is no integer type of the same size as these FP types to bitcast to.
+; CHECK: error: could not allocate output register for constraint '{cr0}'
+define x86_fp80 @cr0_fp80(x86_fp80 %0) {
+  %2 = tail call x86_fp80 asm "", "={cr0},0"(x86_fp80 %0)
+  ret x86_fp80 %2
+}
+
+; CHECK: error: could not allocate input reg for constraint '{cr0}'
+define void @cr0_v3f32(<3 x float> %0) {
+  tail call void asm sideeffect "", "{cr0}"(<3 x float> %0)
+  ret void
+}

``````````

</details>


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


More information about the llvm-commits mailing list