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

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.

---------

Co-authored-by: Nemanja Ivanovic <nemanjaivanovic@nemanjas-air.kpn>
Co-authored-by: Nemanja Ivanovic <nemanja@synopsys.com>
This commit is contained in:
Nemanja Ivanovic
2024-01-26 17:24:50 +01:00
committed by GitHub
parent c177507bd2
commit 67c1c1dbb6
17 changed files with 589 additions and 82 deletions

View File

@@ -727,6 +727,26 @@ def RotateRight : BitInt8_16_32_64BuiltinsTemplate, Builtin {
// FIXME: The builtins marked FunctionWithBuiltinPrefix below should be
// merged with the library definitions. They are currently not because
// the attributes are different.
// Builtins for checking CPU features based on the GCC builtins.
def BuiltinCPUIs : Builtin {
let Spellings = ["__builtin_cpu_is"];
let Attributes = [NoThrow, Const];
let Prototype = "bool(char const*)";
}
def BuiltinCPUSupports : Builtin {
let Spellings = ["__builtin_cpu_supports"];
let Attributes = [NoThrow, Const];
let Prototype = "bool(char const*)";
}
def BuiltinCPUInit : Builtin {
let Spellings = ["__builtin_cpu_init"];
let Attributes = [NoThrow];
let Prototype = "void()";
}
def BuiltinCalloc : Builtin {
let Spellings = ["__builtin_calloc"];
let Attributes = [FunctionWithBuiltinPrefix, NoThrow];

View File

@@ -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:", "")

View File

@@ -1432,6 +1432,12 @@ public:
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; }

View File

@@ -903,3 +903,17 @@ ArrayRef<Builtin::Info> PPCTargetInfo::getTargetBuiltins() const {
return llvm::ArrayRef(BuiltinInfo,
clang::PPC::LastTSBuiltin - Builtin::FirstTSBuiltin);
}
bool PPCTargetInfo::validateCpuSupports(StringRef FeatureStr) const {
#define PPC_LNX_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_LNX_CPU(NAME, NUM) .Case(NAME, true)
return llvm::StringSwitch<bool>(CPUName)
#include "llvm/TargetParser/PPCTargetParser.def"
.Default(false);
}

View File

@@ -359,6 +359,13 @@ public:
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 {

View File

@@ -220,6 +220,10 @@ public:
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;

View File

@@ -14053,11 +14053,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
@@ -16545,6 +16545,43 @@ 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_LNX_CPU(Name, NumericID) .Case(Name, NumericID)
#include "llvm/TargetParser/PPCTargetParser.def"
.Default(-1U);
assert(NumCPUID < -1U && "Invalid CPU name. Missed by SemaChecking?");
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_LNX_FEATURE(Name, Description, EnumName, Bitmask, FA_WORD) \
.Case(Name, {FA_WORD, Bitmask})
#include "llvm/TargetParser/PPCTargetParser.def"
.Default({0, 0});
assert(BitMask && "Invalid target feature string. Missed by SemaChecking?");
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:

View File

@@ -2143,6 +2143,48 @@ static bool checkFPMathBuiltinElementType(Sema &S, SourceLocation Loc,
return false;
}
/// SemaBuiltinCpu{Supports|Is} - Handle __builtin_cpu_{supports|is}(char *).
/// This checks that the target supports the builtin and that the string
/// argument is constant and valid.
static bool SemaBuiltinCpu(Sema &S, const TargetInfo &TI, CallExpr *TheCall,
const TargetInfo *AuxTI, unsigned BuiltinID) {
assert((BuiltinID == Builtin::BI__builtin_cpu_supports ||
BuiltinID == Builtin::BI__builtin_cpu_is) &&
"Expecting __builtin_cpu_...");
bool IsCPUSupports = BuiltinID == Builtin::BI__builtin_cpu_supports;
const TargetInfo *TheTI = &TI;
auto SupportsBI = [=](const TargetInfo *TInfo) {
return TInfo && ((IsCPUSupports && TInfo->supportsCpuSupports()) ||
(!IsCPUSupports && TInfo->supportsCpuIs()));
};
if (!SupportsBI(&TI) && SupportsBI(AuxTI))
TheTI = AuxTI;
if (IsCPUSupports && !TheTI->supportsCpuSupports())
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)
<< SourceRange(TheCall->getBeginLoc(), TheCall->getEndLoc());
Expr *Arg = TheCall->getArg(0)->IgnoreParenImpCasts();
// Check if the argument is a string literal.
if (!isa<StringLiteral>(Arg))
return S.Diag(TheCall->getBeginLoc(), diag::err_expr_not_string_literal)
<< Arg->getSourceRange();
// Check the contents of the string.
StringRef Feature = cast<StringLiteral>(Arg)->getString();
if (IsCPUSupports && !TheTI->validateCpuSupports(Feature))
return S.Diag(TheCall->getBeginLoc(), diag::err_invalid_cpu_supports)
<< Arg->getSourceRange();
if (!IsCPUSupports && !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) {
@@ -2171,6 +2213,19 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
FPOptions FPO;
switch (BuiltinID) {
case Builtin::BI__builtin_cpu_supports:
case Builtin::BI__builtin_cpu_is:
if (SemaBuiltinCpu(*this, Context.getTargetInfo(), TheCall,
Context.getAuxTargetInfo(), BuiltinID))
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
@@ -6216,47 +6271,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.
@@ -6731,12 +6745,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))

View File

@@ -1,12 +1,42 @@
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm < %s| FileCheck %s
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s | \
// RUN: FileCheck %s --check-prefix=CHECK-X86
// RUN: %clang_cc1 -triple ppc64le-linux-gnu -emit-llvm -o - %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-LABEL: define dso_local i32 @main(
// CHECK-X86-SAME: ) #[[ATTR0:[0-9]+]] {
// CHECK-X86-NEXT: entry:
// CHECK-X86-NEXT: [[RETVAL:%.*]] = alloca i32, align 4
// CHECK-X86-NEXT: store i32 0, ptr [[RETVAL]], align 4
// CHECK-X86-NEXT: call void @__cpu_indicator_init()
// CHECK-X86-NEXT: [[TMP0:%.*]] = load i32, ptr getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, ptr @__cpu_model, i32 0, i32 3, i32 0), align 4
// CHECK-X86-NEXT: [[TMP1:%.*]] = and i32 [[TMP0]], 256
// CHECK-X86-NEXT: [[TMP2:%.*]] = icmp eq i32 [[TMP1]], 256
// CHECK-X86-NEXT: [[TMP3:%.*]] = and i1 true, [[TMP2]]
// CHECK-X86-NEXT: br i1 [[TMP3]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
// CHECK-X86: if.then:
// CHECK-X86-NEXT: call void @a(ptr noundef @.str)
// CHECK-X86-NEXT: br label [[IF_END]]
// CHECK-X86: if.end:
// CHECK-X86-NEXT: [[TMP4:%.*]] = load i32, ptr @__cpu_features2, align 4
// CHECK-X86-NEXT: [[TMP5:%.*]] = and i32 [[TMP4]], 1
// CHECK-X86-NEXT: [[TMP6:%.*]] = icmp eq i32 [[TMP5]], 1
// CHECK-X86-NEXT: [[TMP7:%.*]] = and i1 true, [[TMP6]]
// CHECK-X86-NEXT: br i1 [[TMP7]], label [[IF_THEN1:%.*]], label [[IF_END2:%.*]]
// CHECK-X86: if.then1:
// CHECK-X86-NEXT: call void @a(ptr noundef @.str.1)
// CHECK-X86-NEXT: br label [[IF_END2]]
// CHECK-X86: if.end2:
// CHECK-X86-NEXT: ret i32 0
//
int main(void) {
__builtin_cpu_init();
@@ -15,38 +45,117 @@ 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
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
return 0;
}
// CHECK: 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 dso_local i32 @baseline(
// CHECK-X86-SAME: ) #[[ATTR0]] {
// CHECK-X86-NEXT: entry:
// CHECK-X86-NEXT: [[TMP0:%.*]] = load i32, ptr getelementptr inbounds ([3 x i32], ptr @__cpu_features2, i32 0, i32 1), align 4
// CHECK-X86-NEXT: [[TMP1:%.*]] = and i32 [[TMP0]], -2147483648
// CHECK-X86-NEXT: [[TMP2:%.*]] = icmp eq i32 [[TMP1]], -2147483648
// CHECK-X86-NEXT: [[TMP3:%.*]] = and i1 true, [[TMP2]]
// CHECK-X86-NEXT: [[CONV:%.*]] = zext i1 [[TMP3]] to i32
// CHECK-X86-NEXT: ret i32 [[CONV]]
//
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 dso_local i32 @v2(
// CHECK-X86-SAME: ) #[[ATTR0]] {
// CHECK-X86-NEXT: entry:
// CHECK-X86-NEXT: [[TMP0:%.*]] = load i32, ptr getelementptr inbounds ([3 x i32], ptr @__cpu_features2, i32 0, i32 2), align 4
// CHECK-X86-NEXT: [[TMP1:%.*]] = and i32 [[TMP0]], 1
// CHECK-X86-NEXT: [[TMP2:%.*]] = icmp eq i32 [[TMP1]], 1
// CHECK-X86-NEXT: [[TMP3:%.*]] = and i1 true, [[TMP2]]
// CHECK-X86-NEXT: [[CONV:%.*]] = zext i1 [[TMP3]] to i32
// CHECK-X86-NEXT: ret i32 [[CONV]]
//
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 dso_local i32 @v3(
// CHECK-X86-SAME: ) #[[ATTR0]] {
// CHECK-X86-NEXT: entry:
// CHECK-X86-NEXT: [[TMP0:%.*]] = load i32, ptr getelementptr inbounds ([3 x i32], ptr @__cpu_features2, i32 0, i32 2), align 4
// CHECK-X86-NEXT: [[TMP1:%.*]] = and i32 [[TMP0]], 2
// CHECK-X86-NEXT: [[TMP2:%.*]] = icmp eq i32 [[TMP1]], 2
// CHECK-X86-NEXT: [[TMP3:%.*]] = and i1 true, [[TMP2]]
// CHECK-X86-NEXT: [[CONV:%.*]] = zext i1 [[TMP3]] to i32
// CHECK-X86-NEXT: ret i32 [[CONV]]
//
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 dso_local i32 @v4(
// CHECK-X86-SAME: ) #[[ATTR0]] {
// CHECK-X86-NEXT: entry:
// CHECK-X86-NEXT: [[TMP0:%.*]] = load i32, ptr getelementptr inbounds ([3 x i32], ptr @__cpu_features2, i32 0, i32 2), align 4
// CHECK-X86-NEXT: [[TMP1:%.*]] = and i32 [[TMP0]], 4
// CHECK-X86-NEXT: [[TMP2:%.*]] = icmp eq i32 [[TMP1]], 4
// CHECK-X86-NEXT: [[TMP3:%.*]] = and i1 true, [[TMP2]]
// CHECK-X86-NEXT: [[CONV:%.*]] = zext i1 [[TMP3]] to i32
// CHECK-X86-NEXT: ret i32 [[CONV]]
//
int v4() { return __builtin_cpu_supports("x86-64-v4"); }
#else
// CHECK-PPC-LABEL: define dso_local signext i32 @test(
// CHECK-PPC-SAME: i32 noundef signext [[A:%.*]]) #[[ATTR0:[0-9]+]] {
// CHECK-PPC-NEXT: entry:
// CHECK-PPC-NEXT: [[RETVAL:%.*]] = alloca i32, align 4
// CHECK-PPC-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4
// CHECK-PPC-NEXT: store i32 [[A]], ptr [[A_ADDR]], align 4
// CHECK-PPC-NEXT: [[CPU_SUPPORTS:%.*]] = call i32 @llvm.ppc.fixed.addr.ld(i32 2)
// CHECK-PPC-NEXT: [[TMP0:%.*]] = and i32 [[CPU_SUPPORTS]], 8388608
// CHECK-PPC-NEXT: [[TMP1:%.*]] = icmp ne i32 [[TMP0]], 0
// CHECK-PPC-NEXT: br i1 [[TMP1]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
// CHECK-PPC: if.then:
// CHECK-PPC-NEXT: [[TMP2:%.*]] = load i32, ptr [[A_ADDR]], align 4
// CHECK-PPC-NEXT: store i32 [[TMP2]], ptr [[RETVAL]], align 4
// CHECK-PPC-NEXT: br label [[RETURN:%.*]]
// CHECK-PPC: if.else:
// CHECK-PPC-NEXT: [[CPU_SUPPORTS1:%.*]] = call i32 @llvm.ppc.fixed.addr.ld(i32 1)
// CHECK-PPC-NEXT: [[TMP3:%.*]] = and i32 [[CPU_SUPPORTS1]], 67108864
// CHECK-PPC-NEXT: [[TMP4:%.*]] = icmp ne i32 [[TMP3]], 0
// CHECK-PPC-NEXT: br i1 [[TMP4]], label [[IF_THEN2:%.*]], label [[IF_ELSE3:%.*]]
// CHECK-PPC: if.then2:
// CHECK-PPC-NEXT: [[TMP5:%.*]] = load i32, ptr [[A_ADDR]], align 4
// CHECK-PPC-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP5]], 5
// CHECK-PPC-NEXT: store i32 [[SUB]], ptr [[RETVAL]], align 4
// CHECK-PPC-NEXT: br label [[RETURN]]
// CHECK-PPC: if.else3:
// CHECK-PPC-NEXT: [[CPU_IS:%.*]] = call i32 @llvm.ppc.fixed.addr.ld(i32 3)
// CHECK-PPC-NEXT: [[TMP6:%.*]] = icmp eq i32 [[CPU_IS]], 39
// CHECK-PPC-NEXT: br i1 [[TMP6]], label [[IF_THEN4:%.*]], label [[IF_END:%.*]]
// CHECK-PPC: if.then4:
// CHECK-PPC-NEXT: [[TMP7:%.*]] = load i32, ptr [[A_ADDR]], align 4
// CHECK-PPC-NEXT: [[TMP8:%.*]] = load i32, ptr [[A_ADDR]], align 4
// CHECK-PPC-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP7]], [[TMP8]]
// CHECK-PPC-NEXT: store i32 [[ADD]], ptr [[RETVAL]], align 4
// CHECK-PPC-NEXT: br label [[RETURN]]
// CHECK-PPC: if.end:
// CHECK-PPC-NEXT: br label [[IF_END5:%.*]]
// CHECK-PPC: if.end5:
// CHECK-PPC-NEXT: br label [[IF_END6:%.*]]
// CHECK-PPC: if.end6:
// CHECK-PPC-NEXT: [[TMP9:%.*]] = load i32, ptr [[A_ADDR]], align 4
// CHECK-PPC-NEXT: [[ADD7:%.*]] = add nsw i32 [[TMP9]], 5
// CHECK-PPC-NEXT: store i32 [[ADD7]], ptr [[RETVAL]], align 4
// CHECK-PPC-NEXT: br label [[RETURN]]
// CHECK-PPC: return:
// CHECK-PPC-NEXT: [[TMP10:%.*]] = load i32, ptr [[RETVAL]], align 4
// CHECK-PPC-NEXT: ret i32 [[TMP10]]
//
int test(int a) {
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

View File

@@ -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;

View File

@@ -215,6 +215,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 the HWCAP word provided by Glibc. The immediate
// argument is not an address but a value defined in
// include/llvm/TargetParser/PPCTargetParser.def. Each of the values provided
// by Glibc is a 32-bit word.
def int_ppc_fixed_addr_ld
: DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_i32_ty],
[IntrInaccessibleMemOnly, ImmArg<ArgIndex<0>>]>;
}
let TargetPrefix = "ppc" in { // All PPC intrinsics start with "llvm.ppc.".

View File

@@ -0,0 +1,129 @@
//===- PPCTargetParser.def - PPC target parsing defines ---------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file provides defines to build up the PPC target parser's logic.
//
//===----------------------------------------------------------------------===//
// NOTE: NO INCLUDE GUARD DESIRED!
#ifdef PPC_TGT_PARSER_UNDEF_MACROS
#undef PPC_LNX_FEATURE
#undef PPC_LNX_CPU
#undef PPC_FAWORD_HWCAP
#undef PPC_FAWORD_HWCAP2
#undef PPC_FAWORD_CPUID
#undef PPC_HWCAP_OFFSET_LE32
#undef PPC_HWCAP_OFFSET_LE64
#undef PPC_HWCAP_OFFSET_BE32
#undef PPC_HWCAP_OFFSET_BE64
#undef PPC_HWCAP2_OFFSET_LE32
#undef PPC_HWCAP2_OFFSET_LE64
#undef PPC_HWCAP2_OFFSET_BE32
#undef PPC_HWCAP2_OFFSET_BE64
#undef PPC_CPUID_OFFSET_LE32
#undef PPC_CPUID_OFFSET_LE64
#undef PPC_CPUID_OFFSET_BE32
#undef PPC_CPUID_OFFSET_BE64
#else
#ifndef PPC_LNX_FEATURE
#define PPC_LNX_FEATURE(NAME, DESC, ENUMNAME, ENUMVAL, HWCAPN)
#endif
#ifndef PPC_LNX_CPU
#define PPC_LNX_CPU(NAME, NUM)
#endif
#ifndef PPC_FAWORD_HWCAP
#define PPC_FAWORD_HWCAP 1
#endif
#ifndef PPC_FAWORD_HWCAP2
#define PPC_FAWORD_HWCAP2 2
#endif
#ifndef PPC_FAWORD_CPUID
#define PPC_FAWORD_CPUID 3
#endif
// PPC_LNX_FEATURE(Name, Description, EnumName, BitMask, PPC_FAWORD_WORD)
PPC_LNX_FEATURE("4xxmac","4xx CPU has a Multiply Accumulator",PPCF_4XXMAC,0x02000000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("altivec","CPU has a SIMD/Vector Unit",PPCF_ALTIVEC,0x10000000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("arch_2_05","CPU supports ISA 205 (eg, POWER6)",PPCF_ARCH205,0x00001000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("arch_2_06","CPU supports ISA 206 (eg, POWER7)",PPCF_ARCH206,0x00000100,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("arch_2_07","CPU supports ISA 207 (eg, POWER8)",PPCF_ARCH207,0x80000000,PPC_FAWORD_HWCAP2)
PPC_LNX_FEATURE("arch_3_00","CPU supports ISA 30 (eg, POWER9)",PPCF_ARCH30,0x00800000,PPC_FAWORD_HWCAP2)
PPC_LNX_FEATURE("arch_3_1","CPU supports ISA 31 (eg, POWER10)",PPCF_ARCH31,0x00040000,PPC_FAWORD_HWCAP2)
PPC_LNX_FEATURE("archpmu","CPU supports the set of compatible performance monitoring events",PPCF_ARCHPMU,0x00000040,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("booke","CPU supports the Embedded ISA category",PPCF_BOOKE,0x00008000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("cellbe","CPU has a CELL broadband engine",PPCF_CELLBE,0x00010000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("darn","CPU supports the darn (deliver a random number) instruction",PPCF_DARN,0x00200000,PPC_FAWORD_HWCAP2)
PPC_LNX_FEATURE("dfp","CPU has a decimal floating point unit",PPCF_DFP,0x00000400,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("dscr","CPU supports the data stream control register",PPCF_DSCR,0x20000000,PPC_FAWORD_HWCAP2)
PPC_LNX_FEATURE("ebb","CPU supports event base branching",PPCF_EBB,0x10000000,PPC_FAWORD_HWCAP2)
PPC_LNX_FEATURE("efpdouble","CPU has a SPE double precision floating point unit",PPCF_EFPDOUBLE,0x00200000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("efpsingle","CPU has a SPE single precision floating point unit",PPCF_EFPSINGLE,0x00400000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("fpu","CPU has a floating point unit",PPCF_FPU,0x08000000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("htm","CPU has hardware transaction memory instructions",PPCF_HTM,0x40000000,PPC_FAWORD_HWCAP2)
PPC_LNX_FEATURE("htm-nosc","Kernel aborts hardware transactions when a syscall is made",PPCF_HTM_NOSC,0x01000000,PPC_FAWORD_HWCAP2)
PPC_LNX_FEATURE("htm-no-suspend","CPU supports hardware transaction memory but does not support the tsuspend instruction.",PPCF_HTM_NO_SUSPEND,0x00080000,PPC_FAWORD_HWCAP2)
PPC_LNX_FEATURE("ic_snoop","CPU supports icache snooping capabilities",PPCF_IC_SNOOP,0x00002000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("ieee128","CPU supports 128-bit IEEE binary floating point instructions",PPCF_IEEE128,0x00400000,PPC_FAWORD_HWCAP2)
PPC_LNX_FEATURE("isel","CPU supports the integer select instruction",PPCF_ISEL,0x08000000,PPC_FAWORD_HWCAP2)
PPC_LNX_FEATURE("mma","CPU supports the matrix-multiply assist instructions",PPCF_MMA,0x00020000,PPC_FAWORD_HWCAP2)
PPC_LNX_FEATURE("mmu","CPU has a memory management unit",PPCF_MMU,0x04000000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("notb","CPU does not have a timebase (eg, 601 and 403gx)",PPCF_NOTB,0x00100000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("pa6t","CPU supports the PA Semi 6T CORE ISA",PPCF_PA6T,0x00000800,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("power4","CPU supports ISA 200 (eg, POWER4)",PPCF_POWER4,0x00080000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("power5","CPU supports ISA 202 (eg, POWER5)",PPCF_POWER5,0x00040000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("power5+","CPU supports ISA 203 (eg, POWER5+)",PPCF_POWER5P,0x00020000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("power6x","CPU supports ISA 205 (eg, POWER6) extended opcodes mffgpr and mftgpr.",PPCF_POWER6X,0x00000200,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("ppc32","CPU supports 32-bit mode execution",PPCF_PPC32,0x80000000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("ppc601","CPU supports the old POWER ISA (eg, 601)",PPCF_PPC601,0x20000000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("ppc64","CPU supports 64-bit mode execution",PPCF_PPC64,0x40000000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("ppcle","CPU supports a little-endian mode that uses address swizzling",PPCF_PPCLE,0x00000001,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("scv","Kernel supports system call vectored",PPCF_SCV,0x00100000,PPC_FAWORD_HWCAP2)
PPC_LNX_FEATURE("smt","CPU support simultaneous multi-threading",PPCF_SMT,0x00004000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("spe","CPU has a signal processing extension unit",PPCF_SPE,0x00800000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("tar","CPU supports the target address register",PPCF_TAR,0x04000000,PPC_FAWORD_HWCAP2)
PPC_LNX_FEATURE("true_le","CPU supports true little-endian mode",PPCF_TRUE_LE,0x00000002,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("ucache","CPU has unified I/D cache",PPCF_UCACHE,0x01000000,PPC_FAWORD_HWCAP)
PPC_LNX_FEATURE("vcrypto","CPU supports the vector cryptography instructions",PPCF_VCRYPTO,0x02000000,PPC_FAWORD_HWCAP2)
PPC_LNX_FEATURE("vsx","CPU supports the vector-scalar extension",PPCF_VSX,0x00000080,PPC_FAWORD_HWCAP)
// PPC_LNX_CPU(Name, NumericID)
PPC_LNX_CPU("power4",32)
PPC_LNX_CPU("ppc970",33)
PPC_LNX_CPU("power5",34)
PPC_LNX_CPU("power5+",35)
PPC_LNX_CPU("power6",36)
PPC_LNX_CPU("ppc-cell-be",37)
PPC_LNX_CPU("power6x",38)
PPC_LNX_CPU("power7",39)
PPC_LNX_CPU("ppca2",40)
PPC_LNX_CPU("ppc405",41)
PPC_LNX_CPU("ppc440",42)
PPC_LNX_CPU("ppc464",43)
PPC_LNX_CPU("ppc476",44)
PPC_LNX_CPU("power8",45)
PPC_LNX_CPU("power9",46)
PPC_LNX_CPU("power10",47)
#ifdef PPC_LNX_DEFINE_OFFSETS
# define PPC_HWCAP_OFFSET_LE32 -0x703C
# define PPC_HWCAP_OFFSET_LE64 -0x7064
# define PPC_HWCAP_OFFSET_BE32 -0x7040
# define PPC_HWCAP_OFFSET_BE64 -0x7068
# define PPC_HWCAP2_OFFSET_LE32 -0x7040
# define PPC_HWCAP2_OFFSET_LE64 -0x7068
# define PPC_HWCAP2_OFFSET_BE32 -0x703C
# define PPC_HWCAP2_OFFSET_BE64 -0x7064
# define PPC_CPUID_OFFSET_LE32 -0x7034
# define PPC_CPUID_OFFSET_LE64 -0x705C
# define PPC_CPUID_OFFSET_BE32 -0x7034
# define PPC_CPUID_OFFSET_BE64 -0x705C
#endif
#undef PPC_LNX_DEFINE_OFFSETS
#undef PPC_LNX_FEATURE
#undef PPC_LNX_CPU
#endif // !PPC_TGT_PARSER_UNDEF_MACROS

View File

@@ -1821,6 +1821,14 @@ void PPCLinuxAsmPrinter::emitEndOfAsmFile(Module &M) {
PPCTargetStreamer *TS =
static_cast<PPCTargetStreamer *>(OutStreamer->getTargetStreamer());
// If we are using any values provided by Glibc at fixed addresses,
// we need to ensure that the Glibc used at link time actually provides
// those values. All versions of Glibc that do will define the symbol
// named "__parse_hwcap_and_convert_at_platform".
if (static_cast<const PPCTargetMachine &>(TM).hasGlibcHWCAPAccess())
OutStreamer->emitSymbolValue(
GetExternalSymbolSymbol("__parse_hwcap_and_convert_at_platform"),
MAI->getCodePointerSize());
emitGNUAttributes(M);
if (!TOC.empty()) {

View File

@@ -1079,6 +1079,7 @@ bool PPCInstrInfo::isReallyTriviallyReMaterializable(
case PPC::ADDIStocHA8:
case PPC::ADDItocL:
case PPC::LOAD_STACK_GUARD:
case PPC::PPCLdFixedAddr:
case PPC::XXLXORz:
case PPC::XXLXORspz:
case PPC::XXLXORdpz:
@@ -3099,6 +3100,46 @@ bool PPCInstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
.addReg(Reg);
return true;
}
case PPC::PPCLdFixedAddr: {
assert(Subtarget.getTargetTriple().isOSGlibc() &&
"Only targets with Glibc expected to contain PPCLdFixedAddr");
int64_t Offset = 0;
const unsigned Reg = Subtarget.isPPC64() ? PPC::X13 : PPC::R2;
MI.setDesc(get(PPC::LWZ));
uint64_t FAType = MI.getOperand(1).getImm();
#undef PPC_LNX_FEATURE
#undef PPC_LNX_CPU
#define PPC_LNX_DEFINE_OFFSETS
#include "llvm/TargetParser/PPCTargetParser.def"
bool IsLE = Subtarget.isLittleEndian();
bool Is64 = Subtarget.isPPC64();
if (FAType == PPC_FAWORD_HWCAP) {
if (IsLE)
Offset = Is64 ? PPC_HWCAP_OFFSET_LE64 : PPC_HWCAP_OFFSET_LE32;
else
Offset = Is64 ? PPC_HWCAP_OFFSET_BE64 : PPC_HWCAP_OFFSET_BE32;
} else if (FAType == PPC_FAWORD_HWCAP2) {
if (IsLE)
Offset = Is64 ? PPC_HWCAP2_OFFSET_LE64 : PPC_HWCAP2_OFFSET_LE32;
else
Offset = Is64 ? PPC_HWCAP2_OFFSET_BE64 : PPC_HWCAP2_OFFSET_BE32;
} else if (FAType == PPC_FAWORD_CPUID) {
if (IsLE)
Offset = Is64 ? PPC_CPUID_OFFSET_LE64 : PPC_CPUID_OFFSET_LE32;
else
Offset = Is64 ? PPC_CPUID_OFFSET_BE64 : PPC_CPUID_OFFSET_BE32;
}
assert(Offset && "Do not know the offset for this fixed addr load");
MI.removeOperand(1);
Subtarget.getTargetMachine().setGlibcHWCAPAccess();
MachineInstrBuilder(*MI.getParent()->getParent(), MI)
.addImm(Offset)
.addReg(Reg);
return true;
#define PPC_TGT_PARSER_UNDEF_MACROS
#include "llvm/TargetParser/PPCTargetParser.def"
#undef PPC_TGT_PARSER_UNDEF_MACROS
}
case PPC::DFLOADf32:
case PPC::DFLOADf64:
case PPC::DFSTOREf32:

View File

@@ -4830,6 +4830,9 @@ def RLWNMbm : PPCAsmPseudo<"rlwnm $rA, $rS, $n, $b",
(ins g8rc:$rA, g8rc:$rS, u5imm:$n, i32imm:$b)>;
def RLWNMbm_rec : PPCAsmPseudo<"rlwnm. $rA, $rS, $n, $b",
(ins g8rc:$rA, g8rc:$rS, u5imm:$n, i32imm:$b)>;
def PPCLdFixedAddr :
PPCPostRAExpPseudo<(outs gprc:$rT), (ins i32imm:$imm), "#FA_LOAD",
[(set i32:$rT, (int_ppc_fixed_addr_ld timm:$imm))]>;
// These generic branch instruction forms are used for the assembler parser only.
// Defs and Uses are conservative, since we don't know the BO value.

View File

@@ -32,6 +32,7 @@ private:
std::unique_ptr<TargetLoweringObjectFile> TLOF;
PPCABI TargetABI;
Endian Endianness = Endian::NOT_DETECTED;
mutable bool HasGlibcHWCAPAccess = false;
mutable StringMap<std::unique_ptr<PPCSubtarget>> SubtargetMap;
@@ -64,6 +65,8 @@ public:
const TargetSubtargetInfo *STI) const override;
bool isELFv2ABI() const { return TargetABI == PPC_ABI_ELFv2; }
bool hasGlibcHWCAPAccess() const { return HasGlibcHWCAPAccess; }
void setGlibcHWCAPAccess(bool Val = true) const { HasGlibcHWCAPAccess = Val; }
bool isPPC64() const {
const Triple &TT = getTargetTriple();
return (TT.getArch() == Triple::ppc64 || TT.getArch() == Triple::ppc64le);

View File

@@ -0,0 +1,114 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2
; RUN: llc -mcpu=pwr9 -ppc-asm-full-reg-names \
; RUN: -mtriple=powerpc64-linux-gnu < %s | FileCheck %s \
; RUN: -check-prefix=BE64
; RUN: llc -mcpu=pwr9 -ppc-asm-full-reg-names \
; RUN: -mtriple=powerpc-linux-gnu < %s | FileCheck %s \
; RUN: -check-prefix=BE32
; RUN: llc -mcpu=pwr9 -ppc-asm-full-reg-names \
; RUN: -mtriple=powerpc64le-linux-gnu < %s | FileCheck %s \
; RUN: -check-prefix=LE
define dso_local signext i32 @test(i32 noundef signext %a) local_unnamed_addr #0 {
; BE64-LABEL: test:
; BE64: # %bb.0: # %entry
; BE64-NEXT: lwz r4, -28772(r13)
; BE64-NEXT: andis. r4, r4, 128
; BE64-NEXT: bne cr0, .LBB0_3
; BE64-NEXT: # %bb.1: # %if.else
; BE64-NEXT: lwz r4, -28776(r13)
; BE64-NEXT: andis. r4, r4, 1024
; BE64-NEXT: bne cr0, .LBB0_4
; BE64-NEXT: # %bb.2: # %if.else3
; BE64-NEXT: lwz r4, -28764(r13)
; BE64-NEXT: cmplwi r4, 39
; BE64-NEXT: addi r4, r3, 5
; BE64-NEXT: slwi r3, r3, 1
; BE64-NEXT: iseleq r3, r3, r4
; BE64-NEXT: .LBB0_3: # %return
; BE64-NEXT: extsw r3, r3
; BE64-NEXT: blr
; BE64-NEXT: .LBB0_4: # %if.then2
; BE64-NEXT: addi r3, r3, -5
; BE64-NEXT: extsw r3, r3
; BE64-NEXT: blr
; BE64: .quad __parse_hwcap_and_convert_at_platform
;
; BE32-LABEL: test:
; BE32: # %bb.0: # %entry
; BE32-NEXT: lwz r4, -28732(r2)
; BE32-NEXT: andis. r4, r4, 128
; BE32-NEXT: bnelr cr0
; BE32-NEXT: # %bb.1: # %if.else
; BE32-NEXT: lwz r4, -28736(r2)
; BE32-NEXT: andis. r4, r4, 1024
; BE32-NEXT: bne cr0, .LBB0_3
; BE32-NEXT: # %bb.2: # %if.else3
; BE32-NEXT: lwz r4, -28724(r2)
; BE32-NEXT: cmplwi r4, 39
; BE32-NEXT: addi r4, r3, 5
; BE32-NEXT: slwi r3, r3, 1
; BE32-NEXT: iseleq r3, r3, r4
; BE32-NEXT: blr
; BE32-NEXT: .LBB0_3: # %if.then2
; BE32-NEXT: addi r3, r3, -5
; BE32-NEXT: blr
; BE32: .long __parse_hwcap_and_convert_at_platform
;
; LE-LABEL: test:
; LE: # %bb.0: # %entry
; LE-NEXT: lwz r4, -28776(r13)
; LE-NEXT: andis. r4, r4, 128
; LE-NEXT: bne cr0, .LBB0_3
; LE-NEXT: # %bb.1: # %if.else
; LE-NEXT: lwz r4, -28772(r13)
; LE-NEXT: andis. r4, r4, 1024
; LE-NEXT: bne cr0, .LBB0_4
; LE-NEXT: # %bb.2: # %if.else3
; LE-NEXT: lwz r4, -28764(r13)
; LE-NEXT: cmplwi r4, 39
; LE-NEXT: addi r4, r3, 5
; LE-NEXT: slwi r3, r3, 1
; LE-NEXT: iseleq r3, r3, r4
; LE-NEXT: .LBB0_3: # %return
; LE-NEXT: extsw r3, r3
; LE-NEXT: blr
; LE-NEXT: .LBB0_4: # %if.then2
; LE-NEXT: addi r3, r3, -5
; LE-NEXT: extsw r3, r3
; LE-NEXT: blr
; LE: .quad __parse_hwcap_and_convert_at_platform
entry:
%cpu_supports = tail call i32 @llvm.ppc.fixed.addr.ld(i32 2)
%0 = and i32 %cpu_supports, 8388608
%.not = icmp eq i32 %0, 0
br i1 %.not, label %if.else, label %return
if.else: ; preds = %entry
%cpu_supports1 = tail call i32 @llvm.ppc.fixed.addr.ld(i32 1)
%1 = and i32 %cpu_supports1, 67108864
%.not12 = icmp eq i32 %1, 0
br i1 %.not12, label %if.else3, label %if.then2
if.then2: ; preds = %if.else
%sub = add nsw i32 %a, -5
br label %return
if.else3: ; preds = %if.else
%cpu_is = tail call i32 @llvm.ppc.fixed.addr.ld(i32 3)
%2 = icmp eq i32 %cpu_is, 39
br i1 %2, label %if.then4, label %if.end6
if.then4: ; preds = %if.else3
%add = shl nsw i32 %a, 1
br label %return
if.end6: ; preds = %if.else3
%add7 = add nsw i32 %a, 5
br label %return
return: ; preds = %entry, %if.end6, %if.then4, %if.then2
%retval.0 = phi i32 [ %sub, %if.then2 ], [ %add, %if.then4 ], [ %add7, %if.end6 ], [ %a, %entry ]
ret i32 %retval.0
}
declare i32 @llvm.ppc.fixed.addr.ld(i32 immarg) #1