Skip to content

Commit

Permalink
[X86] Add "Ws" constraint and "p" modifier for symbolic address/label…
Browse files Browse the repository at this point in the history
… 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
  • Loading branch information
MaskRay committed Jan 17, 2024
1 parent 2b3cdd6 commit d4cb5d9
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 5 deletions.
11 changes: 11 additions & 0 deletions clang/lib/Basic/Targets/X86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down
11 changes: 11 additions & 0 deletions clang/test/CodeGen/X86/inline-asm-constraints.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
8 changes: 8 additions & 0 deletions clang/test/Sema/inline-asm-validate-x86.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
3 changes: 3 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions llvm/lib/Target/X86/X86AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
29 changes: 24 additions & 5 deletions llvm/lib/Target/X86/X86ISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)) {
Expand Down
14 changes: 14 additions & 0 deletions llvm/test/CodeGen/X86/inline-asm-Ws-constraint-error.ll
Original file line number Diff line number Diff line change
@@ -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
}
36 changes: 36 additions & 0 deletions llvm/test/CodeGen/X86/inline-asm-Ws-constraint.ll
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit d4cb5d9

Please sign in to comment.