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