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. |
26 | static 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 | |
46 | static 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 | |
52 | namespace llvm_libc { |
53 | |
54 | static bool isAsciiStart(char C) { |
55 | return (C >= 'A' && C <= 'Z') || (C >= 'a' && C <= 'z') || C == '_'; |
56 | } |
57 | |
58 | static bool isAsciiContinue(char C) { |
59 | return isAsciiStart(C) || (C >= '0' && C <= '9'); |
60 | } |
61 | |
62 | static 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 | |
73 | static 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 | |
82 | static 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 | |
90 | using AttributeMap = llvm::DenseMap<llvm::StringRef, llvm::Record *>; |
91 | |
92 | template <class SpecMap, class FuncList> |
93 | static 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 | |
110 | static 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 | |
192 | static 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 | |
204 | static 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 | |
212 | static 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 | |
317 | void writePublicAPI(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {} |
318 | |
319 | const char PublicAPICommand::Name[] = "public_api" ; |
320 | |
321 | void PublicAPICommand::run(llvm::raw_ostream &OS, const ArgVector &Args, |
322 | llvm::StringRef , |
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 | |