Skip to content

[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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
Copy link
Contributor

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?

Copy link
Member Author

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.

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 @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -56966,6 +56965,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
}