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-exception 6 // 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" 19 using namespace clang; 20 21 const 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 HEADER 28 }; 29 llvm_unreachable("Unknown HeaderDesc::HeaderID enum"); 30 } 31 32 static constexpr unsigned NumBuiltins = Builtin::FirstTSBuiltin; 33 34 #define GET_BUILTIN_STR_TABLE 35 #include "clang/Basic/Builtins.inc" 36 #undef GET_BUILTIN_STR_TABLE 37 38 static constexpr Builtin::Info BuiltinInfos[] = { 39 Builtin::Info{}, // No-builtin info entry. 40 #define GET_BUILTIN_INFOS 41 #include "clang/Basic/Builtins.inc" 42 #undef GET_BUILTIN_INFOS 43 }; 44 static_assert(std::size(BuiltinInfos) == NumBuiltins); 45 46 std::pair<const Builtin::InfosShard &, const Builtin::Info &> 47 Builtin::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 to 62 // have many shards and so its better to search linearly than with a binary 63 // 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 74 std::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". 80 std::string Builtin::Context::getName(unsigned ID) const { 81 const auto &[Shard, I] = getShardAndInfo(ID); 82 return I.getName(Shard); 83 } 84 85 std::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 92 const char *Builtin::Context::getTypeString(unsigned ID) const { 93 const auto &[Shard, I] = getShardAndInfo(ID); 94 return (*Shard.Strings)[I.Offsets.Type].data(); 95 } 96 97 const char *Builtin::Context::getAttributesString(unsigned ID) const { 98 const auto &[Shard, I] = getShardAndInfo(ID); 99 return (*Shard.Strings)[I.Offsets.Attributes].data(); 100 } 101 102 const char *Builtin::Context::getRequiredFeatures(unsigned ID) const { 103 const auto &[Shard, I] = getShardAndInfo(ID); 104 return (*Shard.Strings)[I.Offsets.Features].data(); 105 } 106 107 Builtin::Context::Context() : BuiltinShards{{&BuiltinStrings, BuiltinInfos}} {} 108 109 void 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 123 bool 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? 139 static 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 /* OopenCL 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 higher 176 // 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 200 /// initializeBuiltins - Mark the identifiers for all the builtins with their 201 /// appropriate builtin ID # and mark any non-portable builtin identifiers as 202 /// such. 203 void Builtin::Context::initializeBuiltins(IdentifierTable &Table, 204 const LangOptions &LangOpts) { 205 { 206 unsigned ID = 0; 207 // Step #1: mark all target-independent builtins with their ID's. 208 for (const auto &Shard : BuiltinShards) 209 for (const auto &I : Shard.Infos) { 210 // If this is a real builtin (ID != 0) and is supported, add it. 211 if (ID != 0 && builtinIsSupported(*Shard.Strings, I, LangOpts)) 212 Table.get(I.getName(Shard)).setBuiltinID(ID); 213 ++ID; 214 } 215 assert(ID == FirstTSBuiltin && "Should have added all non-target IDs!"); 216 217 // Step #2: Register target-specific builtins. 218 for (const auto &Shard : TargetShards) 219 for (const auto &I : Shard.Infos) { 220 if (builtinIsSupported(*Shard.Strings, I, LangOpts)) 221 Table.get(I.getName(Shard)).setBuiltinID(ID); 222 ++ID; 223 } 224 225 // Step #3: Register target-specific builtins for AuxTarget. 226 for (const auto &Shard : AuxTargetShards) 227 for (const auto &I : Shard.Infos) { 228 Table.get(I.getName(Shard)).setBuiltinID(ID); 229 ++ID; 230 } 231 } 232 233 // Step #4: Unregister any builtins specified by -fno-builtin-foo. 234 for (llvm::StringRef Name : LangOpts.NoBuiltinFuncs) { 235 bool InStdNamespace = Name.consume_front("std-"); 236 auto NameIt = Table.find(Name); 237 if (NameIt != Table.end()) { 238 unsigned ID = NameIt->second->getBuiltinID(); 239 if (ID != Builtin::NotBuiltin && isPredefinedLibFunction(ID) && 240 isInStdNamespace(ID) == InStdNamespace) { 241 NameIt->second->clearBuiltinID(); 242 } 243 } 244 } 245 } 246 247 unsigned Builtin::Context::getRequiredVectorWidth(unsigned ID) const { 248 const char *WidthPos = ::strchr(getAttributesString(ID), 'V'); 249 if (!WidthPos) 250 return 0; 251 252 ++WidthPos; 253 assert(*WidthPos == ':' && 254 "Vector width specifier must be followed by a ':'"); 255 ++WidthPos; 256 257 char *EndPos; 258 unsigned Width = ::strtol(WidthPos, &EndPos, 10); 259 assert(*EndPos == ':' && "Vector width specific must end with a ':'"); 260 return Width; 261 } 262 263 bool Builtin::Context::isLike(unsigned ID, unsigned &FormatIdx, 264 bool &HasVAListArg, const char *Fmt) const { 265 assert(Fmt && "Not passed a format string"); 266 assert(::strlen(Fmt) == 2 && 267 "Format string needs to be two characters long"); 268 assert(::toupper(Fmt[0]) == Fmt[1] && 269 "Format string is not in the form \"xX\""); 270 271 const char *Like = ::strpbrk(getAttributesString(ID), Fmt); 272 if (!Like) 273 return false; 274 275 HasVAListArg = (*Like == Fmt[1]); 276 277 ++Like; 278 assert(*Like == ':' && "Format specifier must be followed by a ':'"); 279 ++Like; 280 281 assert(::strchr(Like, ':') && "Format specifier must end with a ':'"); 282 FormatIdx = ::strtol(Like, nullptr, 10); 283 return true; 284 } 285 286 bool Builtin::Context::isPrintfLike(unsigned ID, unsigned &FormatIdx, 287 bool &HasVAListArg) { 288 return isLike(ID, FormatIdx, HasVAListArg, "pP"); 289 } 290 291 bool Builtin::Context::isScanfLike(unsigned ID, unsigned &FormatIdx, 292 bool &HasVAListArg) { 293 return isLike(ID, FormatIdx, HasVAListArg, "sS"); 294 } 295 296 bool Builtin::Context::performsCallback(unsigned ID, 297 SmallVectorImpl<int> &Encoding) const { 298 const char *CalleePos = ::strchr(getAttributesString(ID), 'C'); 299 if (!CalleePos) 300 return false; 301 302 ++CalleePos; 303 assert(*CalleePos == '<' && 304 "Callback callee specifier must be followed by a '<'"); 305 ++CalleePos; 306 307 char *EndPos; 308 int CalleeIdx = ::strtol(CalleePos, &EndPos, 10); 309 assert(CalleeIdx >= 0 && "Callee index is supposed to be positive!"); 310 Encoding.push_back(CalleeIdx); 311 312 while (*EndPos == ',') { 313 const char *PayloadPos = EndPos + 1; 314 315 int PayloadIdx = ::strtol(PayloadPos, &EndPos, 10); 316 Encoding.push_back(PayloadIdx); 317 } 318 319 assert(*EndPos == '>' && "Callback callee specifier must end with a '>'"); 320 return true; 321 } 322 323 bool Builtin::Context::canBeRedeclared(unsigned ID) const { 324 return ID == Builtin::NotBuiltin || ID == Builtin::BI__va_start || 325 ID == Builtin::BI__builtin_assume_aligned || 326 (!hasReferenceArgsOrResult(ID) && !hasCustomTypechecking(ID)) || 327 isInStdNamespace(ID); 328 } 329 330 bool Builtin::evaluateRequiredTargetFeatures( 331 StringRef RequiredFeatures, const llvm::StringMap<bool> &TargetFetureMap) { 332 // Return true if the builtin doesn't have any required features. 333 if (RequiredFeatures.empty()) 334 return true; 335 assert(!RequiredFeatures.contains(' ') && "Space in feature list"); 336 337 TargetFeatures TF(TargetFetureMap); 338 return TF.hasRequiredFeatures(RequiredFeatures); 339 } 340