Skip to content

Commit

Permalink
[libc] Add a tool called WrapperGen.
Browse files Browse the repository at this point in the history
This tool will be used to generate C wrappers for the C++ LLVM libc
implementations. This change does not hook this tool up to anything yet.
However, it can be useful for cases where one does not want to run the
objcopy step (to insert the C symbol in the object file) but can make use
of LTO to eliminate the cost of the additional wrapper call. This can be
relevant for certain downstream platforms. If this tool can benefit other
libc platforms in general, then it can be integrated into the build system
with options to use or not use the wrappers. An example of such a
platform is CUDA.

Reviewed By: abrachet

Differential Revision: https://reviews.llvm.org/D84848
  • Loading branch information
Siva Chandra Reddy committed Jul 30, 2020
1 parent 8dfb5d7 commit a32af82
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 2 deletions.
1 change: 1 addition & 0 deletions libc/utils/CMakeLists.txt
Expand Up @@ -4,4 +4,5 @@ add_subdirectory(LibcTableGenUtil)
add_subdirectory(HdrGen)
add_subdirectory(MPFRWrapper)
add_subdirectory(testutils)
add_subdirectory(tools)
add_subdirectory(UnitTest)
5 changes: 3 additions & 2 deletions libc/utils/LibcTableGenUtil/APIIndexer.cpp
Expand Up @@ -97,8 +97,9 @@ void APIIndexer::indexStandardSpecDef(llvm::Record *StandardSpec) {

auto FunctionSpecList = HeaderSpec->getValueAsListOfDefs("Functions");
for (llvm::Record *FunctionSpec : FunctionSpecList) {
FunctionSpecMap[std::string(FunctionSpec->getValueAsString("Name"))] =
FunctionSpec;
auto FunctionName = std::string(FunctionSpec->getValueAsString("Name"));
FunctionSpecMap[FunctionName] = FunctionSpec;
FunctionToHeaderMap[FunctionName] = std::string(Header);
}

auto EnumerationSpecList =
Expand Down
2 changes: 2 additions & 0 deletions libc/utils/LibcTableGenUtil/APIIndexer.h
Expand Up @@ -65,6 +65,8 @@ class APIIndexer {
NameToRecordMapping MacroDefsMap;
NameToRecordMapping TypeDeclsMap;

std::unordered_map<std::string, std::string> FunctionToHeaderMap;

NameSet Structs;
NameSet Enumerations;
NameSet Functions;
Expand Down
1 change: 1 addition & 0 deletions libc/utils/tools/CMakeLists.txt
@@ -0,0 +1 @@
add_subdirectory(WrapperGen)
8 changes: 8 additions & 0 deletions libc/utils/tools/WrapperGen/CMakeLists.txt
@@ -0,0 +1,8 @@
set(LLVM_LINK_COMPONENTS Support)

add_tablegen(libc-wrappergen llvm-libc
Main.cpp
)

target_include_directories(libc-wrappergen PRIVATE ${LIBC_SOURCE_DIR})
target_link_libraries(libc-wrappergen PRIVATE LibcTableGenUtil)
72 changes: 72 additions & 0 deletions libc/utils/tools/WrapperGen/Main.cpp
@@ -0,0 +1,72 @@
//===-- "main" function of libc-wrappergen --------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "utils/LibcTableGenUtil/APIIndexer.h"

#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Main.h"

#include <sstream>
#include <string>

llvm::cl::opt<std::string>
FunctionName("name", llvm::cl::desc("Name of the function to be wrapped."),
llvm::cl::value_desc("<function name>"), llvm::cl::Required);

static bool WrapperGenMain(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {
llvm_libc::APIIndexer Indexer(Records);
auto Iter = Indexer.FunctionSpecMap.find(FunctionName);
if (Iter == Indexer.FunctionSpecMap.end()) {
llvm::PrintFatalError("Function '" + FunctionName +
"' not found in any standard spec.");
}

// To avoid all confusion, we include the implementation header using the
// full path (relative the libc directory.)
std::string Header = Indexer.FunctionToHeaderMap[FunctionName];
auto RelPath = llvm::StringRef(Header).drop_back(2); // Drop the ".h" suffix.
OS << "#include \"src/" << RelPath << "/" << FunctionName << ".h\"\n";

auto &NameSpecPair = *Iter;
llvm::Record *FunctionSpec = NameSpecPair.second;
llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return");
llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType");
OS << "extern \"C\" " << Indexer.getTypeAsString(ReturnType) << " "
<< FunctionName << "(";

auto ArgsList = FunctionSpec->getValueAsListOfDefs("Args");
std::stringstream CallArgs;
std::string ArgPrefix("__arg");
for (size_t i = 0; i < ArgsList.size(); ++i) {
llvm::Record *ArgType = ArgsList[i]->getValueAsDef("ArgType");
auto TypeName = Indexer.getTypeAsString(ArgType);
OS << TypeName << " " << ArgPrefix << i;
CallArgs << ArgPrefix << i;
if (i < ArgsList.size() - 1) {
OS << ", ";
CallArgs << ", ";
}
}

// TODO: Arg types of the C++ implementation functions need not
// match the standard types. Either handle such differences here, or
// avoid such a thing in the implementations.
OS << ") {\n"
<< " return __llvm_libc::" << FunctionName << "(" << CallArgs.str()
<< ");\n"
<< "}\n";

return false;
}

int main(int argc, char *argv[]) {
llvm::cl::ParseCommandLineOptions(argc, argv);
return TableGenMain(argv[0], WrapperGenMain);
}

0 comments on commit a32af82

Please sign in to comment.