Skip to content

[PowerPC] Take ABI into account for data layout #149725

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 1 commit into
base: main
Choose a base branch
from

Conversation

Gelbpunkt
Copy link
Contributor

Prior to this change, the data layout calculation would not account for explicitly set -mabi=elfv2 on powerpc64-unknown-linux-gnu, a target that defaults to elfv1.

This is loosely inspired by the equivalent ARM / RISC-V code.

make check-llvm passes fine for me, though AFAICT all the tests specify the data layout manually so there isn't really a test for this and I am not really sure what the best way to go about adding one would be.

Prior to this change, the data layout calculation would not account for
explicitly set -mabi=elfv2 on powerpc64-unknown-linux-gnu, a target that
defaults to elfv1.

Signed-off-by: Jens Reidel <[email protected]>
@llvmbot llvmbot added clang Clang issues not falling into any other category backend:PowerPC clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jul 20, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 20, 2025

@llvm/pr-subscribers-clang

Author: Jens Reidel (Gelbpunkt)

Changes

Prior to this change, the data layout calculation would not account for explicitly set -mabi=elfv2 on powerpc64-unknown-linux-gnu, a target that defaults to elfv1.

This is loosely inspired by the equivalent ARM / RISC-V code.

make check-llvm passes fine for me, though AFAICT all the tests specify the data layout manually so there isn't really a test for this and I am not really sure what the best way to go about adding one would be.


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

2 Files Affected:

  • (modified) clang/lib/Basic/Targets/PPC.h (+28-17)
  • (modified) llvm/lib/Target/PowerPC/PPCTargetMachine.cpp (+6-3)
