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