Skip to content

[clang][PowerPC] Add flag to enable compatibility with GNU for complex arguments - #77732

Open
Long5hot wants to merge 1 commit into
llvm:mainfrom
Long5hot:GNU-Complex-Arguments
Open

[clang][PowerPC] Add flag to enable compatibility with GNU for complex arguments#77732
Long5hot wants to merge 1 commit into
llvm:mainfrom
Long5hot:GNU-Complex-Arguments

Conversation

@Long5hot

@Long5hot Long5hot commented Jan 11, 2024

Copy link
Copy Markdown
Contributor
Fixes : https://github.com/llvm/llvm-project/issues/56023

https://godbolt.org/z/1bsW1sKMs

newFlag : -fcomplex-ppc-gnu-abi

GNU uses GPRs for complex parameters and return values storing for PowerPC-32bit,
which can be enabled which above flag.
Intent of this patch is to make clang compatible with GNU libraries of complex.

Following up with this patch : https://reviews.llvm.org/D146942

@Long5hot
Long5hot requested a review from nemanjai January 11, 2024 06:32
@Long5hot Long5hot self-assigned this Jan 11, 2024
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:codegen IR generation bugs: mangling, exceptions, etc. labels Jan 11, 2024
@llvmbot

llvmbot commented Jan 11, 2024

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-clang-driver
@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-codegen

Author: Kishan Parmar (Long5hot)

Changes
Fixes : https://github.com/llvm/llvm-project/issues/56023

https://godbolt.org/z/1bsW1sKMs

newFlag : -fcomplex-ppc-gnu-abi

GNU uses GPRs for complex parameters and return values storing for PowerPC-32bit,
which can be enabled which above flag.
Intent of this patch is to make clang compatible with GNU libraries of complex.

Patch is 23.05 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/77732.diff

9 Files Affected:

  • (modified) clang/include/clang/Basic/CodeGenOptions.def (+2)
  • (modified) clang/include/clang/Basic/CodeGenOptions.h (+7)
  • (modified) clang/include/clang/Driver/Options.td (+4)
  • (modified) clang/lib/CodeGen/CodeGenModule.cpp (+4-2)
  • (modified) clang/lib/CodeGen/TargetInfo.h (+2-1)
  • (modified) clang/lib/CodeGen/Targets/PPC.cpp (+98-12)
  • (modified) clang/lib/Driver/ToolChains/Clang.cpp (+9)
  • (modified) clang/lib/Frontend/CompilerInvocation.cpp (+8)
  • (added) clang/test/CodeGen/PowerPC/ppc32-complex-gnu-abi.c (+132)
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 2c4fb6745bc172..beeefae15c63f8 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -213,6 +213,8 @@ CODEGENOPT(MCDCCoverage , 1, 0) ///< Enable MC/DC code coverage criteria.
 
   /// If -fpcc-struct-return or -freg-struct-return is specified.
 ENUM_CODEGENOPT(StructReturnConvention, StructReturnConventionKind, 2, SRCK_Default)
+  /// If -fcomplex-ppc-gnu-abi for ppc32
+ENUM_CODEGENOPT(ComplexInRegABI, ComplexArgumentConventionKind, 2, CMPLX_Default)
 
 CODEGENOPT(RelaxAll          , 1, 0) ///< Relax all machine code instructions.
 CODEGENOPT(RelaxedAliasing   , 1, 0) ///< Set when -fno-strict-aliasing is enabled.
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 6952b48e898a81..8abca0e3dda334 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -78,6 +78,13 @@ class CodeGenOptions : public CodeGenOptionsBase {
     SRCK_InRegs    // Small structs in registers (-freg-struct-return).
   };
 
