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 
validateOpts()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 
getMangledName()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 
writeAppendToFile(llvm::raw_ostream & OS)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 
getFunctionSpec(const llvm_libc::APIIndexer & Indexer)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 
writeFunctionHeader(llvm_libc::APIIndexer & Indexer,llvm::Record * FunctionSpec,llvm::raw_ostream & OS)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 
generateWrapper(llvm::raw_ostream & OS,llvm::RecordKeeper & Records)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 
generateAlias(llvm::raw_ostream & OS,llvm::RecordKeeper & Records)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 
wrapperGenMain(llvm::raw_ostream & OS,llvm::RecordKeeper & Records)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 
main(int argc,char * argv[])195 int main(int argc, char *argv[]) {
196   llvm::cl::ParseCommandLineOptions(argc, argv);
197   return TableGenMain(argv[0], wrapperGenMain);
198 }
199