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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
7 changes: 0 additions & 7 deletions clang/include/clang/Basic/BuiltinsX86.def
Original file line number Diff line number Diff line change
Expand Up @@ -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:", "")
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -1432,6 +1432,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; }
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/Basic/Targets/PPC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -878,3 +878,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);
}
7 changes: 7 additions & 0 deletions clang/lib/Basic/Targets/PPC.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Basic/Targets/X86.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,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;
Expand Down
43 changes: 40 additions & 3 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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});
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?");

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:
Expand Down
102 changes: 55 additions & 47 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -6256,47 +6311,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.
Expand Down Expand Up @@ -6771,12 +6785,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))
Expand Down
Loading