1 //===-- "main" function of libc-wrappergen --------------------------------===// 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 "utils/LibcTableGenUtil/APIIndexer.h" 10 11 #include "llvm/ADT/StringRef.h" 12 #include "llvm/Support/CommandLine.h" 13 #include "llvm/Support/Error.h" 14 #include "llvm/Support/JSON.h" 15 #include "llvm/Support/MemoryBuffer.h" 16 #include "llvm/TableGen/Error.h" 17 #include "llvm/TableGen/Main.h" 18 19 #include <fstream> 20 #include <map> 21 #include <sstream> 22 #include <string> 23 24 llvm::cl::opt<bool> 25 GenWrapper("gen-wrapper", 26 llvm::cl::desc("Generate a C wrapper for <name>.")); 27 llvm::cl::opt<bool> GenAlias("gen-alias", 28 llvm::cl::desc("Generate a C alias for <name>.")); 29 30 llvm::cl::opt<std::string> 31 FunctionName("name", llvm::cl::desc("Name of the function to be wrapped."), 32 llvm::cl::value_desc("<function name>"), llvm::cl::Required); 33 llvm::cl::opt<std::string> MangledNameString( 34 "mangled-name", llvm::cl::desc("Declare as an alias to this mangled name."), 35 llvm::cl::value_desc("<aliasee string>")); 36 llvm::cl::opt<std::string> MangledNameFile( 37 "mangled-name-file", 38 llvm::cl::desc("Declare as an alias to the C name read from " 39 "this file."), 40 llvm::cl::value_desc("<path to a file containing alias name>")); 41 llvm::cl::opt<std::string> 42 AppendToFile("append-to-file", 43 llvm::cl::desc("Append the generated content at the end of " 44 "the contents of this file."), 45 llvm::cl::value_desc("<path to a file>")); 46 47 void validateOpts() { 48 int ActionCount = 0; 49 if (GenWrapper) 50 ++ActionCount; 51 if (GenAlias) 52 ++ActionCount; 53 if (ActionCount != 1) { 54 llvm::PrintFatalError("Exactly one of {--gen-wrapper, --gen-alias} " 55 "should be specified"); 56 } 57 if (!MangledNameString.empty() && !MangledNameFile.empty()) { 58 llvm::PrintFatalError("The options 'mangled-name' and 'mangled-name-file' " 59 "cannot be specified simultaneously."); 60 } 61 } 62 63 static std::string getMangledName() { 64 if (!MangledNameString.empty()) 65 return MangledNameString; 66 67 if (MangledNameFile.empty()) 68 llvm::PrintFatalError("At least one of --mangled-name or " 69 "--mangled-name-file should be specified."); 70 71 auto ErrorOrBuf = llvm::MemoryBuffer::getFile(MangledNameFile); 72 if (!ErrorOrBuf) 73 llvm::PrintFatalError("Unable to read the mangled name file " + 74 MangledNameFile); 75 llvm::StringRef FileContent = ErrorOrBuf.get()->getBuffer().trim(); 76 llvm::SmallVector<llvm::StringRef> Lines; 77 FileContent.split(Lines, '\n'); 78 for (llvm::StringRef L : Lines) { 79 if (L.contains("__llvm_libc")) 80 return std::string(L); 81 } 82 llvm::PrintFatalError("Did not find an LLVM libc mangled name in " + 83 MangledNameFile); 84 return std::string(); 85 } 86 87 void writeAppendToFile(llvm::raw_ostream &OS) { 88 auto ErrorOrBuf = llvm::MemoryBuffer::getFile(AppendToFile); 89 if (!ErrorOrBuf) { 90 llvm::PrintFatalError("Unable to read the file '" + AppendToFile + 91 "' to append to."); 92 } 93 OS << ErrorOrBuf.get()->getBuffer().trim() << '\n'; 94 } 95 96 llvm::Record *getFunctionSpec(const llvm_libc::APIIndexer &Indexer) { 97 auto Iter = Indexer.FunctionSpecMap.find(FunctionName); 98 if (Iter == Indexer.FunctionSpecMap.end()) { 99 llvm::PrintFatalError("Function '" + FunctionName + 100 "' not found in any standard spec."); 101 } 102 auto &NameSpecPair = *Iter; 103 return NameSpecPair.second; 104 } 105 106 std::pair<std::string, bool> writeFunctionHeader(llvm_libc::APIIndexer &Indexer, 107 llvm::Record *FunctionSpec, 108 llvm::raw_ostream &OS) { 109 llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return"); 110 llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType"); 111 std::string ReturnTypeString = Indexer.getTypeAsString(ReturnType); 112 bool ShouldReturn = true; 113 // We are generating C wrappers in C++ code. So, we should convert the C 114 // _Noreturn to the C++ [[noreturn]]. 115 llvm::StringRef NR("_Noreturn "); // Note the space after _Noreturn 116 llvm::StringRef RT(ReturnTypeString); 117 if (RT.startswith(NR)) { 118 RT = RT.drop_front(NR.size() - 1); // - 1 because of the space. 119 ReturnTypeString = std::string("[[noreturn]]") + std::string(RT); 120 ShouldReturn = false; 121 } 122 OS << "extern \"C\" " << ReturnTypeString << " " << FunctionName << "("; 123 124 auto ArgsList = FunctionSpec->getValueAsListOfDefs("Args"); 125 std::stringstream CallArgs; 126 std::string ArgPrefix("__arg"); 127 for (size_t i = 0; i < ArgsList.size(); ++i) { 128 llvm::Record *ArgType = ArgsList[i]->getValueAsDef("ArgType"); 129 auto TypeName = Indexer.getTypeAsString(ArgType); 130 131 if (TypeName.compare("void") == 0) { 132 if (ArgsList.size() == 1) { 133 break; 134 } else { 135 // the reason this is a fatal error is that a void argument means this 136 // function has no arguments; multiple copies of no arguments is an 137 // error. 138 llvm::PrintFatalError( 139 "The specification for function " + FunctionName + 140 " lists other arguments along with a void argument."); 141 } 142 } 143 144 OS << TypeName << " " << ArgPrefix << i; 145 CallArgs << ArgPrefix << i; 146 if (i < ArgsList.size() - 1) { 147 OS << ", "; 148 CallArgs << ", "; 149 } 150 } 151 return make_pair(CallArgs.str(), ShouldReturn); 152 } 153 154 static bool generateWrapper(llvm::raw_ostream &OS, 155 llvm::RecordKeeper &Records) { 156 llvm_libc::APIIndexer Indexer(Records); 157 llvm::Record *FunctionSpec = getFunctionSpec(Indexer); 158 if (AppendToFile.empty()) { 159 std::string Header = Indexer.FunctionToHeaderMap[FunctionName]; 160 auto RelPath = 161 llvm::StringRef(Header).drop_back(2); // Drop the ".h" suffix. 162 OS << "#include \"src/" << RelPath << "/" << FunctionName << ".h\"\n"; 163 } else { 164 writeAppendToFile(OS); 165 } 166 auto Pair = writeFunctionHeader(Indexer, FunctionSpec, OS); 167 OS << ") {\n" 168 << " " << (Pair.second ? "return " : "") 169 << "__llvm_libc::" << FunctionName << "(" << Pair.first << ");\n" 170 << "}\n"; 171 return false; 172 } 173 174 static bool generateAlias(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) { 175 if (!AppendToFile.empty()) 176 writeAppendToFile(OS); 177 llvm_libc::APIIndexer Indexer(Records); 178 llvm::Record *FunctionSpec = getFunctionSpec(Indexer); 179 auto Pair = writeFunctionHeader(Indexer, FunctionSpec, OS); 180 OS << ") __attribute__((alias(\"" << getMangledName() << "\")));\n"; 181 return false; 182 } 183 184 static bool wrapperGenMain(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) { 185 validateOpts(); 186 187 if (GenWrapper) 188 return generateWrapper(OS, Records); 189 if (GenAlias) 190 return generateAlias(OS, Records); 191 192 __builtin_unreachable(); 193 } 194 195 int main(int argc, char *argv[]) { 196 llvm::cl::ParseCommandLineOptions(argc, argv); 197 return TableGenMain(argv[0], wrapperGenMain); 198 } 199