Skip to content
Open
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
4 changes: 2 additions & 2 deletions clang/lib/Basic/Targets/PPC.h
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,8 @@ class LLVM_LIBRARY_VISIBILITY PPCTargetInfo : public TargetInfo {
break;
case 'a': // Address operand that is an indexed or indirect from a
// register (`p' is preferable for asm statements)
// TODO: Add full support for this constraint
return false;
Info.setAllowsRegister();
break;
case 'R': // AIX TOC entry
case 'S': // Constant suitable as a 64-bit mask operand
case 'T': // Constant suitable as a 32-bit mask operand
Expand Down
14 changes: 10 additions & 4 deletions clang/test/CodeGen/PowerPC/inline-asm-constraints-error.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
// RUN: %clang_cc1 -emit-llvm-only -triple powerpc64-ibm-aix-xcoff -verify %s
// RUN: %clang_cc1 -emit-llvm-only -triple powerpc-ibm-aix-xcoff -verify %s
// This test case exist to test marking the 'a' inline assembly constraint as
// unsupported because powerpc previously marked it as supported.
int foo(int arg){
asm goto ("bc 12,2,%l[TEST_LABEL]" : : "a"(&&TEST_LABEL) : : TEST_LABEL); //expected-error {{invalid input constraint 'a' in asm}}
// This test file validates that we diagnose inline assembly constraint error
// in the clang front-end as expected.

int labelConstraintError(int arg){
asm goto ("bc 12,2,%l[TEST_LABEL]" : : "s"(&&TEST_LABEL) : : TEST_LABEL); //expected-error {{invalid input constraint 's' in asm}}
return 0;
TEST_LABEL: return arg + 1;
}

char wrongAddrConstraint(char* result) {
asm ("stb %1,%0" : "a"(result) : "r"('E') :); //expected-error {{invalid output constraint 'a' in asm}}
return *result;
}
71 changes: 71 additions & 0 deletions clang/test/CodeGen/PowerPC/inline-asm-constraints.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// RUN: %clang_cc1 -emit-llvm -triple powerpc64-ibm-aix-xcoff \
// RUN: %s -o - | FileCheck %s
// RUN: %clang_cc1 -emit-llvm -triple powerpc-ibm-aix-xcoff \
// RUN: %s -o - | FileCheck %s
// This test file checks that we represent common C inline assembly
// PowerPC load address mode operations accureately in the platform
// agnostic LLVM IR.

char loadAddressAConstrained(char* ptr) {
// CHECK-LABEL: define{{.*}} i8 @loadAddressAConstrained(ptr noundef %ptr)
// CHECK: %1 = call ptr asm "addi $0,$1, 0", "=r,a"(ptr %0)
char* result;
asm ("addi %0,%1, 0" : "=r"(result) : "a"(ptr) :);
return *result;
}

char loadAddressZyConstrained(char* ptr) {
// CHECK-LABEL: define{{.*}} i8 @loadAddressZyConstrained(ptr noundef %ptr)
// CHECK: %1 = call ptr asm "add $0,${1:y}", "=r,*Z"(ptr elementtype(i8) %0)
char* result;
asm ("add %0,%y1" : "=r"(result) : "Z"(*ptr) :);
return *result;
}

char xFormRegImmLoadAConstrained(char* ptr) {
// CHECK-LABEL: define{{.*}} i8 @xFormRegImmLoadAConstrained(ptr noundef %ptr)
// CHECK: %1 = call ptr asm "addi $0,$1,$2", "=r,a,I"(ptr %0, i32 10000)
char* result;
asm ("addi %0,%1,%2" : "=r"(result) : "a"(ptr), "I"(10000) :);
return *result;
}

char loadIndirectAddressZConstrained(char* ptr) {
// CHECK-LABEL: define{{.*}} i8 @loadIndirectAddressZConstrained(ptr noundef %ptr)
// CHECK: %1 = call ptr asm "ld $0,$1", "=r,*Z"(ptr elementtype(i8) %arrayidx)
char* result;
asm ("ld %0,%1" : "=r"(result) : "Z"(ptr[100]) :);
return *result;
}

char loadIndirectAddressAConstrained(char** ptr, unsigned index) {
// CHECK-LABEL: define{{.*}} i8 @loadIndirectAddressAConstrained(ptr noundef %ptr, i32 noundef{{[ zeroext]*}} %index)
// CHECK: %2 = call ptr asm "ldx $0,$1,$2", "=r,a,r"(ptr %0, i32 %1)
char* result;
asm ("ldx %0,%1,%2" : "=r"(result) : "a"(ptr), "r"(index) :);
return *result;
}

char dFormLoadZConstrained(char* ptr) {
// CHECK-LABEL: define{{.*}} i8 @dFormLoadZConstrained(ptr noundef %ptr)
// CHECK: %1 = call i8 asm "lbz $0,$1", "=r,*Z"(ptr elementtype(i8) %arrayidx)
char result;
asm ("lbz %0,%1" : "=r"(result) : "Z"(ptr[8]) :);
return result;
}

char xFormRegRegLoadZyConstrained(char* ptr, unsigned index) {
// CHECK-LABEL: define{{.*}} i8 @xFormRegRegLoadZyConstrained(ptr noundef %ptr, i32 noundef{{[ zeroext]*}} %index)
// CHECK: %2 = call i8 asm "lbzx $0, ${1:y}", "=r,*Z"(ptr elementtype(i8) %arrayidx)
char result;
asm("lbzx %0, %y1" : "=r"(result) : "Z"(ptr[index]) :);
return result;
}

char xFormRegRegLoadAConstrained(char* ptr, unsigned index) {
// CHECK-LABEL: define{{.*}} i8 @xFormRegRegLoadAConstrained(ptr noundef %ptr, i32 noundef{{[ zeroext]*}} %index)
// CHECK: %2 = call i8 asm "lbzx $0,$1,$2", "=r,a,r"(ptr %0, i32 %1)
char result;
asm ("lbzx %0,%1,%2" : "=r"(result) : "a"(ptr), "r"(index) :);
return result;
}
3 changes: 3 additions & 0 deletions llvm/include/llvm/IR/InlineAsm.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ class InlineAsm final : public Value {

// Address constraints
p,
a,
ZQ,
ZR,
ZS,
Expand Down Expand Up @@ -517,6 +518,8 @@ class InlineAsm final : public Value {
return "Zy";
case ConstraintCode::p:
return "p";
case ConstraintCode::a:
return "a";
case ConstraintCode::ZQ:
return "ZQ";
case ConstraintCode::ZR:
Expand Down
14 changes: 14 additions & 0 deletions llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,21 @@ bool PPCAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
}
}

// Assert that this an register input operand to an inline asm statement.
assert(MI->getOperand(OpNo).isReg());
// The ConstraintInfo for this Operand is encoded in Op - 1 operand
// as a part of the InlineAsm Flags.
const MachineOperand &FlagsOP = MI->getOperand(OpNo - 1);
if (!FlagsOP.isImm())
return true;
const InlineAsm::Flag Flags(FlagsOP.getImm());
// Address operand is a Register for an X-form instruction, similar to the
// 'y' Extra Code. Constraint 'a' allows reg+reg Addressing without using
// a 'y' input modifier in Extended GCC ASM.
if (Flags.getMemoryConstraintID() == InlineAsm::ConstraintCode::a) {
printOperand(MI, OpNo, O);
return false;
}
O << "0(";
printOperand(MI, OpNo, O);
O << ")";
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ namespace {
errs() << "ConstraintID: "
<< InlineAsm::getMemConstraintName(ConstraintID) << "\n";
llvm_unreachable("Unexpected asm memory constraint");
case InlineAsm::ConstraintCode::a:
case InlineAsm::ConstraintCode::es:
case InlineAsm::ConstraintCode::m:
case InlineAsm::ConstraintCode::o:
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/PowerPC/PPCISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17701,6 +17701,9 @@ PPCTargetLowering::getConstraintType(StringRef Constraint) const {
if (Constraint.size() == 1) {
switch (Constraint[0]) {
default: break;
case 'a':
// 'a' constraints inputs to address operands.
return C_Address;
case 'b':
case 'r':
case 'f':
Expand Down
16 changes: 7 additions & 9 deletions llvm/lib/Target/PowerPC/PPCISelLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -998,15 +998,13 @@ namespace llvm {

InlineAsm::ConstraintCode
getInlineAsmMemConstraint(StringRef ConstraintCode) const override {
if (ConstraintCode == "es")
return InlineAsm::ConstraintCode::es;
else if (ConstraintCode == "Q")
return InlineAsm::ConstraintCode::Q;
else if (ConstraintCode == "Z")
return InlineAsm::ConstraintCode::Z;
else if (ConstraintCode == "Zy")
return InlineAsm::ConstraintCode::Zy;
return TargetLowering::getInlineAsmMemConstraint(ConstraintCode);
return StringSwitch<InlineAsm::ConstraintCode>(ConstraintCode)
.Case("es", InlineAsm::ConstraintCode::es)
.Case("Q", InlineAsm::ConstraintCode::Q)
.Case("Z", InlineAsm::ConstraintCode::Z)
.Case("Zy", InlineAsm::ConstraintCode::Zy)
.Case("a", InlineAsm::ConstraintCode::a)
.Default(TargetLowering::getInlineAsmMemConstraint(ConstraintCode));
}

void CollectTargetIntrinsicOperands(const CallInst &I,
Expand Down
127 changes: 127 additions & 0 deletions llvm/test/CodeGen/PowerPC/inline-asm-constraints.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
; RUN: llc -verify-machineinstrs < %s -mcpu=pwr8 \
; RUN: -mtriple=powerpc64-ibm-aix-xcoff | FileCheck %s
; RUN: llc -verify-machineinstrs < %s -mcpu=pwr8 \
; RUN: -mtriple=powerpc-ibm-aix-xcoff | FileCheck %s

define signext i8 @loadAddressAConstrained(ptr %ptr) {
; CHECK-LABEL: loadAddressAConstrained:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: #APP
; CHECK-NEXT: addi 3, 3, 0
; CHECK-NEXT: #NO_APP
; CHECK-NEXT: lbz 3, 0(3)
; CHECK-NEXT: extsb 3, 3
; CHECK-NEXT: blr
entry:
%0 = tail call ptr asm "addi $0,$1, 0", "=r,a"(ptr %ptr)
%1 = load i8, ptr %0, align 1
ret i8 %1
}

define signext i8 @xFormRegImmLoadAConstrained(ptr %ptr) {
; CHECK-LABEL: xFormRegImmLoadAConstrained:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: #APP
; CHECK-NEXT: addi 3, 3, 10000
; CHECK-NEXT: #NO_APP
; CHECK-NEXT: lbz 3, 0(3)
; CHECK-NEXT: extsb 3, 3
; CHECK-NEXT: blr
entry:
%0 = tail call ptr asm "addi $0,$1,$2", "=r,a,I"(ptr %ptr, i32 10000)
%1 = load i8, ptr %0, align 1
ret i8 %1
}

define signext i8 @loadIndirectAddressZConstrained(ptr %ptr) {
; CHECK-LABEL: loadIndirectAddressZConstrained:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: addi 3, 3, 800
; CHECK-NEXT: #APP
; CHECK-NEXT: ld 3, 0(3)
; CHECK-NEXT: #NO_APP
; CHECK-NEXT: lbz 3, 0(3)
; CHECK-NEXT: extsb 3, 3
; CHECK-NEXT: blr
entry:
%arrayidx = getelementptr inbounds nuw i8, ptr %ptr, i64 800
%0 = tail call ptr asm "ld $0,$1", "=r,*Z"(ptr nonnull elementtype(ptr) %arrayidx)
%1 = load i8, ptr %0, align 1
ret i8 %1
}

define signext i8 @loadIndirectAddressAConstrained(ptr %ptr, i32 zeroext %index) {
; CHECK-LABEL: loadIndirectAddressAConstrained:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: #APP
; CHECK-NEXT: ldx 3, 3, 4
; CHECK-NEXT: #NO_APP
; CHECK-NEXT: lbz 3, 0(3)
; CHECK-NEXT: extsb 3, 3
; CHECK-NEXT: blr
entry:
%0 = tail call ptr asm "ldx $0,$1,$2", "=r,a,r"(ptr %ptr, i32 zeroext %index)
%1 = load i8, ptr %0, align 1
ret i8 %1
}

define signext i8 @dFormLoadZConstrained(ptr %ptr) {
; CHECK-LABEL: dFormLoadZConstrained:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: addi 3, 3, 8
; CHECK-NEXT: #APP
; CHECK-NEXT: lbz 3, 0(3)
; CHECK-NEXT: #NO_APP
; CHECK-NEXT: extsb 3, 3
; CHECK-NEXT: blr
entry:
%arrayidx = getelementptr inbounds nuw i8, ptr %ptr, i64 8
%0 = tail call i8 asm "lbz $0,$1", "=r,*Z"(ptr nonnull elementtype(i8) %arrayidx)
ret i8 %0
}

define signext i8 @xFormRegRegLoadZyConstrained(ptr %ptr, i32 zeroext %index) {
; CHECK-LABEL: xFormRegRegLoadZyConstrained:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: add 3, 3, 4
; CHECK-NEXT: #APP
; CHECK-NEXT: lbzx 3, 0, 3
; CHECK-NEXT: #NO_APP
; CHECK-NEXT: extsb 3, 3
; CHECK-NEXT: blr
entry:
%idxprom = zext i32 %index to i64
%arrayidx = getelementptr inbounds nuw i8, ptr %ptr, i64 %idxprom
%0 = tail call i8 asm "lbzx $0, ${1:y}", "=r,*Z"(ptr elementtype(i8) %arrayidx)
ret i8 %0
}

define signext i8 @xFormRegRegLoadAConstrained(ptr %ptr, i32 zeroext %index) {
; CHECK-LABEL: xFormRegRegLoadAConstrained:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: #APP
; CHECK-NEXT: lbzx 3, 3, 4
; CHECK-NEXT: #NO_APP
; CHECK-NEXT: extsb 3, 3
; CHECK-NEXT: blr
entry:
%0 = tail call i8 asm "lbzx $0,$1,$2", "=r,a,r"(ptr %ptr, i32 %index)
ret i8 %0
}

define i8 @implicitRegImmToRegRegConversion(ptr readnone %ptr, i32 zeroext %index) {
; CHECK-LABEL: implicitRegImmToRegRegConversion:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: add 3, 3, 4
; CHECK-NEXT: #APP
; CHECK-NEXT: lbzx 3, 0, 3
; CHECK-NEXT: #NO_APP
; CHECK-NEXT: blr
entry:
%idx.ext = zext i32 %index to i64
%add.ptr = getelementptr inbounds nuw i8, ptr %ptr, i64 %idx.ext
%0 = tail call i8 asm "lbzx $0, ${1:y}", "=r,a"(ptr %add.ptr)
ret i8 %0
}