-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[X86] Add "Ws" constraint and "p" modifier for symbolic address/label reference #77886
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[X86] Add "Ws" constraint and "p" modifier for symbolic address/label reference #77886
Conversation
Created using spr 1.3.4
@llvm/pr-subscribers-backend-x86 @llvm/pr-subscribers-llvm-ir Author: Fangrui Song (MaskRay) ChangesPrinting the raw symbol is useful in inline asm (e.g. getting the C++
DO NOT SUBMIT wait for Ws patch https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105576 Full diff: https://github.com/llvm/llvm-project/pull/77886.diff 8 Files Affected:
diff --git a/clang/lib/Basic/Targets/X86.cpp b/clang/lib/Basic/Targets/X86.cpp
index 64e281b888a95f..a68b662d9401aa 100644
--- a/clang/lib/Basic/Targets/X86.cpp
+++ b/clang/lib/Basic/Targets/X86.cpp
@@ -1418,6 +1418,14 @@ bool X86TargetInfo::validateAsmConstraint(
case 'O':
Info.setRequiresImmediate(0, 127);
return true;
+ case 'W':
+ switch (*++Name) {
+ default:
+ return false;
+ case 's':
+ Info.setAllowsRegister();
+ return true;
+ }
// Register constraints.
case 'Y': // 'Y' is the first character for several 2-character constraints.
// Shift the pointer to the second character of the constraint.
@@ -1715,6 +1723,9 @@ std::string X86TargetInfo::convertConstraint(const char *&Constraint) const {
return std::string("{st}");
case 'u': // second from top of floating point stack.
return std::string("{st(1)}"); // second from top of floating point stack.
+ case 'W':
+ assert(Constraint[1] == 's');
+ return '^' + std::string(Constraint++, 2);
case 'Y':
switch (Constraint[1]) {
default:
diff --git a/clang/test/CodeGen/X86/inline-asm-constraints.c b/clang/test/CodeGen/X86/inline-asm-constraints.c
index b75a84d7a7bcbf..bfcbbca7c4f6bf 100644
--- a/clang/test/CodeGen/X86/inline-asm-constraints.c
+++ b/clang/test/CodeGen/X86/inline-asm-constraints.c
@@ -53,3 +53,11 @@ __m512 testZMM0(void) {
#endif
return zmm0;
}
+
+extern int var;
+
+// CHECK-LABEL: test_Ws(
+// CHECK: call void asm sideeffect "// ${0:p} ${1:p}", "^Ws,^Ws,~{dirflag},~{fpsr},~{flags}"(ptr @var, ptr @test_Ws)
+void test_Ws(void) {
+ asm("// %p0 %p1" :: "Ws"(&var), "Ws"(test_Ws));
+}
diff --git a/clang/test/Sema/inline-asm-validate-x86.c b/clang/test/Sema/inline-asm-validate-x86.c
index 87b60a0955301a..032d76477c4ae6 100644
--- a/clang/test/Sema/inline-asm-validate-x86.c
+++ b/clang/test/Sema/inline-asm-validate-x86.c
@@ -130,3 +130,7 @@ void pr40890(void) {
__asm__ __volatile__("\n#define BEEF abcd%0\n" : : "n"((int*)0xdeadbeeeeeef));
#endif
}
+
+void test_W() {
+ asm("" : : "Wd"(test_W)); // expected-error{{invalid input constraint 'Wd' in asm}}
+}
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index d881deb30049a2..076029976ffc5d 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -5336,6 +5336,7 @@ X86:
operand in a SSE register. If AVX is also enabled, can also be a 256-bit
vector operand in an AVX register. If AVX-512 is also enabled, can also be a
512-bit vector operand in an AVX512 register. Otherwise, an error.
+- ``Ws``: A symbolic reference or label reference.
- ``x``: The same as ``v``, except that when AVX-512 is enabled, the ``x`` code
only allocates into the first 16 AVX-512 registers, while the ``v`` code
allocates into any of the 32 AVX-512 registers.
@@ -5518,6 +5519,7 @@ X86:
the operand. (The behavior for relocatable symbol expressions is a
target-specific behavior for this typically target-independent modifier)
- ``H``: Print a memory reference with additional offset +8.
+- ``p``: Print a raw symbol name (without syntax-specific prefixes).
- ``P``: Print a memory reference used as the argument of a call instruction or
used with explicit base reg and index reg as its offset. So it can not use
additional regs to present the memory reference. (E.g. omit ``(rip)``, even
diff --git a/llvm/lib/Target/X86/X86AsmPrinter.cpp b/llvm/lib/Target/X86/X86AsmPrinter.cpp
index 15cfd247f125ca..9f0fd4d0938e97 100644
--- a/llvm/lib/Target/X86/X86AsmPrinter.cpp
+++ b/llvm/lib/Target/X86/X86AsmPrinter.cpp
@@ -774,6 +774,14 @@ bool X86AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
PrintOperand(MI, OpNo, O);
return false;
+ case 'p': {
+ const MachineOperand &MO = MI->getOperand(OpNo);
+ if (MO.getType() != MachineOperand::MO_GlobalAddress)
+ return true;
+ PrintSymbolOperand(MO, O);
+ return false;
+ }
+
case 'P': // This is the operand of a call, treat specially.
PrintPCRelImm(MI, OpNo, O);
return false;
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 700ab797b2f69f..aea8e8b40ff631 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -56676,6 +56676,10 @@ X86TargetLowering::getConstraintType(StringRef Constraint) const {
switch (Constraint[0]) {
default:
break;
+ case 'W':
+ if (Constraint[1] != 's')
+ break;
+ return C_Other;
case 'Y':
switch (Constraint[1]) {
default:
@@ -56880,11 +56884,6 @@ void X86TargetLowering::LowerAsmOperandForConstraint(SDValue Op,
std::vector<SDValue> &Ops,
SelectionDAG &DAG) const {
SDValue Result;
-
- // Only support length 1 constraints for now.
- if (Constraint.size() > 1)
- return;
-
char ConstraintLetter = Constraint[0];
switch (ConstraintLetter) {
default: break;
@@ -56966,6 +56965,17 @@ void X86TargetLowering::LowerAsmOperandForConstraint(SDValue Op,
}
return;
}
+ case 'W': {
+ assert(Constraint[1] == 's');
+ if (const auto *GA = dyn_cast<GlobalAddressSDNode>(Op)) {
+ Ops.push_back(DAG.getTargetGlobalAddress(GA->getGlobal(), SDLoc(Op),
+ GA->getValueType(0)));
+ } else if (const auto *BA = dyn_cast<BlockAddressSDNode>(Op)) {
+ Ops.push_back(DAG.getTargetBlockAddress(BA->getBlockAddress(),
+ BA->getValueType(0)));
+ }
+ return;
+ }
case 'Z': {
// 32-bit unsigned value
if (auto *C = dyn_cast<ConstantSDNode>(Op)) {
diff --git a/llvm/test/CodeGen/X86/inline-asm-Ws-constraint-error.ll b/llvm/test/CodeGen/X86/inline-asm-Ws-constraint-error.ll
new file mode 100644
index 00000000000000..38d0f08dfbd865
--- /dev/null
+++ b/llvm/test/CodeGen/X86/inline-asm-Ws-constraint-error.ll
@@ -0,0 +1,9 @@
+; RUN: not llc -mtriple=x86_64 < %s 2>&1 | FileCheck %s
+
+; CHECK: error: invalid operand for inline asm constraint 'Ws'
+define void @test() {
+entry:
+ %x = alloca i32, align 4
+ call void asm sideeffect "// ${0:p}", "^Ws,~{dirflag},~{fpsr},~{flags}"(ptr %x)
+ ret void
+}
diff --git a/llvm/test/CodeGen/X86/inline-asm-Ws-constraint.ll b/llvm/test/CodeGen/X86/inline-asm-Ws-constraint.ll
new file mode 100644
index 00000000000000..72fc13795f08ce
--- /dev/null
+++ b/llvm/test/CodeGen/X86/inline-asm-Ws-constraint.ll
@@ -0,0 +1,34 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=i686 < %s | FileCheck %s
+; RUN: llc -mtriple=x86_64 < %s | FileCheck %s
+
+@var = external dso_local global i32, align 4
+
+define dso_local void @test() {
+; CHECK-LABEL: test:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: # var test
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret{{[l|q]}}
+entry:
+ call void asm sideeffect "// ${0:p} ${1:p}", "^Ws,^Ws,~{dirflag},~{fpsr},~{flags}"(ptr @var, ptr @test)
+ ret void
+}
+
+define dso_local void @test_label() {
+; CHECK-LABEL: test_label:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: .Ltmp0: # Block address taken
+; CHECK-NEXT: # %bb.1: # %label
+; CHECK-NEXT: #APP
+; CHECK-NEXT: # .Ltmp0
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret{{[l|q]}}
+entry:
+ br label %label
+
+label:
+ tail call void asm sideeffect "// ${0:p}", "Ws,~{dirflag},~{fpsr},~{flags}"(ptr blockaddress(@test_label, %label))
+ ret void
+}
|
@llvm/pr-subscribers-clang Author: Fangrui Song (MaskRay) ChangesPrinting the raw symbol is useful in inline asm (e.g. getting the C++
DO NOT SUBMIT wait for Ws patch https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105576 Full diff: https://github.com/llvm/llvm-project/pull/77886.diff 8 Files Affected:
diff --git a/clang/lib/Basic/Targets/X86.cpp b/clang/lib/Basic/Targets/X86.cpp
index 64e281b888a95f..a68b662d9401aa 100644
--- a/clang/lib/Basic/Targets/X86.cpp
+++ b/clang/lib/Basic/Targets/X86.cpp
@@ -1418,6 +1418,14 @@ bool X86TargetInfo::validateAsmConstraint(
case 'O':
Info.setRequiresImmediate(0, 127);
return true;
+ case 'W':
+ switch (*++Name) {
+ default:
+ return false;
+ case 's':
+ Info.setAllowsRegister();
+ return true;
+ }
// Register constraints.
case 'Y': // 'Y' is the first character for several 2-character constraints.
// Shift the pointer to the second character of the constraint.
@@ -1715,6 +1723,9 @@ std::string X86TargetInfo::convertConstraint(const char *&Constraint) const {
return std::string("{st}");
case 'u': // second from top of floating point stack.
return std::string("{st(1)}"); // second from top of floating point stack.
+ case 'W':
+ assert(Constraint[1] == 's');
+ return '^' + std::string(Constraint++, 2);
case 'Y':
switch (Constraint[1]) {
default:
diff --git a/clang/test/CodeGen/X86/inline-asm-constraints.c b/clang/test/CodeGen/X86/inline-asm-constraints.c
index b75a84d7a7bcbf..bfcbbca7c4f6bf 100644
--- a/clang/test/CodeGen/X86/inline-asm-constraints.c
+++ b/clang/test/CodeGen/X86/inline-asm-constraints.c
@@ -53,3 +53,11 @@ __m512 testZMM0(void) {
#endif
return zmm0;
}
+
+extern int var;
+
+// CHECK-LABEL: test_Ws(
+// CHECK: call void asm sideeffect "// ${0:p} ${1:p}", "^Ws,^Ws,~{dirflag},~{fpsr},~{flags}"(ptr @var, ptr @test_Ws)
+void test_Ws(void) {
+ asm("// %p0 %p1" :: "Ws"(&var), "Ws"(test_Ws));
+}
diff --git a/clang/test/Sema/inline-asm-validate-x86.c b/clang/test/Sema/inline-asm-validate-x86.c
index 87b60a0955301a..032d76477c4ae6 100644
--- a/clang/test/Sema/inline-asm-validate-x86.c
+++ b/clang/test/Sema/inline-asm-validate-x86.c
@@ -130,3 +130,7 @@ void pr40890(void) {
__asm__ __volatile__("\n#define BEEF abcd%0\n" : : "n"((int*)0xdeadbeeeeeef));
#endif
}
+
+void test_W() {
+ asm("" : : "Wd"(test_W)); // expected-error{{invalid input constraint 'Wd' in asm}}
+}
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index d881deb30049a2..076029976ffc5d 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -5336,6 +5336,7 @@ X86:
operand in a SSE register. If AVX is also enabled, can also be a 256-bit
vector operand in an AVX register. If AVX-512 is also enabled, can also be a
512-bit vector operand in an AVX512 register. Otherwise, an error.
+- ``Ws``: A symbolic reference or label reference.
- ``x``: The same as ``v``, except that when AVX-512 is enabled, the ``x`` code
only allocates into the first 16 AVX-512 registers, while the ``v`` code
allocates into any of the 32 AVX-512 registers.
@@ -5518,6 +5519,7 @@ X86:
the operand. (The behavior for relocatable symbol expressions is a
target-specific behavior for this typically target-independent modifier)
- ``H``: Print a memory reference with additional offset +8.
+- ``p``: Print a raw symbol name (without syntax-specific prefixes).
- ``P``: Print a memory reference used as the argument of a call instruction or
used with explicit base reg and index reg as its offset. So it can not use
additional regs to present the memory reference. (E.g. omit ``(rip)``, even
diff --git a/llvm/lib/Target/X86/X86AsmPrinter.cpp b/llvm/lib/Target/X86/X86AsmPrinter.cpp
index 15cfd247f125ca..9f0fd4d0938e97 100644
--- a/llvm/lib/Target/X86/X86AsmPrinter.cpp
+++ b/llvm/lib/Target/X86/X86AsmPrinter.cpp
@@ -774,6 +774,14 @@ bool X86AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
PrintOperand(MI, OpNo, O);
return false;
+ case 'p': {
+ const MachineOperand &MO = MI->getOperand(OpNo);
+ if (MO.getType() != MachineOperand::MO_GlobalAddress)
+ return true;
+ PrintSymbolOperand(MO, O);
+ return false;
+ }
+
case 'P': // This is the operand of a call, treat specially.
PrintPCRelImm(MI, OpNo, O);
return false;
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 700ab797b2f69f..aea8e8b40ff631 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -56676,6 +56676,10 @@ X86TargetLowering::getConstraintType(StringRef Constraint) const {
switch (Constraint[0]) {
default:
break;
+ case 'W':
+ if (Constraint[1] != 's')
+ break;
+ return C_Other;
case 'Y':
switch (Constraint[1]) {
default:
@@ -56880,11 +56884,6 @@ void X86TargetLowering::LowerAsmOperandForConstraint(SDValue Op,
std::vector<SDValue> &Ops,
SelectionDAG &DAG) const {
SDValue Result;
-
- // Only support length 1 constraints for now.
- if (Constraint.size() > 1)
- return;
-
char ConstraintLetter = Constraint[0];
switch (ConstraintLetter) {
default: break;
@@ -56966,6 +56965,17 @@ void X86TargetLowering::LowerAsmOperandForConstraint(SDValue Op,
}
return;
}
+ case 'W': {
+ assert(Constraint[1] == 's');
+ if (const auto *GA = dyn_cast<GlobalAddressSDNode>(Op)) {
+ Ops.push_back(DAG.getTargetGlobalAddress(GA->getGlobal(), SDLoc(Op),
+ GA->getValueType(0)));
+ } else if (const auto *BA = dyn_cast<BlockAddressSDNode>(Op)) {
+ Ops.push_back(DAG.getTargetBlockAddress(BA->getBlockAddress(),
+ BA->getValueType(0)));
+ }
+ return;
+ }
case 'Z': {
// 32-bit unsigned value
if (auto *C = dyn_cast<ConstantSDNode>(Op)) {
diff --git a/llvm/test/CodeGen/X86/inline-asm-Ws-constraint-error.ll b/llvm/test/CodeGen/X86/inline-asm-Ws-constraint-error.ll
new file mode 100644
index 00000000000000..38d0f08dfbd865
--- /dev/null
+++ b/llvm/test/CodeGen/X86/inline-asm-Ws-constraint-error.ll
@@ -0,0 +1,9 @@
+; RUN: not llc -mtriple=x86_64 < %s 2>&1 | FileCheck %s
+
+; CHECK: error: invalid operand for inline asm constraint 'Ws'
+define void @test() {
+entry:
+ %x = alloca i32, align 4
+ call void asm sideeffect "// ${0:p}", "^Ws,~{dirflag},~{fpsr},~{flags}"(ptr %x)
+ ret void
+}
diff --git a/llvm/test/CodeGen/X86/inline-asm-Ws-constraint.ll b/llvm/test/CodeGen/X86/inline-asm-Ws-constraint.ll
new file mode 100644
index 00000000000000..72fc13795f08ce
--- /dev/null
+++ b/llvm/test/CodeGen/X86/inline-asm-Ws-constraint.ll
@@ -0,0 +1,34 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=i686 < %s | FileCheck %s
+; RUN: llc -mtriple=x86_64 < %s | FileCheck %s
+
+@var = external dso_local global i32, align 4
+
+define dso_local void @test() {
+; CHECK-LABEL: test:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: # var test
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret{{[l|q]}}
+entry:
+ call void asm sideeffect "// ${0:p} ${1:p}", "^Ws,^Ws,~{dirflag},~{fpsr},~{flags}"(ptr @var, ptr @test)
+ ret void
+}
+
+define dso_local void @test_label() {
+; CHECK-LABEL: test_label:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: .Ltmp0: # Block address taken
+; CHECK-NEXT: # %bb.1: # %label
+; CHECK-NEXT: #APP
+; CHECK-NEXT: # .Ltmp0
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret{{[l|q]}}
+entry:
+ br label %label
+
+label:
+ tail call void asm sideeffect "// ${0:p}", "Ws,~{dirflag},~{fpsr},~{flags}"(ptr blockaddress(@test_label, %label))
+ ret void
+}
|
llvm/docs/LangRef.rst
Outdated
@@ -5336,6 +5336,7 @@ X86: | |||
operand in a SSE register. If AVX is also enabled, can also be a 256-bit | |||
vector operand in an AVX register. If AVX-512 is also enabled, can also be a | |||
512-bit vector operand in an AVX512 register. Otherwise, an error. | |||
- ``Ws``: A symbolic reference or label reference. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason to choose Ws
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
x86 has almost run out of uppercase and lowercase letters... The remaining ones are z
and H
.
I tried z
but one GCC x86 maintainer considers W
prefix better: https://gcc.gnu.org/pipermail/gcc-patches/2024-January/642596.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, thanks for the link! Do we support the pointer offset like GCC &var + 1
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's supported (aarch64 "S" supports it but riscv "S" doesn't) I intend to support it for x86 "Ws".
OK, I've figured out how to do it in SelectionDAG... Updated.
Created using spr 1.3.4
Created using spr 1.3.4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with some nits.
@@ -130,3 +130,7 @@ void pr40890(void) { | |||
__asm__ __volatile__("\n#define BEEF abcd%0\n" : : "n"((int*)0xdeadbeeeeeef)); | |||
#endif | |||
} | |||
|
|||
void test_W(int i) { | |||
asm("" : : "Wd"(test_W)); // expected-error{{invalid input constraint 'Wd' in asm}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use __asm__
for consistency?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And add a positive test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The codegen tests asm("// %p0 %p1 %p2" :: "Ws"(&var), "Ws"(&arr[3]), "Ws"(test_Ws));
(and a struct test I am adding) provide positive tests:)
default: | ||
return false; | ||
case 's': | ||
Info.setAllowsRegister(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if it matters to a print constraint, should it be setAllowsMemory
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setAllowsRegister
is somewhat confusing but its use is correct here (also used by aarch64/riscv S
).
setAllowsMemory
seems to not add extra checks.
Created using spr 1.3.4
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).
Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105576