Skip to content

[PowerPC] Support -fpatchable-function-entry #92997

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
merged 3 commits into from
Jul 22, 2024

Conversation

chenzheng1030
Copy link
Collaborator

For now only PPC big endian Linux 32 and 64 bit are supported.

PPC little endian Linux has XRAY support for 64-bit.
PPC AIX has different patchable function entry implementations.

Fixes #63220
Fixes #57031

@chenzheng1030 chenzheng1030 self-assigned this May 22, 2024
@llvmbot llvmbot added clang Clang issues not falling into any other category backend:PowerPC clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" labels May 22, 2024
@llvmbot
Copy link
Member

llvmbot commented May 22, 2024

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-backend-powerpc

Author: Chen Zheng (chenzheng1030)

Changes

For now only PPC big endian Linux 32 and 64 bit are supported.

PPC little endian Linux has XRAY support for 64-bit.
PPC AIX has different patchable function entry implementations.

Fixes #63220
Fixes #57031


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

10 Files Affected:

  • (modified) clang/include/clang/Basic/Attr.td (+1-1)
  • (modified) clang/include/clang/Basic/AttrDocs.td (+2-1)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+1-1)
  • (modified) clang/lib/Driver/ToolChains/Clang.cpp (+3-1)
  • (modified) clang/lib/Sema/SemaDeclAttr.cpp (+4)
  • (modified) clang/test/Driver/fpatchable-function-entry.c (+7-2)
  • (added) clang/test/Sema/patchable-function-entry-attr-aix.cpp (+4)
  • (modified) clang/test/Sema/patchable-function-entry-attr.cpp (+2)
  • (modified) llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp (+23-1)
  • (added) llvm/test/CodeGen/PowerPC/patchable-function-entry.ll (+49)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 7008bea483c87..e98d0edb6fca0 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -892,7 +892,7 @@ def PatchableFunctionEntry
     : InheritableAttr,
       TargetSpecificAttr<TargetArch<
           ["aarch64", "aarch64_be", "loongarch32", "loongarch64", "riscv32",
-           "riscv64", "x86", "x86_64"]>> {
+           "riscv64", "x86", "x86_64", "ppc", "ppc64"]>> {
   let Spellings = [GCC<"patchable_function_entry">];
   let Subjects = SubjectList<[Function, ObjCMethod]>;
   let Args = [UnsignedArgument<"Count">, DefaultIntArgument<"Offset", 0>];
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 54197d588eb45..fe566cc25bd0d 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -5799,7 +5799,8 @@ takes precedence over the command line option ``-fpatchable-function-entry=N,M``
 ``M`` defaults to 0 if omitted.
 
 This attribute is only supported on
-aarch64/aarch64-be/loongarch32/loongarch64/riscv32/riscv64/i386/x86-64 targets.
+aarch64/aarch64-be/loongarch32/loongarch64/riscv32/riscv64/i386/x86-64/ppc/ppc64 targets.
+For ppc/ppc64 targets, AIX is still not supported.
 }];
 }
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 5a32463763aa6..b807eda3862b2 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3470,7 +3470,7 @@ def err_attr_tlsmodel_arg : Error<"tls_model must be \"global-dynamic\", "
 
 def err_attr_codemodel_arg : Error<"code model '%0' is not supported on this target">;
 
