Skip to content

Support the inline asm 'a' constraint on PowerPC #141604

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

kamaub
Copy link
Contributor

@kamaub kamaub commented May 27, 2025

This patch adds backend and frontend support for address operand
constraint a for GCC style inline assembly. It allows the user
to specify X-FORM addressing mode operations in inline assmebly
without doing a [0 + Reg] index using the y constraint modifier.

This patch adds backend and frontend support for address operand
constraint `a` for GCC style inline assembly. It allows the user
to specify X-FORM addressing mode operations in inline assmebly
without doing a [0 + Reg] index using the y constraint modifier.
@kamaub
Copy link
Contributor Author

kamaub commented May 27, 2025

This is a draft PR because the type promotion errors occurring in test case llvm/test/CodeGen/PowerPC/inline-asm-constraints-error.ll are not being properly handled and need to be resolved and gracefully failed before this patch can go in. Otherwise it would be introducing a way to crash the compiler.

Copy link

github-actions bot commented May 27, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

The backend test case file detailing type promotion errors when
using this constraint has been identified as a separate issue
that is exposed by this patch. A separate issue, and PR will
resolve this problem.
@kamaub kamaub changed the title [WIP] Support the inline asm 'a' constraint on PowerPC Support the inline asm 'a' constraint on PowerPC May 28, 2025
@kamaub
Copy link
Contributor Author

kamaub commented May 28, 2025

After discussion with the IBM llvm on power team I will separate the type promotion error test case file into another patch as the error is only exposed by, not caused by this patch.

@kamaub kamaub marked this pull request as ready for review May 28, 2025 15:24
@kamaub kamaub self-assigned this May 28, 2025
@llvmbot llvmbot added clang Clang issues not falling into any other category backend:PowerPC clang:frontend Language frontend issues, e.g. anything involving "Sema" llvm:ir labels May 28, 2025
@kamaub kamaub added inline-asm and removed clang Clang issues not falling into any other category llvm:ir labels May 28, 2025
@llvmbot
Copy link
Member

llvmbot commented May 28, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-backend-powerpc

Author: Kamau Bridgeman (kamaub)

Changes

This patch adds backend and frontend support for address operand
constraint a for GCC style inline assembly. It allows the user
to specify X-FORM addressing mode operations in inline assmebly
without doing a [0 + Reg] index using the y constraint modifier.


Full diff: https://github.com/llvm/llvm-project/pull/141604.diff

9 Files Affected:

  • (modified) clang/lib/Basic/Targets/PPC.h (+2-2)
  • (modified) clang/test/CodeGen/PowerPC/inline-asm-constraints-error.c (+8-4)
  • (added) clang/test/CodeGen/PowerPC/inline-asm-constraints.c (+66)
  • (modified) llvm/include/llvm/IR/InlineAsm.h (+3)
  • (modified) llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp (+8)
  • (modified) llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp (+1)
  • (modified) llvm/lib/Target/PowerPC/PPCISelLowering.cpp (+2)
  • (modified) llvm/lib/Target/PowerPC/PPCISelLowering.h (+7-9)
  • (added) llvm/test/CodeGen/PowerPC/inline-asm-constraints.ll (+127)
diff --git a/clang/lib/Basic/Targets/PPC.h b/clang/lib/Basic/Targets/PPC.h
index 17057cef97a57..76fb24ed40050 100644
--- a/clang/lib/Basic/Targets/PPC.h
+++ b/clang/lib/Basic/Targets/PPC.h
@@ -303,8 +303,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
diff --git a/clang/test/CodeGen/PowerPC/inline-asm-constraints-error.c b/clang/test/CodeGen/PowerPC/inline-asm-constraints-error.c
index 2f35e52fc0b77..c1bb59ee3c22a 100644
--- a/clang/test/CodeGen/PowerPC/inline-asm-constraints-error.c
+++ b/clang/test/CodeGen/PowerPC/inline-asm-constraints-error.c
@@ -1,9 +1,13 @@
 // 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}}
