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/SmallVector.h"
14 #include "llvm/ADT/StringExtras.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/Support/SourceMgr.h"
17 #include "llvm/TableGen/Record.h"
18 
19 // Text blocks for macro definitions and type decls can be indented to
20 // suit the surrounding tablegen listing. We need to dedent such blocks
21 // before writing them out.
dedentAndWrite(llvm::StringRef Text,llvm::raw_ostream & OS)22 static void dedentAndWrite(llvm::StringRef Text, llvm::raw_ostream &OS) {
23   llvm::SmallVector<llvm::StringRef, 10> Lines;
24   llvm::SplitString(Text, Lines, "\n");
25   size_t shortest_indent = 1024;
26   for (llvm::StringRef L : Lines) {
27     llvm::StringRef Indent = L.take_while([](char c) { return c == ' '; });
28     size_t IndentSize = Indent.size();
29     if (Indent.size() == L.size()) {
30       // Line is all spaces so no point noting the indent.
31       continue;
32     }
33     if (IndentSize < shortest_indent)
34       shortest_indent = IndentSize;
35   }
36   for (llvm::StringRef L : Lines) {
37     if (L.size() >= shortest_indent)
38       OS << L.drop_front(shortest_indent) << '\n';
39   }
40 }
41 
getTypeHdrName(const std::string & Name)42 static std::string getTypeHdrName(const std::string &Name) {
43   llvm::SmallVector<llvm::StringRef> Parts;
44   llvm::SplitString(llvm::StringRef(Name), Parts);
45   return llvm::join(Parts.begin(), Parts.end(), "_");
46 }
47 
48 namespace llvm_libc {
49 
writeAPIFromIndex(APIIndexer & G,std::vector<std::string> EntrypointNameList,llvm::raw_ostream & OS)50 void writeAPIFromIndex(APIIndexer &G,
51                        std::vector<std::string> EntrypointNameList,
52                        llvm::raw_ostream &OS) {
53   for (auto &Pair : G.MacroDefsMap) {
54     const std::string &Name = Pair.first;
55     if (G.MacroSpecMap.find(Name) == G.MacroSpecMap.end())
56       llvm::PrintFatalError(Name + " not found in any standard spec.\n");
57 
58     llvm::Record *MacroDef = Pair.second;
59     dedentAndWrite(MacroDef->getValueAsString("Defn"), OS);
60 
61     OS << '\n';
62   }
63 
64   for (auto &TypeName : G.RequiredTypes) {
65     if (G.TypeSpecMap.find(TypeName) == G.TypeSpecMap.end())
66       llvm::PrintFatalError(TypeName + " not found in any standard spec.\n");
67     OS << "#include <llvm-libc-types/" << getTypeHdrName(TypeName) << ".h>\n";
68   }
69   OS << '\n';
70 
71   if (G.Enumerations.size() != 0)
72     OS << "enum {" << '\n';
73   for (const auto &Name : G.Enumerations) {
74     if (G.EnumerationSpecMap.find(Name) == G.EnumerationSpecMap.end())
75       llvm::PrintFatalError(
76           Name + " is not listed as an enumeration in any standard spec.\n");
77 
78     llvm::Record *EnumerationSpec = G.EnumerationSpecMap[Name];
79     OS << "  " << EnumerationSpec->getValueAsString("Name");
80     auto Value = EnumerationSpec->getValueAsString("Value");
81     if (Value == "__default__") {
82       OS << ",\n";
83     } else {
84       OS << " = " << Value << ",\n";
85     }
86   }
87   if (G.Enumerations.size() != 0)
88     OS << "};\n\n";
89 
90   OS << "__BEGIN_C_DECLS\n\n";
91   for (auto &Name : EntrypointNameList) {
92     if (G.FunctionSpecMap.find(Name) == G.FunctionSpecMap.end()) {
93       continue; // Functions that aren't in this header file are skipped as
94                 // opposed to erroring out because the list of functions being
95                 // iterated over is the complete list of functions with
96                 // entrypoints. Thus this is filtering out the functions that
97                 // don't go to this header file, whereas the other, similar
98                 // conditionals above are more of a sanity check.
99     }
100 
101     llvm::Record *FunctionSpec = G.FunctionSpecMap[Name];
102     llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return");
103     llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType");
104 
105     OS << G.getTypeAsString(ReturnType) << " " << Name << "(";
106 
107     auto ArgsList = FunctionSpec->getValueAsListOfDefs("Args");
108     for (size_t i = 0; i < ArgsList.size(); ++i) {
109       llvm::Record *ArgType = ArgsList[i]->getValueAsDef("ArgType");
110       OS << G.getTypeAsString(ArgType);
111       if (i < ArgsList.size() - 1)
112         OS << ", ";
113     }
114 
115     OS << ");\n\n";
116   }
117 
118   // Make another pass over entrypoints to emit object declarations.
119   for (const auto &Name : EntrypointNameList) {
120     if (G.ObjectSpecMap.find(Name) == G.ObjectSpecMap.end())
121       continue;
122     llvm::Record *ObjectSpec = G.ObjectSpecMap[Name];
123     auto Type = ObjectSpec->getValueAsString("Type");
124     OS << "extern " << Type << " " << Name << ";\n";
125   }
126   OS << "__END_C_DECLS\n";
127 }
128 
writePublicAPI(llvm::raw_ostream & OS,llvm::RecordKeeper & Records)129 void writePublicAPI(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {}
130 
131 const char PublicAPICommand::Name[] = "public_api";
132 
run(llvm::raw_ostream & OS,const ArgVector & Args,llvm::StringRef StdHeader,llvm::RecordKeeper & Records,const Command::ErrorReporter & Reporter) const133 void PublicAPICommand::run(llvm::raw_ostream &OS, const ArgVector &Args,
134                            llvm::StringRef StdHeader,
135                            llvm::RecordKeeper &Records,
136                            const Command::ErrorReporter &Reporter) const {
137   if (Args.size() != 0) {
138     Reporter.printFatalError("public_api command does not take any arguments.");
139   }
140 
141   APIIndexer G(StdHeader, Records);
142   writeAPIFromIndex(G, EntrypointNameList, OS);
143 }
144 
145 } // namespace llvm_libc
146