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"
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 HEADER
28 };
29 llvm_unreachable("Unknown HeaderDesc::HeaderID enum");
30}
31
32static 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
38static 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};
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 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
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{{.Strings: &BuiltinStrings, .Infos: 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(Prefix: "std-");
125 for (const auto &Shard : {InfosShard{.Strings: &BuiltinStrings, .Infos: BuiltinInfos}})
126 if (llvm::StringRef FuncNameSuffix = FuncName;
127 FuncNameSuffix.consume_front(Prefix: Shard.NamePrefix))
128 for (const auto &I : Shard.Infos)
129 if (FuncNameSuffix == (*Shard.Strings)[I.Offsets.Name] &&
130 (bool)strchr(s: (*Shard.Strings)[I.Offsets.Attributes].data(), c: 'z') ==
131 InStdNamespace)
132 return strchr(s: (*Shard.Strings)[I.Offsets.Attributes].data(), c: '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(s: AttributesStr.data(), c: '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 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(s: AttributesStr.data(), c: '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 const
202 // if the target environment is GNU or the target is OS is Windows and we're
203 // targeting the MSVCRT.dll environment.
204 // FIXME: This list can be become outdated. Need to find a way to get it some
205 // 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 disabled
231 // using the '#pragma float_control(precise, off)', and
232 // 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 library
238 // functions except the LLVM op will never set errno while the math library
239 // might. Also, math builtins have the same semantics as their math library
240 // twins. Thus, we can transform math library and builtin calls to their
241 // LLVM counterparts if the call is marked 'const' (known to never set errno).
242 // In case FP exceptions are enabled, the experimental versions of the
243 // intrinsics model those.
244 bool ConstAlways =
245 isConst(ID: BuiltinID) || isBuiltinConstForTriple(BuiltinID, Trip);
246
247 bool ConstWithoutErrnoAndExceptions =
248 isConstWithoutErrnoAndExceptions(ID: BuiltinID);
249 bool ConstWithoutExceptions = isConstWithoutExceptions(ID: BuiltinID);
250
251 // ConstAttr is enabled in fast-math mode. In fast-math mode, math-errno is
252 // disabled.
253 // Math intrinsics are generated only when math-errno is disabled. Any pragmas
254 // or attributes that affect math-errno should prevent or allow math
255 // intrinsics to be generated. Intrinsics are generated:
256 // 1- In fast math mode, unless math-errno is overriden
257 // via '#pragma float_control(precise, on)', or via an
258 // 'attribute__((optnone))'.
259 // 2- If math-errno was enabled on command line but overriden
260 // to false via '#pragma float_control(precise, off))' and
261 // 'attribute__((optnone))' hasn't been used.
262 // 3- If we are compiling with optimization and errno has been disabled
263 // via '#pragma float_control(precise, off)', and
264 // '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 their
291/// appropriate builtin ID # and mark any non-portable builtin identifiers as
292/// 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(Strings: *Shard.Strings, BuiltinInfo: I, LangOpts))
302 Table.get(Name: 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(Strings: *Shard.Strings, BuiltinInfo: I, LangOpts))
311 Table.get(Name: 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(Name: 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(Prefix: "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(s: getAttributesString(ID), c: '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(nptr: WidthPos, endptr: &EndPos, base: 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(s: getAttributesString(ID), accept: 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(nptr: Like, endptr: nullptr, base: 10);
373 return true;
374}
375
376bool Builtin::Context::isPrintfLike(unsigned ID, unsigned &FormatIdx,
377 bool &HasVAListArg) {
378 return isLike(ID, FormatIdx, HasVAListArg, Fmt: "pP");
379}
380
381bool Builtin::Context::isScanfLike(unsigned ID, unsigned &FormatIdx,
382 bool &HasVAListArg) {
383 return isLike(ID, FormatIdx, HasVAListArg, Fmt: "sS");
384}
385
386static void parseCommaSeparatedIndices(const char *CurrPos,
387 llvm::SmallVectorImpl<int> &Indxs) {
388 assert(*CurrPos == '<' && "Expected '<' to start index list");
389 ++CurrPos;
390
391 char *EndPos;
392 int PosIdx = ::strtol(nptr: CurrPos, endptr: &EndPos, base: 10);
393 assert(PosIdx >= 0 && "Index is supposed to be positive!");
394 Indxs.push_back(Elt: PosIdx);
395
396 while (*EndPos == ',') {
397 const char *PayloadPos = EndPos + 1;
398
399 int PayloadIdx = ::strtol(nptr: PayloadPos, endptr: &EndPos, base: 10);
400 Indxs.push_back(Elt: PayloadIdx);
401 }
402
403 assert(*EndPos == '>' && "Index list must end with '>'");
404}
405
406bool Builtin::Context::isNonNull(unsigned ID, llvm::SmallVectorImpl<int> &Indxs,
407 Info::NonNullMode &Mode) const {
408
409 const char *AttrPos = ::strchr(s: getAttributesString(ID), c: 'N');
410 if (!AttrPos)
411 return false;
412
413 ++AttrPos;
414 assert(*AttrPos == ':' && "Format specifier must be followed by a ':'");
415 ++AttrPos;
416 if (*AttrPos == '0')
417 Mode = Info::NonNullMode::NonOptimizing;
418 else if (*AttrPos == '1')
419 Mode = Info::NonNullMode::Optimizing;
420 else
421 llvm_unreachable("Unrecognized NonNull optimization mode");
422 ++AttrPos; // skip mode
423 assert(*AttrPos == ':' && "Mode must be followed by a ':'");
424 ++AttrPos;
425
426 parseCommaSeparatedIndices(CurrPos: AttrPos, Indxs);
427
428 return true;
429}
430
431bool Builtin::Context::performsCallback(unsigned ID,
432 SmallVectorImpl<int> &Encoding) const {
433 const char *CalleePos = ::strchr(s: getAttributesString(ID), c: 'C');
434 if (!CalleePos)
435 return false;
436
437 ++CalleePos;
438 parseCommaSeparatedIndices(CurrPos: CalleePos, Indxs&: Encoding);
439
440 return true;
441}
442
443bool Builtin::Context::canBeRedeclared(unsigned ID) const {
444 return ID == Builtin::NotBuiltin || ID == Builtin::BI__va_start ||
445 ID == Builtin::BI__builtin_assume_aligned ||
446 (!hasReferenceArgsOrResult(ID) && !hasCustomTypechecking(ID)) ||
447 isInStdNamespace(ID);
448}
449
450bool Builtin::evaluateRequiredTargetFeatures(
451 StringRef RequiredFeatures, const llvm::StringMap<bool> &TargetFetureMap) {
452 // Return true if the builtin doesn't have any required features.
453 if (RequiredFeatures.empty())
454 return true;
455 assert(!RequiredFeatures.contains(' ') && "Space in feature list");
456
457 TargetFeatures TF(TargetFetureMap);
458 return TF.hasRequiredFeatures(FeatureList: RequiredFeatures);
459}
460