xref: /freebsd/contrib/llvm-project/clang/lib/Basic/Builtins.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
getName() const21 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 &>
getShardAndInfo(unsigned ID) const47 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 
getName(const Builtin::InfosShard & Shard) const74 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".
getName(unsigned ID) const80 std::string Builtin::Context::getName(unsigned ID) const {
81   const auto &[Shard, I] = getShardAndInfo(ID);
82   return I.getName(Shard);
83 }
84 
getQuotedName(unsigned ID) const85 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 
getTypeString(unsigned ID) const92 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 
getAttributesString(unsigned ID) const97 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 
getRequiredFeatures(unsigned ID) const102 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 
Context()107 Builtin::Context::Context() : BuiltinShards{{&BuiltinStrings, BuiltinInfos}} {}
108 
InitializeTarget(const TargetInfo & Target,const TargetInfo * AuxTarget)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 
isBuiltinFunc(llvm::StringRef FuncName)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?
builtinIsSupported(const llvm::StringTable & Strings,const Builtin::Info & BuiltinInfo,const LangOptions & LangOpts)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.
initializeBuiltins(IdentifierTable & Table,const LangOptions & LangOpts)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 
getRequiredVectorWidth(unsigned ID) const247 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 
isLike(unsigned ID,unsigned & FormatIdx,bool & HasVAListArg,const char * Fmt) const263 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 
isPrintfLike(unsigned ID,unsigned & FormatIdx,bool & HasVAListArg)286 bool Builtin::Context::isPrintfLike(unsigned ID, unsigned &FormatIdx,
287                                     bool &HasVAListArg) {
288   return isLike(ID, FormatIdx, HasVAListArg, "pP");
289 }
290 
isScanfLike(unsigned ID,unsigned & FormatIdx,bool & HasVAListArg)291 bool Builtin::Context::isScanfLike(unsigned ID, unsigned &FormatIdx,
292                                    bool &HasVAListArg) {
293   return isLike(ID, FormatIdx, HasVAListArg, "sS");
294 }
295 
performsCallback(unsigned ID,SmallVectorImpl<int> & Encoding) const296 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 
canBeRedeclared(unsigned ID) const323 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 
evaluateRequiredTargetFeatures(StringRef RequiredFeatures,const llvm::StringMap<bool> & TargetFetureMap)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