+  enum ComplexArgumentConventionKind {
+    CMPLX_Default,
+    CMPLX_OnStack,
+    CMPLX_OnGPR, // if ppc32 -fcomplex-ppc-gnu-abi
+    CMPLX_OnFPR
+  };
+
   enum ProfileInstrKind {
     ProfileNone,       // Profile instrumentation is turned off.
     ProfileClangInstr, // Clang instrumentation to generate execution counts
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 19becba4a5ad83..251bcb8c49782f 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2540,6 +2540,10 @@ def ffp_contract : Joined<["-"], "ffp-contract=">, Group<f_Group>,
   HelpText<"Form fused FP ops (e.g. FMAs)">,
   Values<"fast,on,off,fast-honor-pragmas">;
 
+def fcomplex_ppc_gnu_abi : Flag<["-"], "fcomplex-ppc-gnu-abi">, Group<f_Group>, Visibility<[ClangOption, CC1Option]>,
+  DocBrief<"Follow the GNU ABI, store Complex values in GPR instead of stack for PowerPC-32">,
+  HelpText<"Store Complex values in GPR instead of stack for PowerPC-32">;
+
 defm strict_float_cast_overflow : BoolFOption<"strict-float-cast-overflow",
   CodeGenOpts<"StrictFloatCastOverflow">, DefaultTrue,
   NegFlag<SetFalse, [], [ClangOption, CC1Option],
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 4fd32337cccc09..003848984d5f7a 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -184,11 +184,13 @@ createTargetCodeGenInfo(CodeGenModule &CGM) {
 
     bool IsSoftFloat =
         CodeGenOpts.FloatABI == "soft" || Target.hasFeature("spe");
-    return createPPC32TargetCodeGenInfo(CGM, IsSoftFloat);
+    unsigned RLen = Target.getPointerWidth(LangAS::Default);
+    return createPPC32TargetCodeGenInfo(CGM, IsSoftFloat, RLen);
   }
   case llvm::Triple::ppcle: {
     bool IsSoftFloat = CodeGenOpts.FloatABI == "soft";
-    return createPPC32TargetCodeGenInfo(CGM, IsSoftFloat);
+    unsigned RLen = Target.getPointerWidth(LangAS::Default);
+    return createPPC32TargetCodeGenInfo(CGM, IsSoftFloat, RLen);
   }
   case llvm::Triple::ppc64:
     if (Triple.isOSAIX())
diff --git a/clang/lib/CodeGen/TargetInfo.h b/clang/lib/CodeGen/TargetInfo.h
index 0c0781a2d5ab9d..89ee2965f6f2e1 100644
--- a/clang/lib/CodeGen/TargetInfo.h
+++ b/clang/lib/CodeGen/TargetInfo.h
@@ -486,7 +486,8 @@ std::unique_ptr<TargetCodeGenInfo>
 createAIXTargetCodeGenInfo(CodeGenModule &CGM, bool Is64Bit);
 
 std::unique_ptr<TargetCodeGenInfo>
-createPPC32TargetCodeGenInfo(CodeGenModule &CGM, bool SoftFloatABI);
+createPPC32TargetCodeGenInfo(CodeGenModule &CGM, bool SoftFloatABI,
+                             unsigned RLen);
 
 std::unique_ptr<TargetCodeGenInfo>
 createPPC64TargetCodeGenInfo(CodeGenModule &CGM);
diff --git a/clang/lib/CodeGen/Targets/PPC.cpp b/clang/lib/CodeGen/Targets/PPC.cpp
index 40dddde508c177..f4885a927ab0ba 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -271,22 +271,33 @@ namespace {
 class PPC32_SVR4_ABIInfo : public DefaultABIInfo {
   bool IsSoftFloatABI;
   bool IsRetSmallStructInRegABI;
+  bool isComplexInRegABI;
+  // Size of GPR in bits
+  unsigned RLen;
+  static const int NumArgGPRs = 8;
 
   CharUnits getParamTypeAlignment(QualType Ty) const;
+  ABIArgInfo handleComplex(QualType Ty, uint64_t &TypeSize) const;
 
 public:
   PPC32_SVR4_ABIInfo(CodeGen::CodeGenTypes &CGT, bool SoftFloatABI,
-                     bool RetSmallStructInRegABI)
+                     bool RetSmallStructInRegABI, unsigned RLen,
+                     bool ComplexInRegABI)
       : DefaultABIInfo(CGT), IsSoftFloatABI(SoftFloatABI),
-        IsRetSmallStructInRegABI(RetSmallStructInRegABI) {}
+        IsRetSmallStructInRegABI(RetSmallStructInRegABI),
+        isComplexInRegABI(ComplexInRegABI), RLen(RLen) {}
 
   ABIArgInfo classifyReturnType(QualType RetTy) const;
+  ABIArgInfo classifyArgumentType(QualType Ty, int &ArgGPRsLeft) const;
 
   void computeInfo(CGFunctionInfo &FI) const override {
+
+    int ArgGPRsLeft = NumArgGPRs;
+
     if (!getCXXABI().classifyReturnType(FI))
       FI.getReturnInfo() = classifyReturnType(FI.getReturnType());
     for (auto &I : FI.arguments())
-      I.info = classifyArgumentType(I.type);
+      I.info = classifyArgumentType(I.type, ArgGPRsLeft);
   }
 
   Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
@@ -296,9 +307,11 @@ class PPC32_SVR4_ABIInfo : public DefaultABIInfo {
 class PPC32TargetCodeGenInfo : public TargetCodeGenInfo {
 public:
   PPC32TargetCodeGenInfo(CodeGenTypes &CGT, bool SoftFloatABI,
-                         bool RetSmallStructInRegABI)
+                         bool RetSmallStructInRegABI, unsigned RLen,
+                         bool ComplexInRegABI)
       : TargetCodeGenInfo(std::make_unique<PPC32_SVR4_ABIInfo>(
-            CGT, SoftFloatABI, RetSmallStructInRegABI)) {}
+            CGT, SoftFloatABI, RetSmallStructInRegABI, RLen, ComplexInRegABI)) {
+  }
 
   static bool isStructReturnInRegABI(const llvm::Triple &Triple,
                                      const CodeGenOptions &Opts);
@@ -337,12 +350,77 @@ CharUnits PPC32_SVR4_ABIInfo::getParamTypeAlignment(QualType Ty) const {
   return CharUnits::fromQuantity(4);
 }
 
+ABIArgInfo PPC32_SVR4_ABIInfo::handleComplex(QualType Ty,
+                                             uint64_t &TypeSize) const {
+
+  assert(Ty->isAnyComplexType());
+  llvm::Type *ElemTy;
+  unsigned SizeRegs;
+
+  if (TypeSize == 64) {
+    ElemTy = llvm::Type::getInt64Ty(getVMContext());
+    SizeRegs = 1;
+  } else {
+    ElemTy = llvm::Type::getInt32Ty(getVMContext());
+    SizeRegs = (TypeSize + 31) / 32;
+  }
+  return ABIArgInfo::getDirect(llvm::ArrayType::get(ElemTy, SizeRegs));
+}
+
+ABIArgInfo PPC32_SVR4_ABIInfo::classifyArgumentType(QualType Ty,
+                                                    int &ArgGPRsLeft) const {
+
+  assert(ArgGPRsLeft <= NumArgGPRs && "Arg GPR tracking underflow");
+  Ty = useFirstFieldIfTransparentUnion(Ty);
+
+  ASTContext &Context = getContext();
+
+  uint64_t TypeSize = Context.getTypeSize(Ty);
+
+  if (isComplexInRegABI && Ty->isAnyComplexType() &&
+      TypeSize <= RLen * ArgGPRsLeft) {
+    ArgGPRsLeft -= TypeSize / RLen;
+    return handleComplex(Ty, TypeSize);
+  }
+
+  if (isAggregateTypeForABI(Ty)) {
+    if (ArgGPRsLeft)
+      ArgGPRsLeft -= 1;
+    // Records with non-trivial destructors/copy-constructors should not be
+    // passed by value.
+    if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI())) {
+      return getNaturalAlignIndirect(Ty, RAA == CGCXXABI::RAA_DirectInMemory);
+    }
+    return getNaturalAlignIndirect(Ty);
+  }
+
+  if (!Ty->isFloatingType()) {
+    if (TypeSize > RLen && TypeSize <= 2 * RLen)
+      ArgGPRsLeft -= 2;
+    else
+      ArgGPRsLeft--;
+  }
+
+  // Treat an enum type as its underlying type.
+  if (const EnumType *EnumTy = Ty->getAs<EnumType>())
+    Ty = EnumTy->getDecl()->getIntegerType();
+
+  if (const auto *EIT = Ty->getAs<BitIntType>())
+    if (EIT->getNumBits() >
+        Context.getTypeSize(Context.getTargetInfo().hasInt128Type()
+                                ? Context.Int128Ty
+                                : Context.LongLongTy))
+      return getNaturalAlignIndirect(Ty);
+
+  return (isPromotableIntegerTypeForABI(Ty) ? ABIArgInfo::getExtend(Ty)
+                                            : ABIArgInfo::getDirect());
+}
+
 ABIArgInfo PPC32_SVR4_ABIInfo::classifyReturnType(QualType RetTy) const {
-  uint64_t Size;
+  uint64_t Size = getContext().getTypeSize(RetTy);
 
   // -msvr4-struct-return puts small aggregates in GPR3 and GPR4.
-  if (isAggregateTypeForABI(RetTy) && IsRetSmallStructInRegABI &&
-      (Size = getContext().getTypeSize(RetTy)) <= 64) {
+  if (isAggregateTypeForABI(RetTy) && IsRetSmallStructInRegABI && Size <= 64) {
     // System V ABI (1995), page 3-22, specified:
     // > A structure or union whose size is less than or equal to 8 bytes
     // > shall be returned in r3 and r4, as if it were first stored in the
@@ -361,6 +439,9 @@ ABIArgInfo PPC32_SVR4_ABIInfo::classifyReturnType(QualType RetTy) const {
       return ABIArgInfo::getDirect(CoerceTy);
     }
   }
+  if(isComplexInRegABI && RetTy->isAnyComplexType()) {
+    return handleComplex(RetTy, Size);
+  }
 
   return DefaultABIInfo::classifyReturnType(RetTy);
 }
@@ -372,11 +453,12 @@ Address PPC32_SVR4_ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAList,
   if (getTarget().getTriple().isOSDarwin()) {
     auto TI = getContext().getTypeInfoInChars(Ty);
     TI.Align = getParamTypeAlignment(Ty);
+    int ArgGPRs = NumArgGPRs;
 
     CharUnits SlotSize = CharUnits::fromQuantity(4);
     return emitVoidPtrVAArg(CGF, VAList, Ty,
-                            classifyArgumentType(Ty).isIndirect(), TI, SlotSize,
-                            /*AllowHigherAlign=*/true);
+                            classifyArgumentType(Ty, ArgGPRs).isIndirect(), TI,
+                            SlotSize, /*AllowHigherAlign=*/true);
   }
 
   const unsigned OverflowLimit = 8;
