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