Skip to content

Commit

Permalink
[AIX] Lower intrinsic __builtin_cpu_is into AIX platform-specific cod…
Browse files Browse the repository at this point in the history
…e. (#80069)

On AIX OS, __builtin_cpu_is() references the runtime external variable
_system_configuration from /usr/include/sys/systemcfg.h.

ref issue:  #80042
  • Loading branch information
diggerlin committed Feb 22, 2024
1 parent 770fd38 commit 5b8e560
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 2 deletions.
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -10364,6 +10364,8 @@ def err_x86_builtin_tile_arg_duplicate : Error<

def err_builtin_target_unsupported : Error<
"builtin is not supported on this target">;
def err_builtin_aix_os_unsupported : Error<
"this builtin is available only on AIX 7.2 and later operating systems">;
def err_builtin_longjmp_unsupported : Error<
"__builtin_longjmp is not supported for the current target">;
def err_builtin_setjmp_unsupported : Error<
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/Basic/Targets/PPC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,16 @@ bool PPCTargetInfo::validateCpuSupports(StringRef FeatureStr) const {
}

bool PPCTargetInfo::validateCpuIs(StringRef CPUName) const {
llvm::Triple Triple = getTriple();
if (Triple.isOSAIX()) {
#define PPC_AIX_CPU(NAME, SUPPORT, INDEX, OP, VALUE) .Case(NAME, true)
return llvm::StringSwitch<bool>(CPUName)
#include "llvm/TargetParser/PPCTargetParser.def"
.Default(false);
}

assert(Triple.isOSLinux() &&
"__builtin_cpu_is() is only supported for AIX and Linux.");
#define PPC_LNX_CPU(NAME, NUM) .Case(NAME, true)
return llvm::StringSwitch<bool>(CPUName)
#include "llvm/TargetParser/PPCTargetParser.def"
Expand Down
10 changes: 9 additions & 1 deletion clang/lib/Basic/Targets/PPC.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,16 @@ class LLVM_LIBRARY_VISIBILITY PPCTargetInfo : public TargetInfo {

// 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.
static constexpr int MINIMUM_AIX_OS_MAJOR = 7;
static constexpr int MINIMUM_AIX_OS_MINOR = 2;
bool supportsCpuSupports() const override { return getTriple().isOSGlibc(); }
bool supportsCpuIs() const override { return getTriple().isOSGlibc(); }
bool supportsCpuIs() const override {
llvm::Triple Triple = getTriple();
// AIX 7.2 is the minimum requirement to support __builtin_cpu_is().
return Triple.isOSGlibc() ||
(Triple.isOSAIX() &&
!Triple.isOSVersionLT(MINIMUM_AIX_OS_MAJOR, MINIMUM_AIX_OS_MINOR));
}
bool validateCpuSupports(StringRef Feature) const override;
bool validateCpuIs(StringRef Name) const override;
};
Expand Down
47 changes: 47 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16542,12 +16542,59 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID,

Intrinsic::ID ID = Intrinsic::not_intrinsic;

#include "llvm/TargetParser/PPCTargetParser.def"
auto GenAIXPPCBuiltinCpuExpr = [&](unsigned SupportMethod, unsigned FieldIdx,
unsigned CompOp,
unsigned OpValue) -> Value * {
if (SupportMethod == AIX_BUILTIN_PPC_FALSE)
return llvm::ConstantInt::getFalse(ConvertType(E->getType()));

if (SupportMethod == AIX_BUILTIN_PPC_TRUE)
return llvm::ConstantInt::getTrue(ConvertType(E->getType()));

assert(SupportMethod <= USE_SYS_CONF && "Invalid value for SupportMethod.");
assert((CompOp == COMP_EQ) && "Only equal comparisons are supported.");

llvm::Type *STy = llvm::StructType::get(PPC_SYSTEMCONFIG_TYPE);
llvm::Constant *SysConf =
CGM.CreateRuntimeVariable(STy, "_system_configuration");

// Grab the appropriate field from _system_configuration.
llvm::Value *Idxs[] = {ConstantInt::get(Int32Ty, 0),
ConstantInt::get(Int32Ty, FieldIdx)};

llvm::Value *FieldValue = Builder.CreateGEP(STy, SysConf, Idxs);
FieldValue = Builder.CreateAlignedLoad(Int32Ty, FieldValue,
CharUnits::fromQuantity(4));
assert(FieldValue->getType()->isIntegerTy(32) &&
"Only 32-bit integers are supported in GenAIXPPCBuiltinCpuExpr().");
return Builder.CreateICmp(ICmpInst::ICMP_EQ, FieldValue,
ConstantInt::get(Int32Ty, OpValue));
};

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();
llvm::Triple Triple = getTarget().getTriple();

if (Triple.isOSAIX()) {
unsigned IsCpuSupport, FieldIdx, CompareOp, CpuIdValue;
typedef std::tuple<unsigned, unsigned, unsigned, unsigned> CPUType;
std::tie(IsCpuSupport, FieldIdx, CompareOp, CpuIdValue) =
static_cast<CPUType>(StringSwitch<CPUType>(CPUStr)
#define PPC_AIX_CPU(NAME, SUPPORT_MAGIC, INDEX, COMPARE_OP, VALUE) \
.Case(NAME, {SUPPORT_MAGIC, INDEX, COMPARE_OP, VALUE})
#include "llvm/TargetParser/PPCTargetParser.def"
);
return GenAIXPPCBuiltinCpuExpr(IsCpuSupport, FieldIdx, CompareOp,
CpuIdValue);
}

assert(Triple.isOSLinux() &&
"__builtin_cpu_is() is only supported for AIX and Linux.");
unsigned NumCPUID = StringSwitch<unsigned>(CPUStr)
#define PPC_LNX_CPU(Name, NumericID) .Case(Name, NumericID)
#include "llvm/TargetParser/PPCTargetParser.def"
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2165,7 +2165,10 @@ static bool SemaBuiltinCpu(Sema &S, const TargetInfo &TI, CallExpr *TheCall,
return S.Diag(TheCall->getBeginLoc(), diag::err_builtin_target_unsupported)
<< SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());
if (!IsCPUSupports && !TheTI->supportsCpuIs())
return S.Diag(TheCall->getBeginLoc(), diag::err_builtin_target_unsupported)
return S.Diag(TheCall->getBeginLoc(),
TI.getTriple().isOSAIX()
? diag::err_builtin_aix_os_unsupported
: diag::err_builtin_target_unsupported)
<< SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());

Expr *Arg = TheCall->getArg(0)->IgnoreParenImpCasts();
Expand Down
71 changes: 71 additions & 0 deletions clang/test/CodeGen/aix-builtin-cpu-is.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// RUN: echo "int main() { return __builtin_cpu_is(\"ppc970\");}" > %t.c
// RUN: %clang_cc1 -triple powerpc-ibm-aix7.2.0.0 -emit-llvm -o - %t.c | FileCheck %s

// RUN: echo "int main() { return __builtin_cpu_is(\"ppc-cell-be\");}" > %t.c
// RUN: %clang_cc1 -triple powerpc-ibm-aix7.2.0.0 -emit-llvm -o - %t.c | FileCheck %s

// RUN: echo "int main() { return __builtin_cpu_is(\"ppca2\");}" > %t.c
// RUN: %clang_cc1 -triple powerpc-ibm-aix7.2.0.0 -emit-llvm -o - %t.c | FileCheck %s

// RUN: echo "int main() { return __builtin_cpu_is(\"ppc405\");}" > %t.c
// RUN: %clang_cc1 -triple powerpc-ibm-aix7.2.0.0 -emit-llvm -o - %t.c | FileCheck %s

// RUN: echo "int main() { return __builtin_cpu_is(\"ppc440\");}" > %t.c
// RUN: %clang_cc1 -triple powerpc-ibm-aix7.2.0.0 -emit-llvm -o - %t.c | FileCheck %s

// RUN: echo "int main() { return __builtin_cpu_is(\"ppc464\");}" > %t.c
// RUN: %clang_cc1 -triple powerpc-ibm-aix7.2.0.0 -emit-llvm -o - %t.c | FileCheck %s

// RUN: echo "int main() { return __builtin_cpu_is(\"ppc476\");}" > %t.c
// RUN: %clang_cc1 -triple powerpc-ibm-aix7.2.0.0 -emit-llvm -o - %t.c | FileCheck %s

// RUN: echo "int main() { return __builtin_cpu_is(\"power4\");}" > %t.c
// RUN: %clang_cc1 -triple powerpc-ibm-aix7.2.0.0 -emit-llvm -o - %t.c | FileCheck %s

// RUN: echo "int main() { return __builtin_cpu_is(\"power5\");}" > %t.c
// RUN: %clang_cc1 -triple powerpc-ibm-aix7.2.0.0 -emit-llvm -o - %t.c | FileCheck %s

// RUN: echo "int main() { return __builtin_cpu_is(\"power5+\");}" > %t.c
// RUN: %clang_cc1 -triple powerpc-ibm-aix7.2.0.0 -emit-llvm -o - %t.c | FileCheck %s

// RUN: echo "int main() { return __builtin_cpu_is(\"power6\");}" > %t.c
// RUN: %clang_cc1 -triple powerpc-ibm-aix7.2.0.0 -emit-llvm -o - %t.c | FileCheck %s

// RUN: echo "int main() { return __builtin_cpu_is(\"power6x\");}" > %t.c
// RUN: %clang_cc1 -triple powerpc-ibm-aix7.2.0.0 -emit-llvm -o - %t.c | FileCheck %s

// RUN: echo "int main() { return __builtin_cpu_is(\"power7\");}" > %t.c
// RUN: %clang_cc1 -triple powerpc-ibm-aix7.2.0.0 -emit-llvm -o - %t.c | FileCheck %s -DVALUE=32768 \
// RUN: --check-prefix=CHECKOP

// RUN: echo "int main() { return __builtin_cpu_is(\"power8\");}" > %t.c
// RUN: %clang_cc1 -triple powerpc-ibm-aix7.2.0.0 -emit-llvm -o - %t.c | FileCheck %s -DVALUE=65536 \
// RUN: --check-prefix=CHECKOP

// RUN: echo "int main() { return __builtin_cpu_is(\"power9\");}" > %t.c
// RUN: %clang_cc1 -triple powerpc-ibm-aix7.2.0.0 -emit-llvm -o - %t.c | FileCheck %s -DVALUE=131072\
// RUN: --check-prefix=CHECKOP

// RUN: echo "int main() { return __builtin_cpu_is(\"power10\");}" > %t.c
// RUN: %clang_cc1 -triple powerpc-ibm-aix7.2.0.0 -emit-llvm -o - %t.c | FileCheck %s -DVALUE=262144 \
// RUN: --check-prefix=CHECKOP

// CHECK: define i32 @main() #0 {
// CHECK-NEXT: entry:
// CHECK-NEXT: %retval = alloca i32, align 4
// CHECK-NEXT: store i32 0, ptr %retval, align 4
// CHECK-NEXT: ret i32 0
// CHECK-NEXT: }

// CHECKOP: @_system_configuration = external global { i32, i32, i32 }
// CHECKOP: define i32 @main() #0 {
// CHECKOP-NEXT: entry:
// CHECKOP-NEXT: %retval = alloca i32, align 4
// CHECKOP-NEXT: store i32 0, ptr %retval, align 4
// CHECKOP-NEXT: %0 = load i32, ptr getelementptr inbounds ({ i32, i32, i32 }, ptr @_system_configuration, i32 0, i32 1), align 4
// CHECKOP-NEXT: %1 = icmp eq i32 %0, [[VALUE]]
// CHECKOP-NEXT: %conv = zext i1 %1 to i32
// CHECKOP-NEXT: ret i32 %conv
// CHECKOP-NEXT: }


6 changes: 6 additions & 0 deletions clang/test/Sema/aix-builtin-cpu-unsupports.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// RUN: %clang_cc1 -fsyntax-only -triple powerpc-ibm-aix7.1.0.0 -verify %s

int main(void) {
if (__builtin_cpu_is("power8")) // expected-error {{this builtin is available only on AIX 7.2 and later operating systems}}
return 1;
}
57 changes: 57 additions & 0 deletions llvm/include/llvm/TargetParser/PPCTargetParser.def
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,61 @@ PPC_LNX_CPU("power10",47)
#undef PPC_LNX_DEFINE_OFFSETS
#undef PPC_LNX_FEATURE
#undef PPC_LNX_CPU

// Definition of the following values are found in the AIX header
// file: </usr/include/sys/systemcfg.h>.
#ifndef AIX_POWERPC_USE_SYS_CONF
#define AIX_POWERPC_USE_SYS_CONF
#define AIX_SYSCON_IMPL_IDX 1
#define AIX_PPC7_VALUE 0x00008000
#define AIX_PPC8_VALUE 0x00010000
#define AIX_PPC9_VALUE 0x00020000
#define AIX_PPC10_VALUE 0x00040000

// Supported SUPPORT_METHOD values.
#define AIX_BUILTIN_PPC_TRUE 1
#define AIX_BUILTIN_PPC_FALSE 0
#define USE_SYS_CONF 2

// Supported COMPARE_OP values.
#define COMP_EQ 0

#endif

// The value of SUPPORT_METHOD can be AIX_BUILTIN_PPC_TRUE,
// AIX_BUILTIN_PPC_FALSE, or USE_SYS_CONF.
// When the value of SUPPORT_METHOD is USE_SYS_CONF, the return value
// depends on the result of comparing the data member of
// _system_configuration specified by INDEX with a certain value.

#ifndef PPC_AIX_CPU
#define PPC_AIX_CPU(NAME, SUPPORT_METHOD, INDEX, COMPARE_OP, VALUE)
#endif

// __builtin_cpu_is() is supported only on Power7 and up.
PPC_AIX_CPU("power4",AIX_BUILTIN_PPC_FALSE,0,0,0)
PPC_AIX_CPU("ppc970",AIX_BUILTIN_PPC_FALSE,0,0,0)
PPC_AIX_CPU("power5",AIX_BUILTIN_PPC_FALSE,0,0,0)
PPC_AIX_CPU("power5+",AIX_BUILTIN_PPC_FALSE,0,0,0)
PPC_AIX_CPU("power6",AIX_BUILTIN_PPC_FALSE,0,0,0)
PPC_AIX_CPU("ppc-cell-be",AIX_BUILTIN_PPC_FALSE,0,0,0)
PPC_AIX_CPU("power6x",AIX_BUILTIN_PPC_FALSE,0,0,0)
PPC_AIX_CPU("ppca2",AIX_BUILTIN_PPC_FALSE,0,0,0)
PPC_AIX_CPU("ppc405",AIX_BUILTIN_PPC_FALSE,0,0,0)
PPC_AIX_CPU("ppc440",AIX_BUILTIN_PPC_FALSE,0,0,0)
PPC_AIX_CPU("ppc464",AIX_BUILTIN_PPC_FALSE,0,0,0)
PPC_AIX_CPU("ppc476",AIX_BUILTIN_PPC_FALSE,0,0,0)
PPC_AIX_CPU("power7",USE_SYS_CONF,AIX_SYSCON_IMPL_IDX,COMP_EQ,AIX_PPC7_VALUE)
PPC_AIX_CPU("power8",USE_SYS_CONF,AIX_SYSCON_IMPL_IDX,COMP_EQ,AIX_PPC8_VALUE)
PPC_AIX_CPU("power9",USE_SYS_CONF,AIX_SYSCON_IMPL_IDX,COMP_EQ,AIX_PPC9_VALUE)
PPC_AIX_CPU("power10",USE_SYS_CONF,AIX_SYSCON_IMPL_IDX,COMP_EQ,AIX_PPC10_VALUE)
#undef PPC_AIX_CPU

// PPC_SYSTEMCONFIG_TYPE defines the IR data structure of kernel variable
// `_system_configuration`, that is found in the AIX OS header file: </usr/include/sys/systemcfg.h>.
#ifndef PPC_SYSTEMCONFIG_TYPE
#define PPC_SYSTEMCONFIG_TYPE \
Int32Ty, Int32Ty, Int32Ty
#endif

#endif // !PPC_TGT_PARSER_UNDEF_MACROS

0 comments on commit 5b8e560

Please sign in to comment.