+
+ 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;
+}
diff --git a/clang/test/CodeGen/PowerPC/inline-asm-constraints.c b/clang/test/CodeGen/PowerPC/inline-asm-constraints.c
new file mode 100644
index 0000000000000..ca5a9ffcee0cc
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/inline-asm-constraints.c
@@ -0,0 +1,66 @@
+// RUN: %clang_cc1 -emit-llvm -triple powerpc64-ibm-aix-xcoff \
+// RUN:   %s -o - | FileCheck %s
+
+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;
+}
diff --git a/llvm/include/llvm/IR/InlineAsm.h b/llvm/include/llvm/IR/InlineAsm.h
index e5f506e5694da..87d68acde624b 100644
--- a/llvm/include/llvm/IR/InlineAsm.h
+++ b/llvm/include/llvm/IR/InlineAsm.h
@@ -264,6 +264,7 @@ class InlineAsm final : public Value {
 
     // Address constraints
     p,
+    a,
     ZQ,
     ZR,
     ZS,
@@ -514,6 +515,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:
diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index 79e52635a3675..0c02b90747806 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -442,6 +442,14 @@ bool PPCAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
   }
 
   assert(MI->getOperand(OpNo).isReg());
+  const MachineOperand &FlagsOP = MI->getOperand(OpNo - 1);
+  if (!FlagsOP.isImm())
+    return true;
+  const InlineAsm::Flag Flags(FlagsOP.getImm());
+  if (Flags.getMemoryConstraintID() == InlineAsm::ConstraintCode::a) {
+    printOperand(MI, OpNo, O);
+    return false;
+  }
   O << "0(";
   printOperand(MI, OpNo, O);
   O << ")";
diff --git a/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp b/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
index 0907239153226..e4029d3612b9f 100644
--- a/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
@@ -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:
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
index ab78f33f5a630..843aec1ae4b99 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
@@ -17184,6 +17184,8 @@ PPCTargetLowering::getConstraintType(StringRef Constraint) const {
   if (Constraint.size() == 1) {
     switch (Constraint[0]) {
     default: break;
+    case 'a':
+      return C_Address;
     case 'b':
     case 'r':
     case 'f':
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.h b/llvm/lib/Target/PowerPC/PPCISelLowering.h
index 1f22aa16a89be..d0e272403c978 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.h
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.h
@@ -997,15 +997,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,
diff --git a/llvm/test/CodeGen/PowerPC/inline-asm-constraints.ll b/llvm/test/CodeGen/PowerPC/inline-asm-constraints.ll
new file mode 100644
index 0000000000000..4781bd7d0b235
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/inline-asm-constraints.ll
@@ -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 -no-integrated-as \
+; RUN:  -mtriple=powerpc64-ibm-aix-xcoff | FileCheck %s
+; RUN: llc -verify-machineinstrs < %s -mcpu=pwr8 -no-integrated-as \
+; 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
+}
+

@llvmbot
Copy link
Member

llvmbot commented May 28, 2025

@llvm/pr-subscribers-llvm-ir

Author: Kamau Bridgeman (kamaub)

Changes

This patch adds backend and frontend support for address operand
constraint a for GCC style inline assembly. It allows the user
to specify X-FORM addressing mode operations in inline assmebly
without doing a [0 + Reg] index using the y constraint modifier.


Full diff: https://github.com/llvm/llvm-project/pull/141604.diff

9 Files Affected:

  • (modified) clang/lib/Basic/Targets/PPC.h (+2-2)
  • (modified) clang/test/CodeGen/PowerPC/inline-asm-constraints-error.c (+8-4)
  • (added) clang/test/CodeGen/PowerPC/inline-asm-constraints.c (+66)
  • (modified) llvm/include/llvm/IR/InlineAsm.h (+3)
  • (modified) llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp (+8)
  • (modified) llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp (+1)
  • (modified) llvm/lib/Target/PowerPC/PPCISelLowering.cpp (+2)
  • (modified) llvm/lib/Target/PowerPC/PPCISelLowering.h (+7-9)
  • (added) llvm/test/CodeGen/PowerPC/inline-asm-constraints.ll (+127)
diff --git a/clang/lib/Basic/Targets/PPC.h b/clang/lib/Basic/Targets/PPC.h
index 17057cef97a57..76fb24ed40050 100644
--- a/clang/lib/Basic/Targets/PPC.h
+++ b/clang/lib/Basic/Targets/PPC.h
@@ -303,8 +303,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
diff --git a/clang/test/CodeGen/PowerPC/inline-asm-constraints-error.c b/clang/test/CodeGen/PowerPC/inline-asm-constraints-error.c
index 2f35e52fc0b77..c1bb59ee3c22a 100644
--- a/clang/test/CodeGen/PowerPC/inline-asm-constraints-error.c
+++ b/clang/test/CodeGen/PowerPC/inline-asm-constraints-error.c
@@ -1,9 +1,13 @@
 // 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}}
+
+ 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;
+}
diff --git a/clang/test/CodeGen/PowerPC/inline-asm-constraints.c b/clang/test/CodeGen/PowerPC/inline-asm-constraints.c
new file mode 100644
index 0000000000000..ca5a9ffcee0cc
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/inline-asm-constraints.c
@@ -0,0 +1,66 @@
+// RUN: %clang_cc1 -emit-llvm -triple powerpc64-ibm-aix-xcoff \
+// RUN:   %s -o - | FileCheck %s
+
+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;
+}
diff --git a/llvm/include/llvm/IR/InlineAsm.h b/llvm/include/llvm/IR/InlineAsm.h
index e5f506e5694da..87d68acde624b 100644
--- a/llvm/include/llvm/IR/InlineAsm.h
+++ b/llvm/include/llvm/IR/InlineAsm.h
@@ -264,6 +264,7 @@ class InlineAsm final : public Value {
 
     // Address constraints
     p,
+    a,
     ZQ,
     ZR,
     ZS,
@@ -514,6 +515,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:
diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index 79e52635a3675..0c02b90747806 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -442,6 +442,14 @@ bool PPCAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
   }
 
   assert(MI->getOperand(OpNo).isReg());
