1a32af825SSiva Chandra Reddy //===-- "main" function of libc-wrappergen --------------------------------===//
2a32af825SSiva Chandra Reddy //
3a32af825SSiva Chandra Reddy // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a32af825SSiva Chandra Reddy // See https://llvm.org/LICENSE.txt for license information.
5a32af825SSiva Chandra Reddy // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a32af825SSiva Chandra Reddy //
7a32af825SSiva Chandra Reddy //===----------------------------------------------------------------------===//
8a32af825SSiva Chandra Reddy 
9a32af825SSiva Chandra Reddy #include "utils/LibcTableGenUtil/APIIndexer.h"
10a32af825SSiva Chandra Reddy 
11a32af825SSiva Chandra Reddy #include "llvm/ADT/StringRef.h"
12a32af825SSiva Chandra Reddy #include "llvm/Support/CommandLine.h"
13*bf03eba1SSiva Chandra Reddy #include "llvm/Support/Error.h"
14*bf03eba1SSiva Chandra Reddy #include "llvm/Support/JSON.h"
15d50149b4SSiva Chandra Reddy #include "llvm/Support/MemoryBuffer.h"
16a32af825SSiva Chandra Reddy #include "llvm/TableGen/Error.h"
17a32af825SSiva Chandra Reddy #include "llvm/TableGen/Main.h"
18a32af825SSiva Chandra Reddy 
19*bf03eba1SSiva Chandra Reddy #include <fstream>
20*bf03eba1SSiva Chandra Reddy #include <map>
21a32af825SSiva Chandra Reddy #include <sstream>
22a32af825SSiva Chandra Reddy #include <string>
23a32af825SSiva Chandra Reddy 
24*bf03eba1SSiva Chandra Reddy llvm::cl::opt<bool>
25*bf03eba1SSiva Chandra Reddy     GenWrapper("gen-wrapper",
26*bf03eba1SSiva Chandra Reddy                llvm::cl::desc("Generate a C wrapper for <name>."));
27*bf03eba1SSiva Chandra Reddy llvm::cl::opt<bool> GenAlias("gen-alias",
28*bf03eba1SSiva Chandra Reddy                              llvm::cl::desc("Generate a C alias for <name>."));
29*bf03eba1SSiva Chandra Reddy 
30a32af825SSiva Chandra Reddy llvm::cl::opt<std::string>
31a32af825SSiva Chandra Reddy     FunctionName("name", llvm::cl::desc("Name of the function to be wrapped."),
32a32af825SSiva Chandra Reddy                  llvm::cl::value_desc("<function name>"), llvm::cl::Required);
33*bf03eba1SSiva Chandra Reddy llvm::cl::opt<std::string> MangledNameString(
34*bf03eba1SSiva Chandra Reddy     "mangled-name", llvm::cl::desc("Declare as an alias to this mangled name."),
35d50149b4SSiva Chandra Reddy     llvm::cl::value_desc("<aliasee string>"));
36*bf03eba1SSiva Chandra Reddy llvm::cl::opt<std::string> MangledNameFile(
37*bf03eba1SSiva Chandra Reddy     "mangled-name-file",
38d50149b4SSiva Chandra Reddy     llvm::cl::desc("Declare as an alias to the C name read from "
39d50149b4SSiva Chandra Reddy                    "this file."),
40d50149b4SSiva Chandra Reddy     llvm::cl::value_desc("<path to a file containing alias name>"));
41d50149b4SSiva Chandra Reddy llvm::cl::opt<std::string>
42d50149b4SSiva Chandra Reddy     AppendToFile("append-to-file",
43d50149b4SSiva Chandra Reddy                  llvm::cl::desc("Append the generated content at the end of "
44d50149b4SSiva Chandra Reddy                                 "the contents of this file."),
45d50149b4SSiva Chandra Reddy                  llvm::cl::value_desc("<path to a file>"));
46d50149b4SSiva Chandra Reddy 
validateOpts()47*bf03eba1SSiva Chandra Reddy void validateOpts() {
48*bf03eba1SSiva Chandra Reddy   int ActionCount = 0;
49*bf03eba1SSiva Chandra Reddy   if (GenWrapper)
50*bf03eba1SSiva Chandra Reddy     ++ActionCount;
51*bf03eba1SSiva Chandra Reddy   if (GenAlias)
52*bf03eba1SSiva Chandra Reddy     ++ActionCount;
53*bf03eba1SSiva Chandra Reddy   if (ActionCount != 1) {
54*bf03eba1SSiva Chandra Reddy     llvm::PrintFatalError("Exactly one of {--gen-wrapper, --gen-alias} "
55*bf03eba1SSiva Chandra Reddy                           "should be specified");
56*bf03eba1SSiva Chandra Reddy   }
57*bf03eba1SSiva Chandra Reddy   if (!MangledNameString.empty() && !MangledNameFile.empty()) {
58*bf03eba1SSiva Chandra Reddy     llvm::PrintFatalError("The options 'mangled-name' and 'mangled-name-file' "
59*bf03eba1SSiva Chandra Reddy                           "cannot be specified simultaneously.");
60*bf03eba1SSiva Chandra Reddy   }
61*bf03eba1SSiva Chandra Reddy }
62d50149b4SSiva Chandra Reddy 
getMangledName()63*bf03eba1SSiva Chandra Reddy static std::string getMangledName() {
64*bf03eba1SSiva Chandra Reddy   if (!MangledNameString.empty())
65*bf03eba1SSiva Chandra Reddy     return MangledNameString;
66*bf03eba1SSiva Chandra Reddy 
67*bf03eba1SSiva Chandra Reddy   if (MangledNameFile.empty())
68*bf03eba1SSiva Chandra Reddy     llvm::PrintFatalError("At least one of --mangled-name or "
69*bf03eba1SSiva Chandra Reddy                           "--mangled-name-file should be specified.");
70*bf03eba1SSiva Chandra Reddy 
71*bf03eba1SSiva Chandra Reddy   auto ErrorOrBuf = llvm::MemoryBuffer::getFile(MangledNameFile);
72d50149b4SSiva Chandra Reddy   if (!ErrorOrBuf)
73*bf03eba1SSiva Chandra Reddy     llvm::PrintFatalError("Unable to read the mangled name file " +
74*bf03eba1SSiva Chandra Reddy                           MangledNameFile);
759ab6c1a9SSiva Chandra Reddy   llvm::StringRef FileContent = ErrorOrBuf.get()->getBuffer().trim();
769ab6c1a9SSiva Chandra Reddy   llvm::SmallVector<llvm::StringRef> Lines;
779ab6c1a9SSiva Chandra Reddy   FileContent.split(Lines, '\n');
789ab6c1a9SSiva Chandra Reddy   for (llvm::StringRef L : Lines) {
799ab6c1a9SSiva Chandra Reddy     if (L.contains("__llvm_libc"))
809ab6c1a9SSiva Chandra Reddy       return std::string(L);
819ab6c1a9SSiva Chandra Reddy   }
829ab6c1a9SSiva Chandra Reddy   llvm::PrintFatalError("Did not find an LLVM libc mangled name in " +
83*bf03eba1SSiva Chandra Reddy                         MangledNameFile);
849ab6c1a9SSiva Chandra Reddy   return std::string();
85d50149b4SSiva Chandra Reddy }
86a32af825SSiva Chandra Reddy 
writeAppendToFile(llvm::raw_ostream & OS)87*bf03eba1SSiva Chandra Reddy void writeAppendToFile(llvm::raw_ostream &OS) {
88d50149b4SSiva Chandra Reddy   auto ErrorOrBuf = llvm::MemoryBuffer::getFile(AppendToFile);
89d50149b4SSiva Chandra Reddy   if (!ErrorOrBuf) {
90d50149b4SSiva Chandra Reddy     llvm::PrintFatalError("Unable to read the file '" + AppendToFile +
91d50149b4SSiva Chandra Reddy                           "' to append to.");
92d50149b4SSiva Chandra Reddy   }
93d50149b4SSiva Chandra Reddy   OS << ErrorOrBuf.get()->getBuffer().trim() << '\n';
94d50149b4SSiva Chandra Reddy }
95a32af825SSiva Chandra Reddy 
getFunctionSpec(const llvm_libc::APIIndexer & Indexer)96*bf03eba1SSiva Chandra Reddy llvm::Record *getFunctionSpec(const llvm_libc::APIIndexer &Indexer) {
97*bf03eba1SSiva Chandra Reddy   auto Iter = Indexer.FunctionSpecMap.find(FunctionName);
98*bf03eba1SSiva Chandra Reddy   if (Iter == Indexer.FunctionSpecMap.end()) {
99*bf03eba1SSiva Chandra Reddy     llvm::PrintFatalError("Function '" + FunctionName +
100*bf03eba1SSiva Chandra Reddy                           "' not found in any standard spec.");
101*bf03eba1SSiva Chandra Reddy   }
102a32af825SSiva Chandra Reddy   auto &NameSpecPair = *Iter;
103*bf03eba1SSiva Chandra Reddy   return NameSpecPair.second;
104*bf03eba1SSiva Chandra Reddy }
105*bf03eba1SSiva Chandra Reddy 
writeFunctionHeader(llvm_libc::APIIndexer & Indexer,llvm::Record * FunctionSpec,llvm::raw_ostream & OS)106*bf03eba1SSiva Chandra Reddy std::pair<std::string, bool> writeFunctionHeader(llvm_libc::APIIndexer &Indexer,
107*bf03eba1SSiva Chandra Reddy                                                  llvm::Record *FunctionSpec,
108*bf03eba1SSiva Chandra Reddy                                                  llvm::raw_ostream &OS) {
109a32af825SSiva Chandra Reddy   llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return");
110a32af825SSiva Chandra Reddy   llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType");
111573ade4bSSiva Chandra Reddy   std::string ReturnTypeString = Indexer.getTypeAsString(ReturnType);
112573ade4bSSiva Chandra Reddy   bool ShouldReturn = true;
113573ade4bSSiva Chandra Reddy   // We are generating C wrappers in C++ code. So, we should convert the C
114573ade4bSSiva Chandra Reddy   // _Noreturn to the C++ [[noreturn]].
115573ade4bSSiva Chandra Reddy   llvm::StringRef NR("_Noreturn "); // Note the space after _Noreturn
116573ade4bSSiva Chandra Reddy   llvm::StringRef RT(ReturnTypeString);
117573ade4bSSiva Chandra Reddy   if (RT.startswith(NR)) {
118573ade4bSSiva Chandra Reddy     RT = RT.drop_front(NR.size() - 1); // - 1 because of the space.
119573ade4bSSiva Chandra Reddy     ReturnTypeString = std::string("[[noreturn]]") + std::string(RT);
120573ade4bSSiva Chandra Reddy     ShouldReturn = false;
121573ade4bSSiva Chandra Reddy   }
122573ade4bSSiva Chandra Reddy   OS << "extern \"C\" " << ReturnTypeString << " " << FunctionName << "(";
123a32af825SSiva Chandra Reddy 
124a32af825SSiva Chandra Reddy   auto ArgsList = FunctionSpec->getValueAsListOfDefs("Args");
125a32af825SSiva Chandra Reddy   std::stringstream CallArgs;
126a32af825SSiva Chandra Reddy   std::string ArgPrefix("__arg");
127a32af825SSiva Chandra Reddy   for (size_t i = 0; i < ArgsList.size(); ++i) {
128a32af825SSiva Chandra Reddy     llvm::Record *ArgType = ArgsList[i]->getValueAsDef("ArgType");
129a32af825SSiva Chandra Reddy     auto TypeName = Indexer.getTypeAsString(ArgType);
1305c801de1SMichael Jones 
1315c801de1SMichael Jones     if (TypeName.compare("void") == 0) {
1325c801de1SMichael Jones       if (ArgsList.size() == 1) {
1335c801de1SMichael Jones         break;
1345c801de1SMichael Jones       } else {
1355c801de1SMichael Jones         // the reason this is a fatal error is that a void argument means this
1365c801de1SMichael Jones         // function has no arguments; multiple copies of no arguments is an
1375c801de1SMichael Jones         // error.
1385c801de1SMichael Jones         llvm::PrintFatalError(
1395c801de1SMichael Jones             "The specification for function " + FunctionName +
1405c801de1SMichael Jones             " lists other arguments along with a void argument.");
1415c801de1SMichael Jones       }
1425c801de1SMichael Jones     }
1435c801de1SMichael Jones 
144a32af825SSiva Chandra Reddy     OS << TypeName << " " << ArgPrefix << i;
145a32af825SSiva Chandra Reddy     CallArgs << ArgPrefix << i;
146a32af825SSiva Chandra Reddy     if (i < ArgsList.size() - 1) {
147a32af825SSiva Chandra Reddy       OS << ", ";
148a32af825SSiva Chandra Reddy       CallArgs << ", ";
149a32af825SSiva Chandra Reddy     }
150a32af825SSiva Chandra Reddy   }
151*bf03eba1SSiva Chandra Reddy   return make_pair(CallArgs.str(), ShouldReturn);
152d50149b4SSiva Chandra Reddy }
153*bf03eba1SSiva Chandra Reddy 
generateWrapper(llvm::raw_ostream & OS,llvm::RecordKeeper & Records)154*bf03eba1SSiva Chandra Reddy static bool generateWrapper(llvm::raw_ostream &OS,
155*bf03eba1SSiva Chandra Reddy                             llvm::RecordKeeper &Records) {
156*bf03eba1SSiva Chandra Reddy   llvm_libc::APIIndexer Indexer(Records);
157*bf03eba1SSiva Chandra Reddy   llvm::Record *FunctionSpec = getFunctionSpec(Indexer);
158*bf03eba1SSiva Chandra Reddy   if (AppendToFile.empty()) {
159*bf03eba1SSiva Chandra Reddy     std::string Header = Indexer.FunctionToHeaderMap[FunctionName];
160*bf03eba1SSiva Chandra Reddy     auto RelPath =
161*bf03eba1SSiva Chandra Reddy         llvm::StringRef(Header).drop_back(2); // Drop the ".h" suffix.
162*bf03eba1SSiva Chandra Reddy     OS << "#include \"src/" << RelPath << "/" << FunctionName << ".h\"\n";
163*bf03eba1SSiva Chandra Reddy   } else {
164*bf03eba1SSiva Chandra Reddy     writeAppendToFile(OS);
165*bf03eba1SSiva Chandra Reddy   }
166*bf03eba1SSiva Chandra Reddy   auto Pair = writeFunctionHeader(Indexer, FunctionSpec, OS);
167*bf03eba1SSiva Chandra Reddy   OS << ") {\n"
168*bf03eba1SSiva Chandra Reddy      << "  " << (Pair.second ? "return " : "")
169*bf03eba1SSiva Chandra Reddy      << "__llvm_libc::" << FunctionName << "(" << Pair.first << ");\n"
170*bf03eba1SSiva Chandra Reddy      << "}\n";
171a32af825SSiva Chandra Reddy   return false;
172a32af825SSiva Chandra Reddy }
173a32af825SSiva Chandra Reddy 
generateAlias(llvm::raw_ostream & OS,llvm::RecordKeeper & Records)174*bf03eba1SSiva Chandra Reddy static bool generateAlias(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {
175*bf03eba1SSiva Chandra Reddy   if (!AppendToFile.empty())
176*bf03eba1SSiva Chandra Reddy     writeAppendToFile(OS);
177*bf03eba1SSiva Chandra Reddy   llvm_libc::APIIndexer Indexer(Records);
178*bf03eba1SSiva Chandra Reddy   llvm::Record *FunctionSpec = getFunctionSpec(Indexer);
179*bf03eba1SSiva Chandra Reddy   auto Pair = writeFunctionHeader(Indexer, FunctionSpec, OS);
180*bf03eba1SSiva Chandra Reddy   OS << ") __attribute__((alias(\"" << getMangledName() << "\")));\n";
181*bf03eba1SSiva Chandra Reddy   return false;
182*bf03eba1SSiva Chandra Reddy }
183*bf03eba1SSiva Chandra Reddy 
wrapperGenMain(llvm::raw_ostream & OS,llvm::RecordKeeper & Records)184*bf03eba1SSiva Chandra Reddy static bool wrapperGenMain(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {
185*bf03eba1SSiva Chandra Reddy   validateOpts();
186*bf03eba1SSiva Chandra Reddy 
187*bf03eba1SSiva Chandra Reddy   if (GenWrapper)
188*bf03eba1SSiva Chandra Reddy     return generateWrapper(OS, Records);
189*bf03eba1SSiva Chandra Reddy   if (GenAlias)
190*bf03eba1SSiva Chandra Reddy     return generateAlias(OS, Records);
191*bf03eba1SSiva Chandra Reddy 
192*bf03eba1SSiva Chandra Reddy   __builtin_unreachable();
193*bf03eba1SSiva Chandra Reddy }
194*bf03eba1SSiva Chandra Reddy 
main(int argc,char * argv[])195a32af825SSiva Chandra Reddy int main(int argc, char *argv[]) {
196a32af825SSiva Chandra Reddy   llvm::cl::ParseCommandLineOptions(argc, argv);
197*bf03eba1SSiva Chandra Reddy   return TableGenMain(argv[0], wrapperGenMain);
198a32af825SSiva Chandra Reddy }
199