1 //===--------------- Implementation of PublicAPICommand ----------*-C++ -*-===//
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 "llvm/ADT/StringExtras.h"
12 #include "llvm/ADT/StringRef.h"
13 #include "llvm/Support/SourceMgr.h"
14 #include "llvm/TableGen/Error.h"
15 #include "llvm/TableGen/Record.h"
16 
17 static const char NamedTypeClassName[] = "NamedType";
18 static const char PtrTypeClassName[] = "PtrType";
19 static const char RestrictedPtrTypeClassName[] = "RestrictedPtrType";
20 static const char ConstTypeClassName[] = "ConstType";
21 static const char StructTypeClassName[] = "Struct";
22 
23 static const char StandardSpecClassName[] = "StandardSpec";
24 static const char PublicAPIClassName[] = "PublicAPI";
25 
26 static bool isa(llvm::Record *Def, llvm::Record *TypeClass) {
27   llvm::RecordRecTy *RecordType = Def->getType();
28   llvm::ArrayRef<llvm::Record *> Classes = RecordType->getClasses();
29   // We want exact types. That is, we don't want the classes listed in
30   // spec.td to be subclassed. Hence, we do not want the record |Def|
31   // to be of more than one class type..
32   if (Classes.size() != 1)
33     return false;
34   return Classes[0] == TypeClass;
35 }
36 
37 // Text blocks for macro definitions and type decls can be indented to
38 // suit the surrounding tablegen listing. We need to dedent such blocks
39 // before writing them out.
40 static void dedentAndWrite(llvm::StringRef Text, llvm::raw_ostream &OS) {
41   llvm::SmallVector<llvm::StringRef, 10> Lines;
42   llvm::SplitString(Text, Lines, "\n");
43   size_t shortest_indent = 1024;
44   for (llvm::StringRef L : Lines) {
45     llvm::StringRef Indent = L.take_while([](char c) { return c == ' '; });
46     size_t IndentSize = Indent.size();
47     if (Indent.size() == L.size()) {
48       // Line is all spaces so no point noting the indent.
49       continue;
50     }
51     if (IndentSize < shortest_indent)
52       shortest_indent = IndentSize;
53   }
54   for (llvm::StringRef L : Lines) {
55     if (L.size() >= shortest_indent)
56       OS << L.drop_front(shortest_indent) << '\n';
57   }
58 }
59 
60 class APIGenerator {
61   llvm::StringRef StdHeader;
62 
63   // TableGen classes in spec.td.
64   llvm::Record *NamedTypeClass;
65   llvm::Record *PtrTypeClass;
66   llvm::Record *RestrictedPtrTypeClass;
67   llvm::Record *ConstTypeClass;
68   llvm::Record *StructClass;
69   llvm::Record *StandardSpecClass;
70   llvm::Record *PublicAPIClass;
71 
72   using NameToRecordMapping = std::unordered_map<std::string, llvm::Record *>;
73   using NameSet = std::unordered_set<std::string>;
74 
75   // Mapping from names to records defining them.
76   NameToRecordMapping MacroSpecMap;
77   NameToRecordMapping TypeSpecMap;
78   NameToRecordMapping EnumerationSpecMap;
79   NameToRecordMapping FunctionSpecMap;
80   NameToRecordMapping MacroDefsMap;
81   NameToRecordMapping TypeDeclsMap;
82 
83   NameSet Structs;
84   NameSet Enumerations;
85   NameSet Functions;
86 
87   bool isaNamedType(llvm::Record *Def) { return isa(Def, NamedTypeClass); }
88 
89   bool isaStructType(llvm::Record *Def) { return isa(Def, StructClass); }
90 
91   bool isaPtrType(llvm::Record *Def) { return isa(Def, PtrTypeClass); }
92 
93   bool isaConstType(llvm::Record *Def) { return isa(Def, ConstTypeClass); }
94 
95   bool isaRestrictedPtrType(llvm::Record *Def) {
96     return isa(Def, RestrictedPtrTypeClass);
97   }
98 
99   bool isaStandardSpec(llvm::Record *Def) {
100     return isa(Def, StandardSpecClass);
101   }
102 
103   bool isaPublicAPI(llvm::Record *Def) { return isa(Def, PublicAPIClass); }
104 
105   std::string getTypeAsString(llvm::Record *TypeRecord) {
106     if (isaNamedType(TypeRecord) || isaStructType(TypeRecord)) {
107       return std::string(TypeRecord->getValueAsString("Name"));
108     } else if (isaPtrType(TypeRecord)) {
109       return getTypeAsString(TypeRecord->getValueAsDef("PointeeType")) + " *";
110     } else if (isaConstType(TypeRecord)) {
111       return std::string("const ") +
112              getTypeAsString(TypeRecord->getValueAsDef("UnqualifiedType"));
113     } else if (isaRestrictedPtrType(TypeRecord)) {
114       return getTypeAsString(TypeRecord->getValueAsDef("PointeeType")) +
115              " *__restrict";
116     } else {
117       llvm::PrintFatalError(TypeRecord->getLoc(), "Invalid type.\n");
118     }
119   }
120 
121   void indexStandardSpecDef(llvm::Record *StandardSpec) {
122     auto HeaderSpecList = StandardSpec->getValueAsListOfDefs("Headers");
123     for (llvm::Record *HeaderSpec : HeaderSpecList) {
124       if (HeaderSpec->getValueAsString("Name") == StdHeader) {
125         auto MacroSpecList = HeaderSpec->getValueAsListOfDefs("Macros");
126         // TODO: Trigger a fatal error on duplicate specs.
127         for (llvm::Record *MacroSpec : MacroSpecList)
128           MacroSpecMap[std::string(MacroSpec->getValueAsString("Name"))] =
129               MacroSpec;
130 
131         auto TypeSpecList = HeaderSpec->getValueAsListOfDefs("Types");
132         for (llvm::Record *TypeSpec : TypeSpecList)
133           TypeSpecMap[std::string(TypeSpec->getValueAsString("Name"))] =
134               TypeSpec;
135 
136         auto FunctionSpecList = HeaderSpec->getValueAsListOfDefs("Functions");
137         for (llvm::Record *FunctionSpec : FunctionSpecList) {
138           FunctionSpecMap[std::string(FunctionSpec->getValueAsString("Name"))] =
139               FunctionSpec;
140         }
141 
142         auto EnumerationSpecList =
143             HeaderSpec->getValueAsListOfDefs("Enumerations");
144         for (llvm::Record *EnumerationSpec : EnumerationSpecList) {
145           EnumerationSpecMap[std::string(
146               EnumerationSpec->getValueAsString("Name"))] = EnumerationSpec;
147         }
148       }
149     }
150   }
151 
152   void indexPublicAPIDef(llvm::Record *PublicAPI) {
153     // While indexing the public API, we do not check if any of the entities
154     // requested is from an included standard. Such a check is done while
155     // generating the API.
156     auto MacroDefList = PublicAPI->getValueAsListOfDefs("Macros");
157     for (llvm::Record *MacroDef : MacroDefList)
158       MacroDefsMap[std::string(MacroDef->getValueAsString("Name"))] = MacroDef;
159 
160     auto TypeDeclList = PublicAPI->getValueAsListOfDefs("TypeDeclarations");
161     for (llvm::Record *TypeDecl : TypeDeclList)
162       TypeDeclsMap[std::string(TypeDecl->getValueAsString("Name"))] = TypeDecl;
163 
164     auto StructList = PublicAPI->getValueAsListOfStrings("Structs");
165     for (llvm::StringRef StructName : StructList)
166       Structs.insert(std::string(StructName));
167 
168     auto FunctionList = PublicAPI->getValueAsListOfStrings("Functions");
169     for (llvm::StringRef FunctionName : FunctionList)
170       Functions.insert(std::string(FunctionName));
171 
172     auto EnumerationList = PublicAPI->getValueAsListOfStrings("Enumerations");
173     for (llvm::StringRef EnumerationName : EnumerationList)
174       Enumerations.insert(std::string(EnumerationName));
175   }
176 
177   void index(llvm::RecordKeeper &Records) {
178     NamedTypeClass = Records.getClass(NamedTypeClassName);
179     PtrTypeClass = Records.getClass(PtrTypeClassName);
180     RestrictedPtrTypeClass = Records.getClass(RestrictedPtrTypeClassName);
181     StructClass = Records.getClass(StructTypeClassName);
182     ConstTypeClass = Records.getClass(ConstTypeClassName);
183     StandardSpecClass = Records.getClass(StandardSpecClassName);
184     PublicAPIClass = Records.getClass(PublicAPIClassName);
185 
186     const auto &DefsMap = Records.getDefs();
187     for (auto &Pair : DefsMap) {
188       llvm::Record *Def = Pair.second.get();
189       if (isaStandardSpec(Def))
190         indexStandardSpecDef(Def);
191       if (isaPublicAPI(Def)) {
192         if (Def->getValueAsString("HeaderName") == StdHeader)
193           indexPublicAPIDef(Def);
194       }
195     }
196   }
197 
198 public:
199   APIGenerator(llvm::StringRef Header, llvm::RecordKeeper &Records)
200       : StdHeader(Header) {
201     index(Records);
202   }
203 
204   void write(llvm::raw_ostream &OS) {
205     for (auto &Pair : MacroDefsMap) {
206       const std::string &Name = Pair.first;
207       if (MacroSpecMap.find(Name) == MacroSpecMap.end())
208         llvm::PrintFatalError(Name + " not found in any standard spec.\n");
209 
210       llvm::Record *MacroDef = Pair.second;
211       dedentAndWrite(MacroDef->getValueAsString("Defn"), OS);
212 
213       OS << '\n';
214     }
215 
216     for (auto &Pair : TypeDeclsMap) {
217       const std::string &Name = Pair.first;
218       if (TypeSpecMap.find(Name) == TypeSpecMap.end())
219         llvm::PrintFatalError(Name + " not found in any standard spec.\n");
220 
221       llvm::Record *TypeDecl = Pair.second;
222       dedentAndWrite(TypeDecl->getValueAsString("Decl"), OS);
223 
224       OS << '\n';
225     }
226 
227     if (Enumerations.size() != 0)
228       OS << "enum {" << '\n';
229     for (const auto &Name : Enumerations) {
230       if (EnumerationSpecMap.find(Name) == EnumerationSpecMap.end())
231         llvm::PrintFatalError(
232             Name + " is not listed as an enumeration in any standard spec.\n");
233 
234       llvm::Record *EnumerationSpec = EnumerationSpecMap[Name];
235       OS << "  " << EnumerationSpec->getValueAsString("Name");
236       auto Value = EnumerationSpec->getValueAsString("Value");
237       if (Value == "__default__") {
238         OS << ",\n";
239       } else {
240         OS << " = " << Value << ",\n";
241       }
242     }
243     if (Enumerations.size() != 0)
244       OS << "};\n\n";
245 
246     OS << "__BEGIN_C_DECLS\n\n";
247     for (auto &Name : Functions) {
248       if (FunctionSpecMap.find(Name) == FunctionSpecMap.end())
249         llvm::PrintFatalError(Name + " not found in any standard spec.\n");
250 
251       llvm::Record *FunctionSpec = FunctionSpecMap[Name];
252       llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return");
253       llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType");
254 
255       OS << getTypeAsString(ReturnType) << " " << Name << "(";
256 
257       auto ArgsList = FunctionSpec->getValueAsListOfDefs("Args");
258       for (size_t i = 0; i < ArgsList.size(); ++i) {
259         llvm::Record *ArgType = ArgsList[i]->getValueAsDef("ArgType");
260         OS << getTypeAsString(ArgType);
261         if (i < ArgsList.size() - 1)
262           OS << ", ";
263       }
264 
265       OS << ");\n\n";
266     }
267     OS << "__END_C_DECLS\n";
268   }
269 };
270 
271 namespace llvm_libc {
272 
273 void writePublicAPI(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {}
274 
275 const char PublicAPICommand::Name[] = "public_api";
276 
277 void PublicAPICommand::run(llvm::raw_ostream &OS, const ArgVector &Args,
278                            llvm::StringRef StdHeader,
279                            llvm::RecordKeeper &Records,
280                            const Command::ErrorReporter &Reporter) const {
281   if (Args.size() != 0) {
282     Reporter.printFatalError("public_api command does not take any arguments.");
283   }
284 
285   APIGenerator G(StdHeader, Records);
286   G.write(OS);
287 }
288 
289 } // namespace llvm_libc
290