-def err_aix_attr_unsupported_tls_model : Error<"TLS model '%0' is not yet supported on AIX">;
+def err_aix_attr_unsupported : Error<"%0 attribute is not yet supported on AIX">;
 
 def err_tls_var_aligned_over_maximum : Error<
   "alignment (%0) of thread-local variable %1 is greater than the maximum supported "
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 6d2015b2cd156..c7fa6d9a0ffe8 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6681,7 +6681,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
     StringRef S0 = A->getValue(), S = S0;
     unsigned Size, Offset = 0;
     if (!Triple.isAArch64() && !Triple.isLoongArch() && !Triple.isRISCV() &&
-        !Triple.isX86())
+        !Triple.isX86() &&
+        !(!Triple.isOSAIX() && (Triple.getArch() == llvm::Triple::ppc ||
+                                Triple.getArch() == llvm::Triple::ppc64)))
       D.Diag(diag::err_drv_unsupported_opt_for_target)
           << A->getAsString(Args) << TripleStr;
     else if (S.consumeInteger(10, Size) ||
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index ca5938083917f..17c17032ef96b 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -5922,6 +5922,10 @@ static void handleXRayLogArgsAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
 
 static void handlePatchableFunctionEntryAttr(Sema &S, Decl *D,
                                              const ParsedAttr &AL) {
+  if (S.Context.getTargetInfo().getTriple().isOSAIX()) {
+    S.Diag(AL.getLoc(), diag::err_aix_attr_unsupported) << AL;
+    return;
+  }
   uint32_t Count = 0, Offset = 0;
   if (!checkUInt32Argument(S, AL, AL.getArgAsExpr(0), Count, 0, true))
     return;
diff --git a/clang/test/Driver/fpatchable-function-entry.c b/clang/test/Driver/fpatchable-function-entry.c
index 4d0d609584c8d..8df3311f33b0e 100644
--- a/clang/test/Driver/fpatchable-function-entry.c
+++ b/clang/test/Driver/fpatchable-function-entry.c
@@ -6,6 +6,8 @@
 // RUN: %clang -target loongarch64 %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
 // RUN: %clang -target riscv32 %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
 // RUN: %clang -target riscv64 %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
+// RUN: %clang -target powerpc-unknown-linux-gnu %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
+// RUN: %clang -target powerpc64-unknown-linux-gnu %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
 // CHECK: "-fpatchable-function-entry=1"
 
 // RUN: %clang -target aarch64 -fsyntax-only %s -fpatchable-function-entry=1,1 -c -### 2>&1 | FileCheck --check-prefix=11 %s
@@ -13,8 +15,11 @@
 // RUN: %clang -target aarch64 -fsyntax-only %s -fpatchable-function-entry=2,1 -c -### 2>&1 | FileCheck --check-prefix=21 %s
 // 21: "-fpatchable-function-entry=2" "-fpatchable-function-entry-offset=1"
 
-// RUN: not %clang -target ppc64 -fsyntax-only %s -fpatchable-function-entry=1 2>&1 | FileCheck --check-prefix=TARGET %s
-// TARGET: error: unsupported option '-fpatchable-function-entry=1' for target 'ppc64'
+// RUN: not %clang -target powerpc64-ibm-aix-xcoff -fsyntax-only %s -fpatchable-function-entry=1 2>&1 | FileCheck --check-prefix=AIX64 %s
+// AIX64: error: unsupported option '-fpatchable-function-entry=1' for target 'powerpc64-ibm-aix-xcoff'
+
+// RUN: not %clang -target powerpc-ibm-aix-xcoff -fsyntax-only %s -fpatchable-function-entry=1 2>&1 | FileCheck --check-prefix=AIX32 %s
+// AIX32: error: unsupported option '-fpatchable-function-entry=1' for target 'powerpc-ibm-aix-xcoff'
 
 // RUN: not %clang -target x86_64 -fsyntax-only %s -fpatchable-function-entry=1,0, 2>&1 | FileCheck --check-prefix=EXCESS %s
 // EXCESS: error: invalid argument '1,0,' to -fpatchable-function-entry=
diff --git a/clang/test/Sema/patchable-function-entry-attr-aix.cpp b/clang/test/Sema/patchable-function-entry-attr-aix.cpp
new file mode 100644
index 0000000000000..648ad739b1df7
--- /dev/null
+++ b/clang/test/Sema/patchable-function-entry-attr-aix.cpp
@@ -0,0 +1,4 @@
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -fsyntax-only -verify %s
+
+// expected-error@+1 {{'patchable_function_entry' attribute is not yet supported on AIX}}
+__attribute__((patchable_function_entry(0))) void f();
diff --git a/clang/test/Sema/patchable-function-entry-attr.cpp b/clang/test/Sema/patchable-function-entry-attr.cpp
index 9134c851da588..c08293dfe6672 100644
--- a/clang/test/Sema/patchable-function-entry-attr.cpp
+++ b/clang/test/Sema/patchable-function-entry-attr.cpp
@@ -6,6 +6,8 @@
 // RUN: %clang_cc1 -triple loongarch64 -fsyntax-only -verify=silence %s
 // RUN: %clang_cc1 -triple riscv32 -fsyntax-only -verify=silence %s
 // RUN: %clang_cc1 -triple riscv64 -fsyntax-only -verify=silence %s
+// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -fsyntax-only -verify=silence %s
+// RUN: %clang_cc1 -triple powerpc64-unknown-linux-gnu -fsyntax-only -verify=silence %s
 // RUN: %clang_cc1 -triple ppc64le -fsyntax-only -verify %s
 
 // silence-no-diagnostics
diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index 72e8215fffaf7..76e3fc10877cd 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -909,6 +909,24 @@ void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) {
   // Lower multi-instruction pseudo operations.
   switch (MI->getOpcode()) {
   default: break;
+  case TargetOpcode::PATCHABLE_FUNCTION_ENTER: {
+    assert(!Subtarget->isAIXABI() &&
+           "AIX does not support patchable function entry!");
+    // PATCHABLE_FUNCTION_ENTER on little endian is for XRAY support which is
+    // handled in PPCLinuxAsmPrinter.
+    if (MAI->isLittleEndian())
+      return;
+    const Function &F = MI->getParent()->getParent()->getFunction();
+    if (F.hasFnAttribute("patchable-function-entry")) {
+      unsigned Num = 0;
+      if (F.getFnAttribute("patchable-function-entry")
+              .getValueAsString()
+              .getAsInteger(10, Num))
+        return;
+      emitNops(Num);
+      return;
+    }
+  }
   case TargetOpcode::DBG_VALUE:
     llvm_unreachable("Should be handled target independently");
   case TargetOpcode::STACKMAP:
@@ -1781,7 +1799,7 @@ void PPCLinuxAsmPrinter::emitInstruction(const MachineInstr *MI) {
 
   switch (MI->getOpcode()) {
   default:
-    return PPCAsmPrinter::emitInstruction(MI);
+    break;
   case TargetOpcode::PATCHABLE_FUNCTION_ENTER: {
     // .begin:
     //   b .end # lis 0, FuncId[16..32]
@@ -1794,6 +1812,9 @@ void PPCLinuxAsmPrinter::emitInstruction(const MachineInstr *MI) {
     //
     // Update compiler-rt/lib/xray/xray_powerpc64.cc accordingly when number
     // of instructions change.
+    // XRAY is only supported on PPC Linux little endian.
+    if (!MAI->isLittleEndian())
+      break;
     MCSymbol *BeginOfSled = OutContext.createTempSymbol();
     MCSymbol *EndOfSled = OutContext.createTempSymbol();
     OutStreamer->emitLabel(BeginOfSled);
@@ -1910,6 +1931,7 @@ void PPCLinuxAsmPrinter::emitInstruction(const MachineInstr *MI) {
     llvm_unreachable("Tail call is handled in the normal case. See comments "
                      "around this assert.");
   }
+  return PPCAsmPrinter::emitInstruction(MI);
 }
 
 void PPCLinuxAsmPrinter::emitStartOfAsmFile(Module &M) {
diff --git a/llvm/test/CodeGen/PowerPC/patchable-function-entry.ll b/llvm/test/CodeGen/PowerPC/patchable-function-entry.ll
new file mode 100644
index 0000000000000..088b616eb77fc
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/patchable-function-entry.ll
@@ -0,0 +1,49 @@
+; RUN: llc -mtriple=powerpc-unknown-linux-gnu %s -o - | FileCheck %s --check-prefixes=CHECK,PPC32
+; RUN: llc -mtriple=powerpc64-unknown-linux-gnu %s -o - | FileCheck %s --check-prefixes=CHECK,PPC64
+
+define void @f0() {
+; CHECK-LABEL: f0:
+; CHECK-NOT:   nop
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    blr
+; CHECK-NOT:   .section    __patchable_function_entries
+  ret void
+}
+
+define void @f1() "patchable-function-entry"="0" {
+; CHECK-LABEL: f1:
+; CHECK-NOT:   nop
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    blr
+; CHECK-NOT:   .section    __patchable_function_entries
+  ret void
+}
+
+define void @f2() "patchable-function-entry"="1" {
+; CHECK-LABEL: f2:
+; CHECK-LABEL-NEXT:  .Lfunc_begin2:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    nop
+; CHECK-NEXT:    blr
+; CHECK:       .section    __patchable_function_entries
+; PPC32:       .p2align    2, 0x0
+; PPC64:       .p2align    3, 0x0
+; PPC32-NEXT:  .long   .Lfunc_begin2
+; PPC64-NEXT:  .quad   .Lfunc_begin2
+  ret void
+}
+
+define void @f3() "patchable-function-entry"="1" "patchable-function-prefix"="2" {
+; CHECK-LABEL: .Ltmp0:
+; CHECK-COUNT-2: nop
+; CHECK-LABEL: f3:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    nop
+; CHECK-NEXT:    blr
+; CHECK:       .section    __patchable_function_entries
+; PPC32:       .p2align    2, 0x0
+; PPC64:       .p2align    3, 0x0
+; PPC32-NEXT:  .long   .Ltmp0
+; PPC64-NEXT:  .quad   .Ltmp0
+  ret void
+}

@llvmbot
Copy link
Member

llvmbot commented May 22, 2024

@llvm/pr-subscribers-clang-driver

Author: Chen Zheng (chenzheng1030)

Changes

For now only PPC big endian Linux 32 and 64 bit are supported.

PPC little endian Linux has XRAY support for 64-bit.
PPC AIX has different patchable function entry implementations.

Fixes #63220
Fixes #57031


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

10 Files Affected:

  • (modified) clang/include/clang/Basic/Attr.td (+1-1)
  • (modified) clang/include/clang/Basic/AttrDocs.td (+2-1)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+1-1)
  • (modified) clang/lib/Driver/ToolChains/Clang.cpp (+3-1)
  • (modified) clang/lib/Sema/SemaDeclAttr.cpp (+4)
  • (modified) clang/test/Driver/fpatchable-function-entry.c (+7-2)
  • (added) clang/test/Sema/patchable-function-entry-attr-aix.cpp (+4)
  • (modified) clang/test/Sema/patchable-function-entry-attr.cpp (+2)
  • (modified) llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp (+23-1)
  • (added) llvm/test/CodeGen/PowerPC/patchable-function-entry.ll (+49)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 7008bea483c87..e98d0edb6fca0 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -892,7 +892,7 @@ def PatchableFunctionEntry
     : InheritableAttr,
       TargetSpecificAttr<TargetArch<
           ["aarch64", "aarch64_be", "loongarch32", "loongarch64", "riscv32",
-           "riscv64", "x86", "x86_64"]>> {
+           "riscv64", "x86", "x86_64", "ppc", "ppc64"]>> {
   let Spellings = [GCC<"patchable_function_entry">];
   let Subjects = SubjectList<[Function, ObjCMethod]>;
   let Args = [UnsignedArgument<"Count">, DefaultIntArgument<"Offset", 0>];
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 54197d588eb45..fe566cc25bd0d 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -5799,7 +5799,8 @@ takes precedence over the command line option ``-fpatchable-function-entry=N,M``
 ``M`` defaults to 0 if omitted.
 
 This attribute is only supported on
-aarch64/aarch64-be/loongarch32/loongarch64/riscv32/riscv64/i386/x86-64 targets.
+aarch64/aarch64-be/loongarch32/loongarch64/riscv32/riscv64/i386/x86-64/ppc/ppc64 targets.
+For ppc/ppc64 targets, AIX is still not supported.
 }];
 }
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 5a32463763aa6..b807eda3862b2 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3470,7 +3470,7 @@ def err_attr_tlsmodel_arg : Error<"tls_model must be \"global-dynamic\", "
 
 def err_attr_codemodel_arg : Error<"code model '%0' is not supported on this target">;
 
-def err_aix_attr_unsupported_tls_model : Error<"TLS model '%0' is not yet supported on AIX">;
+def err_aix_attr_unsupported : Error<"%0 attribute is not yet supported on AIX">;
 
 def err_tls_var_aligned_over_maximum : Error<
   "alignment (%0) of thread-local variable %1 is greater than the maximum supported "
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 6d2015b2cd156..c7fa6d9a0ffe8 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6681,7 +6681,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
     StringRef S0 = A->getValue(), S = S0;
     unsigned Size, Offset = 0;
     if (!Triple.isAArch64() && !Triple.isLoongArch() && !Triple.isRISCV() &&
-        !Triple.isX86())
+        !Triple.isX86() &&
+        !(!Triple.isOSAIX() && (Triple.getArch() == llvm::Triple::ppc ||
+                                Triple.getArch() == llvm::Triple::ppc64)))
       D.Diag(diag::err_drv_unsupported_opt_for_target)
           << A->getAsString(Args) << TripleStr;
     else if (S.consumeInteger(10, Size) ||
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index ca5938083917f..17c17032ef96b 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -5922,6 +5922,10 @@ static void handleXRayLogArgsAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
 
 static void handlePatchableFunctionEntryAttr(Sema &S, Decl *D,
                                              const ParsedAttr &AL) {
+  if (S.Context.getTargetInfo().getTriple().isOSAIX()) {
+    S.Diag(AL.getLoc(), diag::err_aix_attr_unsupported) << AL;
+    return;
+  }
   uint32_t Count = 0, Offset = 0;
   if (!checkUInt32Argument(S, AL, AL.getArgAsExpr(0), Count, 0, true))
     return;
diff --git a/clang/test/Driver/fpatchable-function-entry.c b/clang/test/Driver/fpatchable-function-entry.c
index 4d0d609584c8d..8df3311f33b0e 100644
--- a/clang/test/Driver/fpatchable-function-entry.c
+++ b/clang/test/Driver/fpatchable-function-entry.c
@@ -6,6 +6,8 @@
 // RUN: %clang -target loongarch64 %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
 // RUN: %clang -target riscv32 %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
 // RUN: %clang -target riscv64 %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
+// RUN: %clang -target powerpc-unknown-linux-gnu %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
+// RUN: %clang -target powerpc64-unknown-linux-gnu %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
 // CHECK: "-fpatchable-function-entry=1"
 
 // RUN: %clang -target aarch64 -fsyntax-only %s -fpatchable-function-entry=1,1 -c -### 2>&1 | FileCheck --check-prefix=11 %s
@@ -13,8 +15,11 @@
 // RUN: %clang -target aarch64 -fsyntax-only %s -fpatchable-function-entry=2,1 -c -### 2>&1 | FileCheck --check-prefix=21 %s
 // 21: "-fpatchable-function-entry=2" "-fpatchable-function-entry-offset=1"
 
-// RUN: not %clang -target ppc64 -fsyntax-only %s -fpatchable-function-entry=1 2>&1 | FileCheck --check-prefix=TARGET %s
-// TARGET: error: unsupported option '-fpatchable-function-entry=1' for target 'ppc64'
+// RUN: not %clang -target powerpc64-ibm-aix-xcoff -fsyntax-only %s -fpatchable-function-entry=1 2>&1 | FileCheck --check-prefix=AIX64 %s
+// AIX64: error: unsupported option '-fpatchable-function-entry=1' for target 'powerpc64-ibm-aix-xcoff'
+
+// RUN: not %clang -target powerpc-ibm-aix-xcoff -fsyntax-only %s -fpatchable-function-entry=1 2>&1 | FileCheck --check-prefix=AIX32 %s
+// AIX32: error: unsupported option '-fpatchable-function-entry=1' for target 'powerpc-ibm-aix-xcoff'
 
 // RUN: not %clang -target x86_64 -fsyntax-only %s -fpatchable-function-entry=1,0, 2>&1 | FileCheck --check-prefix=EXCESS %s
 // EXCESS: error: invalid argument '1,0,' to -fpatchable-function-entry=
diff --git a/clang/test/Sema/patchable-function-entry-attr-aix.cpp b/clang/test/Sema/patchable-function-entry-attr-aix.cpp
new file mode 100644
index 0000000000000..648ad739b1df7
--- /dev/null
+++ b/clang/test/Sema/patchable-function-entry-attr-aix.cpp
@@ -0,0 +1,4 @@
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -fsyntax-only -verify %s
+
+// expected-error@+1 {{'patchable_function_entry' attribute is not yet supported on AIX}}
+__attribute__((patchable_function_entry(0))) void f();
diff --git a/clang/test/Sema/patchable-function-entry-attr.cpp b/clang/test/Sema/patchable-function-entry-attr.cpp
index 9134c851da588..c08293dfe6672 100644
--- a/clang/test/Sema/patchable-function-entry-attr.cpp
+++ b/clang/test/Sema/patchable-function-entry-attr.cpp
@@ -6,6 +6,8 @@
 // RUN: %clang_cc1 -triple loongarch64 -fsyntax-only -verify=silence %s
 // RUN: %clang_cc1 -triple riscv32 -fsyntax-only -verify=silence %s
 // RUN: %clang_cc1 -triple riscv64 -fsyntax-only -verify=silence %s
+// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -fsyntax-only -verify=silence %s
+// RUN: %clang_cc1 -triple powerpc64-unknown-linux-gnu -fsyntax-only -verify=silence %s
 // RUN: %clang_cc1 -triple ppc64le -fsyntax-only -verify %s
 
 // silence-no-diagnostics
diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index 72e8215fffaf7..76e3fc10877cd 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -909,6 +909,24 @@ void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) {
   // Lower multi-instruction pseudo operations.
   switch (MI->getOpcode()) {
   default: break;
+  case TargetOpcode::PATCHABLE_FUNCTION_ENTER: {
+    assert(!Subtarget->isAIXABI() &&
+           "AIX does not support patchable function entry!");
+    // PATCHABLE_FUNCTION_ENTER on little endian is for XRAY support which is
+    // handled in PPCLinuxAsmPrinter.
+    if (MAI->isLittleEndian())
+      return;
+    const Function &F = MI->getParent()->getParent()->getFunction();
+    if (F.hasFnAttribute("patchable-function-entry")) {
+      unsigned Num = 0;
+      if (F.getFnAttribute("patchable-function-entry")
+              .getValueAsString()
+              .getAsInteger(10, Num))
+        return;
+      emitNops(Num);
+      return;
+    }
+  }
   case TargetOpcode::DBG_VALUE:
     llvm_unreachable("Should be handled target independently");
   case TargetOpcode::STACKMAP:
@@ -1781,7 +1799,7 @@ void PPCLinuxAsmPrinter::emitInstruction(const MachineInstr *MI) {
 
   switch (MI->getOpcode()) {
   default:
-    return PPCAsmPrinter::emitInstruction(MI);
+    break;
   case TargetOpcode::PATCHABLE_FUNCTION_ENTER: {
     // .begin:
     //   b .end # lis 0, FuncId[16..32]
@@ -1794,6 +1812,9 @@ void PPCLinuxAsmPrinter::emitInstruction(const MachineInstr *MI) {
     //
     // Update compiler-rt/lib/xray/xray_powerpc64.cc accordingly when number
     // of instructions change.
+    // XRAY is only supported on PPC Linux little endian.
+    if (!MAI->isLittleEndian())
+      break;
     MCSymbol *BeginOfSled = OutContext.createTempSymbol();
     MCSymbol *EndOfSled = OutContext.createTempSymbol();
     OutStreamer->emitLabel(BeginOfSled);
@@ -1910,6 +1931,7 @@ void PPCLinuxAsmPrinter::emitInstruction(const MachineInstr *MI) {
     llvm_unreachable("Tail call is handled in the normal case. See comments "
                      "around this assert.");
   }
+  return PPCAsmPrinter::emitInstruction(MI);
 }
 
 void PPCLinuxAsmPrinter::emitStartOfAsmFile(Module &M) {
diff --git a/llvm/test/CodeGen/PowerPC/patchable-function-entry.ll b/llvm/test/CodeGen/PowerPC/patchable-function-entry.ll
new file mode 100644
index 0000000000000..088b616eb77fc
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/patchable-function-entry.ll
@@ -0,0 +1,49 @@
+; RUN: llc -mtriple=powerpc-unknown-linux-gnu %s -o - | FileCheck %s --check-prefixes=CHECK,PPC32
+; RUN: llc -mtriple=powerpc64-unknown-linux-gnu %s -o - | FileCheck %s --check-prefixes=CHECK,PPC64
+
+define void @f0() {
+; CHECK-LABEL: f0:
+; CHECK-NOT:   nop
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    blr
+; CHECK-NOT:   .section    __patchable_function_entries
+  ret void
+}
+
+define void @f1() "patchable-function-entry"="0" {
+; CHECK-LABEL: f1:
+; CHECK-NOT:   nop
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    blr
+; CHECK-NOT:   .section    __patchable_function_entries
+  ret void
+}
+
+define void @f2() "patchable-function-entry"="1" {
+; CHECK-LABEL: f2:
+; CHECK-LABEL-NEXT:  .Lfunc_begin2:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    nop
+; CHECK-NEXT:    blr
+; CHECK:       .section    __patchable_function_entries
+; PPC32:       .p2align    2, 0x0
+; PPC64:       .p2align    3, 0x0
+; PPC32-NEXT:  .long   .Lfunc_begin2
+; PPC64-NEXT:  .quad   .Lfunc_begin2
+  ret void
+}
+
+define void @f3() "patchable-function-entry"="1" "patchable-function-prefix"="2" {
+; CHECK-LABEL: .Ltmp0:
+; CHECK-COUNT-2: nop
+; CHECK-LABEL: f3:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    nop
+; CHECK-NEXT:    blr
+; CHECK:       .section    __patchable_function_entries
+; PPC32:       .p2align    2, 0x0
+; PPC64:       .p2align    3, 0x0
+; PPC32-NEXT:  .long   .Ltmp0
+; PPC64-NEXT:  .quad   .Ltmp0
+  ret void
+}

@maryammo
Copy link
Contributor

maryammo commented May 23, 2024

In PPCTargetMachine::PPCABI computeTargetABI it specifies the ABI with this

switch (TT.getArch()) {
 case Triple::ppc64le:
  return PPCTargetMachine::PPC_ABI_ELFv2;
 case Triple::ppc64:
  if (TT.isPPC64ELFv2ABI())
   return PPCTargetMachine::PPC_ABI_ELFv2;
  else
   return PPCTargetMachine::PPC_ABI_ELFv1;
 default:
  return PPCTargetMachine::PPC_ABI_UNKNOWN;
 }

isPPC64ELFv2ABI() is defined as

/// Tests whether the target 64-bit PowerPC big endian ABI is ELFv2.
 bool isPPC64ELFv2ABI() const {
  return (getArch() == Triple::ppc64 &&
      ((getOS() == Triple::FreeBSD &&
       (getOSMajorVersion() >= 13 || getOSVersion().empty())) ||
       getOS() == Triple::OpenBSD || isMusl()));
 }

So PPC64 can use ELFv2 for Triple::OpenBSD. We probably want to diagnose this OS for PPC64, since with ELFv2 we might emit separate local and global entry points which means only certain values can be passed to -fpatchable-function-entry option.

@@ -6681,7 +6681,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
StringRef S0 = A->getValue(), S = S0;
unsigned Size, Offset = 0;
if (!Triple.isAArch64() && !Triple.isLoongArch() && !Triple.isRISCV() &&
!Triple.isX86())
!Triple.isX86() &&
!(!Triple.isOSAIX() && (Triple.getArch() == llvm::Triple::ppc ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

little-endian variants (ppcle ppc64le) are omitted here.

Use isPPC

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linux little endian targets are this patch's scope. This patch tries to support -fpatchable-function-entry for big endian targets but not AIX.

If we are going to support little endian, we first need to check the possibility about removal of XRAY. (XRAY also uses backend node PATCHABLE_FUNCTION_ENTER)

@@ -6,15 +6,20 @@
// RUN: %clang -target loongarch64 %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
// RUN: %clang -target riscv32 %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
// RUN: %clang -target riscv64 %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
// RUN: %clang -target powerpc-unknown-linux-gnu %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use --target= for new tests. -target has been deprecated for a long time

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. Updated.

@@ -0,0 +1,49 @@
; RUN: llc -mtriple=powerpc-unknown-linux-gnu %s -o - | FileCheck %s --check-prefixes=CHECK,PPC32
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-mtriple=powerpc. Prefer a generic ELF triple to a Linux specific one.

Suggest 2 RUN lines, powerpc and powerpc64le, to catch both 32-bit/64-bit and LE/BE.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, changed the triple. Little endian is not going to be supported in this patch.

define void @f3() "patchable-function-entry"="1" "patchable-function-prefix"="2" {
; CHECK-LABEL: .Ltmp0:
; CHECK-COUNT-2: nop
; CHECK-LABEL: f3:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test addis/addi, instructions at the global entry point.

Per https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99888#c5 #c8 and #15, GCC will change the current strategy to match us :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@MaskRay
Copy link
Member

MaskRay commented May 23, 2024

So PPC64 can use ELFv2 for Triple::OpenBSD. We probably want to diagnose this OS for PPC64, since with ELFv2 we might emit separate local and global entry points which means only certain values can be passed to -fpatchable-function-entry option.

This patch matches the intended end result: NOPs before the global entry and after the local entry.

The current way GCC handles N,M where M>0 for PPC64 ELFv2 is undesired and will hopefully be soon changed: https://gcc.gnu.org/pipermail/gcc-patches/2024-May/651025.html , matching what I suggested in 2022: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99888#c5

Copy link
Collaborator Author

@chenzheng1030 chenzheng1030 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So PPC64 can use ELFv2 for Triple::OpenBSD. We probably want to diagnose this OS for PPC64, since with ELFv2 we might emit separate local and global entry points which means only certain values can be passed to -fpatchable-function-entry option.

For targets with ABI elfv2 which means there are dual entries, this patch can generate expected patchable entry:
For -mtriple=powerpc64-unknown-freebsd-unknown

.Ltmp0:
	nop
	nop
f3:                                     # @f3
.Lfunc_begin0:
	.cfi_startproc
.Lfunc_gep0:
	addis 2, 12, .TOC.-.Lfunc_gep0@ha
	addi 2, 2, .TOC.-.Lfunc_gep0@l
.Lfunc_lep0:
	.localentry	f3, .Lfunc_lep0-.Lfunc_gep0
# %bb.0:                                # %entry
	nop

@@ -6681,7 +6681,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
StringRef S0 = A->getValue(), S = S0;
unsigned Size, Offset = 0;
if (!Triple.isAArch64() && !Triple.isLoongArch() && !Triple.isRISCV() &&
!Triple.isX86())
!Triple.isX86() &&
!(!Triple.isOSAIX() && (Triple.getArch() == llvm::Triple::ppc ||
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linux little endian targets are this patch's scope. This patch tries to support -fpatchable-function-entry for big endian targets but not AIX.

If we are going to support little endian, we first need to check the possibility about removal of XRAY. (XRAY also uses backend node PATCHABLE_FUNCTION_ENTER)

@@ -6,15 +6,20 @@
// RUN: %clang -target loongarch64 %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
// RUN: %clang -target riscv32 %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
// RUN: %clang -target riscv64 %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
// RUN: %clang -target powerpc-unknown-linux-gnu %s -fpatchable-function-entry=1,0 -c -### 2>&1 | FileCheck %s
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. Updated.

@@ -0,0 +1,49 @@
; RUN: llc -mtriple=powerpc-unknown-linux-gnu %s -o - | FileCheck %s --check-prefixes=CHECK,PPC32
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, changed the triple. Little endian is not going to be supported in this patch.

define void @f3() "patchable-function-entry"="1" "patchable-function-prefix"="2" {
; CHECK-LABEL: .Ltmp0:
; CHECK-COUNT-2: nop
; CHECK-LABEL: f3:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Chen Zheng added 3 commits May 24, 2024 06:05
For now only PPC big endian Linux is supported.
PPC little endian Linux has XRAY support for 64-bit.
PPC AIX has different patchable function entry implementations.

Fixes llvm#63220
Fixes llvm#57031
@chenzheng1030 chenzheng1030 force-pushed the patchable-entry-point branch from b3abeed to 0552f85 Compare May 24, 2024 10:07
@chenzheng1030 chenzheng1030 requested a review from MaskRay May 24, 2024 10:16
@maryammo
Copy link
Contributor

So PPC64 can use ELFv2 for Triple::OpenBSD. We probably want to diagnose this OS for PPC64, since with ELFv2 we might emit separate local and global entry points which means only certain values can be passed to -fpatchable-function-entry option.

This patch matches the intended end result: NOPs before the global entry and after the local entry.

The current way GCC handles N,M where M>0 for PPC64 ELFv2 is undesired and will hopefully be soon changed: https://gcc.gnu.org/pipermail/gcc-patches/2024-May/651025.html , matching what I suggested in 2022: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99888#c5

I see, thanks.

@maryammo
Copy link
Contributor

So PPC64 can use ELFv2 for Triple::OpenBSD. We probably want to diagnose this OS for PPC64, since with ELFv2 we might emit separate local and global entry points which means only certain values can be passed to -fpatchable-function-entry option.

For targets with ABI elfv2 which means there are dual entries, this patch can generate expected patchable entry: For -mtriple=powerpc64-unknown-freebsd-unknown

.Ltmp0:
	nop
	nop
f3:                                     # @f3
.Lfunc_begin0:
	.cfi_startproc
.Lfunc_gep0:
	addis 2, 12, .TOC.-.Lfunc_gep0@ha
	addi 2, 2, .TOC.-.Lfunc_gep0@l
.Lfunc_lep0:
	.localentry	f3, .Lfunc_lep0-.Lfunc_gep0
# %bb.0:                                # %entry
	nop

Ok, so there is no NOPs between lep and gep. Thanks for verifying.

@chenzheng1030 chenzheng1030 merged commit 4321300 into llvm:main Jul 22, 2024
6 of 9 checks passed
@chenzheng1030 chenzheng1030 deleted the patchable-entry-point branch July 22, 2024 00:51
yuxuanchen1997 pushed a commit that referenced this pull request Jul 25, 2024
For now only PPC big endian Linux 32 and 64 bit are supported.

PPC little endian Linux has XRAY support for 64-bit.
PPC AIX has different patchable function entry implementations.

Fixes #63220
Fixes #57031
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:PowerPC clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[PowerPC] call to _mcount emitted by -pg needs to be emitted BEFORE function prolog [clang][PPC] support for -mprofile-kernel
5 participants