//===-- "main" function of libc-wrappergen --------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "utils/LibcTableGenUtil/APIIndexer.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/JSON.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Main.h" #include #include #include #include llvm::cl::opt GenWrapper("gen-wrapper", llvm::cl::desc("Generate a C wrapper for .")); llvm::cl::opt GenAlias("gen-alias", llvm::cl::desc("Generate a C alias for .")); llvm::cl::opt FunctionName("name", llvm::cl::desc("Name of the function to be wrapped."), llvm::cl::value_desc(""), llvm::cl::Required); llvm::cl::opt MangledNameString( "mangled-name", llvm::cl::desc("Declare as an alias to this mangled name."), llvm::cl::value_desc("")); llvm::cl::opt MangledNameFile( "mangled-name-file", llvm::cl::desc("Declare as an alias to the C name read from " "this file."), llvm::cl::value_desc("")); llvm::cl::opt AppendToFile("append-to-file", llvm::cl::desc("Append the generated content at the end of " "the contents of this file."), llvm::cl::value_desc("")); void validateOpts() { int ActionCount = 0; if (GenWrapper) ++ActionCount; if (GenAlias) ++ActionCount; if (ActionCount != 1) { llvm::PrintFatalError("Exactly one of {--gen-wrapper, --gen-alias} " "should be specified"); } if (!MangledNameString.empty() && !MangledNameFile.empty()) { llvm::PrintFatalError("The options 'mangled-name' and 'mangled-name-file' " "cannot be specified simultaneously."); } } static std::string getMangledName() { if (!MangledNameString.empty()) return MangledNameString; if (MangledNameFile.empty()) llvm::PrintFatalError("At least one of --mangled-name or " "--mangled-name-file should be specified."); auto ErrorOrBuf = llvm::MemoryBuffer::getFile(MangledNameFile); if (!ErrorOrBuf) llvm::PrintFatalError("Unable to read the mangled name file " + MangledNameFile); llvm::StringRef FileContent = ErrorOrBuf.get()->getBuffer().trim(); llvm::SmallVector Lines; FileContent.split(Lines, '\n'); for (llvm::StringRef L : Lines) { if (L.contains("__llvm_libc")) return std::string(L); } llvm::PrintFatalError("Did not find an LLVM libc mangled name in " + MangledNameFile); return std::string(); } void writeAppendToFile(llvm::raw_ostream &OS) { auto ErrorOrBuf = llvm::MemoryBuffer::getFile(AppendToFile); if (!ErrorOrBuf) { llvm::PrintFatalError("Unable to read the file '" + AppendToFile + "' to append to."); } OS << ErrorOrBuf.get()->getBuffer().trim() << '\n'; } llvm::Record *getFunctionSpec(const llvm_libc::APIIndexer &Indexer) { auto Iter = Indexer.FunctionSpecMap.find(FunctionName); if (Iter == Indexer.FunctionSpecMap.end()) { llvm::PrintFatalError("Function '" + FunctionName + "' not found in any standard spec."); } auto &NameSpecPair = *Iter; return NameSpecPair.second; } std::pair writeFunctionHeader(llvm_libc::APIIndexer &Indexer, llvm::Record *FunctionSpec, llvm::raw_ostream &OS) { llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return"); llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType"); std::string ReturnTypeString = Indexer.getTypeAsString(ReturnType); bool ShouldReturn = true; // We are generating C wrappers in C++ code. So, we should convert the C // _Noreturn to the C++ [[noreturn]]. llvm::StringRef NR("_Noreturn "); // Note the space after _Noreturn llvm::StringRef RT(ReturnTypeString); if (RT.startswith(NR)) { RT = RT.drop_front(NR.size() - 1); // - 1 because of the space. ReturnTypeString = std::string("[[noreturn]]") + std::string(RT); ShouldReturn = false; } OS << "extern \"C\" " << ReturnTypeString << " " << FunctionName << "("; auto ArgsList = FunctionSpec->getValueAsListOfDefs("Args"); std::stringstream CallArgs; std::string ArgPrefix("__arg"); for (size_t i = 0; i < ArgsList.size(); ++i) { llvm::Record *ArgType = ArgsList[i]->getValueAsDef("ArgType"); auto TypeName = Indexer.getTypeAsString(ArgType); if (TypeName.compare("void") == 0) { if (ArgsList.size() == 1) { break; } else { // the reason this is a fatal error is that a void argument means this // function has no arguments; multiple copies of no arguments is an // error. llvm::PrintFatalError( "The specification for function " + FunctionName + " lists other arguments along with a void argument."); } } OS << TypeName << " " << ArgPrefix << i; CallArgs << ArgPrefix << i; if (i < ArgsList.size() - 1) { OS << ", "; CallArgs << ", "; } } return make_pair(CallArgs.str(), ShouldReturn); } static bool generateWrapper(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) { llvm_libc::APIIndexer Indexer(Records); llvm::Record *FunctionSpec = getFunctionSpec(Indexer); if (AppendToFile.empty()) { std::string Header = Indexer.FunctionToHeaderMap[FunctionName]; auto RelPath = llvm::StringRef(Header).drop_back(2); // Drop the ".h" suffix. OS << "#include \"src/" << RelPath << "/" << FunctionName << ".h\"\n"; } else { writeAppendToFile(OS); } auto Pair = writeFunctionHeader(Indexer, FunctionSpec, OS); OS << ") {\n" << " " << (Pair.second ? "return " : "") << "__llvm_libc::" << FunctionName << "(" << Pair.first << ");\n" << "}\n"; return false; } static bool generateAlias(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) { if (!AppendToFile.empty()) writeAppendToFile(OS); llvm_libc::APIIndexer Indexer(Records); llvm::Record *FunctionSpec = getFunctionSpec(Indexer); auto Pair = writeFunctionHeader(Indexer, FunctionSpec, OS); OS << ") __attribute__((alias(\"" << getMangledName() << "\")));\n"; return false; } static bool wrapperGenMain(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) { validateOpts(); if (GenWrapper) return generateWrapper(OS, Records); if (GenAlias) return generateAlias(OS, Records); __builtin_unreachable(); } int main(int argc, char *argv[]) { llvm::cl::ParseCommandLineOptions(argc, argv); return TableGenMain(argv[0], wrapperGenMain); }