@@ -974,11 +1056,15 @@ CodeGen::createAIXTargetCodeGenInfo(CodeGenModule &CGM, bool Is64Bit) {
 }
 
 std::unique_ptr<TargetCodeGenInfo>
-CodeGen::createPPC32TargetCodeGenInfo(CodeGenModule &CGM, bool SoftFloatABI) {
+CodeGen::createPPC32TargetCodeGenInfo(CodeGenModule &CGM, bool SoftFloatABI,
+                                      unsigned RLen) {
   bool RetSmallStructInRegABI = PPC32TargetCodeGenInfo::isStructReturnInRegABI(
       CGM.getTriple(), CGM.getCodeGenOpts());
+  bool isComplexInRegABI = (CGM.getCodeGenOpts().getComplexInRegABI() ==
+                            CodeGenOptions::CMPLX_OnGPR);
   return std::make_unique<PPC32TargetCodeGenInfo>(CGM.getTypes(), SoftFloatABI,
-                                                  RetSmallStructInRegABI);
+                                                  RetSmallStructInRegABI, RLen,
+                                                  isComplexInRegABI);
 }
 
 std::unique_ptr<TargetCodeGenInfo>
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 2d8ef841d4f6be..47ad15c100ef45 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5454,6 +5454,15 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
     }
   }
 
+  if (Arg *A = Args.getLastArg(options::OPT_fcomplex_ppc_gnu_abi)) {
+    if (!TC.getTriple().isPPC32() || !TC.getTriple().isOSBinFormatELF()) {
+      D.Diag(diag::err_drv_unsupported_opt_for_target)
+          << A->getSpelling() << RawTriple.str();
+    } else {
+      CmdArgs.push_back("-fcomplex-ppc-gnu-abi");
+    }
+  }
+
   if (Args.hasFlag(options::OPT_mrtd, options::OPT_mno_rtd, false)) {
     if (Triple.getArch() == llvm::Triple::m68k)
       CmdArgs.push_back("-fdefault-calling-conv=rtdcall");
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 11f3f2c2d6425c..5c1f7da805db87 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1640,6 +1640,10 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
     GenerateArg(Consumer, Opt);
   }
 
