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. 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 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 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 OS << "__END_C_DECLS\n"; 118 } 119 120 void writePublicAPI(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {} 121 122 const char PublicAPICommand::Name[] = "public_api"; 123 124 void PublicAPICommand::run(llvm::raw_ostream &OS, const ArgVector &Args, 125 llvm::StringRef StdHeader, 126 llvm::RecordKeeper &Records, 127 const Command::ErrorReporter &Reporter) const { 128 if (Args.size() != 0) { 129 Reporter.printFatalError("public_api command does not take any arguments."); 130 } 131 132 APIIndexer G(StdHeader, Records); 133 writeAPIFromIndex(G, EntrypointNameList, OS); 134 } 135 136 } // namespace llvm_libc 137