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/TableGen/Error.h"
14 #include "llvm/TableGen/Main.h"
15 
16 #include <sstream>
17 #include <string>
18 
19 llvm::cl::opt<std::string>
20     FunctionName("name", llvm::cl::desc("Name of the function to be wrapped."),
21                  llvm::cl::value_desc("<function name>"), llvm::cl::Required);
22 
23 static bool WrapperGenMain(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {
24   llvm_libc::APIIndexer Indexer(Records);
25   auto Iter = Indexer.FunctionSpecMap.find(FunctionName);
26   if (Iter == Indexer.FunctionSpecMap.end()) {
27     llvm::PrintFatalError("Function '" + FunctionName +
28                           "' not found in any standard spec.");
29   }
30 
31   // To avoid all confusion, we include the implementation header using the
32   // full path (relative the libc directory.)
33   std::string Header = Indexer.FunctionToHeaderMap[FunctionName];
34   auto RelPath = llvm::StringRef(Header).drop_back(2); // Drop the ".h" suffix.
35   OS << "#include \"src/" << RelPath << "/" << FunctionName << ".h\"\n";
36 
37   auto &NameSpecPair = *Iter;
38   llvm::Record *FunctionSpec = NameSpecPair.second;
39   llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return");
40   llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType");
41   std::string ReturnTypeString = Indexer.getTypeAsString(ReturnType);
42   bool ShouldReturn = true;
43   // We are generating C wrappers in C++ code. So, we should convert the C
44   // _Noreturn to the C++ [[noreturn]].
45   llvm::StringRef NR("_Noreturn "); // Note the space after _Noreturn
46   llvm::StringRef RT(ReturnTypeString);
47   if (RT.startswith(NR)) {
48     RT = RT.drop_front(NR.size() - 1); // - 1 because of the space.
49     ReturnTypeString = std::string("[[noreturn]]") + std::string(RT);
50     ShouldReturn = false;
51   }
52   OS << "extern \"C\" " << ReturnTypeString << " " << FunctionName << "(";
53 
54   auto ArgsList = FunctionSpec->getValueAsListOfDefs("Args");
55   std::stringstream CallArgs;
56   std::string ArgPrefix("__arg");
57   for (size_t i = 0; i < ArgsList.size(); ++i) {
58     llvm::Record *ArgType = ArgsList[i]->getValueAsDef("ArgType");
59     auto TypeName = Indexer.getTypeAsString(ArgType);
60 
61     if (TypeName.compare("void") == 0) {
62       if (ArgsList.size() == 1) {
63         break;
64       } else {
65         // the reason this is a fatal error is that a void argument means this
66         // function has no arguments; multiple copies of no arguments is an
67         // error.
68         llvm::PrintFatalError(
69             "The specification for function " + FunctionName +
70             " lists other arguments along with a void argument.");
71       }
72     }
73 
74     OS << TypeName << " " << ArgPrefix << i;
75     CallArgs << ArgPrefix << i;
76     if (i < ArgsList.size() - 1) {
77       OS << ", ";
78       CallArgs << ", ";
79     }
80   }
81 
82   // TODO: Arg types of the C++ implementation functions need not
83   // match the standard types. Either handle such differences here, or
84   // avoid such a thing in the implementations.
85   OS << ") {\n"
86      << "  " << (ShouldReturn ? "return " : "")
87      << "__llvm_libc::" << FunctionName << "(" << CallArgs.str() << ");\n"
88      << "}\n";
89 
90   return false;
91 }
92 
93 int main(int argc, char *argv[]) {
94   llvm::cl::ParseCommandLineOptions(argc, argv);
95   return TableGenMain(argv[0], WrapperGenMain);
96 }
97