+  if (T.isPPC32() && Opts.ComplexInRegABI == CodeGenOptions::CMPLX_OnGPR) {
+    GenerateArg(Consumer, OPT_fcomplex_ppc_gnu_abi);
+  }
+
   if (Opts.EnableAIXExtendedAltivecABI)
     GenerateArg(Consumer, OPT_mabi_EQ_vec_extabi);
 
@@ -2034,6 +2038,10 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
     }
   }
 
+  if (Args.getLastArg(OPT_fcomplex_ppc_gnu_abi)) {
+    Opts.setComplexInRegABI(CodeGenOptions::CMPLX_OnGPR);
+  }
+
   if (Arg *A = Args.getLastArg(OPT_mxcoff_roptr)) {
     if (!T.isOSAIX())
       Diags.Report(diag::err_drv_unsupported_opt_for_target)
diff --git a/clang/test/CodeGen/PowerPC/ppc32-complex-gnu-abi.c b/clang/test/CodeGen/PowerPC/ppc32-complex-gnu-abi.c
new file mode 100644
index 00000000000000..b0df1dc836ba81
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/ppc32-complex-gnu-abi.c
@@ -0,0 +1,132 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2
+
+// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -target-cpu pwr8 \
+// RUN:   -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-DEF
+// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -target-cpu pwr8 -fcomplex-ppc-gnu-abi \
+// RUN:   -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-GNU
+
+// CHECK-DEF-LABEL: define dso_local void @foo1
+// CHECK-DEF-SAME: (ptr noalias sret({ float, float }) align 4 [[AGG_RESULT:%.*]], ptr noundef byval({ float, float }) align 4 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-DEF-NEXT:  entry:
+// CHECK-DEF-NEXT:    [[X_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[X]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[X_REAL:%.*]] = load float, ptr [[X_REALP]], align 4
+// CHECK-DEF-NEXT:    [[X_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[X]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[X_IMAG:%.*]] = load float, ptr [[X_IMAGP]], align 4
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store float [[X_REAL]], ptr [[AGG_RESULT_REALP]], align 4
+// CHECK-DEF-NEXT:    store float [[X_IMAG]], ptr [[AGG_RESULT_IMAGP]], align 4
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP1:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REAL:%.*]] = load float, ptr [[AGG_RESULT_REALP1]], align 4
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP2:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAG:%.*]] = load float, ptr [[AGG_RESULT_IMAGP2]], align 4
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP3:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP4:%.*]] = getelementptr inbounds { float, float }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store float [[AGG_RESULT_REAL]], ptr [[AGG_RESULT_REALP3]], align 4
+// CHECK-DEF-NEXT:    store float [[AGG_RESULT_IMAG]], ptr [[AGG_RESULT_IMAGP4]], align 4
+// CHECK-DEF-NEXT:    ret void
+//
+// CHECK-GNU-LABEL: define dso_local [1 x i64] @foo1
+// CHECK-GNU-SAME: ([1 x i64] noundef [[X_COERCE:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-GNU-NEXT:  entry:
+// CHECK-GNU-NEXT:    [[RETVAL:%.*]] = alloca { float, float }, align 4
+// CHECK-GNU-NEXT:    [[X:%.*]] = alloca { float, float }, align 4
+// CHECK-GNU-NEXT:    store [1 x i64] [[X_COERCE]], ptr [[X]], align 4
+// CHECK-GNU-NEXT:    [[X_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[X]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[X_REAL:%.*]] = load float, ptr [[X_REALP]], align 4
+// CHECK-GNU-NEXT:    [[X_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[X]], i32 0, i32 1
+// CHECK-GNU-NEXT:    [[X_IMAG:%.*]] = load float, ptr [[X_IMAGP]], align 4
+// CHECK-GNU-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[RETVAL]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[RETVAL]], i32 0, i32 1
+// CHECK-GNU-NEXT:    store float [[X_REAL]], ptr [[RETVAL_REALP]], align 4
+// CHECK-GNU-NEXT:    store float [[X_IMAG]], ptr [[RETVAL_IMAGP]], align 4
+// CHECK-GNU-NEXT:    [[TMP0:%.*]] = load [1 x i64], ptr [[RETVAL]], align 4
+// CHECK-GNU-NEXT:    ret [1 x i64] [[TMP0]]
+//
+_Complex float foo1(_Complex float x) {
+  return x;
+}
+
+// CHECK-DEF-LABEL: define dso_local void @foo2
+// CHECK-DEF-SAME: (ptr noalias sret({ double, double }) align 8 [[AGG_RESULT:%.*]], ptr noundef byval({ double, double }) align 8 [[X:%.*]]) #[[ATTR0]] {
+// CHECK-DEF-NEXT:  entry:
+// CHECK-DEF-NEXT:    [[X_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[X]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[X_REAL:%.*]] = load double, ptr [[X_REALP]], align 8
+// CHECK-DEF-NEXT:    [[X_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[X]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[X_IMAG:%.*]] = load double, ptr [[X_IMAGP]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store double [[X_REAL]], ptr [[AGG_RESULT_REALP]], align 8
+// CHECK-DEF-NEXT:    store double [[X_IMAG]], ptr [[AGG_RESULT_IMAGP]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP1:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REAL:%.*]] = load double, ptr [[AGG_RESULT_REALP1]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP2:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAG:%.*]] = load double, ptr [[AGG_RESULT_IMAGP2]], align 8
+// CHECK-DEF-NEXT:    [[AGG_RESULT_REALP3:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-DEF-NEXT:    [[AGG_RESULT_IMAGP4:%.*]] = getelementptr inbounds { double, double }, ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-DEF-NEXT:    store double [[AGG_RESULT_REAL]], ptr [[AGG_RESULT_REALP3]], align 8
+// CHECK-DEF-NEXT:    store double [[AGG_RESULT_IMAG]], ptr [[AGG_RESULT_IMAGP4]], align 8
+// CHECK-DEF-NEXT:    ret void
+//
+// CHECK-GNU-LABEL: define dso_local [4 x i32] @foo2
+// CHECK-GNU-SAME: ([4 x i32] noundef [[X_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-GNU-NEXT:  entry:
+// CHECK-GNU-NEXT:    [[RETVAL:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT:    [[X:%.*]] = alloca { double, double }, align 8
+// CHECK-GNU-NEXT:    store [4 x i32] [[X_COERCE]], ptr [[X]], align 8
+// CHECK-GNU-NEXT:    [[X_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[X]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[X_REAL:%.*]] = load double, ptr [[X_REALP]], align 8
+// CHECK-GNU-NEXT:    [[X_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[X]], i32 0, i32 1
+// CHECK-GNU-NEXT:    [[X_IMAG:%.*]] = load double, ptr [[X_IMAGP]], align 8
+// CHECK-GNU-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds { double, double }, ptr [[RETVAL]], i32 0, i32 0
+// CHECK-GNU-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds { double, double }, ptr [[RETVAL]], i32 0, i32 1
+// CHECK-GNU-NEXT:    store double [[X_REAL]], ptr [[RETVAL_REALP]], align 8
+// CHECK-GNU-NEXT:    store double [[X_IMAG]], ptr [[RETVAL_IMAGP]], align 8
+// CHECK-GNU-NEXT:    [[TMP0:%.*]] = load [4 x i32], ptr [[RETVAL]], align 8
+// CHECK-GNU-NEXT:    ret [4 x i32] [[TMP0]]
+//
+_Complex double foo2(_Complex double x) {
+  return x;
+}
+
+// CHECK-DEF-LABEL: define dso_local void @foo3
+// CHECK-DEF-SAME: (ptr noalias sret({ ppc_fp128, ppc_fp128 }) align 16 [[AGG_RESULT:%.*]], ptr noundef byval({ ppc_fp128, ppc_fp128 }) align 16 [[X:%.*]]) #[[ATTR0]] {
+// CHECK-DEF-NEXT:  entry:
+// CHECK-DEF-NEXT:    [[X_REALP:%.*]] = getelementp...
[truncated]

