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