1//===-- Implementation of PublicAPICommand --------------------------------===//
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#include "PublicAPICommand.h"
10
11#include "utils/LibcTableGenUtil/APIIndexer.h"
12
13#include "llvm/ADT/STLExtras.h"
14#include "llvm/ADT/SmallVector.h"
15#include "llvm/ADT/StringExtras.h"
16#include "llvm/ADT/StringRef.h"
17#include "llvm/ADT/StringSwitch.h"
18#include "llvm/Support/SourceMgr.h"
19#include "llvm/TableGen/Record.h"
20#include <algorithm>
21#include <vector>
22
23// Text blocks for macro definitions and type decls can be indented to
24// suit the surrounding tablegen listing. We need to dedent such blocks
25// before writing them out.
26static void dedentAndWrite(llvm::StringRef Text, llvm::raw_ostream &OS) {
27 llvm::SmallVector<llvm::StringRef, 10> Lines;
28 llvm::SplitString(Text, Lines, "\n");
29 size_t shortest_indent = 1024;
30 for (llvm::StringRef L : Lines) {
31 llvm::StringRef Indent = L.take_while([](char c) { return c == ' '; });
32 size_t IndentSize = Indent.size();
33 if (Indent.size() == L.size()) {
34 // Line is all spaces so no point noting the indent.
35 continue;
36 }
37 if (IndentSize < shortest_indent)
38 shortest_indent = IndentSize;
39 }
40 for (llvm::StringRef L : Lines) {
41 if (L.size() >= shortest_indent)
42 OS << L.drop_front(shortest_indent) << '\n';
43 }
44}
45
46static std::string getTypeHdrName(const std::string &Name) {
47 llvm::SmallVector<llvm::StringRef> Parts;
48 llvm::SplitString(llvm::StringRef(Name), Parts);
49 return llvm::join(Parts.begin(), Parts.end(), "_");
50}
51
52namespace llvm_libc {
53
54static bool isAsciiStart(char C) {
55 return (C >= 'A' && C <= 'Z') || (C >= 'a' && C <= 'z') || C == '_';
56}
57
58static bool isAsciiContinue(char C) {
59 return isAsciiStart(C) || (C >= '0' && C <= '9');
60}
61
62static bool isAsciiIdentifier(llvm::StringRef S) {
63 if (S.empty())
64 return false;
65 if (!isAsciiStart(S[0]))
66 return false;
67 for (char C : S.drop_front())
68 if (!isAsciiContinue(C))
69 return false;
70 return true;
71}
72
73static AttributeStyle getAttributeStyle(llvm::Record *Instance) {
74 llvm::StringRef Style = Instance->getValueAsString("Style");
75 return llvm::StringSwitch<AttributeStyle>(Style)
76 .Case("cxx11", AttributeStyle::Cxx11)
77 .Case("gnu", AttributeStyle::Gnu)
78 .Case("declspec", AttributeStyle::Declspec)
79 .Default(AttributeStyle::Gnu);
80}
81
82static AttributeNamespace getAttributeNamespace(llvm::Record *Instance) {
83 llvm::StringRef Namespace = Instance->getValueAsString("Namespace");
84 return llvm::StringSwitch<AttributeNamespace>(Namespace)
85 .Case("clang", AttributeNamespace::Clang)
86 .Case("gnu", AttributeNamespace::Gnu)
87 .Default(AttributeNamespace::None);
88}
89
90using AttributeMap = llvm::DenseMap<llvm::StringRef, llvm::Record *>;
91
92template <class SpecMap, class FuncList>
93static AttributeMap collectAttributeMacros(const SpecMap &Spec,
94 const FuncList &Funcs) {
95 llvm::DenseMap<llvm::StringRef, llvm::Record *> MacroAttr;
96 for (const auto &Name : Funcs) {
97 auto Iter = Spec.find(Name);
98 if (Iter == Spec.end())
99 continue;
100
101 llvm::Record *FunctionSpec = Iter->second;
102 std::vector<llvm::Record *> Attributes =
103 FunctionSpec->getValueAsListOfDefs("Attributes");
104 for (llvm::Record *Attr : Attributes)
105 MacroAttr[Attr->getValueAsString("Macro")] = Attr;
106 }
107 return MacroAttr;
108}
109
110static void emitAttributeMacroDecls(const AttributeMap &MacroAttr,
111 llvm::raw_ostream &OS) {
112 for (auto &[Macro, Attr] : MacroAttr) {
113 std::vector<llvm::Record *> Instances =
114 Attr->getValueAsListOfDefs("Instances");
115 llvm::SmallVector<std::pair<AttributeStyle, llvm::Record *>> Styles;
116 std::transform(Instances.begin(), Instances.end(),
117 std::back_inserter(Styles),
118 [&](llvm::Record *Instance)
119 -> std::pair<AttributeStyle, llvm::Record *> {
120 auto Style = getAttributeStyle(Instance);
121 return {Style, Instance};
122 });
123 // 1. If __cplusplus is defined and cxx11 style is provided, define the
124 // macro using cxx11 version with the following priority:
125 // 1a. If there is no namespace (so the macro is supposed to be
126 // compiler-independent), use this version first. This macro will be
127 // tested via __has_cpp_attribute.
128 // 1b. If the attribute is a clang attribute, check for __clang__.
129 // 1c. If the attribute is a gnu attribute, check for __GNUC__.
130 // 2. Otherwise, if __GNUC__ is defined and gnu style is provided,
131 // define the macro using gnu version;
132 // 3. Otherwise, if _MSC_VER is defined and __declspec is provided, define
133 // the macro using __declspec version;
134 // 4. Fallback to empty macro.
135 std::sort(Styles.begin(), Styles.end(), [&](auto &a, auto &b) {
136 if (a.first == AttributeStyle::Cxx11 && b.first == AttributeStyle::Cxx11)
137 return getAttributeNamespace(a.second) <
138 getAttributeNamespace(b.second);
139 return a.first < b.first;
140 });
141 for (auto &[Style, Instance] : Styles) {
142 llvm::StringRef Attr = Instance->getValueAsString("Attr");
143 if (Style == AttributeStyle::Cxx11) {
144 OS << "#if !defined(" << Macro << ") && defined(__cplusplus)";
145 AttributeNamespace Namespace = getAttributeNamespace(Instance);
146 if (Namespace == AttributeNamespace::Clang)
147 OS << " && defined(__clang__)\n";
148 else if (Namespace == AttributeNamespace::Gnu)
149 OS << " && defined(__GNUC__)\n";
150 else
151 OS << '\n';
152 if (isAsciiIdentifier(Attr) && Namespace != AttributeNamespace::None)
153 OS << "#if __has_attribute(" << Attr << ")\n";
154 else
155 OS << "#if __has_cpp_attribute(" << Attr << ")\n";
156 OS << "#define " << Macro << " [[";
157 if (Namespace == AttributeNamespace::Clang)
158 OS << "clang::";
159 else if (Namespace == AttributeNamespace::Gnu)
160 OS << "gnu::";
161 OS << Attr << "]]\n";
162 if (isAsciiIdentifier(Attr))
163 OS << "#endif\n";
164 OS << "#endif\n";
165 }
166 if (Style == AttributeStyle::Gnu) {
167 OS << "#if !defined(" << Macro << ") && defined(__GNUC__)\n";
168 if (isAsciiIdentifier(Attr))
169 OS << "#if __has_attribute(" << Attr << ")\n";
170 OS << "#define " << Macro << " __attribute__((";
171 OS << Attr << "))\n";
172 if (isAsciiIdentifier(Attr))
173 OS << "#endif\n";
174 OS << "#endif\n";
175 }
176 if (Style == AttributeStyle::Declspec) {
177 OS << "#if !defined(" << Macro << ") && defined(_MSC_VER)\n";
178 OS << "#define " << Macro << " __declspec(";
179 OS << Attr << ")\n";
180 OS << "#endif\n";
181 }
182 }
183 OS << "#if !defined(" << Macro << ")\n";
184 OS << "#define " << Macro << '\n';
185 OS << "#endif\n";
186 }
187
188 if (!MacroAttr.empty())
189 OS << '\n';
190}
191
192static void emitAttributeMacroForFunction(const llvm::Record *FunctionSpec,
193 llvm::raw_ostream &OS) {
194 std::vector<llvm::Record *> Attributes =
195 FunctionSpec->getValueAsListOfDefs("Attributes");
196 llvm::interleave(
197 Attributes.begin(), Attributes.end(),
198 [&](llvm::Record *Attr) { OS << Attr->getValueAsString("Macro"); },
199 [&]() { OS << ' '; });
200 if (!Attributes.empty())
201 OS << ' ';
202}
203
204static void emitUndefsForAttributeMacros(const AttributeMap &MacroAttr,
205 llvm::raw_ostream &OS) {
206 if (!MacroAttr.empty())
207 OS << '\n';
208 for (auto &[Macro, Attr] : MacroAttr)
209 OS << "#undef " << Macro << '\n';
210}
211
212static void writeAPIFromIndex(APIIndexer &G,
213 std::vector<std::string> EntrypointNameList,
214 llvm::raw_ostream &OS) {
215 for (auto &Pair : G.MacroDefsMap) {
216 const std::string &Name = Pair.first;
217 if (!G.MacroSpecMap.count(Name))
218 llvm::PrintFatalError(Name + " not found in any standard spec.\n");
219
220 llvm::Record *MacroDef = Pair.second;
221 dedentAndWrite(MacroDef->getValueAsString("Defn"), OS);
222
223 OS << '\n';
224 }
225
226 for (auto &TypeName : G.RequiredTypes) {
227 if (!G.TypeSpecMap.count(TypeName))
228 llvm::PrintFatalError(TypeName + " not found in any standard spec.\n");
229 OS << "#include <llvm-libc-types/" << getTypeHdrName(Name: TypeName) << ".h>\n";
230 }
231 OS << '\n';
232
233 if (G.Enumerations.size() != 0)
234 OS << "enum {" << '\n';
235 for (const auto &Name : G.Enumerations) {
236 if (!G.EnumerationSpecMap.count(Name))
237 llvm::PrintFatalError(
238 Name + " is not listed as an enumeration in any standard spec.\n");
239
240 llvm::Record *EnumerationSpec = G.EnumerationSpecMap[Name];
241 OS << " " << EnumerationSpec->getValueAsString("Name");
242 auto Value = EnumerationSpec->getValueAsString("Value");
243 if (Value == "__default__") {
244 OS << ",\n";
245 } else {
246 OS << " = " << Value << ",\n";
247 }
248 }
249 if (G.Enumerations.size() != 0)
250 OS << "};\n\n";
251
252 // Collect and declare macros for attributes
253 AttributeMap MacroAttr =
254 collectAttributeMacros(G.FunctionSpecMap, EntrypointNameList);
255 emitAttributeMacroDecls(MacroAttr, OS);
256
257 OS << "__BEGIN_C_DECLS\n\n";
258 for (auto &Name : EntrypointNameList) {
259 auto Iter = G.FunctionSpecMap.find(x: Name);
260
261 // Functions that aren't in this header file are skipped as
262 // opposed to erroring out because the list of functions being
263 // iterated over is the complete list of functions with
264 // entrypoints. Thus this is filtering out the functions that
265 // don't go to this header file, whereas the other, similar
266 // conditionals above are more of a sanity check.
267 if (Iter == G.FunctionSpecMap.end())
268 continue;
269
270 llvm::Record *FunctionSpec = Iter->second;
271 llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return");
272 llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType");
273
274 // TODO: https://github.com/llvm/llvm-project/issues/81208
275 // Ideally, we should group functions based on their guarding macros.
276 bool Guarded =
277 (FunctionSpec->getType()->getAsString() == "GuardedFunctionSpec");
278
279 if (Guarded)
280 OS << "#ifdef " << FunctionSpec->getValueAsString("Guard") << "\n";
281
282 // Emit attribute macros for the function. Space is automatically added.
283 emitAttributeMacroForFunction(FunctionSpec, OS);
284 OS << G.getTypeAsString(TypeRecord: ReturnType) << " " << Name << "(";
285
286 auto ArgsList = FunctionSpec->getValueAsListOfDefs("Args");
287 for (size_t i = 0; i < ArgsList.size(); ++i) {
288 llvm::Record *ArgType = ArgsList[i]->getValueAsDef("ArgType");
289 OS << G.getTypeAsString(TypeRecord: ArgType);
290 if (i < ArgsList.size() - 1)
291 OS << ", ";
292 }
293
294 OS << ") __NOEXCEPT;\n";
295
296 if (Guarded)
297 OS << "#endif // " << FunctionSpec->getValueAsString("Guard") << "\n";
298
299 OS << "\n";
300 }
301
302 // Make another pass over entrypoints to emit object declarations.
303 for (const auto &Name : EntrypointNameList) {
304 auto Iter = G.ObjectSpecMap.find(x: Name);
305 if (Iter == G.ObjectSpecMap.end())
306 continue;
307 llvm::Record *ObjectSpec = Iter->second;
308 auto Type = ObjectSpec->getValueAsString("Type");
309 OS << "extern " << Type << " " << Name << ";\n";
310 }
311 OS << "__END_C_DECLS\n";
312
313 // Undef file-level attribute macros.
314 emitUndefsForAttributeMacros(MacroAttr, OS);
315}
316
317void writePublicAPI(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {}
318
319const char PublicAPICommand::Name[] = "public_api";
320
321void PublicAPICommand::run(llvm::raw_ostream &OS, const ArgVector &Args,
322 llvm::StringRef StdHeader,
323 llvm::RecordKeeper &Records,
324 const Command::ErrorReporter &Reporter) const {
325 if (Args.size() != 0)
326 Reporter.printFatalError("public_api command does not take any arguments.");
327
328 APIIndexer G(StdHeader, Records);
329 writeAPIFromIndex(G, EntrypointNameList, OS);
330}
331
332} // namespace llvm_libc
333

source code of libc/utils/HdrGen/PublicAPICommand.cpp