Skip to content

Commit 9482083

Browse files
committed
[AArch64] Implement "rZ" inline asm constraint
Add support for the "rZ" inline assembly constraint. The constraint accepts literal zero values and emits the arch zero register (xzr/wzr) instead of materializing zero in a gpr. In AArch64.cpp: - validateAsmConstraint: recognize "rZ"/"rz" constraint - convertConstraint: convert to "^rZ" In AArch64ISelLowering.cpp: - getConstraintType: return C_RegisterClass - getRegForInlineAsmConstraint: return appropriate register class based on value type (GPR32/GPR64/GPR64x8 for LS64, reject scalable vectors) - LowerAsmOperandForConstraint: substitute XZR/WZR for literal zero values
1 parent 04f87c6 commit 9482083

File tree

4 files changed

+164
-0
lines changed

4 files changed

+164
-0
lines changed

clang/lib/Basic/Targets/AArch64.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,17 @@ std::string
14951495
AArch64TargetInfo::convertConstraint(const char *&Constraint) const {
14961496
std::string R;
14971497
switch (*Constraint) {
1498+
case 'r':
1499+
// Check for "rZ" or "rz" constraint (register or zero)
1500+
if (Constraint[1] == 'Z' || Constraint[1] == 'z') {
1501+
// Return with "^" prefix to indicate 2-character constraint
1502+
R = "^r";
1503+
R += Constraint[1];
1504+
Constraint += 1;
1505+
return R;
1506+
}
1507+
R = TargetInfo::convertConstraint(Constraint);
1508+
break;
14981509
case 'U': // Three-character constraint; add "@3" hint for later parsing.
14991510
R = std::string("@3") + std::string(Constraint, 3);
15001511
Constraint += 2;
@@ -1518,6 +1529,14 @@ bool AArch64TargetInfo::validateAsmConstraint(
15181529
switch (*Name) {
15191530
default:
15201531
return false;
1532+
case 'r':
1533+
// Check if this is "rZ" constraint (register or zero)
1534+
if (Name[1] == 'Z' || Name[1] == 'z') {
1535+
Info.setAllowsRegister();
1536+
Name++;
1537+
return true;
1538+
}
1539+
return false;
15211540
case 'w': // Floating point and SIMD registers (V0-V31)
15221541
Info.setAllowsRegister();
15231542
return true;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: not %clang_cc1 -triple aarch64-linux-gnu -O2 -S -o /dev/null %s 2>&1 | FileCheck %s
2+
3+
// Test that the "rZ" inline assembly constraint properly rejects non-constant values.
4+
// The "rZ" constraint is only valid for literal zero values.
5+
6+
// CHECK: error: invalid operand for inline asm constraint 'rZ'
7+
void test_rZ_runtime_value(long *addr, long val) {
8+
__asm__ volatile("str %1, [%0]" : : "r"(addr), "rZ"(val));
9+
}
10+
11+
// CHECK: error: invalid operand for inline asm constraint 'rZ'
12+
void test_rZ_runtime_i32(int *addr, int val) {
13+
__asm__ volatile("str %w1, [%0]" : : "r"(addr), "rZ"(val));
14+
}
15+
16+
// CHECK: error: invalid operand for inline asm constraint 'rZ'
17+
void test_rZ_non_constant(long val) {
18+
__asm__ volatile("mov x2, %0" : : "rZ"(val));
19+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// RUN: %clang_cc1 -triple aarch64-linux-gnu -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-IR
2+
// RUN: %clang_cc1 -triple aarch64-linux-gnu -O2 -S -o - %s | FileCheck %s --check-prefix=CHECK-ASM
3+
4+
// Test the "rZ" inline assembly constraint for AArch64.
5+
6+
// CHECK-IR-LABEL: define dso_local void @test_rZ_zero_i64(
7+
// CHECK-IR: tail call void asm sideeffect "str $1, [$0]", "r,^rZ"(ptr %addr, i64 0)
8+
//
9+
// CHECK-ASM-LABEL: test_rZ_zero_i64:
10+
// CHECK-ASM: str xzr, [x0]
11+
void test_rZ_zero_i64(long *addr) {
12+
__asm__ volatile("str %1, [%0]" : : "r"(addr), "rZ"(0L));
13+
}
14+
15+
// CHECK-IR-LABEL: define dso_local void @test_rZ_zero_i32(
16+
// CHECK-IR: tail call void asm sideeffect "str ${1:w}, [$0]", "r,^rZ"(ptr %addr, i32 0)
17+
//
18+
// CHECK-ASM-LABEL: test_rZ_zero_i32:
19+
// CHECK-ASM: str wzr, [x0]
20+
void test_rZ_zero_i32(int *addr) {
21+
__asm__ volatile("str %w1, [%0]" : : "r"(addr), "rZ"(0));
22+
}
23+
24+
// CHECK-IR-LABEL: define dso_local void @test_rZ_zero_i16(
25+
// CHECK-IR: tail call void asm sideeffect "strh ${1:w}, [$0]", "r,^rZ"(ptr %addr, i16 0)
26+
//
27+
// CHECK-ASM-LABEL: test_rZ_zero_i16:
28+
// CHECK-ASM: strh wzr, [x0]
29+
void test_rZ_zero_i16(short *addr) {
30+
__asm__ volatile("strh %w1, [%0]" : : "r"(addr), "rZ"((short)0));
31+
}
32+
33+
// CHECK-IR-LABEL: define dso_local void @test_rZ_zero_i8(
34+
// CHECK-IR: tail call void asm sideeffect "strb ${1:w}, [$0]", "r,^rZ"(ptr %addr, i8 0)
35+
//
36+
// CHECK-ASM-LABEL: test_rZ_zero_i8:
37+
// CHECK-ASM: strb wzr, [x0]
38+
void test_rZ_zero_i8(char *addr) {
39+
__asm__ volatile("strb %w1, [%0]" : : "r"(addr), "rZ"((char)0));
40+
}
41+
42+
// CHECK-IR-LABEL: define dso_local void @test_rz_lowercase(
43+
// CHECK-IR: tail call void asm sideeffect "str $1, [$0]", "r,^rz"(ptr %addr, i64 0)
44+
//
45+
// CHECK-ASM-LABEL: test_rz_lowercase:
46+
// CHECK-ASM: str xzr, [x0]
47+
void test_rz_lowercase(long *addr) {
48+
__asm__ volatile("str %1, [%0]" : : "r"(addr), "rz"(0L));
49+
}
50+
51+
// CHECK-IR-LABEL: define dso_local void @test_rZ_explicit_x(
52+
// CHECK-IR: tail call void asm sideeffect "mov ${0:x}, xzr", "^rZ"(i64 0)
53+
//
54+
// CHECK-ASM-LABEL: test_rZ_explicit_x:
55+
// CHECK-ASM: mov xzr, xzr
56+
void test_rZ_explicit_x(void) {
57+
__asm__ volatile("mov %x0, xzr" : : "rZ"(0L));
58+
}
59+
60+
// CHECK-IR-LABEL: define dso_local void @test_rZ_explicit_w(
61+
// CHECK-IR: tail call void asm sideeffect "mov ${0:w}, wzr", "^rZ"(i32 0)
62+
//
63+
// CHECK-ASM-LABEL: test_rZ_explicit_w:
64+
// CHECK-ASM: mov wzr, wzr
65+
void test_rZ_explicit_w(void) {
66+
__asm__ volatile("mov %w0, wzr" : : "rZ"(0));
67+
}
68+
69+
// CHECK-IR-LABEL: define dso_local void @test_rZ_x_modifier(
70+
// CHECK-IR: tail call void asm sideeffect "add x2, x1, ${0:x}", "^rZ"(i64 0)
71+
//
72+
// CHECK-ASM-LABEL: test_rZ_x_modifier:
73+
// CHECK-ASM: add x2, x1, xzr
74+
void test_rZ_x_modifier(void) {
75+
__asm__ volatile("add x2, x1, %x0" : : "rZ"(0L));
76+
}
77+
78+
// CHECK-IR-LABEL: define dso_local void @test_rZ_w_modifier(
79+
// CHECK-IR: tail call void asm sideeffect "add w2, w1, ${0:w}", "^rZ"(i32 0)
80+
//
81+
// CHECK-ASM-LABEL: test_rZ_w_modifier:
82+
// CHECK-ASM: add w2, w1, wzr
83+
void test_rZ_w_modifier(void) {
84+
__asm__ volatile("add w2, w1, %w0" : : "rZ"(0));
85+
}
86+

llvm/lib/Target/AArch64/AArch64ISelLowering.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13018,6 +13018,9 @@ AArch64TargetLowering::getConstraintType(StringRef Constraint) const {
1301813018
case 'S': // A symbol or label reference with a constant offset
1301913019
return C_Other;
1302013020
}
13021+
} else if (Constraint.size() == 2 &&
13022+
(Constraint == "rZ" || Constraint == "rz")) {
13023+
return C_RegisterClass;
1302113024
} else if (parsePredicateConstraint(Constraint))
1302213025
return C_RegisterClass;
1302313026
else if (parseReducedGprConstraint(Constraint))
@@ -13045,6 +13048,14 @@ AArch64TargetLowering::getSingleConstraintMatchWeight(
1304513048
default:
1304613049
weight = TargetLowering::getSingleConstraintMatchWeight(info, constraint);
1304713050
break;
13051+
case 'r':
13052+
// Check for "rZ" or "rz" constraint (register or zero)
13053+
if (constraint[1] == 'Z' || constraint[1] == 'z') {
13054+
weight = CW_Register;
13055+
break;
13056+
}
13057+
weight = TargetLowering::getSingleConstraintMatchWeight(info, constraint);
13058+
break;
1304813059
case 'x':
1304913060
case 'w':
1305013061
case 'y':
@@ -13066,6 +13077,18 @@ AArch64TargetLowering::getSingleConstraintMatchWeight(
1306613077
std::pair<unsigned, const TargetRegisterClass *>
1306713078
AArch64TargetLowering::getRegForInlineAsmConstraint(
1306813079
const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const {
13080+
// Handle "rZ" and "rz" constraints
13081+
if (Constraint.size() == 2 && Constraint[0] == 'r' &&
13082+
(Constraint[1] == 'Z' || Constraint[1] == 'z')) {
13083+
if (VT.isScalableVector())
13084+
return std::make_pair(0U, nullptr);
13085+
if (Subtarget->hasLS64() && VT.getSizeInBits() == 512)
13086+
return std::make_pair(0U, &AArch64::GPR64x8ClassRegClass);
13087+
if (VT.getFixedSizeInBits() == 64)
13088+
return std::make_pair(0U, &AArch64::GPR64commonRegClass);
13089+
return std::make_pair(0U, &AArch64::GPR32commonRegClass);
13090+
}
13091+
1306913092
if (Constraint.size() == 1) {
1307013093
switch (Constraint[0]) {
1307113094
case 'r':
@@ -13196,6 +13219,23 @@ void AArch64TargetLowering::LowerAsmOperandForConstraint(
1319613219
SelectionDAG &DAG) const {
1319713220
SDValue Result;
1319813221

13222+
// Handle "rZ" and "rz" constraints (register or zero)
13223+
if (Constraint.size() == 2 && Constraint[0] == 'r' &&
13224+
(Constraint[1] == 'Z' || Constraint[1] == 'z')) {
13225+
if (isNullConstant(Op)) {
13226+
if (Op.getValueType() == MVT::i64)
13227+
Result = DAG.getRegister(AArch64::XZR, MVT::i64);
13228+
else
13229+
Result = DAG.getRegister(AArch64::WZR, MVT::i32);
13230+
13231+
if (Result.getNode()) {
13232+
Ops.push_back(Result);
13233+
return;
13234+
}
13235+
}
13236+
return;
13237+
}
13238+
1319913239
// Currently only support length 1 constraints.
1320013240
if (Constraint.size() != 1)
1320113241
return;

0 commit comments

Comments
 (0)