[clang] d4cb5d9 - [X86] Add "Ws" constraint and "p" modifier for symbolic address/label reference (#77886)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jan 16 23:57:46 PST 2024
Author: Fangrui Song
Date: 2024-01-16T23:57:42-08:00
New Revision: d4cb5d9f2ba636b0049fc5791d378e224e3a3ae7
URL: https://github.com/llvm/llvm-project/commit/d4cb5d9f2ba636b0049fc5791d378e224e3a3ae7
DIFF: https://github.com/llvm/llvm-project/commit/d4cb5d9f2ba636b0049fc5791d378e224e3a3ae7.diff
LOG: [X86] Add "Ws" constraint and "p" modifier for symbolic address/label reference (#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
Added:
llvm/test/CodeGen/X86/inline-asm-Ws-constraint-error.ll
llvm/test/CodeGen/X86/inline-asm-Ws-constraint.ll
Modified:
clang/lib/Basic/Targets/X86.cpp
clang/test/CodeGen/X86/inline-asm-constraints.c
clang/test/Sema/inline-asm-validate-x86.c
llvm/docs/LangRef.rst
llvm/lib/Target/X86/X86AsmPrinter.cpp
llvm/lib/Target/X86/X86ISelLowering.cpp
Removed:
################################################################################
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..c89d94cab946b3 100644
--- a/clang/test/CodeGen/X86/inline-asm-constraints.c
+++ b/clang/test/CodeGen/X86/inline-asm-constraints.c
@@ -53,3 +53,14 @@ __m512 testZMM0(void) {
#endif
return zmm0;
}
+
+extern int var, arr[4];
+struct Pair { int a, b; } pair;
+
+// CHECK-LABEL: test_Ws(
+// 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)
+// CHECK: call void asm sideeffect "// $0", "^Ws,~{dirflag},~{fpsr},~{flags}"(ptr getelementptr inbounds (%struct.Pair, ptr @pair, i32 0, i32 1))
+void test_Ws(void) {
+ asm("// %p0 %p1 %p2" :: "Ws"(&var), "Ws"(&arr[3]), "Ws"(test_Ws));
+ asm("// %0" :: "Ws"(&pair.b));
+}
diff --git a/clang/test/Sema/inline-asm-validate-x86.c b/clang/test/Sema/inline-asm-validate-x86.c
index 87b60a0955301a..d4c10c36f5fbea 100644
--- a/clang/test/Sema/inline-asm-validate-x86.c
+++ b/clang/test/Sema/inline-asm-validate-x86.c
@@ -130,3 +130,11 @@ 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}}
+
+ __asm__("" : : "Ws"(test_W(0))); // expected-error{{invalid type 'void' in asm input for constraint 'Ws'}}
+ // Codegen error
+ __asm__("" : : "Ws"(i));
+}
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index d881deb30049a2..27429ad1f43c99 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -5336,6 +5336,8 @@ 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 with an optional constant addend or a 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 +5520,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 ff2014d8fa7b1c..68634068fee31c 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -56686,6 +56686,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:
@@ -56890,11 +56894,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;
@@ -56976,6 +56975,26 @@ void X86TargetLowering::LowerAsmOperandForConstraint(SDValue Op,
}
return;
}
+ case 'W': {
+ assert(Constraint[1] == 's');
+ // Op is a BlockAddressSDNode or a GlobalAddressSDNode with an optional
+ // offset.
+ if (const auto *BA = dyn_cast<BlockAddressSDNode>(Op)) {
+ Ops.push_back(DAG.getTargetBlockAddress(BA->getBlockAddress(),
+ BA->getValueType(0)));
+ } else {
+ int64_t Offset = 0;
+ if (Op->getOpcode() == ISD::ADD &&
+ isa<ConstantSDNode>(Op->getOperand(1))) {
+ Offset = cast<ConstantSDNode>(Op->getOperand(1))->getSExtValue();
+ Op = Op->getOperand(0);
+ }
+ if (const auto *GA = dyn_cast<GlobalAddressSDNode>(Op))
+ Ops.push_back(DAG.getTargetGlobalAddress(GA->getGlobal(), SDLoc(Op),
+ GA->getValueType(0), Offset));
+ }
+ 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..2929b11d2e58d8
--- /dev/null
+++ b/llvm/test/CodeGen/X86/inline-asm-Ws-constraint-error.ll
@@ -0,0 +1,14 @@
+; RUN: not llc -mtriple=x86_64 < %s 2>&1 | FileCheck %s
+
+ at a = external global [4 x i32], align 16
+
+; CHECK-COUNT-2: error: invalid operand for inline asm constraint 'Ws'
+; CHECK-NOT: error:
+define void @test(i64 %i) {
+entry:
+ %x = alloca i32, align 4
+ %ai = getelementptr inbounds [4 x i32], ptr @a, i64 0, i64 %i
+ call void asm sideeffect "", "^Ws,~{dirflag},~{fpsr},~{flags}"(ptr %x)
+ call void asm sideeffect "", "^Ws,~{dirflag},~{fpsr},~{flags}"(ptr %ai)
+ 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..c10d631e9c434d
--- /dev/null
+++ b/llvm/test/CodeGen/X86/inline-asm-Ws-constraint.ll
@@ -0,0 +1,36 @@
+; 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
+
+ at var = external dso_local global i32, align 4
+ at a = external global [4 x i32], align 16
+
+define dso_local void @test() {
+; CHECK-LABEL: test:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: #APP
+; CHECK-NEXT: # var a+12 test
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: ret{{[l|q]}}
+entry:
+ %ai = getelementptr inbounds [4 x i32], ptr @a, i64 0, i64 3
+ call void asm sideeffect "// ${0:p} ${1:p} ${2:p}", "^Ws,^Ws,^Ws,~{dirflag},~{fpsr},~{flags}"(ptr @var, ptr %ai, 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
+}
More information about the cfe-commits
mailing list