Skip to content

Commit 2ed12ee

Browse files
committed
[AArch64] Fix handling of x29/x30 in inline assembly clobbers
The AArch64 backend was silently ignoring inline assembly clobbers when numeric register names (x29, x30) were used instead of their architectural aliases (fp, lr). I found this bug via inline assembly in Zig, which not normalize the register names the way clang does. There is an incoplete workaround for this in Rust, but that only handles `x30/lr`, not `x29/fp`. I thought it would make sense to fix this properly rather than adding a workaround to Zig. This patch adds explicit handling in getRegForInlineAsmConstraint() to map both numeric and alias forms to the correct physical registers, following the same pattern used by the RISC-V backend. I've left `x31/sp` without changes, it would nice to have to have warning when trying to clobber `x31`, just like there is for `sp`, but I'm not sure what is the right fix.
1 parent cf9cb54 commit 2ed12ee

File tree

5 files changed

+52
-4
lines changed

5 files changed

+52
-4
lines changed

llvm/lib/Target/AArch64/AArch64ISelLowering.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "llvm/ADT/SmallVectorExtras.h"
3131
#include "llvm/ADT/Statistic.h"
3232
#include "llvm/ADT/StringRef.h"
33+
#include "llvm/ADT/StringSwitch.h"
3334
#include "llvm/ADT/Twine.h"
3435
#include "llvm/Analysis/LoopInfo.h"
3536
#include "llvm/Analysis/MemoryLocation.h"
@@ -13126,6 +13127,17 @@ AArch64TargetLowering::getRegForInlineAsmConstraint(
1312613127
return std::make_pair(unsigned(AArch64::ZT0), &AArch64::ZTRRegClass);
1312713128
}
1312813129

13130+
// Clang will correctly decode the usage of register name aliases into their
13131+
// official names. However, other frontends like `rustc` do not. This allows
13132+
// users of these frontends to use the ABI names for registers in LLVM-style
13133+
// register constraints.
13134+
unsigned XRegFromAlias = StringSwitch<unsigned>(Constraint.lower())
13135+
.Cases({"{x29}", "{fp}"}, AArch64::FP)
13136+
.Cases({"{x30}", "{lr}"}, AArch64::LR)
13137+
.Default(AArch64::NoRegister);
13138+
if (XRegFromAlias != AArch64::NoRegister)
13139+
return std::make_pair(XRegFromAlias, &AArch64::GPR64RegClass);
13140+
1312913141
// Use the default implementation in TargetLowering to convert the register
1313013142
// constraint into a member of a register class.
1313113143
std::pair<unsigned, const TargetRegisterClass *> Res;

llvm/test/CodeGen/AArch64/arm64-anyregcc.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ entry:
446446
define i64 @patchpoint_spilldef(i64 %p1, i64 %p2, i64 %p3, i64 %p4) {
447447
entry:
448448
%result = tail call anyregcc i64 (i64, i32, ptr, i32, ...) @llvm.experimental.patchpoint.i64(i64 12, i32 16, ptr inttoptr (i64 0 to ptr), i32 2, i64 %p1, i64 %p2)
449-
tail call void asm sideeffect "nop", "~{x0},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x18},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{x29},~{x30},~{x31}"() nounwind
449+
tail call void asm sideeffect "nop", "~{x0},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x18},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{x30}"() nounwind
450450
ret i64 %result
451451
}
452452

@@ -494,7 +494,7 @@ entry:
494494
; CHECK-NEXT: .long -88
495495
define i64 @patchpoint_spillargs(i64 %p1, i64 %p2, i64 %p3, i64 %p4) {
496496
entry:
497-
tail call void asm sideeffect "nop", "~{x0},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x18},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{x29},~{x30},~{x31}"() nounwind
497+
tail call void asm sideeffect "nop", "~{x0},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x18},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{x30}"() nounwind
498498
%result = tail call anyregcc i64 (i64, i32, ptr, i32, ...) @llvm.experimental.patchpoint.i64(i64 13, i32 16, ptr inttoptr (i64 0 to ptr), i32 2, i64 %p1, i64 %p2, i64 %p3, i64 %p4)
499499
ret i64 %result
500500
}

llvm/test/CodeGen/AArch64/arm64-patchpoint-scratch-regs.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
; CHECK-NEXT: nop
99
define void @clobberScratch(ptr %p) {
1010
%v = load i32, ptr %p
11-
tail call void asm sideeffect "nop", "~{x0},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x18},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{x29},~{x30},~{x31}"() nounwind
11+
tail call void asm sideeffect "nop", "~{x0},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x18},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{x30}"() nounwind
1212
tail call void (i64, i32, ptr, i32, ...) @llvm.experimental.patchpoint.void(i64 5, i32 20, ptr null, i32 0, ptr %p, i32 %v)
1313
store i32 %v, ptr %p
1414
ret void

llvm/test/CodeGen/AArch64/arm64-stackmap.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ define void @liveConstant() {
320320
; CHECK-NEXT: .short 0
321321
; CHECK-NEXT: .long -{{[0-9]+}}
322322
define void @clobberLR(i32 %a) {
323-
tail call void asm sideeffect "nop", "~{x0},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x18},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{x29},~{x31}"() nounwind
323+
tail call void asm sideeffect "nop", "~{x0},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x18},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{x30}"() nounwind
324324
tail call void (i64, i32, ...) @llvm.experimental.stackmap(i64 16, i32 8, i32 %a)
325325
ret void
326326
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
; RUN: llc -mtriple=aarch64 -verify-machineinstrs < %s | FileCheck %s
2+
3+
; Test that both numeric register names (x29, x30) and their architectural
4+
; aliases (fp, lr) work correctly as clobbers in inline assembly.
5+
6+
define void @clobber_x29() nounwind {
7+
; CHECK-LABEL: clobber_x29:
8+
; CHECK: str x29, [sp
9+
; CHECK: ldr x29, [sp
10+
tail call void asm sideeffect "", "~{x29}"()
11+
ret void
12+
}
13+
14+
define void @clobber_fp() nounwind {
15+
; CHECK-LABEL: clobber_fp:
16+
; CHECK: str x29, [sp
17+
; CHECK: ldr x29, [sp
18+
tail call void asm sideeffect "", "~{fp}"()
19+
ret void
20+
}
21+
22+
define void @clobber_x30() nounwind {
23+
; CHECK-LABEL: clobber_x30:
24+
; CHECK: str x30, [sp
25+
; CHECK: ldr x30, [sp
26+
tail call void asm sideeffect "", "~{x30}"()
27+
ret void
28+
}
29+
30+
define void @clobber_lr() nounwind {
31+
; CHECK-LABEL: clobber_lr:
32+
; CHECK: str x30, [sp
33+
; CHECK: ldr x30, [sp
34+
tail call void asm sideeffect "", "~{lr}"()
35+
ret void
36+
}

0 commit comments

Comments
 (0)