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/MemoryBuffer.h" 14 #include "llvm/TableGen/Error.h" 15 #include "llvm/TableGen/Main.h" 16 17 #include <sstream> 18 #include <string> 19 20 llvm::cl::opt<std::string> 21 FunctionName("name", llvm::cl::desc("Name of the function to be wrapped."), 22 llvm::cl::value_desc("<function name>"), llvm::cl::Required); 23 llvm::cl::opt<std::string> 24 AliaseeString("aliasee", 25 llvm::cl::desc("Declare as an alias to this C name."), 26 llvm::cl::value_desc("<aliasee string>")); 27 llvm::cl::opt<std::string> 28 AliaseeFile("aliasee-file", 29 llvm::cl::desc("Declare as an alias to the C name read from " 30 "this file."), 31 llvm::cl::value_desc("<path to a file containing alias name>")); 32 llvm::cl::opt<std::string> 33 AppendToFile("append-to-file", 34 llvm::cl::desc("Append the generated content at the end of " 35 "the contents of this file."), 36 llvm::cl::value_desc("<path to a file>")); 37 38 static std::string GetAliaseeName() { 39 if (AliaseeString.size() > 0) 40 return AliaseeString; 41 42 auto ErrorOrBuf = llvm::MemoryBuffer::getFile(AliaseeFile); 43 if (!ErrorOrBuf) 44 llvm::PrintFatalError("Unable to read the aliasee file " + AliaseeFile); 45 llvm::StringRef FileContent = ErrorOrBuf.get()->getBuffer().trim(); 46 llvm::SmallVector<llvm::StringRef> Lines; 47 FileContent.split(Lines, '\n'); 48 for (llvm::StringRef L : Lines) { 49 if (L.contains("__llvm_libc")) 50 return std::string(L); 51 } 52 llvm::PrintFatalError("Did not find an LLVM libc mangled name in " + 53 AliaseeFile); 54 return std::string(); 55 } 56 57 static bool WrapperGenMain(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) { 58 if (!AliaseeString.empty() && !AliaseeFile.empty()) { 59 llvm::PrintFatalError("The options 'aliasee' and 'aliasee-file' cannot " 60 "be specified simultaniously."); 61 } 62 63 llvm_libc::APIIndexer Indexer(Records); 64 auto Iter = Indexer.FunctionSpecMap.find(FunctionName); 65 if (Iter == Indexer.FunctionSpecMap.end()) { 66 llvm::PrintFatalError("Function '" + FunctionName + 67 "' not found in any standard spec."); 68 } 69 70 bool EmitAlias = !(AliaseeString.empty() && AliaseeFile.empty()); 71 72 if (!EmitAlias && AppendToFile.empty()) { 73 // If not emitting an alias, and not appending to another file, 74 // we should include the implementation header to ensure the wrapper 75 // compiles. 76 // To avoid all confusion, we include the implementation header using the 77 // full path (relative to the libc directory.) 78 std::string Header = Indexer.FunctionToHeaderMap[FunctionName]; 79 auto RelPath = 80 llvm::StringRef(Header).drop_back(2); // Drop the ".h" suffix. 81 OS << "#include \"src/" << RelPath << "/" << FunctionName << ".h\"\n"; 82 } 83 if (!AppendToFile.empty()) { 84 auto ErrorOrBuf = llvm::MemoryBuffer::getFile(AppendToFile); 85 if (!ErrorOrBuf) { 86 llvm::PrintFatalError("Unable to read the file '" + AppendToFile + 87 "' to append to."); 88 } 89 OS << ErrorOrBuf.get()->getBuffer().trim() << '\n'; 90 } 91 92 auto &NameSpecPair = *Iter; 93 llvm::Record *FunctionSpec = NameSpecPair.second; 94 llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return"); 95 llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType"); 96 std::string ReturnTypeString = Indexer.getTypeAsString(ReturnType); 97 bool ShouldReturn = true; 98 // We are generating C wrappers in C++ code. So, we should convert the C 99 // _Noreturn to the C++ [[noreturn]]. 100 llvm::StringRef NR("_Noreturn "); // Note the space after _Noreturn 101 llvm::StringRef RT(ReturnTypeString); 102 if (RT.startswith(NR)) { 103 RT = RT.drop_front(NR.size() - 1); // - 1 because of the space. 104 ReturnTypeString = std::string("[[noreturn]]") + std::string(RT); 105 ShouldReturn = false; 106 } 107 OS << "extern \"C\" " << ReturnTypeString << " " << FunctionName << "("; 108 109 auto ArgsList = FunctionSpec->getValueAsListOfDefs("Args"); 110 std::stringstream CallArgs; 111 std::string ArgPrefix("__arg"); 112 for (size_t i = 0; i < ArgsList.size(); ++i) { 113 llvm::Record *ArgType = ArgsList[i]->getValueAsDef("ArgType"); 114 auto TypeName = Indexer.getTypeAsString(ArgType); 115 116 if (TypeName.compare("void") == 0) { 117 if (ArgsList.size() == 1) { 118 break; 119 } else { 120 // the reason this is a fatal error is that a void argument means this 121 // function has no arguments; multiple copies of no arguments is an 122 // error. 123 llvm::PrintFatalError( 124 "The specification for function " + FunctionName + 125 " lists other arguments along with a void argument."); 126 } 127 } 128 129 OS << TypeName << " " << ArgPrefix << i; 130 CallArgs << ArgPrefix << i; 131 if (i < ArgsList.size() - 1) { 132 OS << ", "; 133 CallArgs << ", "; 134 } 135 } 136 137 if (EmitAlias) { 138 OS << ") __attribute__((alias(\"" << GetAliaseeName() << "\")));\n"; 139 } else { 140 // TODO: Arg types of the C++ implementation functions need not 141 // match the standard types. Either handle such differences here, or 142 // avoid such a thing in the implementations. 143 OS << ") {\n" 144 << " " << (ShouldReturn ? "return " : "") 145 << "__llvm_libc::" << FunctionName << "(" << CallArgs.str() << ");\n" 146 << "}\n"; 147 } 148 return false; 149 } 150 151 int main(int argc, char *argv[]) { 152 llvm::cl::ParseCommandLineOptions(argc, argv); 153 return TableGenMain(argv[0], WrapperGenMain); 154 } 155