This change adds two builtins for AMDGPU: - `__builtin_amdgcn_processor_is`, which is similar in observable behaviour with `__builtin_cpu_is`, except that it is never "evaluated" at run time; - `__builtin_amdgcn_is_invocable`, which is behaviourally similar with `__has_builtin`, except that it is not a macro (i.e. not evaluated at preprocessing time). Neither of these are `constexpr`, even though when compiling for concrete (i.e. `gfxXXX` / `gfxXXX-generic`) targets they get evaluated in Clang, so they shouldn't tear the AST too badly / at all for multi-pass compilation cases like HIP. They can only be used in specific contexts (as args to control structures). The motivation for adding these is two-fold: - as a nice to have, it provides an AST-visible way to incorporate architecture specific code, rather than having to rely on macros and the preprocessor, which burn in the choice quite early; - as a must have, it allows featureful AMDGCN flavoured SPIR-V to be produced, where target specific capability is guarded and chosen or discarded when finalising compilation for a concrete target; this is built atop the Speciali\ation Constant concept which is described in the SPIR-V specification under section [2.12 Specialization](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_specialization_2) I've tried to keep the overall footprint of the change small. The changes to Sema are a bit unpleasant, but there was a strong desire to have Clang validate these, and to constrain their uses, and this was the most compact solution I could come up with (suggestions welcome). --------- Co-authored-by: Juan Manuel Martinez Caamaño <jmartinezcaamao@gmail.com> Co-authored-by: Voicu <avoicu@amd.com>
799 lines
27 KiB
C++
799 lines
27 KiB
C++
//===-- ClangBuiltinsEmitter.cpp - Generate Clang builtins tables ---------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This tablegen backend emits Clang's builtins tables.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "TableGenBackends.h"
|
|
#include "llvm/ADT/MapVector.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include "llvm/TableGen/Error.h"
|
|
#include "llvm/TableGen/Record.h"
|
|
#include "llvm/TableGen/StringToOffsetTable.h"
|
|
#include "llvm/TableGen/TableGenBackend.h"
|
|
#include <sstream>
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
enum class BuiltinType {
|
|
Builtin,
|
|
AtomicBuiltin,
|
|
LibBuiltin,
|
|
LangBuiltin,
|
|
TargetBuiltin,
|
|
TargetLibBuiltin,
|
|
};
|
|
|
|
class HeaderNameParser {
|
|
public:
|
|
HeaderNameParser(const Record *Builtin) {
|
|
for (char c : Builtin->getValueAsString("Header")) {
|
|
if (std::islower(c))
|
|
HeaderName += static_cast<char>(std::toupper(c));
|
|
else if (c == '.' || c == '_' || c == '/' || c == '-')
|
|
HeaderName += '_';
|
|
else
|
|
PrintFatalError(Builtin->getLoc(), "Unexpected header name");
|
|
}
|
|
}
|
|
|
|
void Print(raw_ostream &OS) const { OS << HeaderName; }
|
|
|
|
private:
|
|
std::string HeaderName;
|
|
};
|
|
|
|
struct Builtin {
|
|
BuiltinType BT;
|
|
std::string Name;
|
|
std::string Type;
|
|
std::string Attributes;
|
|
|
|
const Record *BuiltinRecord;
|
|
|
|
void EmitEnumerator(llvm::raw_ostream &OS) const {
|
|
OS << " BI";
|
|
// If there is a required name prefix, include its spelling in the
|
|
// enumerator.
|
|
if (auto *PrefixRecord =
|
|
BuiltinRecord->getValueAsOptionalDef("RequiredNamePrefix"))
|
|
OS << PrefixRecord->getValueAsString("Spelling");
|
|
OS << Name << ",\n";
|
|
}
|
|
|
|
void EmitInfo(llvm::raw_ostream &OS, const StringToOffsetTable &Table) const {
|
|
OS << " Builtin::Info{Builtin::Info::StrOffsets{"
|
|
<< Table.GetStringOffset(Name) << " /* " << Name << " */, "
|
|
<< Table.GetStringOffset(Type) << " /* " << Type << " */, "
|
|
<< Table.GetStringOffset(Attributes) << " /* " << Attributes << " */, ";
|
|
if (BT == BuiltinType::TargetBuiltin) {
|
|
const auto &Features = BuiltinRecord->getValueAsString("Features");
|
|
OS << Table.GetStringOffset(Features) << " /* " << Features << " */";
|
|
} else {
|
|
OS << "0";
|
|
}
|
|
OS << "}, ";
|
|
if (BT == BuiltinType::LibBuiltin || BT == BuiltinType::TargetLibBuiltin) {
|
|
OS << "HeaderDesc::";
|
|
HeaderNameParser{BuiltinRecord}.Print(OS);
|
|
} else {
|
|
OS << "HeaderDesc::NO_HEADER";
|
|
}
|
|
OS << ", ";
|
|
if (BT == BuiltinType::LibBuiltin || BT == BuiltinType::LangBuiltin ||
|
|
BT == BuiltinType::TargetLibBuiltin) {
|
|
OS << BuiltinRecord->getValueAsString("Languages");
|
|
} else {
|
|
OS << "ALL_LANGUAGES";
|
|
}
|
|
OS << "},\n";
|
|
}
|
|
|
|
void EmitXMacro(llvm::raw_ostream &OS) const {
|
|
if (BuiltinRecord->getValueAsBit("RequiresUndef"))
|
|
OS << "#undef " << Name << '\n';
|
|
switch (BT) {
|
|
case BuiltinType::LibBuiltin:
|
|
OS << "LIBBUILTIN";
|
|
break;
|
|
case BuiltinType::LangBuiltin:
|
|
OS << "LANGBUILTIN";
|
|
break;
|
|
case BuiltinType::Builtin:
|
|
OS << "BUILTIN";
|
|
break;
|
|
case BuiltinType::AtomicBuiltin:
|
|
OS << "ATOMIC_BUILTIN";
|
|
break;
|
|
case BuiltinType::TargetBuiltin:
|
|
OS << "TARGET_BUILTIN";
|
|
break;
|
|
case BuiltinType::TargetLibBuiltin:
|
|
OS << "TARGET_HEADER_BUILTIN";
|
|
break;
|
|
}
|
|
|
|
OS << "(" << Name << ", \"" << Type << "\", \"" << Attributes << "\"";
|
|
|
|
switch (BT) {
|
|
case BuiltinType::LibBuiltin: {
|
|
OS << ", ";
|
|
HeaderNameParser{BuiltinRecord}.Print(OS);
|
|
[[fallthrough]];
|
|
}
|
|
case BuiltinType::LangBuiltin: {
|
|
OS << ", " << BuiltinRecord->getValueAsString("Languages");
|
|
break;
|
|
}
|
|
case BuiltinType::TargetLibBuiltin: {
|
|
OS << ", ";
|
|
HeaderNameParser{BuiltinRecord}.Print(OS);
|
|
OS << ", " << BuiltinRecord->getValueAsString("Languages");
|
|
[[fallthrough]];
|
|
}
|
|
case BuiltinType::TargetBuiltin: {
|
|
OS << ", \"" << BuiltinRecord->getValueAsString("Features") << "\"";
|
|
break;
|
|
}
|
|
case BuiltinType::AtomicBuiltin:
|
|
case BuiltinType::Builtin:
|
|
break;
|
|
}
|
|
OS << ")\n";
|
|
}
|
|
};
|
|
|
|
class PrototypeParser {
|
|
public:
|
|
PrototypeParser(StringRef Substitution, const Record *Builtin)
|
|
: Loc(Builtin->getFieldLoc("Prototype")), Substitution(Substitution),
|
|
EnableOpenCLLong(Builtin->getValueAsBit("EnableOpenCLLong")) {
|
|
ParsePrototype(Builtin->getValueAsString("Prototype"));
|
|
}
|
|
|
|
std::string takeTypeString() && { return std::move(Type); }
|
|
|
|
private:
|
|
void ParsePrototype(StringRef Prototype) {
|
|
Prototype = Prototype.trim();
|
|
|
|
// Some builtins don't have an expressible prototype, simply emit an empty
|
|
// string for them.
|
|
if (Prototype.empty()) {
|
|
Type = "";
|
|
return;
|
|
}
|
|
|
|
ParseTypes(Prototype);
|
|
}
|
|
|
|
void ParseTypes(StringRef &Prototype) {
|
|
auto ReturnType = Prototype.take_until([](char c) { return c == '('; });
|
|
ParseType(ReturnType);
|
|
Prototype = Prototype.drop_front(ReturnType.size() + 1);
|
|
if (!Prototype.ends_with(")"))
|
|
PrintFatalError(Loc, "Expected closing brace at end of prototype");
|
|
Prototype = Prototype.drop_back();
|
|
|
|
// Look through the input parameters.
|
|
const size_t end = Prototype.size();
|
|
for (size_t I = 0; I != end;) {
|
|
const StringRef Current = Prototype.substr(I, end);
|
|
// Skip any leading space or commas
|
|
if (Current.starts_with(" ") || Current.starts_with(",")) {
|
|
++I;
|
|
continue;
|
|
}
|
|
|
|
// Check if we are in _ExtVector. We do this first because
|
|
// extended vectors are written in template form with the syntax
|
|
// _ExtVector< ..., ...>, so we need to make sure we are not
|
|
// detecting the comma of the template class as a separator for
|
|
// the parameters of the prototype. Note: the assumption is that
|
|
// we cannot have nested _ExtVector.
|
|
if (Current.starts_with("_ExtVector<") ||
|
|
Current.starts_with("_Vector<")) {
|
|
size_t Pos = Current.find('<');
|
|
int Depth = 1;
|
|
|
|
// There may be a nested address_space<...> modifier on the type.
|
|
while (Depth > 0 && ++Pos < Current.size()) {
|
|
if (Current[Pos] == '<')
|
|
++Depth;
|
|
else if (Current[Pos] == '>')
|
|
--Depth;
|
|
}
|
|
|
|
const size_t EndTemplate = Pos;
|
|
ParseType(Current.substr(0, EndTemplate + 1));
|
|
// Move the prototype beyond _ExtVector<...>
|
|
I += EndTemplate + 1;
|
|
continue;
|
|
}
|
|
|
|
// We know that we are past _ExtVector, therefore the first seen
|
|
// comma is the boundary of a parameter in the prototype.
|
|
if (size_t CommaPos = Current.find(',', 0)) {
|
|
if (CommaPos != StringRef::npos) {
|
|
StringRef T = Current.substr(0, CommaPos);
|
|
ParseType(T);
|
|
// Move the prototype beyond the comma.
|
|
I += CommaPos + 1;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// No more commas, parse final parameter.
|
|
ParseType(Current);
|
|
I = end;
|
|
}
|
|
}
|
|
|
|
void ParseType(StringRef T) {
|
|
T = T.trim();
|
|
|
|
auto ConsumeAddrSpace = [&]() -> std::optional<unsigned> {
|
|
T = T.trim();
|
|
if (!T.consume_back(">"))
|
|
return std::nullopt;
|
|
|
|
auto Open = T.find_last_of('<');
|
|
if (Open == StringRef::npos)
|
|
PrintFatalError(Loc, "Mismatched angle-brackets in type");
|
|
|
|
StringRef ArgStr = T.substr(Open + 1);
|
|
T = T.slice(0, Open);
|
|
if (!T.consume_back("address_space"))
|
|
PrintFatalError(Loc,
|
|
"Only `address_space<N>` supported as a parameterized "
|
|
"pointer or reference type qualifier");
|
|
|
|
unsigned Number = 0;
|
|
if (ArgStr.getAsInteger(10, Number))
|
|
PrintFatalError(
|
|
Loc, "Expected an integer argument to the address_space qualifier");
|
|
return Number;
|
|
};
|
|
|
|
if (T.consume_back("*")) {
|
|
// Pointers may have an address space qualifier immediately before them.
|
|
std::optional<unsigned> AS = ConsumeAddrSpace();
|
|
// Pointers can apply to already parsed types, like vectors.
|
|
if (!T.empty())
|
|
ParseType(T);
|
|
Type += "*";
|
|
if (AS)
|
|
Type += std::to_string(*AS);
|
|
} else if (T.consume_back("const")) {
|
|
ParseType(T);
|
|
Type += "C";
|
|
} else if (T.consume_back("volatile")) {
|
|
ParseType(T);
|
|
Type += "D";
|
|
} else if (T.consume_back("restrict")) {
|
|
ParseType(T);
|
|
Type += "R";
|
|
} else if (T.consume_back("&")) {
|
|
// References may have an address space qualifier immediately before them.
|
|
std::optional<unsigned> AS = ConsumeAddrSpace();
|
|
ParseType(T);
|
|
Type += "&";
|
|
if (AS)
|
|
Type += std::to_string(*AS);
|
|
} else if (T.consume_back(")")) {
|
|
ParseType(T);
|
|
Type += "&";
|
|
} else if (EnableOpenCLLong && T.consume_front("long long")) {
|
|
Type += "O";
|
|
ParseType(T);
|
|
} else if (T.consume_front("long")) {
|
|
Type += "L";
|
|
ParseType(T);
|
|
} else if (T.consume_front("signed")) {
|
|
Type += "S";
|
|
ParseType(T);
|
|
} else if (T.consume_front("unsigned")) {
|
|
Type += "U";
|
|
ParseType(T);
|
|
} else if (T.consume_front("_Complex")) {
|
|
Type += "X";
|
|
ParseType(T);
|
|
} else if (T.consume_front("_Constant")) {
|
|
Type += "I";
|
|
ParseType(T);
|
|
} else if (T.consume_front("T")) {
|
|
if (Substitution.empty())
|
|
PrintFatalError(Loc, "Not a template");
|
|
ParseType(Substitution);
|
|
} else if (auto IsExt = T.consume_front("_ExtVector");
|
|
IsExt || T.consume_front("_Vector")) {
|
|
// Clang extended vector types are mangled as follows:
|
|
//
|
|
// '_ExtVector<' <lanes> ',' <scalar type> '>'
|
|
|
|
// Before parsing T(=<scalar type>), make sure the syntax of
|
|
// `_ExtVector<N, T>` is correct...
|
|
if (!T.consume_front("<"))
|
|
PrintFatalError(Loc, "Expected '<' after '_ExtVector'");
|
|
unsigned long long Lanes;
|
|
if (consumeUnsignedInteger(T, 10, Lanes))
|
|
PrintFatalError(Loc, "Expected number of lanes after '_ExtVector<'");
|
|
Type += (IsExt ? "E" : "V") + std::to_string(Lanes);
|
|
if (!T.consume_front(","))
|
|
PrintFatalError(Loc,
|
|
"Expected ',' after number of lanes in '_ExtVector<'");
|
|
if (!T.consume_back(">"))
|
|
PrintFatalError(
|
|
Loc, "Expected '>' after scalar type in '_ExtVector<N, type>'");
|
|
|
|
// ...all good, we can check if we have a valid `<scalar type>`.
|
|
ParseType(T);
|
|
} else {
|
|
auto ReturnTypeVal = StringSwitch<std::string>(T)
|
|
.Case("__builtin_va_list_ref", "A")
|
|
.Case("__builtin_va_list", "a")
|
|
.Case("__float128", "LLd")
|
|
.Case("__fp16", "h")
|
|
.Case("__hlsl_resource_t", "Qr")
|
|
.Case("__amdgpu_buffer_rsrc_t", "Qb")
|
|
.Case("__amdgpu_feature_predicate_t", "Qc")
|
|
.Case("__amdgpu_texture_t", "Qt")
|
|
.Case("__int128_t", "LLLi")
|
|
.Case("_Float16", "x")
|
|
.Case("__bf16", "y")
|
|
.Case("bool", "b")
|
|
.Case("char", "c")
|
|
.Case("constant_CFString", "F")
|
|
.Case("double", "d")
|
|
.Case("FILE", "P")
|
|
.Case("float", "f")
|
|
.Case("id", "G")
|
|
.Case("int", "i")
|
|
.Case("int32_t", "Zi")
|
|
.Case("int64_t", "Wi")
|
|
.Case("jmp_buf", "J")
|
|
.Case("msint32_t", "Ni")
|
|
.Case("msuint32_t", "UNi")
|
|
.Case("objc_super", "M")
|
|
.Case("pid_t", "p")
|
|
.Case("ptrdiff_t", "Y")
|
|
.Case("SEL", "H")
|
|
.Case("short", "s")
|
|
.Case("sigjmp_buf", "SJ")
|
|
.Case("size_t", "z")
|
|
.Case("ucontext_t", "K")
|
|
.Case("uint32_t", "UZi")
|
|
.Case("uint64_t", "UWi")
|
|
.Case("void", "v")
|
|
.Case("wchar_t", "w")
|
|
.Case("...", ".")
|
|
.Default("error");
|
|
if (ReturnTypeVal == "error")
|
|
PrintFatalError(Loc, "Unknown Type: " + T);
|
|
Type += ReturnTypeVal;
|
|
}
|
|
}
|
|
|
|
SMLoc Loc;
|
|
StringRef Substitution;
|
|
bool EnableOpenCLLong;
|
|
std::string Type;
|
|
};
|
|
|
|
std::string renderAttributes(const Record *Builtin, BuiltinType BT) {
|
|
std::string Attributes;
|
|
raw_string_ostream OS(Attributes);
|
|
if (Builtin->isSubClassOf("LibBuiltin")) {
|
|
if (BT == BuiltinType::LibBuiltin) {
|
|
OS << 'f';
|
|
} else {
|
|
OS << 'F';
|
|
if (Builtin->getValueAsBit("OnlyBuiltinPrefixedAliasIsConstexpr"))
|
|
OS << 'E';
|
|
}
|
|
}
|
|
|
|
if (auto NS = Builtin->getValueAsOptionalString("Namespace")) {
|
|
if (NS != "std")
|
|
PrintFatalError(Builtin->getFieldLoc("Namespace"), "Unknown namespace: ");
|
|
OS << "z";
|
|
}
|
|
|
|
for (const auto *Attr : Builtin->getValueAsListOfDefs("Attributes")) {
|
|
OS << Attr->getValueAsString("Mangling");
|
|
if (Attr->isSubClassOf("IndexedAttribute")) {
|
|
OS << ':' << Attr->getValueAsInt("Index") << ':';
|
|
} else if (Attr->isSubClassOf("MultiIndexAttribute")) {
|
|
OS << '<';
|
|
llvm::ListSeparator Sep(",");
|
|
for (int64_t Index : Attr->getValueAsListOfInts("Indices"))
|
|
OS << Sep << Index;
|
|
OS << '>';
|
|
}
|
|
}
|
|
return Attributes;
|
|
}
|
|
|
|
Builtin buildBuiltin(StringRef Substitution, const Record *BuiltinRecord,
|
|
Twine Spelling, BuiltinType BT) {
|
|
Builtin B;
|
|
B.BT = BT;
|
|
B.Name = Spelling.str();
|
|
B.Type = PrototypeParser(Substitution, BuiltinRecord).takeTypeString();
|
|
B.Attributes = renderAttributes(BuiltinRecord, BT);
|
|
B.BuiltinRecord = BuiltinRecord;
|
|
return B;
|
|
}
|
|
|
|
struct TemplateInsts {
|
|
std::vector<std::string> Substitution;
|
|
std::vector<std::string> Affix;
|
|
bool IsPrefix;
|
|
};
|
|
|
|
TemplateInsts getTemplateInsts(const Record *R) {
|
|
TemplateInsts temp;
|
|
auto Substitutions = R->getValueAsListOfStrings("Substitutions");
|
|
auto Affixes = R->getValueAsListOfStrings("Affixes");
|
|
temp.IsPrefix = R->getValueAsBit("AsPrefix");
|
|
|
|
if (Substitutions.size() != Affixes.size())
|
|
PrintFatalError(R->getLoc(), "Substitutions and affixes "
|
|
"don't have the same lengths");
|
|
|
|
for (auto [Affix, Substitution] : zip(Affixes, Substitutions)) {
|
|
temp.Substitution.emplace_back(Substitution);
|
|
temp.Affix.emplace_back(Affix);
|
|
}
|
|
return temp;
|
|
}
|
|
|
|
void collectBuiltins(const Record *BuiltinRecord,
|
|
SmallVectorImpl<Builtin> &Builtins) {
|
|
TemplateInsts Templates = {};
|
|
if (BuiltinRecord->isSubClassOf("Template")) {
|
|
Templates = getTemplateInsts(BuiltinRecord);
|
|
} else {
|
|
Templates.Affix.emplace_back();
|
|
Templates.Substitution.emplace_back();
|
|
}
|
|
|
|
for (auto [Substitution, Affix] :
|
|
zip(Templates.Substitution, Templates.Affix)) {
|
|
for (StringRef Spelling :
|
|
BuiltinRecord->getValueAsListOfStrings("Spellings")) {
|
|
auto FullSpelling =
|
|
(Templates.IsPrefix ? Affix + Spelling : Spelling + Affix).str();
|
|
BuiltinType BT = BuiltinType::Builtin;
|
|
if (BuiltinRecord->isSubClassOf("AtomicBuiltin")) {
|
|
BT = BuiltinType::AtomicBuiltin;
|
|
} else if (BuiltinRecord->isSubClassOf("LangBuiltin")) {
|
|
BT = BuiltinType::LangBuiltin;
|
|
} else if (BuiltinRecord->isSubClassOf("TargetLibBuiltin")) {
|
|
BT = BuiltinType::TargetLibBuiltin;
|
|
} else if (BuiltinRecord->isSubClassOf("TargetBuiltin")) {
|
|
BT = BuiltinType::TargetBuiltin;
|
|
} else if (BuiltinRecord->isSubClassOf("LibBuiltin")) {
|
|
BT = BuiltinType::LibBuiltin;
|
|
if (BuiltinRecord->getValueAsBit("AddBuiltinPrefixedAlias"))
|
|
Builtins.push_back(buildBuiltin(
|
|
Substitution, BuiltinRecord,
|
|
std::string("__builtin_") + FullSpelling, BuiltinType::Builtin));
|
|
}
|
|
Builtins.push_back(
|
|
buildBuiltin(Substitution, BuiltinRecord, FullSpelling, BT));
|
|
}
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
void clang::EmitClangBuiltins(const RecordKeeper &Records, raw_ostream &OS) {
|
|
emitSourceFileHeader("List of builtins that Clang recognizes", OS);
|
|
|
|
SmallVector<Builtin> Builtins;
|
|
// AtomicBuiltins are order dependent. Emit them first to make manual checking
|
|
// easier and so we can build a special atomic builtin X-macro.
|
|
for (const auto *BuiltinRecord :
|
|
Records.getAllDerivedDefinitions("AtomicBuiltin"))
|
|
collectBuiltins(BuiltinRecord, Builtins);
|
|
unsigned NumAtomicBuiltins = Builtins.size();
|
|
|
|
for (const auto *BuiltinRecord :
|
|
Records.getAllDerivedDefinitions("Builtin")) {
|
|
if (BuiltinRecord->isSubClassOf("AtomicBuiltin"))
|
|
continue;
|
|
// Prefixed builtins are also special and we emit them last so they can have
|
|
// their own representation that skips the prefix.
|
|
if (BuiltinRecord->getValueAsOptionalDef("RequiredNamePrefix"))
|
|
continue;
|
|
|
|
collectBuiltins(BuiltinRecord, Builtins);
|
|
}
|
|
|
|
// Now collect (and count) the prefixed builtins.
|
|
unsigned NumPrefixedBuiltins = Builtins.size();
|
|
const Record *FirstPrefix = nullptr;
|
|
for (const auto *BuiltinRecord :
|
|
Records.getAllDerivedDefinitions("Builtin")) {
|
|
auto *Prefix = BuiltinRecord->getValueAsOptionalDef("RequiredNamePrefix");
|
|
if (!Prefix)
|
|
continue;
|
|
|
|
if (!FirstPrefix)
|
|
FirstPrefix = Prefix;
|
|
assert(Prefix == FirstPrefix &&
|
|
"Multiple distinct prefixes which is not currently supported!");
|
|
assert(!BuiltinRecord->isSubClassOf("AtomicBuiltin") &&
|
|
"Cannot require a name prefix for an atomic builtin.");
|
|
collectBuiltins(BuiltinRecord, Builtins);
|
|
}
|
|
NumPrefixedBuiltins = Builtins.size() - NumPrefixedBuiltins;
|
|
|
|
auto AtomicBuiltins = ArrayRef(Builtins).slice(0, NumAtomicBuiltins);
|
|
auto UnprefixedBuiltins = ArrayRef(Builtins).drop_back(NumPrefixedBuiltins);
|
|
auto PrefixedBuiltins = ArrayRef(Builtins).take_back(NumPrefixedBuiltins);
|
|
|
|
// Collect strings into a table.
|
|
StringToOffsetTable Table;
|
|
Table.GetOrAddStringOffset("");
|
|
for (const auto &B : Builtins) {
|
|
Table.GetOrAddStringOffset(B.Name);
|
|
Table.GetOrAddStringOffset(B.Type);
|
|
Table.GetOrAddStringOffset(B.Attributes);
|
|
if (B.BT == BuiltinType::TargetBuiltin)
|
|
Table.GetOrAddStringOffset(B.BuiltinRecord->getValueAsString("Features"));
|
|
}
|
|
|
|
// Emit enumerators.
|
|
OS << R"c++(
|
|
#ifdef GET_BUILTIN_ENUMERATORS
|
|
)c++";
|
|
for (const auto &B : Builtins)
|
|
B.EmitEnumerator(OS);
|
|
OS << R"c++(
|
|
#endif // GET_BUILTIN_ENUMERATORS
|
|
)c++";
|
|
|
|
// Emit a string table that can be referenced for these builtins.
|
|
OS << R"c++(
|
|
#ifdef GET_BUILTIN_STR_TABLE
|
|
)c++";
|
|
Table.EmitStringTableDef(OS, "BuiltinStrings");
|
|
OS << R"c++(
|
|
#endif // GET_BUILTIN_STR_TABLE
|
|
)c++";
|
|
|
|
// Emit a direct set of `Builtin::Info` initializers, first for the unprefixed
|
|
// builtins and then for the prefixed builtins.
|
|
OS << R"c++(
|
|
#ifdef GET_BUILTIN_INFOS
|
|
)c++";
|
|
for (const auto &B : UnprefixedBuiltins)
|
|
B.EmitInfo(OS, Table);
|
|
OS << R"c++(
|
|
#endif // GET_BUILTIN_INFOS
|
|
)c++";
|
|
|
|
OS << R"c++(
|
|
#ifdef GET_BUILTIN_PREFIXED_INFOS
|
|
)c++";
|
|
for (const auto &B : PrefixedBuiltins)
|
|
B.EmitInfo(OS, Table);
|
|
OS << R"c++(
|
|
#endif // GET_BUILTIN_PREFIXED_INFOS
|
|
)c++";
|
|
|
|
// Emit X-macros for the atomic builtins to support various custome patterns
|
|
// used exclusively with those builtins.
|
|
//
|
|
// FIXME: We should eventually move this to a separate file so that users
|
|
// don't need to include the full set of builtins.
|
|
OS << R"c++(
|
|
#ifdef ATOMIC_BUILTIN
|
|
)c++";
|
|
for (const auto &Builtin : AtomicBuiltins) {
|
|
Builtin.EmitXMacro(OS);
|
|
}
|
|
OS << R"c++(
|
|
#endif // ATOMIC_BUILTIN
|
|
#undef ATOMIC_BUILTIN
|
|
)c++";
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Builtin documentation emitter
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Holds the data needed to emit documentation for a single builtin.
|
|
namespace {
|
|
struct BuiltinDocData {
|
|
const Record *Documentation = nullptr;
|
|
const Record *BuiltinRecord = nullptr;
|
|
std::string Heading;
|
|
|
|
BuiltinDocData(const Record *D, const Record *B)
|
|
: Documentation(D), BuiltinRecord(B) {
|
|
// Use the Heading field if set, otherwise use the builtin's first
|
|
// spelling.
|
|
StringRef HeadingStr = D->getValueAsString("Heading");
|
|
if (HeadingStr.empty()) {
|
|
std::vector<StringRef> Spellings =
|
|
B->getValueAsListOfStrings("Spellings");
|
|
if (!Spellings.empty())
|
|
Heading = Spellings[0].str();
|
|
else
|
|
Heading = B->getName().str();
|
|
} else {
|
|
Heading = HeadingStr.str();
|
|
}
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
static void writeCategoryHeader(const Record *Category, raw_ostream &OS) {
|
|
StringRef CategoryName = Category->getValueAsString("Name");
|
|
OS << "\n" << CategoryName << "\n";
|
|
for (size_t I = 0, E = CategoryName.size(); I < E; ++I)
|
|
OS << "=";
|
|
OS << "\n\n";
|
|
|
|
StringRef CategoryContent = Category->getValueAsString("Content");
|
|
if (!CategoryContent.trim().empty())
|
|
OS << CategoryContent.trim() << "\n\n";
|
|
}
|
|
|
|
/// Split a parameter list string into individual parameter type strings,
|
|
/// respecting nested angle brackets (e.g. address_space<4>, _ExtVector<4,
|
|
/// float>).
|
|
static SmallVector<StringRef> splitParams(StringRef Params) {
|
|
SmallVector<StringRef> Result;
|
|
if (Params.empty())
|
|
return Result;
|
|
|
|
int Depth = 0;
|
|
size_t Start = 0;
|
|
for (size_t I = 0, E = Params.size(); I < E; ++I) {
|
|
if (Params[I] == '<') {
|
|
++Depth;
|
|
} else if (Params[I] == '>') {
|
|
--Depth;
|
|
} else if (Params[I] == ',' && Depth == 0) {
|
|
Result.push_back(Params.substr(Start, I - Start).trim());
|
|
Start = I + 1;
|
|
}
|
|
}
|
|
// Add the last parameter.
|
|
StringRef Last = Params.substr(Start).trim();
|
|
if (!Last.empty())
|
|
Result.push_back(Last);
|
|
return Result;
|
|
}
|
|
|
|
static void writeBuiltinDocumentation(const BuiltinDocData &Doc,
|
|
raw_ostream &OS) {
|
|
// Write heading with '-' underline (subsection).
|
|
std::string HeadingText = "``" + Doc.Heading + "``";
|
|
OS << HeadingText << "\n";
|
|
for (size_t I = 0, E = HeadingText.size(); I < E; ++I)
|
|
OS << "-";
|
|
OS << "\n\n";
|
|
|
|
// Write prototype as a code block.
|
|
StringRef Prototype = Doc.BuiltinRecord->getValueAsString("Prototype");
|
|
if (!Prototype.empty()) {
|
|
std::vector<StringRef> Spellings =
|
|
Doc.BuiltinRecord->getValueAsListOfStrings("Spellings");
|
|
StringRef Name =
|
|
Spellings.empty() ? Doc.BuiltinRecord->getName() : Spellings[0];
|
|
|
|
// Split prototype into return type and params at the first '('.
|
|
size_t ParenPos = Prototype.find('(');
|
|
if (ParenPos != StringRef::npos) {
|
|
StringRef RetType = Prototype.substr(0, ParenPos).rtrim();
|
|
StringRef ParamStr =
|
|
Prototype.substr(ParenPos + 1, Prototype.size() - ParenPos - 2);
|
|
|
|
OS << "**Prototype:**\n\n";
|
|
OS << ".. code-block:: c\n\n";
|
|
OS << " " << RetType << " " << Name << "(";
|
|
|
|
std::vector<StringRef> ArgNames =
|
|
Doc.BuiltinRecord->getValueAsListOfStrings("ArgNames");
|
|
if (!ArgNames.empty()) {
|
|
SmallVector<StringRef> ParamTypes = splitParams(ParamStr);
|
|
bool IsVariadic = !ParamTypes.empty() && ParamTypes.back() == "...";
|
|
size_t NamedParams = ParamTypes.size() - (IsVariadic ? 1 : 0);
|
|
if (ArgNames.size() != NamedParams)
|
|
PrintFatalError(Doc.BuiltinRecord->getLoc(),
|
|
"number of ArgNames (" + Twine(ArgNames.size()) +
|
|
") does not match number of prototype "
|
|
"parameters (" +
|
|
Twine(NamedParams) + ")");
|
|
for (size_t I = 0, E = ParamTypes.size(); I < E; ++I) {
|
|
if (I > 0)
|
|
OS << ", ";
|
|
if (I < NamedParams)
|
|
OS << ParamTypes[I] << " " << ArgNames[I];
|
|
else
|
|
OS << ParamTypes[I];
|
|
}
|
|
} else {
|
|
OS << ParamStr;
|
|
}
|
|
|
|
OS << ")\n\n";
|
|
}
|
|
}
|
|
|
|
// Write target features if this is a TargetBuiltin with features.
|
|
if (Doc.BuiltinRecord->isSubClassOf("TargetBuiltin")) {
|
|
StringRef Features = Doc.BuiltinRecord->getValueAsString("Features");
|
|
if (!Features.empty())
|
|
OS << "**Target Features:** " << Features << "\n\n";
|
|
}
|
|
|
|
// Write documentation content.
|
|
StringRef Content = Doc.Documentation->getValueAsString("Content");
|
|
OS << Content.trim() << "\n\n\n";
|
|
}
|
|
|
|
void clang::EmitClangBuiltinDocs(const RecordKeeper &Records, raw_ostream &OS) {
|
|
// Get the documentation introduction paragraph.
|
|
const Record *Doc = Records.getDef("GlobalDocumentation");
|
|
if (!Doc) {
|
|
PrintFatalError("The GlobalDocumentation top-level definition is missing, "
|
|
"no documentation will be generated.");
|
|
}
|
|
|
|
OS << Doc->getValueAsString("Intro") << "\n";
|
|
|
|
// Gather documentation from each builtin, grouped by category.
|
|
llvm::MapVector<const Record *, std::vector<BuiltinDocData>> SplitDocs;
|
|
|
|
for (const Record *B : Records.getAllDerivedDefinitions("Builtin")) {
|
|
for (const Record *D : B->getValueAsListOfDefs("Documentation")) {
|
|
const Record *Category = D->getValueAsDef("Category");
|
|
StringRef Cat = Category->getValueAsString("Name");
|
|
// Skip builtins that are explicitly internal-only.
|
|
if (Cat == "InternalOnly")
|
|
continue;
|
|
SplitDocs[Category].emplace_back(D, B);
|
|
}
|
|
}
|
|
|
|
// Sort categories alphabetically by name for deterministic output, but
|
|
// push the "Undocumented" category to the end so that documented sections
|
|
// always appear first.
|
|
llvm::sort(SplitDocs, [](const auto &A, const auto &B) {
|
|
StringRef NameA = A.first->getValueAsString("Name");
|
|
StringRef NameB = B.first->getValueAsString("Name");
|
|
bool UndocA = (NameA == "Undocumented");
|
|
bool UndocB = (NameB == "Undocumented");
|
|
if (UndocA != UndocB)
|
|
return UndocB;
|
|
return NameA < NameB;
|
|
});
|
|
|
|
// Write out each category and its builtins.
|
|
for (auto &[Category, Docs] : SplitDocs) {
|
|
writeCategoryHeader(Category, OS);
|
|
|
|
// Sort entries alphabetically by heading.
|
|
llvm::sort(Docs, [](const BuiltinDocData &A, const BuiltinDocData &B) {
|
|
return A.Heading < B.Heading;
|
|
});
|
|
|
|
for (const BuiltinDocData &D : Docs)
|
|
writeBuiltinDocumentation(D, OS);
|
|
}
|
|
}
|