430 lines · cpp
1//===--- Builtins.cpp - Builtin function implementation -------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements various things for builtin functions.10//11//===----------------------------------------------------------------------===//12 13#include "clang/Basic/Builtins.h"14#include "BuiltinTargetFeatures.h"15#include "clang/Basic/IdentifierTable.h"16#include "clang/Basic/LangOptions.h"17#include "clang/Basic/TargetInfo.h"18#include "llvm/ADT/StringRef.h"19using namespace clang;20 21const char *HeaderDesc::getName() const {22 switch (ID) {23#define HEADER(ID, NAME) \24 case ID: \25 return NAME;26#include "clang/Basic/BuiltinHeaders.def"27#undef HEADER28 };29 llvm_unreachable("Unknown HeaderDesc::HeaderID enum");30}31 32static constexpr unsigned NumBuiltins = Builtin::FirstTSBuiltin;33 34#define GET_BUILTIN_STR_TABLE35#include "clang/Basic/Builtins.inc"36#undef GET_BUILTIN_STR_TABLE37 38static constexpr Builtin::Info BuiltinInfos[] = {39 Builtin::Info{}, // No-builtin info entry.40#define GET_BUILTIN_INFOS41#include "clang/Basic/Builtins.inc"42#undef GET_BUILTIN_INFOS43};44static_assert(std::size(BuiltinInfos) == NumBuiltins);45 46std::pair<const Builtin::InfosShard &, const Builtin::Info &>47Builtin::Context::getShardAndInfo(unsigned ID) const {48 assert((ID < (Builtin::FirstTSBuiltin + NumTargetBuiltins +49 NumAuxTargetBuiltins)) &&50 "Invalid builtin ID!");51 52 ArrayRef<InfosShard> Shards = BuiltinShards;53 if (isAuxBuiltinID(ID)) {54 Shards = AuxTargetShards;55 ID = getAuxBuiltinID(ID) - Builtin::FirstTSBuiltin;56 } else if (ID >= Builtin::FirstTSBuiltin) {57 Shards = TargetShards;58 ID -= Builtin::FirstTSBuiltin;59 }60 61 // Loop over the shards to find the one matching this ID. We don't expect to62 // have many shards and so its better to search linearly than with a binary63 // search.64 for (const auto &Shard : Shards) {65 if (ID < Shard.Infos.size()) {66 return {Shard, Shard.Infos[ID]};67 }68 69 ID -= Shard.Infos.size();70 }71 llvm_unreachable("Invalid target builtin shard structure!");72}73 74std::string Builtin::Info::getName(const Builtin::InfosShard &Shard) const {75 return (Twine(Shard.NamePrefix) + (*Shard.Strings)[Offsets.Name]).str();76}77 78/// Return the identifier name for the specified builtin,79/// e.g. "__builtin_abs".80std::string Builtin::Context::getName(unsigned ID) const {81 const auto &[Shard, I] = getShardAndInfo(ID);82 return I.getName(Shard);83}84 85std::string Builtin::Context::getQuotedName(unsigned ID) const {86 const auto &[Shard, I] = getShardAndInfo(ID);87 return (Twine("'") + Shard.NamePrefix + (*Shard.Strings)[I.Offsets.Name] +88 "'")89 .str();90}91 92const char *Builtin::Context::getTypeString(unsigned ID) const {93 const auto &[Shard, I] = getShardAndInfo(ID);94 return (*Shard.Strings)[I.Offsets.Type].data();95}96 97const char *Builtin::Context::getAttributesString(unsigned ID) const {98 const auto &[Shard, I] = getShardAndInfo(ID);99 return (*Shard.Strings)[I.Offsets.Attributes].data();100}101 102const char *Builtin::Context::getRequiredFeatures(unsigned ID) const {103 const auto &[Shard, I] = getShardAndInfo(ID);104 return (*Shard.Strings)[I.Offsets.Features].data();105}106 107Builtin::Context::Context() : BuiltinShards{{&BuiltinStrings, BuiltinInfos}} {}108 109void Builtin::Context::InitializeTarget(const TargetInfo &Target,110 const TargetInfo *AuxTarget) {111 assert(TargetShards.empty() && "Already initialized target?");112 assert(NumTargetBuiltins == 0 && "Already initialized target?");113 TargetShards = Target.getTargetBuiltins();114 for (const auto &Shard : TargetShards)115 NumTargetBuiltins += Shard.Infos.size();116 if (AuxTarget) {117 AuxTargetShards = AuxTarget->getTargetBuiltins();118 for (const auto &Shard : AuxTargetShards)119 NumAuxTargetBuiltins += Shard.Infos.size();120 }121}122 123bool Builtin::Context::isBuiltinFunc(llvm::StringRef FuncName) {124 bool InStdNamespace = FuncName.consume_front("std-");125 for (const auto &Shard : {InfosShard{&BuiltinStrings, BuiltinInfos}})126 if (llvm::StringRef FuncNameSuffix = FuncName;127 FuncNameSuffix.consume_front(Shard.NamePrefix))128 for (const auto &I : Shard.Infos)129 if (FuncNameSuffix == (*Shard.Strings)[I.Offsets.Name] &&130 (bool)strchr((*Shard.Strings)[I.Offsets.Attributes].data(), 'z') ==131 InStdNamespace)132 return strchr((*Shard.Strings)[I.Offsets.Attributes].data(), 'f') !=133 nullptr;134 135 return false;136}137 138/// Is this builtin supported according to the given language options?139static bool builtinIsSupported(const llvm::StringTable &Strings,140 const Builtin::Info &BuiltinInfo,141 const LangOptions &LangOpts) {142 auto AttributesStr = Strings[BuiltinInfo.Offsets.Attributes];143 144 /* Builtins Unsupported */145 if (LangOpts.NoBuiltin && strchr(AttributesStr.data(), 'f') != nullptr)146 return false;147 /* CorBuiltins Unsupported */148 if (!LangOpts.Coroutines && (BuiltinInfo.Langs & COR_LANG))149 return false;150 /* MathBuiltins Unsupported */151 if (LangOpts.NoMathBuiltin && BuiltinInfo.Header.ID == HeaderDesc::MATH_H)152 return false;153 /* GnuMode Unsupported */154 if (!LangOpts.GNUMode && (BuiltinInfo.Langs & GNU_LANG))155 return false;156 /* MSMode Unsupported */157 if (!LangOpts.MicrosoftExt && (BuiltinInfo.Langs & MS_LANG))158 return false;159 /* HLSLMode Unsupported */160 if (!LangOpts.HLSL && (BuiltinInfo.Langs & HLSL_LANG))161 return false;162 /* ObjC Unsupported */163 if (!LangOpts.ObjC && BuiltinInfo.Langs == OBJC_LANG)164 return false;165 /* OpenCLC Unsupported */166 if (!LangOpts.OpenCL && (BuiltinInfo.Langs & ALL_OCL_LANGUAGES))167 return false;168 /* OpenCL GAS Unsupported */169 if (!LangOpts.OpenCLGenericAddressSpace && (BuiltinInfo.Langs & OCL_GAS))170 return false;171 /* OpenCL Pipe Unsupported */172 if (!LangOpts.OpenCLPipes && (BuiltinInfo.Langs & OCL_PIPE))173 return false;174 175 // Device side enqueue is not supported until OpenCL 2.0. In 2.0 and higher176 // support is indicated with language option for blocks.177 178 /* OpenCL DSE Unsupported */179 if ((LangOpts.getOpenCLCompatibleVersion() < 200 || !LangOpts.Blocks) &&180 (BuiltinInfo.Langs & OCL_DSE))181 return false;182 /* OpenMP Unsupported */183 if (!LangOpts.OpenMP && BuiltinInfo.Langs == OMP_LANG)184 return false;185 /* CUDA Unsupported */186 if (!LangOpts.CUDA && BuiltinInfo.Langs == CUDA_LANG)187 return false;188 /* CPlusPlus Unsupported */189 if (!LangOpts.CPlusPlus && BuiltinInfo.Langs == CXX_LANG)190 return false;191 /* consteval Unsupported */192 if (!LangOpts.CPlusPlus20 && strchr(AttributesStr.data(), 'G') != nullptr)193 return false;194 /* C23 unsupported */195 if (!LangOpts.C23 && BuiltinInfo.Langs == C23_LANG)196 return false;197 return true;198}199 200static bool isBuiltinConstForTriple(unsigned BuiltinID, llvm::Triple Trip) {201 // There's a special case with the fma builtins where they are always const202 // if the target environment is GNU or the target is OS is Windows and we're203 // targeting the MSVCRT.dll environment.204 // FIXME: This list can be become outdated. Need to find a way to get it some205 // other way.206 switch (BuiltinID) {207 case Builtin::BI__builtin_fma:208 case Builtin::BI__builtin_fmaf:209 case Builtin::BI__builtin_fmal:210 case Builtin::BI__builtin_fmaf16:211 case Builtin::BIfma:212 case Builtin::BIfmaf:213 case Builtin::BIfmal: {214 if (Trip.isGNUEnvironment() || Trip.isOSMSVCRT())215 return true;216 break;217 }218 default:219 break;220 }221 222 return false;223}224 225bool Builtin::Context::shouldGenerateFPMathIntrinsic(226 unsigned BuiltinID, llvm::Triple Trip, std::optional<bool> ErrnoOverwritten,227 bool MathErrnoEnabled, bool HasOptNoneAttr,228 bool IsOptimizationEnabled) const {229 230 // True if we are compiling at -O2 and errno has been disabled231 // using the '#pragma float_control(precise, off)', and232 // attribute opt-none hasn't been seen.233 bool ErrnoOverridenToFalseWithOpt = ErrnoOverwritten.has_value() &&234 !ErrnoOverwritten.value() &&235 !HasOptNoneAttr && IsOptimizationEnabled;236 237 // There are LLVM math intrinsics/instructions corresponding to math library238 // functions except the LLVM op will never set errno while the math library239 // might. Also, math builtins have the same semantics as their math library240 // twins. Thus, we can transform math library and builtin calls to their241 // LLVM counterparts if the call is marked 'const' (known to never set errno).242 // In case FP exceptions are enabled, the experimental versions of the243 // intrinsics model those.244 bool ConstAlways =245 isConst(BuiltinID) || isBuiltinConstForTriple(BuiltinID, Trip);246 247 bool ConstWithoutErrnoAndExceptions =248 isConstWithoutErrnoAndExceptions(BuiltinID);249 bool ConstWithoutExceptions = isConstWithoutExceptions(BuiltinID);250 251 // ConstAttr is enabled in fast-math mode. In fast-math mode, math-errno is252 // disabled.253 // Math intrinsics are generated only when math-errno is disabled. Any pragmas254 // or attributes that affect math-errno should prevent or allow math255 // intrinsics to be generated. Intrinsics are generated:256 // 1- In fast math mode, unless math-errno is overriden257 // via '#pragma float_control(precise, on)', or via an258 // 'attribute__((optnone))'.259 // 2- If math-errno was enabled on command line but overriden260 // to false via '#pragma float_control(precise, off))' and261 // 'attribute__((optnone))' hasn't been used.262 // 3- If we are compiling with optimization and errno has been disabled263 // via '#pragma float_control(precise, off)', and264 // 'attribute__((optnone))' hasn't been used.265 266 bool ConstWithoutErrnoOrExceptions =267 ConstWithoutErrnoAndExceptions || ConstWithoutExceptions;268 bool GenerateIntrinsics =269 (ConstAlways && !HasOptNoneAttr) ||270 (!MathErrnoEnabled &&271 !(ErrnoOverwritten.has_value() && ErrnoOverwritten.value()) &&272 !HasOptNoneAttr);273 if (!GenerateIntrinsics) {274 GenerateIntrinsics =275 ConstWithoutErrnoOrExceptions && !ConstWithoutErrnoAndExceptions;276 if (!GenerateIntrinsics)277 GenerateIntrinsics =278 ConstWithoutErrnoOrExceptions &&279 (!MathErrnoEnabled &&280 !(ErrnoOverwritten.has_value() && ErrnoOverwritten.value()) &&281 !HasOptNoneAttr);282 if (!GenerateIntrinsics)283 GenerateIntrinsics =284 ConstWithoutErrnoOrExceptions && ErrnoOverridenToFalseWithOpt;285 }286 287 return GenerateIntrinsics;288}289 290/// initializeBuiltins - Mark the identifiers for all the builtins with their291/// appropriate builtin ID # and mark any non-portable builtin identifiers as292/// such.293void Builtin::Context::initializeBuiltins(IdentifierTable &Table,294 const LangOptions &LangOpts) {295 {296 unsigned ID = 0;297 // Step #1: mark all target-independent builtins with their ID's.298 for (const auto &Shard : BuiltinShards)299 for (const auto &I : Shard.Infos) {300 // If this is a real builtin (ID != 0) and is supported, add it.301 if (ID != 0 && builtinIsSupported(*Shard.Strings, I, LangOpts))302 Table.get(I.getName(Shard)).setBuiltinID(ID);303 ++ID;304 }305 assert(ID == FirstTSBuiltin && "Should have added all non-target IDs!");306 307 // Step #2: Register target-specific builtins.308 for (const auto &Shard : TargetShards)309 for (const auto &I : Shard.Infos) {310 if (builtinIsSupported(*Shard.Strings, I, LangOpts))311 Table.get(I.getName(Shard)).setBuiltinID(ID);312 ++ID;313 }314 315 // Step #3: Register target-specific builtins for AuxTarget.316 for (const auto &Shard : AuxTargetShards)317 for (const auto &I : Shard.Infos) {318 Table.get(I.getName(Shard)).setBuiltinID(ID);319 ++ID;320 }321 }322 323 // Step #4: Unregister any builtins specified by -fno-builtin-foo.324 for (llvm::StringRef Name : LangOpts.NoBuiltinFuncs) {325 bool InStdNamespace = Name.consume_front("std-");326 auto NameIt = Table.find(Name);327 if (NameIt != Table.end()) {328 unsigned ID = NameIt->second->getBuiltinID();329 if (ID != Builtin::NotBuiltin && isPredefinedLibFunction(ID) &&330 isInStdNamespace(ID) == InStdNamespace) {331 NameIt->second->clearBuiltinID();332 }333 }334 }335}336 337unsigned Builtin::Context::getRequiredVectorWidth(unsigned ID) const {338 const char *WidthPos = ::strchr(getAttributesString(ID), 'V');339 if (!WidthPos)340 return 0;341 342 ++WidthPos;343 assert(*WidthPos == ':' &&344 "Vector width specifier must be followed by a ':'");345 ++WidthPos;346 347 char *EndPos;348 unsigned Width = ::strtol(WidthPos, &EndPos, 10);349 assert(*EndPos == ':' && "Vector width specific must end with a ':'");350 return Width;351}352 353bool Builtin::Context::isLike(unsigned ID, unsigned &FormatIdx,354 bool &HasVAListArg, const char *Fmt) const {355 assert(Fmt && "Not passed a format string");356 assert(::strlen(Fmt) == 2 &&357 "Format string needs to be two characters long");358 assert(::toupper(Fmt[0]) == Fmt[1] &&359 "Format string is not in the form \"xX\"");360 361 const char *Like = ::strpbrk(getAttributesString(ID), Fmt);362 if (!Like)363 return false;364 365 HasVAListArg = (*Like == Fmt[1]);366 367 ++Like;368 assert(*Like == ':' && "Format specifier must be followed by a ':'");369 ++Like;370 371 assert(::strchr(Like, ':') && "Format specifier must end with a ':'");372 FormatIdx = ::strtol(Like, nullptr, 10);373 return true;374}375 376bool Builtin::Context::isPrintfLike(unsigned ID, unsigned &FormatIdx,377 bool &HasVAListArg) {378 return isLike(ID, FormatIdx, HasVAListArg, "pP");379}380 381bool Builtin::Context::isScanfLike(unsigned ID, unsigned &FormatIdx,382 bool &HasVAListArg) {383 return isLike(ID, FormatIdx, HasVAListArg, "sS");384}385 386bool Builtin::Context::performsCallback(unsigned ID,387 SmallVectorImpl<int> &Encoding) const {388 const char *CalleePos = ::strchr(getAttributesString(ID), 'C');389 if (!CalleePos)390 return false;391 392 ++CalleePos;393 assert(*CalleePos == '<' &&394 "Callback callee specifier must be followed by a '<'");395 ++CalleePos;396 397 char *EndPos;398 int CalleeIdx = ::strtol(CalleePos, &EndPos, 10);399 assert(CalleeIdx >= 0 && "Callee index is supposed to be positive!");400 Encoding.push_back(CalleeIdx);401 402 while (*EndPos == ',') {403 const char *PayloadPos = EndPos + 1;404 405 int PayloadIdx = ::strtol(PayloadPos, &EndPos, 10);406 Encoding.push_back(PayloadIdx);407 }408 409 assert(*EndPos == '>' && "Callback callee specifier must end with a '>'");410 return true;411}412 413bool Builtin::Context::canBeRedeclared(unsigned ID) const {414 return ID == Builtin::NotBuiltin || ID == Builtin::BI__va_start ||415 ID == Builtin::BI__builtin_assume_aligned ||416 (!hasReferenceArgsOrResult(ID) && !hasCustomTypechecking(ID)) ||417 isInStdNamespace(ID);418}419 420bool Builtin::evaluateRequiredTargetFeatures(421 StringRef RequiredFeatures, const llvm::StringMap<bool> &TargetFetureMap) {422 // Return true if the builtin doesn't have any required features.423 if (RequiredFeatures.empty())424 return true;425 assert(!RequiredFeatures.contains(' ') && "Space in feature list");426 427 TargetFeatures TF(TargetFetureMap);428 return TF.hasRequiredFeatures(RequiredFeatures);429}430