@github-actions

github-actions Bot commented Jan 11, 2024

Copy link
Copy Markdown

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

@Long5hot

Copy link
Copy Markdown
Contributor Author

ping! @chmeeedalf @nemanjai

@Long5hot
Long5hot requested a review from chmeeedalf January 25, 2024 08:19
@chmeeedalf

Copy link
Copy Markdown
Contributor

ping! @chmeeedalf @nemanjai

I know nothing about the complex ABI, so all I can say is the code looks okay from a structural point, can't say anything to the logic.

@lei137
lei137 requested review from daltenty and diggerlin February 8, 2024 18:11
enum ComplexArgumentConventionKind {
CMPLX_Default,
CMPLX_OnStack,
CMPLX_OnGPR, // if ppc32 -fcomplex-ppc-gnu-abi

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: can we use In[GF]PR in the names as "in registers"/"on stack" are in common usage.

Comment thread clang/include/clang/Driver/Options.td Outdated
Values<"fast,on,off,fast-honor-pragmas">;

def fcomplex_ppc_gnu_abi : Flag<["-"], "fcomplex-ppc-gnu-abi">, Group<f_Group>, Visibility<[ClangOption, CC1Option]>,
DocBrief<"Follow the GNU ABI, store Complex values in GPR instead of stack for PowerPC-32">,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please use "Pass" instead of "Store" as the latter generally refers to storing values in memory and this actually affects parameter passing.

Comment thread clang/lib/CodeGen/TargetInfo.h Outdated
std::unique_ptr<TargetCodeGenInfo>
createPPC32TargetCodeGenInfo(CodeGenModule &CGM, bool SoftFloatABI);
createPPC32TargetCodeGenInfo(CodeGenModule &CGM, bool SoftFloatABI,
unsigned RLen);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please explain this change. It is not clear to me why we're adding this parameter. Are we adding support for 32-bit targets with 64-bit pointers? If so, please justify. Also, this is something that would belong in a separate patch.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@nemanjai

nemanjai commented Feb 8, 2024

Copy link
Copy Markdown
Member

My review is not complete, I just submitted what I have so far so at least we can get started on answering the questions I have so far.

Comment thread clang/lib/CodeGen/CodeGenModule.cpp Outdated
bool IsSoftFloat =
CodeGenOpts.FloatABI == "soft" || Target.hasFeature("spe");
return createPPC32TargetCodeGenInfo(CGM, IsSoftFloat);
unsigned RLen = Target.getPointerWidth(LangAS::Default);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the variable is used only once , do not need to introduce a new variable.

Comment thread clang/lib/CodeGen/CodeGenModule.cpp Outdated
bool IsSoftFloat = CodeGenOpts.FloatABI == "soft";
return createPPC32TargetCodeGenInfo(CGM, IsSoftFloat);
unsigned RLen = Target.getPointerWidth(LangAS::Default);
return createPPC32TargetCodeGenInfo(CGM, IsSoftFloat, RLen);

@diggerlin diggerlin Feb 22, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the variable RLen is used only once , do not need to introduce a new variable.

Comment thread clang/lib/CodeGen/Targets/PPC.cpp Outdated
bool IsRetSmallStructInRegABI;
bool isComplexInRegABI;
// Size of GPR in bits
unsigned RLen;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

RLen is too short to understand what R means , suggest to change to RegLen and RegisterLen. and Do we need to add RLen ? since the class is PPC32_SVR4_ABIInfo, Should the Register Len be default to 32 bit ?

Comment thread clang/lib/CodeGen/Targets/PPC.cpp Outdated

assert(Ty->isAnyComplexType());
llvm::Type *ElemTy;
unsigned SizeRegs;

@diggerlin diggerlin Feb 22, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

can you add a comment for SizeRegs what is stand for?
should it rename to ElemNum ?

Comment thread clang/lib/CodeGen/Targets/PPC.cpp Outdated
ABIArgInfo PPC32_SVR4_ABIInfo::handleComplex(QualType Ty,
uint64_t &TypeSize) const {

assert(Ty->isAnyComplexType());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

it looks the Ty pass in the function is only used for assert ?

ElemTy = llvm::Type::getInt64Ty(getVMContext());
SizeRegs = 1;
} else {
ElemTy = llvm::Type::getInt32Ty(getVMContext());

@diggerlin diggerlin Feb 22, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I am curiously why not get ElemTy = llvm::Type::getInt64Ty(getVMContext()); instead of ElemTy = llvm::Type::getInt32Ty(getVMContext());

and change following code as

llvm::Type *ElemTy = llvm::Type::getInt64Ty(getVMContext()): 
unsigned SizeRegs = TypeSize/64;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Reason for using llvm::Type::getInt64Ty(getVMContext()) for floats here was to follow ABI ATR-PASS-COMPLEX-IN-GPRS

complex single-precision float : If gr is even, set gr = gr + 1. Load the lower-addressed word of the
argument into gr and the higher-addressed word into gr + 1, set gr = gr + 2.

complex double-precision float: Load the words of the argument, in memory-address order, into gr, gr + 1,
gr + 2 and gr + 3, set gr = gr + 4.

You can check the previous discussion here. : https://reviews.llvm.org/D146942

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

llvm::Type *ElemTy = llvm::Type::getInt64Ty(getVMContext());
unsigned SizeRegs = TypeSize/64;

Since Typesize will be 64, in if is it necessary to replace 1 to TypeSize/64?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

got it, thanks for explain. can you add your explain as comment ?

Comment thread clang/lib/CodeGen/Targets/PPC.cpp Outdated
if (TypeSize > RLen && TypeSize <= 2 * RLen)
ArgGPRsLeft -= 2;
else
ArgGPRsLeft--;

@diggerlin diggerlin Feb 22, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There is a potential bug here, you decrease the ArgGPRsLeft no mater whether ArgGPRsLeft >0;

and change the code here as

if(!Ty->isFloatingType() && TypeSize <= RLen * ArgGPRsLeft) 
    ArgGPRsLeft -= TypeSize / RLen;

@amy-kwan amy-kwan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think the braces can also be omitted on the conditions within the clang/* files.

Comment thread clang/include/clang/Driver/Options.td Outdated
Values<"fast,on,off,fast-honor-pragmas">;

def fcomplex_ppc_gnu_abi : Flag<["-"], "fcomplex-ppc-gnu-abi">, Group<f_Group>, Visibility<[ClangOption, CC1Option]>,
DocBrief<"Follow the GNU ABI, store Complex values in GPR instead of stack for PowerPC-32">,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
DocBrief<"Follow the GNU ABI, store Complex values in GPR instead of stack for PowerPC-32">,
DocBrief<"Follow the GNU ABI, pass Complex values in GPRs instead of the stack for PowerPC-32">,


/// If -fpcc-struct-return or -freg-struct-return is specified.
ENUM_CODEGENOPT(StructReturnConvention, StructReturnConventionKind, 2, SRCK_Default)
/// If -fcomplex-ppc-gnu-abi for ppc32

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
/// If -fcomplex-ppc-gnu-abi for ppc32
/// If -fcomplex-ppc-gnu-abi is specified on ppc32.

This sentence might read a bit better.

Comment thread clang/lib/CodeGen/Targets/PPC.cpp Outdated
Comment on lines +274 to +275
bool isComplexInRegABI;
// Size of GPR in bits

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
bool isComplexInRegABI;
// Size of GPR in bits
bool IsComplexInRegABI;
// Size of GPR in bits.

End sentence with a period, and capitalize the boolean variable for consistency.

Comment thread clang/lib/CodeGen/Targets/PPC.cpp Outdated
ABIArgInfo PPC32_SVR4_ABIInfo::handleComplex(QualType Ty,
uint64_t &TypeSize) const {

assert(Ty->isAnyComplexType());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It would also be good if the assert has a message.
Additionally, for people who do not do assert builds, since Ty is only used for the assert, they may get an unused variable warning.

@diggerlin diggerlin Feb 26, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

if only use for the assert, suggest putting assert outside the function,

Comment thread clang/lib/CodeGen/Targets/PPC.cpp Outdated
return ABIArgInfo::getDirect(CoerceTy);
}
}
if (isComplexInRegABI && RetTy->isAnyComplexType()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Braces not needed here.

Comment thread clang/lib/CodeGen/Targets/PPC.cpp Outdated
: Context.LongLongTy))
return getNaturalAlignIndirect(Ty);

return (isPromotableIntegerTypeForABI(Ty) ? ABIArgInfo::getExtend(Ty)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

since most of the code is same as DefaultABIInfo::classifyArgumentType(QualType Ty) , I suggest rewrite the function PPC32_SVR4_ABIInfo::classifyArgumentType as

ABIArgInfo PPC32_SVR4_ABIInfo::classifyArgumentType(QualType Ty,
                                                    int &ArgGPRsLeft) const { 
   ....
   special functionality code of the function here
   .....
   
    DefaultABIInfo::classifyArgumentType(Ty)               
   }

duplication code is not a good idea.


enum ComplexArgumentConventionKind {
CMPLX_Default,
CMPLX_OnStack,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

it looks you define the CMPLX_Default and CMPLX_OnStack , but never use them.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This was for future reference, if any arch wants to use this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think this needs to be a target-independent thing; this doesn't really generalize to other targets. I mean, maybe different targets have sort of similar schemes, but the same ABI compatibility issue shouldn't exist anywhere else. Maybe pass this as a target feature (ref getPPCTargetFeatures()).

Comment thread clang/lib/CodeGen/Targets/PPC.cpp Outdated

void computeInfo(CGFunctionInfo &FI) const override {

int ArgGPRsLeft = NumArgGPRs;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: move the definition to the closest first be used.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Couldn't understand what you mean here..

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

put int ArgGPRsLeft = NumArgGPRs; in front of

for (auto &I : FI.arguments()) I.info = classifyArgumentType(I.type, ArgGPRsLeft);

@@ -0,0 +1,132 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2

// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -target-cpu pwr8 \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

do you really need -target-cpu pwr8 since the test case only check generated IR?


// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -target-cpu pwr8 \
// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-DEF
// RUN: %clang_cc1 -triple powerpc-unknown-linux-gnu -target-cpu pwr8 -fcomplex-ppc-gnu-abi \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ditto

Comment thread clang/lib/CodeGen/Targets/PPC.cpp Outdated
return handleComplex(Ty, TypeSize);
}

if (isAggregateTypeForABI(Ty)) {

@diggerlin diggerlin Feb 27, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

it look that you only has Complex as parameter in your test case .for example _Complex float foo1(_Complex float x)

I think you need more test case with other parameters to test the functionality of function classifyArgumentType()

@Long5hot
Long5hot force-pushed the GNU-Complex-Arguments branch from 6a209fb to a89cd20 Compare March 4, 2024 13:18
@Long5hot

Long5hot commented Aug 3, 2024

Copy link
Copy Markdown
Contributor Author

ping!

@AaronBallman AaronBallman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm wondering why the GNU-compatible behavior is not on by default, especially when in GNU mode (e.g., -std=gnu17)?

Also, the changes should come with a release note in clang/docs/ReleaseNotes.rst so users know about the new functionality.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (T.isPPC32() && Opts.ComplexInRegABI == CodeGenOptions::CMPLX_InGPR) {
GenerateArg(Consumer, OPT_fcomplex_ppc_gnu_abi);
}
if (T.isPPC32() && Opts.ComplexInRegABI == CodeGenOptions::CMPLX_InGPR)
GenerateArg(Consumer, OPT_fcomplex_ppc_gnu_abi);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (Args.getLastArg(OPT_fcomplex_ppc_gnu_abi)) {
Opts.setComplexInRegABI(CodeGenOptions::CMPLX_InGPR);
}
if (Args.getLastArg(OPT_fcomplex_ppc_gnu_abi))
Opts.setComplexInRegABI(CodeGenOptions::CMPLX_InGPR);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It'd be nice for this to check -### output to ensure that it's passing along the correct -cc1 option.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We just use one single blank line as a separator.

@Long5hot
Long5hot force-pushed the GNU-Complex-Arguments branch from 473e188 to 2d4ae49 Compare August 8, 2024 10:01
@Long5hot

Long5hot commented Aug 8, 2024

Copy link
Copy Markdown
Contributor Author

I'm wondering why the GNU-compatible behavior is not on by default, especially when in GNU mode (e.g., -std=gnu17)?

ATR-PASS-COMPLEX-IN-GPRS was missing in clang as shown in ABI doc : https://example61560.wordpress.com/wp-content/uploads/2016/11/powerpc_abi.pdf

Also, the changes should come with a release note in clang/docs/ReleaseNotes.rst so users know about the new functionality.

@AaronBallman, can you guide me, in which section of ReleaseNotes.rst i should add info about new flag?

@AaronBallman

Copy link
Copy Markdown
Contributor

I'm wondering why the GNU-compatible behavior is not on by default, especially when in GNU mode (e.g., -std=gnu17)?

ATR-PASS-COMPLEX-IN-GPRS was missing in clang as shown in ABI doc : https://example61560.wordpress.com/wp-content/uploads/2016/11/powerpc_abi.pdf

I'm sorry but I still don't understand why it's not on by default in GNU mode. If the user is in GNU mode, they presumably are doing so because they want to be GNU-compatible, yes?

Also, the changes should come with a release note in clang/docs/ReleaseNotes.rst so users know about the new functionality.

@AaronBallman, can you guide me, in which section of ReleaseNotes.rst i should add info about new flag?

I'd add it under New Compiler Flags

@Long5hot

Copy link
Copy Markdown
Contributor Author

We were compiling simple testcase with complex parameters with clang and it was crashing because libraries was built using gcc.

@AaronBallman, Reason for new flag was to enable this for other C standards as well. Currently we use c11 as standard in VxWorks toolchain, which has to work with gcc compiled libraries.
gcc with c11 as well passes complex parameters in GPRs.

@Long5hot
Long5hot force-pushed the GNU-Complex-Arguments branch from 2d4ae49 to f463f28 Compare August 12, 2024 09:00
@AaronBallman

Copy link
Copy Markdown
Contributor

We were compiling simple testcase with complex parameters with clang and it was crashing because libraries was built using gcc.

@AaronBallman, Reason for new flag was to enable this for other C standards as well. Currently we use c11 as standard in VxWorks toolchain, which has to work with gcc compiled libraries. gcc with c11 as well passes complex parameters in GPRs.

Ah, perhaps I was unclear -- I'm not opposed to the flag existing so users can opt into/out of it as they want. I'm wondering why the flag needs to be specified when compiling with -triple powerpc-unknown-linux-gnu (because that specifies a GNU environment with a powerpc target) or with -triple powerpc -std=gnu<whatever> (because that specifies GNU extensions with a powerpc target). It seems to me that the flag should be implied in those cases, so users shouldn't have to specify it manually, right?

@Long5hot

Copy link
Copy Markdown
Contributor Author

@AaronBallman, Yes you are right. I will work on it!

…x arguments (llvm#77732)

Fixes : llvm#56023

https://godbolt.org/z/1bsW1sKMs

newFlag : -fcomplex-ppc-gnu-abi

GNU uses GPRs for complex parameters and return values storing for PowerPC-32bit,
which can be enabled which above flag.
Intent of this patch is to make clang compatible with GNU libraries of complex.

Following up with this patch : https://reviews.llvm.org/D146942
@Long5hot
Long5hot force-pushed the GNU-Complex-Arguments branch from 869a686 to d5c328d Compare February 5, 2025 09:42
@Long5hot

Copy link
Copy Markdown
Contributor Author

I'm able to query if triple has gnu but can't query for -std=gnu..!!

@efriedma-quic

Copy link
Copy Markdown
Contributor

-std=gnu99 etc. sets LangOpts.GNUMode... but we don't want to base the ABI on that; it's not an ABI flag. The ABI should only be based on the triple, and flags specifically intended to modify the ABI.

I think we should just use the GNU-compatible ABI by default. Unless there's some existing OS where clang is the platform compiler that wants to preserve the old ABI, but even if such a platform exists, I doubt there's anyone we can ask.

@brad0

brad0 commented Feb 18, 2025

Copy link
Copy Markdown
Contributor

cc @kernigh

@Long5hot

Long5hot commented Feb 21, 2025

Copy link
Copy Markdown
Contributor Author

@efriedma-quic for the same reason, we created this switch. There was a debate regarding this conversation , reason being we concluded to creating the flag and not choosing GNU by default.
So now should we go ahead and push this patch?

@Long5hot

Long5hot commented Mar 4, 2025

Copy link
Copy Markdown
Contributor Author

ping!

@AaronBallman
AaronBallman requested a review from rjmccall March 13, 2025 12:28
@kernigh

kernigh commented Mar 28, 2025

Copy link
Copy Markdown
Contributor

I am distracted by issue #133507 (clang-19 miscompiles itself for PPC32), so I can't check this pull request right now. My comment of August 29, 2023 had this example:

#include "complex.h"

double complex
third(int no, double complex no_again, double complex yes)
{
    return yes;
}

gcc passed yes on the stack, but clang with the 2023 patch split yes between registers and the stack, so it was not yet compatible. Has the patch been fixed? I don't want clang to invent a 3rd complex ABI that is incompatible with both gcc and old clang.

Unless there's some existing OS where clang is the platform compiler that wants to preserve the old ABI, but even if such a platform exists, I doubt there's anyone we can ask.

For 32-bit PowerPC, I believe that clang can target Linux, NetBSD, FreeBSD, and OpenBSD. There are complex math functions like csqrt(3). I believe that Linux and NetBSD compile these functions with gcc, while FreeBSD and OpenBSD compile them with clang. For OpenBSD, talk to me. For FreeBSD, I don't know.

I might want OpenBSD to switch back to gcc's ABI. OpenBSD 6.7 (in 2020) had changed its powerpc compiler from gcc to clang. This was before issue #56023 (in 2022) reported the ABI change. Back in 2020, nobody noticed OpenBSD's ABI change, but now, if I use gcc, I can't call csqrt(3). If clang switched to gcc's ABI, then gcc would work again.

@Long5hot

Long5hot commented Apr 8, 2025

Copy link
Copy Markdown
Contributor Author

Has the patch been fixed?

Yess, Works expected as gnu!

@efriedma-quic efriedma-quic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Being incompatible with the platform compiler is the wrong default; we should be compatible by default. And the platform compiler is gcc for almost actively developed ppc32 targets, so we should just switch the default. Having a flag in addition to that seems fine if you think there's a chance people will need to control it.


enum ComplexArgumentConventionKind {
CMPLX_Default,
CMPLX_OnStack,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think this needs to be a target-independent thing; this doesn't really generalize to other targets. I mean, maybe different targets have sort of similar schemes, but the same ABI compatibility issue shouldn't exist anywhere else. Maybe pass this as a target feature (ref getPPCTargetFeatures()).

@folkertdev

Copy link
Copy Markdown
Contributor

I'm interested in picking this up. Do we really think that this should be configurable though? Mirroring GCC sounds like clearly the right solution, if you need to be compatible with older clang you can just wrap the complex value in a struct.

@efriedma-quic

Copy link
Copy Markdown
Contributor

If we want to support a compatibility mode, we should probably just roll it into -fclang-abi-compat; I don't think it makes sense to carry it as an independent option.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:PowerPC clang:codegen IR generation bugs: mangling, exceptions, etc. 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.