Skip to content

[PowerPC][X86] Make cpu id builtins target independent and lower for PPC #68919

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 5 commits into from
Jan 26, 2024

Conversation

nemanjai
Copy link
Member

Make _builtin_cpu{init|supports|is} target independent and provide an opt-in query for targets that want to support it. Each target is still responsible for their specific lowering/code-gen. Also provide code-gen for PowerPC.

I originally proposed this in https://reviews.llvm.org/D152914 and this addresses the comments I received there.

@nemanjai nemanjai self-assigned this Oct 12, 2023
@llvmbot llvmbot added clang Clang issues not falling into any other category backend:X86 clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:codegen IR generation bugs: mangling, exceptions, etc. llvm:ir labels Oct 12, 2023
@llvmbot
Copy link
Member

llvmbot commented Oct 12, 2023

@llvm/pr-subscribers-llvm-ir
@llvm/pr-subscribers-clang-codegen
@llvm/pr-subscribers-backend-x86

@llvm/pr-subscribers-clang

Author: Nemanja Ivanovic (nemanjai)

Changes

Make _builtin_cpu{init|supports|is} target independent and provide an opt-in query for targets that want to support it. Each target is still responsible for their specific lowering/code-gen. Also provide code-gen for PowerPC.

I originally proposed this in https://reviews.llvm.org/D152914 and this addresses the comments I received there.


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

17 Files Affected:

  • (modified) clang/include/clang/Basic/Builtins.def (+5)
  • (modified) clang/include/clang/Basic/BuiltinsX86.def (-7)
  • (modified) clang/include/clang/Basic/TargetInfo.h (+6)
  • (modified) clang/lib/Basic/Targets/PPC.cpp (+14)
  • (modified) clang/lib/Basic/Targets/PPC.h (+7)
  • (modified) clang/lib/Basic/Targets/X86.h (+4)
  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+39-3)
  • (modified) clang/lib/Sema/SemaChecking.cpp (+77-47)
  • (modified) clang/test/CodeGen/builtin-cpu-supports.c (+46-22)
  • (modified) clang/test/Sema/builtin-cpu-supports.c (+5-3)
  • (modified) llvm/include/llvm/IR/IntrinsicsPowerPC.td (+6)
  • (added) llvm/include/llvm/TargetParser/PPCTargetParser.def (+80)
  • (modified) llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp (+4)
  • (modified) llvm/lib/Target/PowerPC/PPCInstrInfo.cpp (+33)
  • (modified) llvm/lib/Target/PowerPC/PPCInstrInfo.td (+3)
  • (modified) llvm/lib/Target/PowerPC/PPCTargetMachine.h (+3)
  • (added) llvm/test/CodeGen/PowerPC/cpu-supports.ll (+111)
diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def
index 6ea8484606cfd5d..5e1f4088ff63f8a 100644
--- a/clang/include/clang/Basic/Builtins.def
+++ b/clang/include/clang/Basic/Builtins.def
@@ -118,6 +118,11 @@
 #  define LANGBUILTIN(ID, TYPE, ATTRS, BUILTIN_LANG) BUILTIN(ID, TYPE, ATTRS)
 #endif
 
+// Builtins for checking CPU features based on the GCC builtins.
+BUILTIN(__builtin_cpu_supports, "bcC*", "nc")
+BUILTIN(__builtin_cpu_is, "bcC*", "nc")
+BUILTIN(__builtin_cpu_init, "v", "n")
+
 // Standard libc/libm functions:
 BUILTIN(__builtin_atan2 , "ddd"  , "Fne")
 BUILTIN(__builtin_atan2f, "fff"  , "Fne")
diff --git a/clang/include/clang/Basic/BuiltinsX86.def b/clang/include/clang/Basic/BuiltinsX86.def
index e4802f8ab1c1562..2acc5ce0f4a3653 100644
--- a/clang/include/clang/Basic/BuiltinsX86.def
+++ b/clang/include/clang/Basic/BuiltinsX86.def
@@ -26,13 +26,6 @@
 #  define TARGET_HEADER_BUILTIN(ID, TYPE, ATTRS, HEADER, LANG, FEATURE) BUILTIN(ID, TYPE, ATTRS)
 #endif
 
-// Miscellaneous builtin for checking x86 cpu features.
-// TODO: Make this somewhat generic so that other backends
-// can use it?
-BUILTIN(__builtin_cpu_init, "v", "n")
-BUILTIN(__builtin_cpu_supports, "bcC*", "nc")
-BUILTIN(__builtin_cpu_is, "bcC*", "nc")
-
 // Undefined Values
 //
 TARGET_BUILTIN(__builtin_ia32_undef128, "V2d", "ncV:128:", "")
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 9d56e97a3d4bb88..3d83b387aac0931 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1415,6 +1415,12 @@ class TargetInfo : public TransferrableTargetInfo,
             getTriple().isOSFreeBSD());
   }
 
+  // Identify whether this target supports __builtin_cpu_supports and
+  // __builtin_cpu_is.
+  virtual bool supportsCpuSupports() const { return false; }
+  virtual bool supportsCpuIs() const { return false; }
+  virtual bool supportsCpuInit() const { return false; }
+
   // Validate the contents of the __builtin_cpu_supports(const char*)
   // argument.
   virtual bool validateCpuSupports(StringRef Name) const { return false; }
