Skip to content

Commit d4cb5d9

Browse files
authored
[X86] Add "Ws" constraint and "p" modifier for symbolic address/label reference (llvm#77886)
Printing the raw symbol is useful in inline asm (e.g. getting the C++ mangled name, referencing a symbol in a custom way while ensuring it is not optimized out even if internal). Similar constraints are available in other targets (e.g. "S" for aarch64/riscv, "Cs" for m68k). ``` namespace ns { extern int var, a[4]; } void foo() { asm(".pushsection .xxx,\"aw\"; .dc.a %p0; .popsection" :: "Ws"(&ns::var)); asm(".reloc ., BFD_RELOC_NONE, %p0" :: "Ws"(&ns::a[3])); } ``` Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105576
1 parent 2b3cdd6 commit d4cb5d9

File tree

8 files changed

+115
-5
lines changed

8 files changed

+115
-5
lines changed

clang/lib/Basic/Targets/X86.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,6 +1418,14 @@ bool X86TargetInfo::validateAsmConstraint(
14181418
case 'O':
14191419
Info.setRequiresImmediate(0, 127);
14201420
return true;
1421+
case 'W':
1422+
switch (*++Name) {
1423+
default:
1424+
return false;
1425+
case 's':
1426+
Info.setAllowsRegister();
1427+
return true;
1428+
}
14211429
// Register constraints.
14221430
case 'Y': // 'Y' is the first character for several 2-character constraints.
14231431
// Shift the pointer to the second character of the constraint.
@@ -1715,6 +1723,9 @@ std::string X86TargetInfo::convertConstraint(const char *&Constraint) const {
17151723
return std::string("{st}");
17161724
case 'u': // second from top of floating point stack.
17171725
return std::string("{st(1)}"); // second from top of floating point stack.
1726+
case 'W':
1727+
assert(Constraint[1] == 's');
1728+
return '^' + std::string(Constraint++, 2);
17181729
case 'Y':
17191730
switch (Constraint[1]) {
17201731
default:

clang/test/CodeGen/X86/inline-asm-constraints.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,14 @@ __m512 testZMM0(void) {
5353
#endif
5454
return zmm0;
5555
}
56+
57+
extern int var, arr[4];
58+
struct Pair { int a, b; } pair;
59+
60+
// CHECK-LABEL: test_Ws(
61+
// CHECK: call void asm sideeffect "// ${0:p} ${1:p} ${2:p}", "^Ws,^Ws,^Ws,~{dirflag},~{fpsr},~{flags}"(ptr @var, ptr getelementptr inbounds ([4 x i32], ptr @arr, i64 0, i64 3), ptr @test_Ws)
62+
// CHECK: call void asm sideeffect "// $0", "^Ws,~{dirflag},~{fpsr},~{flags}"(ptr getelementptr inbounds (%struct.Pair, ptr @pair, i32 0, i32 1))
63+
void test_Ws(void) {
64+
asm("// %p0 %p1 %p2" :: "Ws"(&var), "Ws"(&arr[3]), "Ws"(test_Ws));
65+
asm("// %0" :: "Ws"(&pair.b));
66+
}

clang/test/Sema/inline-asm-validate-x86.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,11 @@ void pr40890(void) {
130130
__asm__ __volatile__("\n#define BEEF abcd%0\n" : : "n"((int*)0xdeadbeeeeeef));
131131
#endif
132132
}
133+
134+
void test_W(int i) {
135+
__asm__("" : : "Wd"(test_W)); // expected-error{{invalid input constraint 'Wd' in asm}}
136+
137+
__asm__("" : : "Ws"(test_W(0))); // expected-error{{invalid type 'void' in asm input for constraint 'Ws'}}
138+
// Codegen error
139+
__asm__("" : : "Ws"(i));
140+
}

llvm/docs/LangRef.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5336,6 +5336,8 @@ X86:
53365336
operand in a SSE register. If AVX is also enabled, can also be a 256-bit
53375337
vector operand in an AVX register. If AVX-512 is also enabled, can also be a
53385338
512-bit vector operand in an AVX512 register. Otherwise, an error.
5339+
- ``Ws``: A symbolic reference with an optional constant addend or a label
5340+
reference.
53395341
- ``x``: The same as ``v``, except that when AVX-512 is enabled, the ``x`` code
53405342
only allocates into the first 16 AVX-512 registers, while the ``v`` code
53415343
allocates into any of the 32 AVX-512 registers.
@@ -5518,6 +5520,7 @@ X86:
55185520
the operand. (The behavior for relocatable symbol expressions is a
55195521
target-specific behavior for this typically target-independent modifier)
55205522
- ``H``: Print a memory reference with additional offset +8.
5523+
- ``p``: Print a raw symbol name (without syntax-specific prefixes).
55215524
- ``P``: Print a memory reference used as the argument of a call instruction or
55225525
used with explicit base reg and index reg as its offset. So it can not use
55235526
additional regs to present the memory reference. (E.g. omit ``(rip)``, even

llvm/lib/Target/X86/X86AsmPrinter.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,14 @@ bool X86AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
774774
PrintOperand(MI, OpNo, O);
775775
return false;
776776

777+
case 'p': {
778+
const MachineOperand &MO = MI->getOperand(OpNo);
779+
if (MO.getType() != MachineOperand::MO_GlobalAddress)
780+
return true;
781+
PrintSymbolOperand(MO, O);
782+
return false;
783+
}
784+
777785
case 'P': // This is the operand of a call, treat specially.
778786
PrintPCRelImm(MI, OpNo, O);
779787
return false;

llvm/lib/Target/X86/X86ISelLowering.cpp

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56686,6 +56686,10 @@ X86TargetLowering::getConstraintType(StringRef Constraint) const {
5668656686
switch (Constraint[0]) {
5668756687
default:
5668856688
break;
56689+
case 'W':
56690+
if (Constraint[1] != 's')
56691+
break;
56692+
return C_Other;
5668956693
case 'Y':
5669056694
switch (Constraint[1]) {
5669156695
default:
@@ -56890,11 +56894,6 @@ void X86TargetLowering::LowerAsmOperandForConstraint(SDValue Op,
5689056894
std::vector<SDValue> &Ops,
5689156895
SelectionDAG &DAG) const {
5689256896
SDValue Result;
56893-
56894-
// Only support length 1 constraints for now.
56895-
if (Constraint.size() > 1)
56896-
return;
56897-
5689856897
char ConstraintLetter = Constraint[0];
5689956898
switch (ConstraintLetter) {
5690056899
default: break;
@@ -56976,6 +56975,26 @@ void X86TargetLowering::LowerAsmOperandForConstraint(SDValue Op,
5697656975
}
5697756976
return;
5697856977
}
56978+
case 'W': {
56979+
assert(Constraint[1] == 's');
56980+
// Op is a BlockAddressSDNode or a GlobalAddressSDNode with an optional
56981+
// offset.
56982+
if (const auto *BA = dyn_cast<BlockAddressSDNode>(Op)) {
56983+
Ops.push_back(DAG.getTargetBlockAddress(BA->getBlockAddress(),
56984+
BA->getValueType(0)));
56985+
} else {
56986+
int64_t Offset = 0;
56987+
if (Op->getOpcode() == ISD::ADD &&
56988+
isa<ConstantSDNode>(Op->getOperand(1))) {
56989+
Offset = cast<ConstantSDNode>(Op->getOperand(1))->getSExtValue();
56990+
Op = Op->getOperand(0);
56991+
}
56992+
if (const auto *GA = dyn_cast<GlobalAddressSDNode>(Op))
56993+
Ops.push_back(DAG.getTargetGlobalAddress(GA->getGlobal(), SDLoc(Op),
56994+
GA->getValueType(0), Offset));
56995+
}
56996+
return;
56997+
}
5697956998
case 'Z': {
5698056999
// 32-bit unsigned value
5698157000
if (auto *C = dyn_cast<ConstantSDNode>(Op)) {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
; RUN: not llc -mtriple=x86_64 < %s 2>&1 | FileCheck %s
2+
3+
@a = external global [4 x i32], align 16
4+
5+
; CHECK-COUNT-2: error: invalid operand for inline asm constraint 'Ws'
6+
; CHECK-NOT: error:
7+
define void @test(i64 %i) {
8+
entry:
9+
%x = alloca i32, align 4
10+
%ai = getelementptr inbounds [4 x i32], ptr @a, i64 0, i64 %i
11+
call void asm sideeffect "", "^Ws,~{dirflag},~{fpsr},~{flags}"(ptr %x)
12+
call void asm sideeffect "", "^Ws,~{dirflag},~{fpsr},~{flags}"(ptr %ai)
13+
ret void
14+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2+
; RUN: llc -mtriple=i686 < %s | FileCheck %s
3+
; RUN: llc -mtriple=x86_64 < %s | FileCheck %s
4+
5+
@var = external dso_local global i32, align 4
6+
@a = external global [4 x i32], align 16
7+
8+
define dso_local void @test() {
9+
; CHECK-LABEL: test:
10+
; CHECK: # %bb.0: # %entry
11+
; CHECK-NEXT: #APP
12+
; CHECK-NEXT: # var a+12 test
13+
; CHECK-NEXT: #NO_APP
14+
; CHECK-NEXT: ret{{[l|q]}}
15+
entry:
16+
%ai = getelementptr inbounds [4 x i32], ptr @a, i64 0, i64 3
17+
call void asm sideeffect "// ${0:p} ${1:p} ${2:p}", "^Ws,^Ws,^Ws,~{dirflag},~{fpsr},~{flags}"(ptr @var, ptr %ai, ptr @test)
18+
ret void
19+
}
20+
21+
define dso_local void @test_label() {
22+
; CHECK-LABEL: test_label:
23+
; CHECK: # %bb.0: # %entry
24+
; CHECK-NEXT: .Ltmp0: # Block address taken
25+
; CHECK-NEXT: # %bb.1: # %label
26+
; CHECK-NEXT: #APP
27+
; CHECK-NEXT: # .Ltmp0
28+
; CHECK-NEXT: #NO_APP
29+
; CHECK-NEXT: ret{{[l|q]}}
30+
entry:
31+
br label %label
32+
33+
label:
34+
tail call void asm sideeffect "// ${0:p}", "^Ws,~{dirflag},~{fpsr},~{flags}"(ptr blockaddress(@test_label, %label))
35+
ret void
36+
}

0 commit comments

Comments
 (0)