[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