diff --git a/clang/lib/Basic/Targets/PPC.cpp b/clang/lib/Basic/Targets/PPC.cpp
index 0d87a3a4e8c20f3..d8759c86c9932ca 100644
--- a/clang/lib/Basic/Targets/PPC.cpp
+++ b/clang/lib/Basic/Targets/PPC.cpp
@@ -873,3 +873,17 @@ ArrayRef<Builtin::Info> PPCTargetInfo::getTargetBuiltins() const {
   return llvm::ArrayRef(BuiltinInfo,
                         clang::PPC::LastTSBuiltin - Builtin::FirstTSBuiltin);
 }
+
+bool PPCTargetInfo::validateCpuSupports(StringRef FeatureStr) const {
+#define PPC_FEATURE(NAME, DESC, ENUMNAME, ENUMVAL, HWCAPN) .Case(NAME, true)
+  return llvm::StringSwitch<bool>(FeatureStr)
+#include "llvm/TargetParser/PPCTargetParser.def"
+      .Default(false);
+}
+
+bool PPCTargetInfo::validateCpuIs(StringRef CPUName) const {
+#define PPC_CPU(NAME, NUM) .Case(NAME, true)
+  return llvm::StringSwitch<bool>(CPUName)
+#include "llvm/TargetParser/PPCTargetParser.def"
+      .Default(false);
+}
diff --git a/clang/lib/Basic/Targets/PPC.h b/clang/lib/Basic/Targets/PPC.h
index 4d62673ba7fb8c5..f700b625b790309 100644
--- a/clang/lib/Basic/Targets/PPC.h
+++ b/clang/lib/Basic/Targets/PPC.h
@@ -359,6 +359,13 @@ class LLVM_LIBRARY_VISIBILITY PPCTargetInfo : public TargetInfo {
   bool isSPRegName(StringRef RegName) const override {
     return RegName.equals("r1") || RegName.equals("x1");
   }
+
+  // We support __builtin_cpu_supports/__builtin_cpu_is on targets that
+  // have GLIBC since it is GLIBC that provides the HWCAP[2] in the auxv.
+  bool supportsCpuSupports() const override { return getTriple().isOSGlibc(); }
+  bool supportsCpuIs() const override { return getTriple().isOSGlibc(); }
+  bool validateCpuSupports(StringRef Feature) const override;
+  bool validateCpuIs(StringRef Name) const override;
 };
 
 class LLVM_LIBRARY_VISIBILITY PPC32TargetInfo : public PPCTargetInfo {
diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h
index 4fdc94de1e0cb4d..3b379715163a8a8 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -211,6 +211,10 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo {
     return RegName.equals("esp") || RegName.equals("rsp");
   }
 
+  bool supportsCpuSupports() const override { return true; }
+  bool supportsCpuIs() const override { return true; }
+  bool supportsCpuInit() const override { return true; }
+
   bool validateCpuSupports(StringRef FeatureStr) const override;
 
   bool validateCpuIs(StringRef FeatureStr) const override;
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 8cb7943df9a7822..583b2be69ba1664 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -13585,11 +13585,11 @@ CodeGenFunction::EmitAArch64CpuSupports(ArrayRef<StringRef> FeaturesStrs) {
 
 Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID,
                                            const CallExpr *E) {
-  if (BuiltinID == X86::BI__builtin_cpu_is)
+  if (BuiltinID == Builtin::BI__builtin_cpu_is)
     return EmitX86CpuIs(E);
-  if (BuiltinID == X86::BI__builtin_cpu_supports)
+  if (BuiltinID == Builtin::BI__builtin_cpu_supports)
     return EmitX86CpuSupports(E);
-  if (BuiltinID == X86::BI__builtin_cpu_init)
+  if (BuiltinID == Builtin::BI__builtin_cpu_init)
     return EmitX86CpuInit();
 
   // Handle MSVC intrinsics before argument evaluation to prevent double
@@ -16086,6 +16086,42 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID,
   switch (BuiltinID) {
   default: return nullptr;
 
+  case Builtin::BI__builtin_cpu_is: {
+    const Expr *CPUExpr = E->getArg(0)->IgnoreParenCasts();
+    StringRef CPUStr = cast<clang::StringLiteral>(CPUExpr)->getString();
+    unsigned NumCPUID = StringSwitch<unsigned>(CPUStr)
+#define PPC_CPU(Name, NumericID) .Case(Name, NumericID)
+#include "llvm/TargetParser/PPCTargetParser.def"
+                            .Default(-1U);
+    Value *Op0 =
+        llvm::ConstantInt::get(Int32Ty, PPC_FAWORD_CPUID);
+    llvm::Function *F = CGM.getIntrinsic(Intrinsic::ppc_fixed_addr_ld);
+    Value *TheCall = Builder.CreateCall(F, {Op0}, "cpu_is");
+    return Builder.CreateICmpEQ(TheCall,
+                                llvm::ConstantInt::get(Int32Ty, NumCPUID));
+  }
+  case Builtin::BI__builtin_cpu_supports: {
+    unsigned FeatureWord;
+    unsigned BitMask;
+    const Expr *CPUExpr = E->getArg(0)->IgnoreParenCasts();
+    StringRef CPUStr = cast<clang::StringLiteral>(CPUExpr)->getString();
+    std::tie(FeatureWord, BitMask) =
+        StringSwitch<std::pair<unsigned, unsigned>>(CPUStr)
+#define PPC_FEATURE(Name, Description, EnumName, Bitmask, FA_WORD)             \
+  .Case(Name, {FA_WORD, Bitmask})
+#include "llvm/TargetParser/PPCTargetParser.def"
+            .Default({0, 0});
+    Value *Op0 = llvm::ConstantInt::get(Int32Ty, FeatureWord);
+    llvm::Function *F = CGM.getIntrinsic(Intrinsic::ppc_fixed_addr_ld);
+    Value *TheCall = Builder.CreateCall(F, {Op0}, "cpu_supports");
+    Value *Mask =
+        Builder.CreateAnd(TheCall, llvm::ConstantInt::get(Int32Ty, BitMask));
+    return Builder.CreateICmpNE(Mask, llvm::Constant::getNullValue(Int32Ty));
+#undef PPC_FAWORD_HWCAP
+#undef PPC_FAWORD_HWCAP2
+#undef PPC_FAWORD_CPUID
+  }
+
   // __builtin_ppc_get_timebase is GCC 4.8+'s PowerPC-specific name for what we
   // call __builtin_readcyclecounter.
   case PPC::BI__builtin_ppc_get_timebase:
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 2594a8f97f7d94e..6e67f4edc9da32e 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -2110,6 +2110,66 @@ static bool checkFPMathBuiltinElementType(Sema &S, SourceLocation Loc,
   return false;
 }
 
+/// SemaBuiltinCpuSupports - Handle __builtin_cpu_supports(char *).
+/// This checks that the target supports __builtin_cpu_supports and
+/// that the string argument is constant and valid.
+static bool SemaBuiltinCpuSupports(Sema &S, const TargetInfo &TI,
+                                   const TargetInfo *AuxTI, CallExpr *TheCall) {
+  Expr *Arg = TheCall->getArg(0);
+
+  const TargetInfo *TheTI = nullptr;
+  if (TI.supportsCpuSupports())
+    TheTI = &TI;
+  else if (AuxTI && AuxTI->supportsCpuSupports())
+    TheTI = AuxTI;
+  else
+    return S.Diag(TheCall->getBeginLoc(), diag::err_builtin_target_unsupported)
+           << SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());
+
+  // Check if the argument is a string literal.
+  if (!isa<StringLiteral>(Arg->IgnoreParenImpCasts()))
+    return S.Diag(TheCall->getBeginLoc(), diag::err_expr_not_string_literal)
+           << Arg->getSourceRange();
+
+  // Check the contents of the string.
+  StringRef Feature =
+      cast<StringLiteral>(Arg->IgnoreParenImpCasts())->getString();
+  if (!TheTI->validateCpuSupports(Feature))
+    return S.Diag(TheCall->getBeginLoc(), diag::err_invalid_cpu_supports)
+           << Arg->getSourceRange();
+  return false;
+}
+
+/// SemaBuiltinCpuIs - Handle __builtin_cpu_is(char *).
+/// This checks that the target supports __builtin_cpu_is and
+/// that the string argument is constant and valid.
+static bool SemaBuiltinCpuIs(Sema &S, const TargetInfo &TI,
+                             const TargetInfo *AuxTI, CallExpr *TheCall) {
+  Expr *Arg = TheCall->getArg(0);
+
+  const TargetInfo *TheTI = nullptr;
+  if (TI.supportsCpuIs())
+    TheTI = &TI;
+  else if (AuxTI && AuxTI->supportsCpuIs())
+    TheTI = AuxTI;
+  else
+    return S.Diag(TheCall->getBeginLoc(), diag::err_builtin_target_unsupported)
+           << SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());
+
+  // Check if the argument is a string literal.
+  if (!isa<StringLiteral>(Arg->IgnoreParenImpCasts()))
+    return S.Diag(TheCall->getBeginLoc(), diag::err_expr_not_string_literal)
+           << Arg->getSourceRange();
+
+  // Check the contents of the string.
+  StringRef Feature =
+      cast<StringLiteral>(Arg->IgnoreParenImpCasts())->getString();
+  if (!TheTI->validateCpuIs(Feature))
+    return S.Diag(TheCall->getBeginLoc(), diag::err_invalid_cpu_is)
+           << Arg->getSourceRange();
+  return false;
+}
+
 ExprResult
 Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
                                CallExpr *TheCall) {
@@ -2137,6 +2197,23 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
   }
 
   switch (BuiltinID) {
+  case Builtin::BI__builtin_cpu_supports:
+    if (SemaBuiltinCpuSupports(*this, Context.getTargetInfo(),
+                               Context.getAuxTargetInfo(), TheCall))
+      return ExprError();
+    break;
+  case Builtin::BI__builtin_cpu_is:
+    if (SemaBuiltinCpuIs(*this, Context.getTargetInfo(),
+                         Context.getAuxTargetInfo(), TheCall))
+      return ExprError();
+    break;
+  case Builtin::BI__builtin_cpu_init:
+    if (!Context.getTargetInfo().supportsCpuInit()) {
+      Diag(TheCall->getBeginLoc(), diag::err_builtin_target_unsupported)
+          << SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());
+      return ExprError();
+    }
+    break;
   case Builtin::BI__builtin___CFStringMakeConstantString:
     // CFStringMakeConstantString is currently not implemented for GOFF (i.e.,
     // on z/OS) and for XCOFF (i.e., on AIX). Emit unsupported