+  const MachineOperand &FlagsOP = MI->getOperand(OpNo - 1);
+  if (!FlagsOP.isImm())
+    return true;
+  const InlineAsm::Flag Flags(FlagsOP.getImm());
+  if (Flags.getMemoryConstraintID() == InlineAsm::ConstraintCode::a) {
+    printOperand(MI, OpNo, O);
+    return false;
+  }
   O << "0(";
   printOperand(MI, OpNo, O);
   O << ")";
diff --git a/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp b/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
index 0907239153226..e4029d3612b9f 100644
--- a/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
@@ -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:
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
index ab78f33f5a630..843aec1ae4b99 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
@@ -17184,6 +17184,8 @@ PPCTargetLowering::getConstraintType(StringRef Constraint) const {
   if (Constraint.size() == 1) {
     switch (Constraint[0]) {
     default: break;
+    case 'a':
+      return C_Address;
     case 'b':
     case 'r':
     case 'f':
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.h b/llvm/lib/Target/PowerPC/PPCISelLowering.h
index 1f22aa16a89be..d0e272403c978 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.h
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.h
@@ -997,15 +997,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,
diff --git a/llvm/test/CodeGen/PowerPC/inline-asm-constraints.ll b/llvm/test/CodeGen/PowerPC/inline-asm-constraints.ll
new file mode 100644
index 0000000000000..4781bd7d0b235
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/inline-asm-constraints.ll
@@ -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 -no-integrated-as \
+; RUN:  -mtriple=powerpc64-ibm-aix-xcoff | FileCheck %s
+; RUN: llc -verify-machineinstrs < %s -mcpu=pwr8 -no-integrated-as \
+; 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
+}
+

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:PowerPC clang:frontend Language frontend issues, e.g. anything involving "Sema" inline-asm
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants