diff --git a/clang/lib/Basic/Targets/X86.cpp b/clang/lib/Basic/Targets/X86.cpp index 64e281b888a95..a68b662d9401a 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 b75a84d7a7bcb..c89d94cab946b 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 87b60a0955301..d4c10c36f5fbe 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 d881deb30049a..27429ad1f43c9 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 15cfd247f125c..9f0fd4d0938e9 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 ff2014d8fa7b1..68634068fee31 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 &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(Op)) { + Ops.push_back(DAG.getTargetBlockAddress(BA->getBlockAddress(), + BA->getValueType(0))); + } else { + int64_t Offset = 0; + if (Op->getOpcode() == ISD::ADD && + isa(Op->getOperand(1))) { + Offset = cast(Op->getOperand(1))->getSExtValue(); + Op = Op->getOperand(0); + } + if (const auto *GA = dyn_cast(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(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 0000000000000..2929b11d2e58d --- /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 + +@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 0000000000000..c10d631e9c434 --- /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 + +@var = external dso_local global i32, align 4 +@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 +}