@@ -5582,47 +5659,6 @@ bool Sema::CheckNVPTXBuiltinFunctionCall(const TargetInfo &TI,
   return false;
 }
 
-/// SemaBuiltinCpuSupports - Handle __builtin_cpu_supports(char *).
-/// This checks that the target supports __builtin_cpu_supports and
-/// that the string argument is constant and valid.
-static bool SemaBuiltinCpuSupports(Sema &S, const TargetInfo &TI,
-                                   CallExpr *TheCall) {
-  Expr *Arg = TheCall->getArg(0);
-
-  // Check if the argument is a string literal.
-  if (!isa<StringLiteral>(Arg->IgnoreParenImpCasts()))
-    return S.Diag(TheCall->getBeginLoc(), diag::err_expr_not_string_literal)
-           << Arg->getSourceRange();
-
-  // Check the contents of the string.
-  StringRef Feature =
-      cast<StringLiteral>(Arg->IgnoreParenImpCasts())->getString();
-  if (!TI.validateCpuSupports(Feature))
-    return S.Diag(TheCall->getBeginLoc(), diag::err_invalid_cpu_supports)
-           << Arg->getSourceRange();
-  return false;
-}
-
-/// SemaBuiltinCpuIs - Handle __builtin_cpu_is(char *).
-/// This checks that the target supports __builtin_cpu_is and
-/// that the string argument is constant and valid.
-static bool SemaBuiltinCpuIs(Sema &S, const TargetInfo &TI, CallExpr *TheCall) {
-  Expr *Arg = TheCall->getArg(0);
-
-  // Check if the argument is a string literal.
-  if (!isa<StringLiteral>(Arg->IgnoreParenImpCasts()))
-    return S.Diag(TheCall->getBeginLoc(), diag::err_expr_not_string_literal)
-           << Arg->getSourceRange();
-
-  // Check the contents of the string.
-  StringRef Feature =
-      cast<StringLiteral>(Arg->IgnoreParenImpCasts())->getString();
-  if (!TI.validateCpuIs(Feature))
-    return S.Diag(TheCall->getBeginLoc(), diag::err_invalid_cpu_is)
-           << Arg->getSourceRange();
-  return false;
-}
-
 // Check if the rounding mode is legal.
 bool Sema::CheckX86BuiltinRoundingOrSAE(unsigned BuiltinID, CallExpr *TheCall) {
   // Indicates if this instruction has rounding control or just SAE.
@@ -6097,12 +6133,6 @@ static bool isX86_32Builtin(unsigned BuiltinID) {
 
 bool Sema::CheckX86BuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID,
                                        CallExpr *TheCall) {
-  if (BuiltinID == X86::BI__builtin_cpu_supports)
-    return SemaBuiltinCpuSupports(*this, TI, TheCall);
-
-  if (BuiltinID == X86::BI__builtin_cpu_is)
-    return SemaBuiltinCpuIs(*this, TI, TheCall);
-
   // Check for 32-bit only builtins on a 64-bit target.
   const llvm::Triple &TT = TI.getTriple();
   if (TT.getArch() != llvm::Triple::x86 && isX86_32Builtin(BuiltinID))
diff --git a/clang/test/CodeGen/builtin-cpu-supports.c b/clang/test/CodeGen/builtin-cpu-supports.c
index 796611c1fcd9afd..42497d8845437a4 100644
--- a/clang/test/CodeGen/builtin-cpu-supports.c
+++ b/clang/test/CodeGen/builtin-cpu-supports.c
@@ -1,11 +1,16 @@
-// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm < %s| FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm < %s | FileCheck %s \
+// RUN:   --check-prefix=CHECK-X86
+// RUN: %clang_cc1 -triple ppc64le-linux-gnu -emit-llvm < %s | FileCheck %s \
+// RUN:   --check-prefix=CHECK-PPC
+
+#ifndef __PPC__
 
 // Test that we have the structure definition, the gep offsets, the name of the
 // global, the bit grab, and the icmp correct.
 extern void a(const char *);
 
-// CHECK: @__cpu_model = external dso_local global { i32, i32, i32, [1 x i32] }
-// CHECK: @__cpu_features2 = external dso_local global [3 x i32]
+// CHECK-X86: @__cpu_model = external dso_local global { i32, i32, i32, [1 x i32] }
+// CHECK-X86: @__cpu_features2 = external dso_local global [3 x i32]
 
 int main(void) {
   __builtin_cpu_init();
@@ -15,38 +20,57 @@ int main(void) {
   if (__builtin_cpu_supports("sse4.2"))
     a("sse4.2");
 
-  // CHECK: [[LOAD:%[^ ]+]] = load i32, ptr getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, ptr @__cpu_model, i32 0, i32 3, i32 0)
-  // CHECK: [[AND:%[^ ]+]] = and i32 [[LOAD]], 256
-  // CHECK: = icmp eq i32 [[AND]], 256
+  // CHECK-X86: [[LOAD:%[^ ]+]] = load i32, ptr getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, ptr @__cpu_model, i32 0, i32 3, i32 0)
+  // CHECK-X86: [[AND:%[^ ]+]] = and i32 [[LOAD]], 256
+  // CHECK-X86: = icmp eq i32 [[AND]], 256
 
   if (__builtin_cpu_supports("gfni"))
     a("gfni");
 
-  // CHECK: [[LOAD:%[^ ]+]] = load i32, ptr @__cpu_features2
-  // CHECK: [[AND:%[^ ]+]] = and i32 [[LOAD]], 1
-  // CHECK: = icmp eq i32 [[AND]], 1
+  // CHECK-X86: [[LOAD:%[^ ]+]] = load i32, ptr @__cpu_features2
+  // CHECK-X86: [[AND:%[^ ]+]] = and i32 [[LOAD]], 1
+  // CHECK-X86: = icmp eq i32 [[AND]], 1
 
   return 0;
 }
 
-// CHECK: declare dso_local void @__cpu_indicator_init()
+// CHECK-X86: declare dso_local void @__cpu_indicator_init()
 
-// CHECK-LABEL: define{{.*}} @baseline(
-// CHECK:         [[LOAD:%.*]] = load i32, ptr getelementptr inbounds ([[[#]] x i32], ptr @__cpu_features2, i32 0, i32 1)
-// CHECK-NEXT:    and i32 [[LOAD]], -2147483648
+// CHECK-X86-LABEL: define{{.*}} @baseline(
+// CHECK-X86:         [[LOAD:%.*]] = load i32, ptr getelementptr inbounds ([[[#]] x i32], ptr @__cpu_features2, i32 0, i32 1)
+// CHECK-X86-NEXT:    and i32 [[LOAD]], -2147483648
 int baseline() { return __builtin_cpu_supports("x86-64"); }
 
-// CHECK-LABEL: define{{.*}} @v2(
-// CHECK:         [[LOAD:%.*]] = load i32, ptr getelementptr inbounds ([[[#]] x i32], ptr @__cpu_features2, i32 0, i32 2)
-// CHECK-NEXT:    and i32 [[LOAD]], 1
+// CHECK-X86-LABEL: define{{.*}} @v2(
+// CHECK-X86:         [[LOAD:%.*]] = load i32, ptr getelementptr inbounds ([[[#]] x i32], ptr @__cpu_features2, i32 0, i32 2)
+// CHECK-X86-NEXT:    and i32 [[LOAD]], 1
 int v2() { return __builtin_cpu_supports("x86-64-v2"); }
 
-// CHECK-LABEL: define{{.*}} @v3(
-// CHECK:         [[LOAD:%.*]] = load i32, ptr getelementptr inbounds ([[[#]] x i32], ptr @__cpu_features2, i32 0, i32 2)
-// CHECK-NEXT:    and i32 [[LOAD]], 2
+// CHECK-X86-LABEL: define{{.*}} @v3(
+// CHECK-X86:         [[LOAD:%.*]] = load i32, ptr getelementptr inbounds ([[[#]] x i32], ptr @__cpu_features2, i32 0, i32 2)
+// CHECK-X86-NEXT:    and i32 [[LOAD]], 2
 int v3() { return __builtin_cpu_supports("x86-64-v3"); }
 
-// CHECK-LABEL: define{{.*}} @v4(
-// CHECK:         [[LOAD:%.*]] = load i32, ptr getelementptr inbounds ([[[#]] x i32], ptr @__cpu_features2, i32 0, i32 2)
-// CHECK-NEXT:    and i32 [[LOAD]], 4
+// CHECK-X86-LABEL: define{{.*}} @v4(
+// CHECK-X86:         [[LOAD:%.*]] = load i32, ptr getelementptr inbounds ([[[#]] x i32], ptr @__cpu_features2, i32 0, i32 2)
+// CHECK-X86-NEXT:    and i32 [[LOAD]], 4
 int v4() { return __builtin_cpu_supports("x86-64-v4"); }
+#else
+int test(int a) {
+// CHECK-PPC: [[CPUSUP:%[^ ]+]] = call i32 @llvm.ppc.fixed.addr.ld(i32 2)
+// CHECK-PPC: [[AND:%[^ ]+]] = and i32 [[CPUSUP]], 8388608
+// CHECK-PPC: icmp ne i32 [[AND]], 0
+// CHECK-PPC: [[CPUSUP2:%[^ ]+]] = call i32 @llvm.ppc.fixed.addr.ld(i32 1)
+// CHECK-PPC: [[AND2:%[^ ]+]] = and i32 [[CPUSUP2]], 67108864
+// CHECK-PPC: icmp ne i32 [[AND2]], 0
+// CHECK-PPC: [[CPUID:%[^ ]+]] = call i32 @llvm.ppc.fixed.addr.ld(i32 3)
+// CHECK-PPC: icmp eq i32 [[CPUID]], 39
+  if (__builtin_cpu_supports("arch_3_00")) // HWCAP2
+    return a;
+  else if (__builtin_cpu_supports("mmu"))  // HWCAP
+    return a - 5;
+  else if (__builtin_cpu_is("power7"))     // CPUID
+    return a + a;
+  return a + 5;
+}
+#endif
diff --git a/clang/test/Sema/builtin-cpu-supports.c b/clang/test/Sema/builtin-cpu-supports.c
index ad310128fecebf6..cc6f1beb5d8a7c4 100644
--- a/clang/test/Sema/builtin-cpu-supports.c
+++ b/clang/test/Sema/builtin-cpu-supports.c
@@ -1,5 +1,5 @@
 // RUN: %clang_cc1 -fsyntax-only -triple x86_64-pc-linux-gnu -verify %s
-// RUN: %clang_cc1 -fsyntax-only -triple powerpc64le-linux-gnu -verify %s
+// RUN: %clang_cc1 -fsyntax-only -triple aarch64-linux-gnu -verify %s
 
 extern void a(const char *);
 
@@ -27,11 +27,13 @@ int main(void) {
   (void)__builtin_cpu_supports("x86-64-v4");
   (void)__builtin_cpu_supports("x86-64-v5"); // expected-error {{invalid cpu feature string for builtin}}
 #else
-  if (__builtin_cpu_supports("vsx")) // expected-error {{use of unknown builtin}}
+  if (__builtin_cpu_supports("aes")) // expected-error {{builtin is not supported on this target}}
     a("vsx");
 
-  if (__builtin_cpu_is("pwr9")) // expected-error {{use of unknown builtin}}
+  if (__builtin_cpu_is("cortex-x3")) // expected-error {{builtin is not supported on this target}}
     a("pwr9");
+
+  __builtin_cpu_init(); // expected-error {{builtin is not supported on this target}}
 #endif
 
   return 0;
diff --git a/llvm/include/llvm/IR/IntrinsicsPowerPC.td b/llvm/include/llvm/IR/IntrinsicsPowerPC.td
index 3ede2a3736bf30d..f4e64764b4d1ccd 100644
--- a/llvm/include/llvm/IR/IntrinsicsPowerPC.td
+++ b/llvm/include/llvm/IR/IntrinsicsPowerPC.td
@@ -210,6 +210,12 @@ let TargetPrefix = "ppc" in {  // All intrinsics start with "llvm.ppc.".
             [llvm_float_ty],
             [llvm_float_ty, llvm_float_ty, llvm_float_ty, ll...
[truncated]

@github-actions
Copy link

github-actions bot commented Oct 12, 2023

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

Copy link
Contributor

@ilinpv ilinpv left a comment

Choose a reason for hiding this comment

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

Except for the AuxTarget question, target independent part looks good to me. Thank you for the patch.

Comment on lines 213 to 214
// Load of a value provided by the system library at a fixed address. Used for
// accessing things like HWCAP word provided by GLIBC.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Load of a value provided by the system library at a fixed address. Used for
// accessing things like HWCAP word provided by GLIBC.
// This intrinsic is provided to allow back ends to emit load
// instructions that load a value from a fixed address. The
// parameter to the intrinsic is not an address, but an
// immediate index into an enumeration that contains the
// union of all such values available on all back ends.
// An example is the HWCAP/HWCAP2/CPUID words
// provided by GLIBC on PowerPC to allow fast access
// to commonly used parts of AUXV. These are provided
// at a fixed offset into the TCB (accessible through the
// thread pointer).

Missing expanded comment from previous round of reviews in https://reviews.llvm.org/D152914

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think that comment would make sense any longer now that I have made this intrinsic PPC-specific. This is why I removed it and shortened it. I will put back the PPC-specific portions of the elaborated comment though.

@@ -0,0 +1,80 @@
#ifndef PPC_FEATURE
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing file description similar to the head of clang/include/clang/Basic/Builtins.def

Copy link
Member Author

Choose a reason for hiding this comment

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

Sounds good.

<< SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());

// Check if the argument is a string literal.
if (!isa<StringLiteral>(Arg->IgnoreParenImpCasts()))
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems like we can pull out some of the calls are are repeated, such as Arg->IgnoreParenImpCasts().

Copy link
Member Author

Choose a reason for hiding this comment

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

Done. In fact, it makes sense to point to the source location of the actual argument in the messages too, rather than the parens.

<< SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());

// Check if the argument is a string literal.
if (!isa<StringLiteral>(Arg->IgnoreParenImpCasts()))
Copy link
Contributor

Choose a reason for hiding this comment

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

Likewise here, perhaps we can pull out some of the calls that we call more than once.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yup.

Copy link
Contributor

@amy-kwan amy-kwan left a comment

Choose a reason for hiding this comment

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

Initial group code review comments.

/// This checks that the target supports __builtin_cpu_supports and
/// that the string argument is constant and valid.
static bool SemaBuiltinCpuSupports(Sema &S, const TargetInfo &TI,
const TargetInfo *AuxTI, CallExpr *TheCall) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Question:
Just wondering, what is and why are we checking an auxiliary target?

Copy link
Member Author

Choose a reason for hiding this comment

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

I believe this is for OpenMP when compiling for both the host and device. I had it in here because it was in the original code but seems to have been removed.

Copy link
Member Author

Choose a reason for hiding this comment

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

Turns out this is actually needed. There is a test that builds with -triple aarch64 -aux-triple x86_64. I will have to put the AUX target checks back.

Copy link
Contributor

@amy-kwan amy-kwan left a comment

Choose a reason for hiding this comment

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

Additional group code review comments.

<< Arg->getSourceRange();

// Check the contents of the string.
StringRef Feature =
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it more accurate to call this CPU instead of Feature here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure. But I ended up merging the two.

// The HWCAP and HWCAP2 word offsets are reversed on big endian Linux.
if ((FAType == PPC_FAWORD_HWCAP && Subtarget.isLittleEndian()) ||
(FAType == PPC_FAWORD_HWCAP2 && !Subtarget.isLittleEndian()))
Offset = Subtarget.isPPC64() ? -0x7064 : -0x703C;
Copy link
Contributor

@diggerlin diggerlin Nov 9, 2023

Choose a reason for hiding this comment

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

suggest do not use integer 0x7064 here, it is not easy to know what the 0x7064 stand for. please using a constexpr int Name=-0x7064 to explain what is the -0x7064 stand for.

Copy link
Member Author

Choose a reason for hiding this comment

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

That is a good point. These aren't externally documented- by Glibc, but they should be documented here in the code.

/// SemaBuiltinCpuSupports - Handle __builtin_cpu_supports(char *).
/// This checks that the target supports __builtin_cpu_supports and
/// that the string argument is constant and valid.
static bool SemaBuiltinCpuSupports(Sema &S, const TargetInfo &TI,
Copy link
Contributor

@diggerlin diggerlin Nov 14, 2023

Choose a reason for hiding this comment

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

most of the source code of function SemaBuiltinCpuSupports and SemaBuiltinCpuIs are same , we can add helper function to reduce the code. TI.supportsCpuSupports() and diag::err_invalid_cpu_supports can be argument of the helper function.

Copy link
Member Author

Choose a reason for hiding this comment

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

Makes sense.

Copy link
Contributor

@amy-kwan amy-kwan left a comment

Choose a reason for hiding this comment

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

Additional group review comments.

@nikic nikic removed their request for review November 15, 2023 11:02
Copy link
Contributor

@amy-kwan amy-kwan left a comment

Choose a reason for hiding this comment

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

Additional group review comments.

#else
int test(int a) {
// CHECK-PPC: [[CPUSUP:%[^ ]+]] = call i32 @llvm.ppc.fixed.addr.ld(i32 2)
// CHECK-PPC: [[AND:%[^ ]+]] = and i32 [[CPUSUP]], 8388608
Copy link
Contributor

Choose a reason for hiding this comment

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

change to CHECK-PPC-NEXT:

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure. I just re-generated the checks using the script.

int test(int a) {
// CHECK-PPC: [[CPUSUP:%[^ ]+]] = call i32 @llvm.ppc.fixed.addr.ld(i32 2)
// CHECK-PPC: [[AND:%[^ ]+]] = and i32 [[CPUSUP]], 8388608
// CHECK-PPC: icmp ne i32 [[AND]], 0
Copy link
Contributor

Choose a reason for hiding this comment

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

change to CHECK-PPC-NEXT:

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure. I just re-generated the checks using the script.

// CHECK-PPC: [[AND:%[^ ]+]] = and i32 [[CPUSUP]], 8388608
// CHECK-PPC: icmp ne i32 [[AND]], 0
// CHECK-PPC: [[CPUSUP2:%[^ ]+]] = call i32 @llvm.ppc.fixed.addr.ld(i32 1)
// CHECK-PPC: [[AND2:%[^ ]+]] = and i32 [[CPUSUP2]], 67108864
Copy link
Contributor

Choose a reason for hiding this comment

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

change to CHECK-PPC-NEXT:

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure. I just re-generated the checks using the script.

// CHECK-PPC: icmp ne i32 [[AND]], 0
// CHECK-PPC: [[CPUSUP2:%[^ ]+]] = call i32 @llvm.ppc.fixed.addr.ld(i32 1)
// CHECK-PPC: [[AND2:%[^ ]+]] = and i32 [[CPUSUP2]], 67108864
// CHECK-PPC: icmp ne i32 [[AND2]], 0
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure. I just re-generated the checks using the script.

// CHECK-PPC: [[AND2:%[^ ]+]] = and i32 [[CPUSUP2]], 67108864
// CHECK-PPC: icmp ne i32 [[AND2]], 0
// CHECK-PPC: [[CPUID:%[^ ]+]] = call i32 @llvm.ppc.fixed.addr.ld(i32 3)
// CHECK-PPC: icmp eq i32 [[CPUID]], 39
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure. I just re-generated the checks using the script.

@@ -0,0 +1,80 @@
#ifndef PPC_FEATURE
Copy link
Contributor

Choose a reason for hiding this comment

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

we will support these feature in AIX OS soon.
can we change PPC_FEATURE to PPC_LINUX_FEATURE

and PPC_CPU to PPC_LINUX_CPU

and when implement the __builtin_cpu_is and __builtin_cpu_supports in AIX OD

we can PPC_AIX_FEATURE and PPC_AIX_CPU

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't have an issue with that, assuming we will use the same mechanism on AIX.

#define PPC_LNX_FEATURE(Name, Description, EnumName, Bitmask, FA_WORD) \
.Case(Name, {FA_WORD, Bitmask})
#include "llvm/TargetParser/PPCTargetParser.def"
.Default({0, 0});
Copy link
Contributor

@diggerlin diggerlin Jan 12, 2024

Choose a reason for hiding this comment

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

Do we want a default here? without a default it will hit an assert here if fell off the end of the string-switch.
in the class of StringSwitch , there is function as

  [[nodiscard]] operator R() {
    assert(Result && "Fell off the end of a string-switch");
    return std::move(*Result);
  }

Copy link
Member Author

Choose a reason for hiding this comment

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

I think this is a good point. If we somehow have a string argument that would produce the default case and it has made it past Sema checking, it would be good to crash here rather than produce an invalid call to the intrinsic (or an invalid mask).
Of course, that assert is not all that friendly and it might be better to assert within this function.

What do you think about keeping the default and adding an assert below such as:

assert(BitMask && "Invalid target feature string. Missed by SemaChecking?");

Copy link
Contributor

@amy-kwan amy-kwan left a comment

Choose a reason for hiding this comment

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

I think I primarily have minor nit comments. Thank you for the update, Nemanja.

@@ -210,6 +210,15 @@ let TargetPrefix = "ppc" in { // All intrinsics start with "llvm.ppc.".
[llvm_float_ty],
[llvm_float_ty, llvm_float_ty, llvm_float_ty, llvm_vararg_ty],
[IntrNoMem]>;
// Load of a value provided by the system library at a fixed address. Used for
// accessing things like HWCAP word provided by GLIBC. The immediate argument
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor nits:

  • Should it be the HWCAP word?
  • We use GLIBC on this line but Glibc on line 217. I think it would be better if we were consistent and use one way of addressing GLIBC.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm fine with both of these suggestions.

Copy link
Contributor

@amy-kwan amy-kwan left a comment

Choose a reason for hiding this comment

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

I meant to approve this before but forgot. I think LGTM once the comments are addressed.

Nemanja Ivanovic and others added 5 commits January 26, 2024 07:32
Make __builtin_cpu_{init|supports|is} target independent and provide
an opt-in query for targets that want to support it. Each target is
still responsible for their specific lowering/code-gen.
Also provide code-gen for PowerPC.
- Use script for generating test checks
- Clean up code and rename macros for clarity
- Remove magic number offsets
- Get rid of auxiliary targets in Sema checking
- Clarify some comments and add missing ones
- Add check for undefined symbol added for safety
The way we define target-independent builtins has changed which
required a rebase. Also cleaned up some comments and improved
asserts as suggested in the review.
@nemanjai nemanjai force-pushed the nemanja/builtin-cpu branch from 4253c52 to b121664 Compare January 26, 2024 06:45
@lei137 lei137 merged commit 67c1c1d into llvm:main Jan 26, 2024
@lei137
Copy link
Contributor

lei137 commented Jan 30, 2024 via email

@nemanjai nemanjai deleted the nemanja/builtin-cpu branch January 30, 2024 20:29
@nemanjai
Copy link
Member Author

Sorry, I'll put up a patch for review shortly to fix this problem.

@nemanjai
Copy link
Member Author

Fix in #80058

ilinpv added a commit to ilinpv/llvm-project that referenced this pull request Feb 20, 2024
The patch complements llvm#68919
and adds AArch64 support for builtin
__builtin_cpu_supports("feature1+...+featureN")
which return true if all specified CPU features in argument are
detected. Also compiler-rt aarch64 native run tests for features
detection mechanism were added and 'cpu_model' check was fixed after its
refactor merged llvm#75635
Original RFC was https://reviews.llvm.org/D153153
ilinpv added a commit to ilinpv/llvm-project that referenced this pull request Feb 22, 2024
The patch complements llvm#68919
and adds AArch64 support for builtin
__builtin_cpu_supports("feature1+...+featureN")
which return true if all specified CPU features in argument are
detected. Also compiler-rt aarch64 native run tests for features
detection mechanism were added and 'cpu_model' check was fixed after its
refactor merged llvm#75635
Original RFC was https://reviews.llvm.org/D153153
ilinpv added a commit that referenced this pull request Feb 22, 2024
The patch complements #68919
and adds AArch64 support for builtin
`__builtin_cpu_supports("feature1+...+featureN")`
which return true if all specified CPU features in argument are
detected. Also compiler-rt aarch64 native run tests for features
detection mechanism were added and 'cpu_model' check was fixed after its
refactor merged #75635 Original
RFC was https://reviews.llvm.org/D153153
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:PowerPC backend:X86 clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category llvm:ir
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants