diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md index fffd696e59baf..12b7ad458e93e 100644 --- a/llvm/docs/ReleaseNotes.md +++ b/llvm/docs/ReleaseNotes.md @@ -148,6 +148,11 @@ Makes programs 10x faster by doing Special New Thing. disabled by default to maintain compatibility with Binutils and LLVM older toolchains that do not define the `R_AARCH64_TLS_DTPREL64` static relocation type for TLS offsets. +* A bug was fixed that caused LLVM IR inline assembly clobbers of the x29 and + x30 registers to be ignored when they were written using their xN names + instead of the ABI names FP and LR. Note that LLVM IR produced by Clang + always uses the ABI names, but other frontends may not. + ([#167783](https://github.com/llvm/llvm-project/pull/167783)) ### Changes to the AMDGPU Backend diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp index 0eac7bc11a777..3016e1b94c22d 100644 --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -30,6 +30,7 @@ #include "llvm/ADT/SmallVectorExtras.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/MemoryLocation.h" @@ -13698,6 +13699,20 @@ AArch64TargetLowering::getRegForInlineAsmConstraint( return std::make_pair(unsigned(AArch64::ZT0), &AArch64::ZTRRegClass); } + // Clang will correctly decode the usage of register name aliases into their + // official names. However, other frontends like `rustc` do not. The + // conversion below allows users of these frontends to use the ABI names for + // registers in LLVM-style register constraints. + // + // x31->sp is not included here because it's not a general register and + // needs different handling + unsigned XRegFromAlias = StringSwitch(Constraint.lower()) + .Cases({"{x29}", "{fp}"}, AArch64::FP) + .Cases({"{x30}", "{lr}"}, AArch64::LR) + .Default(AArch64::NoRegister); + if (XRegFromAlias != AArch64::NoRegister) + return std::make_pair(XRegFromAlias, &AArch64::GPR64RegClass); + // Use the default implementation in TargetLowering to convert the register // constraint into a member of a register class. std::pair Res; diff --git a/llvm/test/CodeGen/AArch64/inline-asm-clobber-x29-x30.ll b/llvm/test/CodeGen/AArch64/inline-asm-clobber-x29-x30.ll new file mode 100644 index 0000000000000..cde55522fb19b --- /dev/null +++ b/llvm/test/CodeGen/AArch64/inline-asm-clobber-x29-x30.ll @@ -0,0 +1,44 @@ +; RUN: llc -mtriple=aarch64 -verify-machineinstrs < %s | FileCheck %s + +; Test that both numeric register names (x29, x30) and their architectural +; aliases (fp, lr) work correctly as clobbers in inline assembly. + +define void @clobber_x29() nounwind { +; CHECK-LABEL: clobber_x29: +; CHECK: str x29, [sp +; CHECK-NEXT: //APP +; CHECK-NEXT: //NO_APP +; CHECK-NEXT: ldr x29, [sp + tail call void asm sideeffect "", "~{x29}"() + ret void +} + +define void @clobber_fp() nounwind { +; CHECK-LABEL: clobber_fp: +; CHECK: str x29, [sp +; CHECK-NEXT: //APP +; CHECK-NEXT: //NO_APP +; CHECK-NEXT: ldr x29, [sp + tail call void asm sideeffect "", "~{fp}"() + ret void +} + +define void @clobber_x30() nounwind { +; CHECK-LABEL: clobber_x30: +; CHECK: str x30, [sp +; CHECK-NEXT: //APP +; CHECK-NEXT: //NO_APP +; CHECK-NEXT: ldr x30, [sp + tail call void asm sideeffect "", "~{x30}"() + ret void +} + +define void @clobber_lr() nounwind { +; CHECK-LABEL: clobber_lr: +; CHECK: str x30, [sp +; CHECK-NEXT: //APP +; CHECK-NEXT: //NO_APP +; CHECK-NEXT: ldr x30, [sp + tail call void asm sideeffect "", "~{lr}"() + ret void +}