diff --git a/clang/lib/Basic/Targets/PPC.h b/clang/lib/Basic/Targets/PPC.h
index 9f3a4cd2da716..4cceebf6c2826 100644
--- a/clang/lib/Basic/Targets/PPC.h
+++ b/clang/lib/Basic/Targets/PPC.h
@@ -445,27 +445,17 @@ class LLVM_LIBRARY_VISIBILITY PPC64TargetInfo : public PPCTargetInfo {
     LongWidth = LongAlign = PointerWidth = PointerAlign = 64;
     IntMaxType = SignedLong;
     Int64Type = SignedLong;
-    std::string DataLayout;
 
     if (Triple.isOSAIX()) {
       // TODO: Set appropriate ABI for AIX platform.
-      DataLayout = "E-m:a-Fi64-i64:64-i128:128-n32:64";
       LongDoubleWidth = 64;
       LongDoubleAlign = DoubleAlign = 32;
       LongDoubleFormat = &llvm::APFloat::IEEEdouble();
-    } else if ((Triple.getArch() == llvm::Triple::ppc64le)) {
-      DataLayout = "e-m:e-Fn32-i64:64-i128:128-n32:64";
+    } else if ((Triple.getArch() == llvm::Triple::ppc64le) ||
+               Triple.isPPC64ELFv2ABI()) {
       ABI = "elfv2";
     } else {
-      DataLayout = "E-m:e";
-      if (Triple.isPPC64ELFv2ABI()) {
-        ABI = "elfv2";
-        DataLayout += "-Fn32";
-      } else {
-        ABI = "elfv1";
-        DataLayout += "-Fi64";
-      }
-      DataLayout += "-i64:64-i128:128-n32:64";
+      ABI = "elfv1";
     }
 
     if (Triple.isOSFreeBSD() || Triple.isOSOpenBSD() || Triple.isMusl()) {
@@ -473,14 +463,12 @@ class LLVM_LIBRARY_VISIBILITY PPC64TargetInfo : public PPCTargetInfo {
       LongDoubleFormat = &llvm::APFloat::IEEEdouble();
     }
 
-    if (Triple.isOSAIX() || Triple.isOSLinux())
-      DataLayout += "-S128-v256:256:256-v512:512:512";
-    resetDataLayout(DataLayout);
-
     // Newer PPC64 instruction sets support atomics up to 16 bytes.
     MaxAtomicPromoteWidth = 128;
     // Baseline PPC64 supports inlining atomics up to 8 bytes.
     MaxAtomicInlineWidth = 64;
+
+    calculateDataLayout();
   }
 
   void setMaxAtomicWidth() override {
@@ -495,10 +483,33 @@ class LLVM_LIBRARY_VISIBILITY PPC64TargetInfo : public PPCTargetInfo {
     return TargetInfo::CharPtrBuiltinVaList;
   }
 
+  void calculateDataLayout() {
+    std::string DataLayout;
+
+    if (getTriple().isOSAIX()) {
+      DataLayout = "E-m:a-Fi64-i64:64-i128:128-n32:64";
+    } else if ((getTriple().getArch() == llvm::Triple::ppc64le)) {
+      DataLayout = "e-m:e-Fn32-i64:64-i128:128-n32:64";
+    } else {
+      DataLayout = "E-m:e";
+      if (ABI == "elfv2") {
+        DataLayout += "-Fn32";
+      } else {
+        DataLayout += "-Fi64";
+      }
+      DataLayout += "-i64:64-i128:128-n32:64";
+    }
+
+    if (getTriple().isOSAIX() || getTriple().isOSLinux())
+      DataLayout += "-S128-v256:256:256-v512:512:512";
+    resetDataLayout(DataLayout);
+  }
+
   // PPC64 Linux-specific ABI options.
   bool setABI(const std::string &Name) override {
     if (Name == "elfv1" || Name == "elfv2") {
       ABI = Name;
+      calculateDataLayout();
       return true;
     }
     return false;
diff --git a/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp b/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp
index b5c6ac111dff0..10d7f12884728 100644
--- a/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp
+++ b/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp
@@ -154,8 +154,10 @@ static bool isLittleEndianTriple(const Triple &T) {
 }
 
 /// Return the datalayout string of a subtarget.
-static std::string getDataLayoutString(const Triple &T) {
+static std::string computeDataLayout(const Triple &T,
+                                     const TargetOptions &Options) {
   bool is64Bit = T.getArch() == Triple::ppc64 || T.getArch() == Triple::ppc64le;
+  StringRef ABIName = Options.MCOptions.getABIName();
   std::string Ret;
 
   // Most PPC* platforms are big endian, PPC(64)LE is little endian.
@@ -174,7 +176,8 @@ static std::string getDataLayoutString(const Triple &T) {
   // If the target ABI uses function descriptors, then the alignment of function
   // pointers depends on the alignment used to emit the descriptor. Otherwise,
   // function pointers are aligned to 32 bits because the instructions must be.
-  if ((T.getArch() == Triple::ppc64 && !T.isPPC64ELFv2ABI())) {
+  if ((T.getArch() == Triple::ppc64 &&
+       (!T.isPPC64ELFv2ABI() && ABIName != "elfv2"))) {
     Ret += "-Fi64";
   } else if (T.isOSAIX()) {
     Ret += is64Bit ? "-Fi64" : "-Fi32";
@@ -348,7 +351,7 @@ PPCTargetMachine::PPCTargetMachine(const Target &T, const Triple &TT,
                                    std::optional<Reloc::Model> RM,
                                    std::optional<CodeModel::Model> CM,
                                    CodeGenOptLevel OL, bool JIT)
-    : CodeGenTargetMachineImpl(T, getDataLayoutString(TT), TT, CPU,
+    : CodeGenTargetMachineImpl(T, computeDataLayout(TT, Options), TT, CPU,
                                computeFSAdditions(FS, OL, TT), Options,
                                getEffectiveRelocModel(TT, RM),
                                getEffectivePPCCodeModel(TT, CM, JIT), OL),

@llvmbot
Copy link
Member

llvmbot commented Jul 20, 2025

@llvm/pr-subscribers-backend-powerpc

Author: Jens Reidel (Gelbpunkt)

Changes

Prior to this change, the data layout calculation would not account for explicitly set -mabi=elfv2 on powerpc64-unknown-linux-gnu, a target that defaults to elfv1.

This is loosely inspired by the equivalent ARM / RISC-V code.

make check-llvm passes fine for me, though AFAICT all the tests specify the data layout manually so there isn't really a test for this and I am not really sure what the best way to go about adding one would be.


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

2 Files Affected:

  • (modified) clang/lib/Basic/Targets/PPC.h (+28-17)
  • (modified) llvm/lib/Target/PowerPC/PPCTargetMachine.cpp (+6-3)
diff --git a/clang/lib/Basic/Targets/PPC.h b/clang/lib/Basic/Targets/PPC.h
index 9f3a4cd2da716..4cceebf6c2826 100644
--- a/clang/lib/Basic/Targets/PPC.h
+++ b/clang/lib/Basic/Targets/PPC.h
@@ -445,27 +445,17 @@ class LLVM_LIBRARY_VISIBILITY PPC64TargetInfo : public PPCTargetInfo {
     LongWidth = LongAlign = PointerWidth = PointerAlign = 64;
     IntMaxType = SignedLong;
     Int64Type = SignedLong;
-    std::string DataLayout;
 
     if (Triple.isOSAIX()) {
       // TODO: Set appropriate ABI for AIX platform.
-      DataLayout = "E-m:a-Fi64-i64:64-i128:128-n32:64";
       LongDoubleWidth = 64;
       LongDoubleAlign = DoubleAlign = 32;
       LongDoubleFormat = &llvm::APFloat::IEEEdouble();
-    } else if ((Triple.getArch() == llvm::Triple::ppc64le)) {
-      DataLayout = "e-m:e-Fn32-i64:64-i128:128-n32:64";
+    } else if ((Triple.getArch() == llvm::Triple::ppc64le) ||
+               Triple.isPPC64ELFv2ABI()) {
       ABI = "elfv2";
     } else {
-      DataLayout = "E-m:e";
-      if (Triple.isPPC64ELFv2ABI()) {
-        ABI = "elfv2";
-        DataLayout += "-Fn32";
-      } else {
-        ABI = "elfv1";
-        DataLayout += "-Fi64";
-      }
-      DataLayout += "-i64:64-i128:128-n32:64";
+      ABI = "elfv1";
     }
 
     if (Triple.isOSFreeBSD() || Triple.isOSOpenBSD() || Triple.isMusl()) {
@@ -473,14 +463,12 @@ class LLVM_LIBRARY_VISIBILITY PPC64TargetInfo : public PPCTargetInfo {
       LongDoubleFormat = &llvm::APFloat::IEEEdouble();
     }
 
-    if (Triple.isOSAIX() || Triple.isOSLinux())
-      DataLayout += "-S128-v256:256:256-v512:512:512";
-    resetDataLayout(DataLayout);
-
     // Newer PPC64 instruction sets support atomics up to 16 bytes.
     MaxAtomicPromoteWidth = 128;
     // Baseline PPC64 supports inlining atomics up to 8 bytes.
     MaxAtomicInlineWidth = 64;
+
+    calculateDataLayout();
   }
 
   void setMaxAtomicWidth() override {
@@ -495,10 +483,33 @@ class LLVM_LIBRARY_VISIBILITY PPC64TargetInfo : public PPCTargetInfo {
     return TargetInfo::CharPtrBuiltinVaList;
   }
 
+  void calculateDataLayout() {
+    std::string DataLayout;
+
+    if (getTriple().isOSAIX()) {
+      DataLayout = "E-m:a-Fi64-i64:64-i128:128-n32:64";
+    } else if ((getTriple().getArch() == llvm::Triple::ppc64le)) {
+      DataLayout = "e-m:e-Fn32-i64:64-i128:128-n32:64";
+    } else {
+      DataLayout = "E-m:e";
+      if (ABI == "elfv2") {
+        DataLayout += "-Fn32";
+      } else {
+        DataLayout += "-Fi64";
+      }
+      DataLayout += "-i64:64-i128:128-n32:64";
+    }
+
+    if (getTriple().isOSAIX() || getTriple().isOSLinux())
+      DataLayout += "-S128-v256:256:256-v512:512:512";
+    resetDataLayout(DataLayout);
+  }
+
   // PPC64 Linux-specific ABI options.
   bool setABI(const std::string &Name) override {
     if (Name == "elfv1" || Name == "elfv2") {
       ABI = Name;
+      calculateDataLayout();
       return true;
     }
     return false;
diff --git a/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp b/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp
index b5c6ac111dff0..10d7f12884728 100644
--- a/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp
+++ b/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp
@@ -154,8 +154,10 @@ static bool isLittleEndianTriple(const Triple &T) {
 }
 
 /// Return the datalayout string of a subtarget.
-static std::string getDataLayoutString(const Triple &T) {
+static std::string computeDataLayout(const Triple &T,
+                                     const TargetOptions &Options) {
   bool is64Bit = T.getArch() == Triple::ppc64 || T.getArch() == Triple::ppc64le;
+  StringRef ABIName = Options.MCOptions.getABIName();
   std::string Ret;
 
   // Most PPC* platforms are big endian, PPC(64)LE is little endian.
@@ -174,7 +176,8 @@ static std::string getDataLayoutString(const Triple &T) {
   // If the target ABI uses function descriptors, then the alignment of function
   // pointers depends on the alignment used to emit the descriptor. Otherwise,
   // function pointers are aligned to 32 bits because the instructions must be.
-  if ((T.getArch() == Triple::ppc64 && !T.isPPC64ELFv2ABI())) {
+  if ((T.getArch() == Triple::ppc64 &&
+       (!T.isPPC64ELFv2ABI() && ABIName != "elfv2"))) {
     Ret += "-Fi64";
   } else if (T.isOSAIX()) {
     Ret += is64Bit ? "-Fi64" : "-Fi32";
@@ -348,7 +351,7 @@ PPCTargetMachine::PPCTargetMachine(const Target &T, const Triple &TT,
                                    std::optional<Reloc::Model> RM,
                                    std::optional<CodeModel::Model> CM,
                                    CodeGenOptLevel OL, bool JIT)
-    : CodeGenTargetMachineImpl(T, getDataLayoutString(TT), TT, CPU,
+    : CodeGenTargetMachineImpl(T, computeDataLayout(TT, Options), TT, CPU,
                                computeFSAdditions(FS, OL, TT), Options,
                                getEffectiveRelocModel(TT, RM),
                                getEffectivePPCCodeModel(TT, CM, JIT), OL),

@alexrp
Copy link
Member

alexrp commented Jul 21, 2025

From https://sourceware.org/glibc/wiki/ABIList#powerpc (emphasis mine):

(The GCC distinction for 64-bit is actually ELFv1/ELFv2, but the BE ELFv2 and LE ELFv1 combinations aren't supported.)

Is this no longer the case?

@alexrp
Copy link
Member

alexrp commented Jul 21, 2025

The only Linux distro I can find that actually claims support for powerpc64 + elfv2 + glibc is Void:

Void-ppc uses a variant of the modern ELFv2 ABI (loosened not to impose the POWER8/VSX requirements of the specification) on 64-bit glibc, as the only distribution known to do so.

... but that's not really ELFv2 then, is it? If that ABI variant is what this PR is actually about, then it seems to me that a new name should be created for this to avoid confusion. (elfv2g5 or something? 🤷)

@Gelbpunkt
Copy link
Contributor Author

Is this no longer the case?

The page is probably correct in so far that glibc doesn't explicitly support it. However, out of the 64-bit big endian Linux distributions using glibc that I'm aware of (Arch Linux POWER, Void Linux for PowerPC, Gentoo), the first two use ELFv2 and Gentoo at least lists it as a theoretical option.

Also, this isn't exactly a fix limited to glibc target triples. It fixes the data layout on any target triple that defaults to ELFv1 when -mabi=elfv2 is specified, which is a supported usecase and explicitly specified in the ABI document.

@alexrp
Copy link
Member

alexrp commented Jul 21, 2025

Arch Linux POWER

This one claims to support old processors too, so seems like also a case of "loosened" ELFv2?

Also, this isn't exactly a fix limited to glibc target triples. It fixes the data layout on any target triple that defaults to ELFv1 when -mabi=elfv2 is specified, which is a supported usecase and explicitly specified in the ABI document.

Sure, I'm not objecting to the fix. From a quick glance, it seems ok to me. I'm just concerned about what downstream projects seem to be doing with the term elfv2...

@Gelbpunkt
Copy link
Contributor Author

This one claims to support old processors too, so seems like also a case of "loosened" ELFv2?

Correct, it isn't strictly ELFv2.

I'm just concerned about what downstream projects seem to be doing with the term elfv2...

Fair point, ideally I'd like both the "proper" ELFv2 with BE to have its target triple and the "loosened" ELFv2 to have one. Currently, LLVM will enable the VSX and POWER8-specific parts of the ABI based on the endianess or CPU, not the ABI. That should probably be changed then? I could do that in a follow-up PR.

@alexrp
Copy link
Member

alexrp commented Jul 21, 2025

Fair point, ideally I'd like both the "proper" ELFv2 with BE to have its target triple and the "loosened" ELFv2 to have one. Currently, LLVM will enable the VSX and POWER8-specific parts of the ABI based on the endianess or CPU, not the ABI. That should probably be changed then? I could do that in a follow-up PR.

Potentially, but we should probably get some input from the PPC backend maintainers (I'm not one) before making changes on that front.

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" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants