diff --git a/libclc/cmake/modules/AddLibclc.cmake b/libclc/cmake/modules/AddLibclc.cmake index 1913399f06a73..c149673bce991 100644 --- a/libclc/cmake/modules/AddLibclc.cmake +++ b/libclc/cmake/modules/AddLibclc.cmake @@ -108,6 +108,9 @@ macro(add_libclc_builtin_set arch_suffix) # Generate remangled variants if requested if( LIBCLC_GENERATE_REMANGLED_VARIANTS ) + set(dummy_in "${CMAKE_BINARY_DIR}/lib/clc/libclc_dummy_in.cc") + add_custom_command( OUTPUT ${dummy_in} + COMMAND ${CMAKE_COMMAND} -E touch ${dummy_in} ) set(long_widths l32 l64) set(char_signedness signed unsigned) if( ${obj_suffix} STREQUAL "libspirv-nvptx64--nvidiacl.bc") @@ -128,10 +131,11 @@ macro(add_libclc_builtin_set arch_suffix) -o "${builtins_remangle_path}" --long-width=${long_width} --char-signedness=${signedness} - "$" - DEPENDS "${builtins_obj_path}" "prepare-${obj_suffix}" libclc-remangler ) + --input-ir="$" + ${dummy_in} + DEPENDS "${builtins_obj_path}" "prepare-${obj_suffix}" libclc-remangler ${dummy_in}) add_custom_target( "remangled-${long_width}-${signedness}_char.${obj_suffix_mangled}" ALL - DEPENDS "${builtins_remangle_path}" ) + DEPENDS "${builtins_remangle_path}" "${dummy_in}") set_target_properties("remangled-${long_width}-${signedness}_char.${obj_suffix_mangled}" PROPERTIES TARGET_FILE "${builtins_remangle_path}") diff --git a/libclc/test/CMakeLists.txt b/libclc/test/CMakeLists.txt index 80ec1f05d0264..2b5af7df26872 100644 --- a/libclc/test/CMakeLists.txt +++ b/libclc/test/CMakeLists.txt @@ -43,3 +43,27 @@ foreach( t ${LIBCLC_TARGET_TO_TEST} ) endforeach( d ) endforeach( t ) + +if(LIBCLC_GENERATE_REMANGLED_VARIANTS) + # Run remangler in test mode if generating remangled variants and make sure + # it depends on check-libclc target. + # Both `long_widths` and `char_signedness` are set in AddLibclc.cmake and can + # be used here. + foreach(long_width ${long_widths}) + foreach(signedness ${char_signedness}) + # In `-t` (TestRun) the remangler does not perform any substitutions, it + # needs to make sure that the remangled name matches the original mangled + # one. + set (test_target_name "test-remangled-${long_width}-${signedness}_char") + add_custom_target(${test_target_name} + COMMAND libclc-remangler + --long-width=${long_width} + --char-signedness=${signedness} + --input-ir="$" + ${dummy_in} -t -o - + DEPENDS "${builtins_obj_path}" "prepare-${obj_suffix}" "${dummy_in}" libclc-remangler) + + add_dependencies(check-libclc ${test_target_name}) + endforeach() + endforeach() +endif() diff --git a/libclc/utils/libclc-remangler/CMakeLists.txt b/libclc/utils/libclc-remangler/CMakeLists.txt index 7101129d6825f..cb162358f86d1 100644 --- a/libclc/utils/libclc-remangler/CMakeLists.txt +++ b/libclc/utils/libclc-remangler/CMakeLists.txt @@ -14,4 +14,12 @@ target_include_directories(libclc-remangler PRIVATE ${CMAKE_SOURCE_DIR}/../clang/include ${CMAKE_BINARY_DIR}/tools/clang/include) -clang_target_link_libraries(libclc-remangler PRIVATE clangBasic) +clang_target_link_libraries(libclc-remangler + PRIVATE + clangAST + clangBasic + clangFrontend + clangTooling + clangSerialization + LLVMOption + ) diff --git a/libclc/utils/libclc-remangler/LibclcRemangler.cpp b/libclc/utils/libclc-remangler/LibclcRemangler.cpp index 274840ee63c9f..8a8e72cb825ea 100644 --- a/libclc/utils/libclc-remangler/LibclcRemangler.cpp +++ b/libclc/utils/libclc-remangler/LibclcRemangler.cpp @@ -27,45 +27,32 @@ // Remangled Clone Example: // In cases where the remangled name squashes valid versions of a // function a clone is created. `f(long, char, signed char)` would be -// mangled to -// `_Z1flca`. The remangler would rename this function to `_Z1fyaa` -// (`f(long long, signed char, signed char)`). If the target uses a -// signed char then a valid clone `_Z1fyca`, -// (`f(long long, char, signed char)`), is not defined. The remangler -// creates a clone of the renamed function,`_Z1fyaa` , to this -// permutation, `_Z1fyca`. +// mangled to `_Z1flca`. The remangler would rename this function to +// `_Z1fxaa` (`f(long long, signed char, signed char)`). If the target +// uses a signed char then a valid clone `_Z1fxca`, (`f(long long, +// char, signed char)`), is not defined. The remangler creates a clone +// of the renamed function,`_Z1fxaa`, to this permutation, `_Z1fxca`. // //===----------------------------------------------------------------------===// -#include "clang/Basic/Diagnostic.h" -#include "clang/Basic/TargetInfo.h" -#include "clang/Basic/TargetOptions.h" -#include "llvm/ADT/STLExtras.h" +#include "clang/AST/Mangle.h" +#include "clang/AST/NestedNameSpecifier.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Tooling.h" #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/Demangle/ItaniumDemangle.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" -#include "llvm/IR/IntrinsicInst.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Type.h" -#include "llvm/Option/OptTable.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/InitLLVM.h" -#include "llvm/Support/Signals.h" #include "llvm/Support/ToolOutputFile.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/ValueMapper.h" #include -#include -#include using namespace clang; +using namespace clang::tooling; using namespace llvm; using namespace llvm::itanium_demangle; @@ -74,55 +61,44 @@ enum class SupportedLongWidth { L32, L64 }; static ExitOnError ExitOnErr; -static cl::opt InputFilename(cl::Positional, - cl::desc("")); +// Apply a custom category to all command-line options so that they are the +// only ones displayed. +static llvm::cl::OptionCategory + LibCLCRemanglerToolCategory("libclc-remangler-tool options"); + +static cl::opt + InputIRFilename("input-ir", cl::desc(""), + cl::cat(LibCLCRemanglerToolCategory)); static cl::opt OutputFilename("o", cl::desc("Output filename")); static cl::opt LongWidth("long-width", cl::values(clEnumValN(SupportedLongWidth::L32, "l32", "long is 32-bit wide."), clEnumValN(SupportedLongWidth::L64, "l64", - "long is 64-bit wide."))); + "long is 64-bit wide.")), + cl::cat(LibCLCRemanglerToolCategory)); static cl::opt CharSignedness( "char-signedness", cl::values(clEnumValN(Signedness::Signed, "signed", "char is signed."), clEnumValN(Signedness::Unsigned, "unsigned", - "char is unsigned."))); + "char is unsigned.")), + cl::cat(LibCLCRemanglerToolCategory)); static cl::opt Verbose("v", cl::desc("Enable verbose output"), - cl::init(false)); + cl::init(false), + cl::cat(LibCLCRemanglerToolCategory)); +static cl::opt TestRun("t", cl::desc("Enable test run"), cl::init(false), + cl::cat(LibCLCRemanglerToolCategory)); + +// CommonOptionsParser declares HelpMessage with a description of the common +// command-line options related to the compilation database and input files. +// It's nice to have this help message in all tools. +static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); namespace { class BumpPointerAllocator { - struct BlockMeta { - BlockMeta *Next; - size_t Current; - }; - - static constexpr size_t AllocSize = 4096; - static constexpr size_t UsableAllocSize = AllocSize - sizeof(BlockMeta); - - alignas(long double) char InitialBuffer[AllocSize]; - BlockMeta *BlockList = nullptr; - - void grow() { - char *NewMeta = static_cast(std::malloc(AllocSize)); - if (NewMeta == nullptr) - std::terminate(); - BlockList = new (NewMeta) BlockMeta{BlockList, 0}; - } - - void *allocateMassive(size_t NBytes) { - NBytes += sizeof(BlockMeta); - BlockMeta *NewMeta = reinterpret_cast(std::malloc(NBytes)); - if (NewMeta == nullptr) - std::terminate(); - BlockList->Next = new (NewMeta) BlockMeta{BlockList->Next, 0}; - return static_cast(NewMeta + 1); - } - public: BumpPointerAllocator() - : BlockList(new (InitialBuffer) BlockMeta{nullptr, 0}) {} + : BlockList(new(InitialBuffer) BlockMeta{nullptr, 0}) {} void *allocate(size_t N) { N = (N + 15u) & ~15u; @@ -147,331 +123,593 @@ class BumpPointerAllocator { } ~BumpPointerAllocator() { reset(); } + +private: + void grow() { + char *NewMeta = static_cast(std::malloc(AllocSize)); + if (NewMeta == nullptr) + std::terminate(); + BlockList = new (NewMeta) BlockMeta{BlockList, 0}; + } + + void *allocateMassive(size_t NBytes) { + NBytes += sizeof(BlockMeta); + BlockMeta *NewMeta = reinterpret_cast(std::malloc(NBytes)); + if (NewMeta == nullptr) + std::terminate(); + BlockList->Next = new (NewMeta) BlockMeta{BlockList->Next, 0}; + return static_cast(NewMeta + 1); + } + +private: + struct BlockMeta { + BlockMeta *Next; + size_t Current; + }; + + static constexpr size_t AllocSize = 4096; + static constexpr size_t UsableAllocSize = AllocSize - sizeof(BlockMeta); + + alignas(max_align_t) char InitialBuffer[AllocSize]; + BlockMeta *BlockList = nullptr; }; class DefaultAllocator { - BumpPointerAllocator Alloc; - public: void reset() { Alloc.reset(); } - template T *makeNode(Args &&...args) { - return new (Alloc.allocate(sizeof(T))) T(std::forward(args)...); + template T *makeNode(Args &&...A) { + return new (Alloc.allocate(sizeof(T))) T(std::forward(A)...); } - void *allocateNodeArray(size_t sz) { - return Alloc.allocate(sizeof(Node *) * sz); + void *allocateNodeArray(size_t Sz) { + return Alloc.allocate(sizeof(itanium_demangle::Node *) * Sz); } + +private: + BumpPointerAllocator Alloc; }; } // unnamed namespace using Demangler = ManglingParser; class Remangler { - const Node *Root; - SmallDenseMap TypeReplacements; - - SmallVector Subs; - bool Failed = false; - - void printNode(const Node *node, OutputBuffer &nodeOutBuffer) { - node->print(nodeOutBuffer); +public: + Remangler(ASTContext *ContextAST, const Node *Root, + SmallDenseMap TypeReplacements) + : ContextAST(ContextAST), Root(Root), TypeReplacements(TypeReplacements) { + MangleContext.reset(ItaniumMangleContext::create( + *ContextAST, ContextAST->getDiagnostics())); } - void addSub(const Node *node) { - OutputBuffer nodeOut; - printNode(node, nodeOut); - char *nodeOutBuf = nodeOut.getBuffer(); - auto nodeOutStr = - std::string(nodeOutBuf, nodeOutBuf + nodeOut.getCurrentPosition()); - free(nodeOutBuf); - Subs.push_back(nodeOutStr); - } + bool hasFailed() { return Failed; } - bool findSub(const Node *node, size_t *index) { - OutputBuffer nodeOut; - printNode(node, nodeOut); - char *nodeOutBuf = nodeOut.getBuffer(); - auto nodeOutStr = - std::string(nodeOutBuf, nodeOutBuf + nodeOut.getCurrentPosition()); - free(nodeOutBuf); - - for (size_t i = Subs.size(); i > 0; --i) { - if (nodeOutStr == Subs[i - 1]) { - *index = i - 1; - return true; - } - } - return false; + // Generate mangled function name, based on a given itanium_demangle `Node`. + std::string remangle() { + clang::QualType RetTy; + SmallVector TemplateArgTys; + SmallVector InputArgTys; + bool IsVariadic = false; + nodeToQualTypes(RetTy, TemplateArgTys, InputArgTys, IsVariadic); + auto *FD = createKernelDecl(RetTy, TemplateArgTys, InputArgTys, IsVariadic); + assert(MangleContext->shouldMangleDeclName(FD) && + "It should always be possible to mangle libclc func."); + + SmallString<256> Buffer; + raw_svector_ostream Out(Buffer); + MangleContext->mangleName(FD, Out); + return std::string{Out.str()}; } - bool remangleSub(const Node *node, OutputBuffer &OB) { - size_t index = 0; - if (findSub(node, &index)) { - OB << 'S'; - if (index != 0) - OB << index; - OB << '_'; - return true; +private: + // Helper struct to aggregate information about types. + struct NodeKindInfo { + NodeKindInfo(Node::Kind K) : K(K) {} + NodeKindInfo(Node::Kind K, size_t NumElemsOrAS) : K(K) { + Data = NumElemsOrAS; } - return false; - } - - void remangleOpenCLCName(const Node *nameNode, OutputBuffer &OB, - bool Substitutable, bool isNameRoot = true) { - if (Substitutable && remangleSub(nameNode, OB)) - return; - switch (nameNode->getKind()) { - case Node::Kind::KNameType: { - const NameType *name = static_cast(nameNode); - OB << name->getName().size(); - OB << name->getName(); - break; + NodeKindInfo(Node::Kind K, itanium_demangle::Qualifiers Quals) : K(K) { + Data = 0; + if (Quals & itanium_demangle::Qualifiers::QualConst) + Data |= clang::Qualifiers::TQ::Const; + if (Quals & itanium_demangle::Qualifiers::QualVolatile) + Data |= clang::Qualifiers::TQ::Volatile; + if (Quals & itanium_demangle::Qualifiers::QualRestrict) + Data |= clang::Qualifiers::TQ::Restrict; } - case Node::Kind::KNestedName: { - if (isNameRoot) - OB << 'N'; - const NestedName *nestedName = static_cast(nameNode); - remangleOpenCLCName(nestedName->Qual, OB, Substitutable, - /* isNameRoot= */ false); - remangleOpenCLCName(nestedName->Name, OB, /* Substitutable= */ false, - /* isNameRoot= */ false); - if (isNameRoot) - OB << 'E'; - break; - } - case Node::Kind::KNameWithTemplateArgs: { - const NameWithTemplateArgs *templateName = - static_cast(nameNode); - assert(templateName->TemplateArgs->getKind() == - Node::Kind::KTemplateArgs); - remangleOpenCLCName(templateName->Name, OB, /* Substitutable= */ false, - /* isNameRoot= */ false); - OB << 'I'; - const TemplateArgs *templateArgs = - static_cast(templateName->TemplateArgs); - for (auto templateArgType : templateArgs->getParams()) - remangleOpenCLCType(templateArgType, OB); - OB << 'E'; - break; + NodeKindInfo(Node::Kind K, const char *NestedName, size_t NestedNameSize) + : K(K) { + + DataStr.assign(NestedName, NestedNameSize); } - default: { - OutputBuffer errorTypeOut; - errorTypeOut << "Unhandled name : "; - nameNode->print(errorTypeOut); - errorTypeOut << "\n"; - errs() << errorTypeOut.getBuffer(); - free(errorTypeOut.getBuffer()); - Failed = true; + Node::Kind K; + size_t Data = 0; + std::string DataStr; + }; + +private: + // Construct FunctionDecl from return, argument and template types. + FunctionDecl *createKernelDecl( + clang::QualType RetTy, const SmallVector &TemplateArgTys, + const SmallVector &InputArgTys, bool IsVariadic) { + // Copy in InputArgTys as this function can mutate them. + auto ArgTys{InputArgTys}; + // Create this with a void ret no args prototype, will be fixed up after + // we've seen all the params. + FunctionProtoType::ExtProtoInfo Info(CC_OpenCLKernel); + Info.Variadic = IsVariadic; + clang::QualType const VoidFuncType = + ContextAST->getFunctionType(ContextAST->VoidTy, {}, Info); + FunctionDecl *FD = FunctionDecl::Create( + *ContextAST, ContextAST->getTranslationUnitDecl(), SourceLocation{}, + DeclarationNameInfo(), VoidFuncType, + ContextAST->getTrivialTypeSourceInfo(ContextAST->VoidTy), SC_None, + false, false, false, ConstexprSpecKind::Unspecified, nullptr); + FD->setImplicitlyInline(false); + + // Set the name. + const FunctionEncoding *Encoding = + static_cast(Root); + assert( + (Encoding->getName()->getKind() == Node::Kind::KNameType || + Encoding->getName()->getKind() == Node::Kind::KNameWithTemplateArgs) && + "Expected KNameType or KNameWithTemplateArgs node."); + std::string KernelNameStr; + if (Encoding->getName()->getKind() == Node::Kind::KNameType) { + auto *NT = static_cast(Encoding->getName()); + KernelNameStr.assign(NT->getBaseName().begin(), NT->getBaseName().size()); + } else { + auto *NT = static_cast(Encoding->getName()); + KernelNameStr.assign(NT->getBaseName().begin(), NT->getBaseName().size()); } + StringRef const KernelName(KernelNameStr); + FD->setDeclName(&ContextAST->Idents.get(KernelName)); + + // Construct the argument list. + SmallVector ArgParams; + for (auto &QT : ArgTys) { + auto &II = ContextAST->Idents.get(""); + auto *TTSI = ContextAST->getTrivialTypeSourceInfo(QT); + auto *NewParam = ParmVarDecl::Create(*ContextAST, FD, SourceLocation(), + SourceLocation(), &II, QT, TTSI, + SC_None, nullptr); + NewParam->setScopeInfo(0, ArgParams.size()); + NewParam->setDeclContext(FD); + ArgParams.push_back(NewParam); } - if (Substitutable) - addSub(nameNode); - } - void remangleOpenCLCTypeName(const NameType *typeName, OutputBuffer &OB) { - StringView name = typeName->getName(); - - auto it = TypeReplacements.find(name.begin()); - if (it != TypeReplacements.end()) - name = StringView(it->second); - - if (name == "void") - OB << 'v'; - else if (name == "wchar_t") - OB << 'w'; - else if (name == "bool") - OB << 'b'; - else if (name == "char") - OB << 'c'; - else if (name == "signed char") - OB << 'a'; - else if (name == "unsigned char") - OB << 'h'; - else if (name == "short") - OB << 's'; - else if (name == "unsigned short") - OB << 't'; - else if (name == "int") - OB << 'i'; - else if (name == "unsigned int") - OB << 'j'; - else if (name == "long") - OB << 'l'; - else if (name == "unsigned long") - OB << 'm'; - else if (name == "long long") - OB << 'x'; - else if (name == "unsigned long long") - OB << 'y'; - else if (name == "__int128") - OB << 'n'; - else if (name == "unsigned __int128") - OB << 'o'; - else if (name == "float") - OB << 'f'; - else if (name == "double") - OB << 'd'; - else if (name == "long double") - OB << 'e'; - else if (name == "__float128") - OB << 'g'; - else if (name == "...") - OB << 'z'; - // TODO: u - else if (name == "decimal64") - OB << "Dd"; - else if (name == "decimal128") - OB << "De"; - else if (name == "decimal32") - OB << "Df"; - else if (name == "decimal16") - OB << "Dh"; - else if (name == "char32_t") - OB << "Di"; - else if (name == "char16_t") - OB << "Ds"; - else if (name == "char8_t") - OB << "Du"; - else if (name == "_Float16") - OB << "DF16_"; - else if (name == "auto") - OB << 'a'; - else if (name == "decltype(auto)") - OB << 'c'; - else if (name == "std::nullptr_t") - OB << 'n'; - // Enum - else - remangleOpenCLCName(typeName, OB, /* Substitutable= */ true); - } + // If not templated, finish here. + if (TemplateArgTys.empty()) { + clang::QualType const FuncType = + ContextAST->getFunctionType(RetTy, ArgTys, Info); + FD->setType(FuncType); + FD->setParams(ArgParams); + return FD; + } - void remangleOpenCLCQualifiers(const itanium_demangle::Qualifiers quals, - OutputBuffer &OB) { - if (quals & QualConst) - OB << "K"; - if (quals & QualVolatile) - OB << "V"; - if (quals & QualRestrict) - OB << "r"; + // Use FD as a base for a future function specialisation. + FunctionDecl *FDSpecialization = FunctionDecl::Create( + *ContextAST, ContextAST->getTranslationUnitDecl(), SourceLocation{}, + DeclarationNameInfo(), VoidFuncType, + ContextAST->getTrivialTypeSourceInfo(ContextAST->VoidTy), SC_None, + false, false, false, ConstexprSpecKind::Unspecified, nullptr); + FDSpecialization->setImplicitlyInline(false); + + FDSpecialization->setDeclName(&ContextAST->Idents.get(KernelName)); + + // Will be used to build template parameter list. + SmallVector TemplateNamedDecls; + // Used for setting template specialisation. + SmallVector TemplateArguments; + // Used for the fixups. + SmallVector TemplateTypeParamTys; + unsigned TemplateIndex = 0; + for (auto &TemplateArgQT : TemplateArgTys) { + std::string const Name{std::string{"TempTy"} + + std::to_string(TemplateIndex)}; + auto &II = ContextAST->Idents.get(Name); + auto *TTPD = TemplateTypeParmDecl::Create( + *ContextAST, FDSpecialization->getDeclContext(), SourceLocation(), + SourceLocation(), 0, TemplateIndex, &II, /* Typename */ true, + /*ParameterPack*/ false); + TTPD->setDefaultArgument( + ContextAST->getTrivialTypeSourceInfo(TemplateArgQT)); + + TemplateNamedDecls.emplace_back(TTPD); + auto TA = TemplateArgument(TemplateArgQT); + TemplateArguments.emplace_back(TA); + + // Store this qualified type with newly created proper template type + // param qualified type. + TemplateTypeParamTys.push_back( + ContextAST->getTemplateTypeParmType(0, TemplateIndex, false, TTPD)); + + ++TemplateIndex; + } + // Fix up the template types in the original FD's arg tys and return ty. + auto AreQTsEqual = [&](const clang::QualType &LHS, + const clang::QualType &RHS) -> bool { + return (LHS.getBaseTypeIdentifier() && RHS.getBaseTypeIdentifier() && + LHS.getBaseTypeIdentifier()->isStr( + RHS.getBaseTypeIdentifier()->getName())) || + LHS == RHS; + }; + unsigned NumReplaced = 0; + unsigned Idx = 0; + for (auto &TemplateArgQT : TemplateArgTys) { + if (AreQTsEqual(TemplateArgQT, RetTy)) { + RetTy = TemplateTypeParamTys[Idx]; + goto Found; + } + for (unsigned i = 0; i < ArgTys.size(); ++i) { + if (AreQTsEqual(ArgTys[i], TemplateArgQT)) { + ArgTys[i] = TemplateTypeParamTys[Idx]; + goto Found; + } + } + Found: + ++NumReplaced; + ++Idx; + } + assert(NumReplaced >= TemplateTypeParamTys.size() && + "Expected full specialization."); + // Now that the template types have been patched up, set functions type. + clang::QualType const TemplateFuncType = + ContextAST->getFunctionType(RetTy, ArgTys, Info); + FD->setType(TemplateFuncType); + FD->setParams(ArgParams); + FDSpecialization->setType(TemplateFuncType); + FDSpecialization->setParams(ArgParams); + + auto *TPL = TemplateParameterList::Create( + *ContextAST, SourceLocation(), SourceLocation(), TemplateNamedDecls, + SourceLocation(), nullptr); + auto *FTD = FunctionTemplateDecl::Create(*ContextAST, FD->getDeclContext(), + SourceLocation(), + DeclarationName(), TPL, FD); + auto TAArr = + makeArrayRef(TemplateArguments.begin(), TemplateArguments.size()); + auto *TAL = TemplateArgumentList::CreateCopy(*ContextAST, TAArr); + FDSpecialization->setTemplateParameterListsInfo(*ContextAST, TPL); + FDSpecialization->setFunctionTemplateSpecialization( + FTD, TAL, nullptr, TSK_ExplicitSpecialization); + + return FDSpecialization; } - void remangleOpenCLCType(const Node *typeNode, OutputBuffer &OB) { - switch (typeNode->getKind()) { + // Peal off additional type info, such as CV qualifiers or pointers, by + // recursively calling itself. The information is appended to `PossibleKinds` + // vector. + // The base case is achieved in `handleLeafTypeNode`. + std::pair + handleTypeNode(const Node *TypeNode, + SmallVector &PossibleKinds) { + auto Kind = TypeNode->getKind(); + switch (Kind) { case Node::Kind::KPointerType: { - const itanium_demangle::PointerType *ptype = - static_cast(typeNode); - OB << 'P'; - remangleOpenCLCType(ptype->getPointee(), OB); - break; + PossibleKinds.push_back(NodeKindInfo(Kind)); + const itanium_demangle::PointerType *PType = + static_cast(TypeNode); + return handleTypeNode(PType->getPointee(), PossibleKinds); } case Node::Kind::KVectorType: { - if (remangleSub(typeNode, OB)) - return; - - const itanium_demangle::VectorType *vecType = - static_cast(typeNode); - assert(vecType->getDimension()->getKind() == Node::Kind::KNameType); - const NameType *dims = - static_cast(vecType->getDimension()); - OB << "Dv"; - OB << dims->getName(); - OB << '_'; - remangleOpenCLCType(vecType->getBaseType(), OB); - addSub(typeNode); - break; + const itanium_demangle::VectorType *VecType = + static_cast(TypeNode); + assert(VecType->getDimension()->getKind() == Node::Kind::KNameType); + const itanium_demangle::NameType *Dims = + static_cast( + VecType->getDimension()); + std::string const DN{Dims->getName().begin(), Dims->getName().size()}; + auto D = std::atoi(DN.c_str()); + PossibleKinds.push_back(NodeKindInfo(Kind, D)); + return handleTypeNode(VecType->getBaseType(), PossibleKinds); } case Node::Kind::KBinaryFPType: { - if (remangleSub(typeNode, OB)) - return; - - const BinaryFPType *BFPType = static_cast(typeNode); + const itanium_demangle::BinaryFPType *BFPType = + static_cast(TypeNode); assert(BFPType->getDimension()->getKind() == Node::Kind::KNameType); - const NameType *dims = - static_cast(BFPType->getDimension()); - - OB << "DF"; - OB << dims->getName(); - OB << '_'; - break; + const auto *NameTypeNode = + static_cast( + BFPType->getDimension()); + std::string const D{NameTypeNode->getBaseName().begin(), + NameTypeNode->getBaseName().size()}; + assert(D == "16" && "Unexpected binary floating point type."); + (void)D; + // BinaryFPType is encoded as: BinaryFPType(NameType("16")), manually + // construct "_Float16" NamedType node so we can pass it directly to + // handleLeafTypeNode. + NameType const FP16{"_Float16"}; + return handleLeafTypeNode(&FP16, PossibleKinds); } case Node::Kind::KVendorExtQualType: { - if (remangleSub(typeNode, OB)) - return; - - const VendorExtQualType *extQualType = - static_cast(typeNode); - OB << 'U'; - OB << extQualType->getExt().size(); - OB << extQualType->getExt(); - remangleOpenCLCType(extQualType->getTy(), OB); - addSub(typeNode); - break; + const itanium_demangle::VendorExtQualType *ExtQualType = + static_cast(TypeNode); + std::string const AS(ExtQualType->getExt().begin(), + ExtQualType->getExt().size()); + if (AS.rfind("AS", 0) != 0) { + assert(false && "Unexpected ExtQualType."); + break; + } + auto ASVal = std::atoi(AS.c_str() + 2); + PossibleKinds.push_back(NodeKindInfo(Kind, ASVal)); + return handleTypeNode(ExtQualType->getTy(), PossibleKinds); } case Node::Kind::KQualType: { - const itanium_demangle::QualType *qtype = - static_cast(typeNode); - remangleOpenCLCQualifiers(qtype->getQuals(), OB); - remangleOpenCLCType(qtype->getChild(), OB); - break; + const itanium_demangle::QualType *QType = + static_cast(TypeNode); + auto Qual = QType->getQuals(); + PossibleKinds.push_back(NodeKindInfo(Kind, Qual)); + return handleTypeNode(QType->getChild(), PossibleKinds); } case Node::Kind::KNameType: { - const NameType *typeName = static_cast(typeNode); - remangleOpenCLCTypeName(typeName, OB); - break; + const itanium_demangle::NameType *TypeName = + static_cast(TypeNode); + return handleLeafTypeNode(TypeName, PossibleKinds); } case Node::Kind::KNestedName: { - // Enum type with nested name - remangleOpenCLCName(typeNode, OB, /* Substitutable= */ true); - break; + const itanium_demangle::NestedName *NestedName = + static_cast(TypeNode); + OutputBuffer NestedNameBuff; + NestedName->Qual->print(NestedNameBuff); + PossibleKinds.push_back( + NodeKindInfo(Kind, NestedNameBuff.getBuffer(), + NestedNameBuff.getCurrentPosition())); + const itanium_demangle::NameType *TypeName = + static_cast(NestedName->Name); + return handleLeafTypeNode(TypeName, PossibleKinds); } default: { - OutputBuffer errorTypeOut; - errorTypeOut << "Unhandled type : "; - typeNode->print(errorTypeOut); - errorTypeOut << "\n"; - errs() << errorTypeOut.getBuffer(); - free(errorTypeOut.getBuffer()); + OutputBuffer ErrorTypeOut; + ErrorTypeOut << "Unhandled type : "; + TypeNode->print(ErrorTypeOut); + ErrorTypeOut << "\n"; + errs() << ErrorTypeOut.getBuffer(); + free(ErrorTypeOut.getBuffer()); Failed = true; } } + + llvm_unreachable("Unhandled type."); + return std::make_pair(clang::QualType{}, false); } - void remangleOpenCLCFunction(const Node *root, OutputBuffer &OB) { - assert(root->getKind() == Node::Kind::KFunctionEncoding); - OB << "_Z"; + // Handle undecorated type that can be matched against `QualType`, also + // returning if variadic. + std::pair + handleLeafTypeNode(const NameType *NT, + SmallVector &PossibleKinds) { + StringView Name = NT->getName(); + + // When in test run, don't enable replacements and assert that re-mangled + // name matches the original. + if (!TestRun) { + auto It = TypeReplacements.find(Name.begin()); + if (It != TypeReplacements.end()) + Name = StringView(It->second); + } - const FunctionEncoding *encoding = - static_cast(root); + clang::QualType Res; + bool IsVariadic = false; + + // First find the match against `QualType`... + if (Name == "void") + Res = ContextAST->VoidTy; + else if (Name == "wchar_t") + Res = ContextAST->WCharTy; + else if (Name == "bool") + Res = ContextAST->BoolTy; + else if (Name == "char") + Res = ContextAST->CharTy; + else if (Name == "signed char") + Res = ContextAST->SignedCharTy; + else if (Name == "unsigned char") + Res = ContextAST->UnsignedCharTy; + else if (Name == "short") + Res = ContextAST->ShortTy; + else if (Name == "unsigned short") + Res = ContextAST->UnsignedShortTy; + else if (Name == "int") + Res = ContextAST->IntTy; + else if (Name == "unsigned int") + Res = ContextAST->UnsignedIntTy; + else if (Name == "long") + Res = ContextAST->LongTy; + else if (Name == "unsigned long") + Res = ContextAST->UnsignedLongTy; + else if (Name == "long long") + Res = ContextAST->LongLongTy; + else if (Name == "unsigned long long") + Res = ContextAST->UnsignedLongLongTy; + else if (Name == "__int128") + Res = ContextAST->Int128Ty; + else if (Name == "unsigned __int128") + Res = ContextAST->UnsignedInt128Ty; + else if (Name == "float") + Res = ContextAST->FloatTy; + else if (Name == "double") + Res = ContextAST->DoubleTy; + else if (Name == "long double") + Res = ContextAST->LongDoubleTy; + else if (Name == "__float128") + Res = ContextAST->Float128Ty; + else if (Name == "...") { + Res = clang::QualType{}; + IsVariadic = true; + } else if (Name == "decimal64") + assert(false && "unhandled type name: decimal64"); + else if (Name == "decimal128") + assert(false && "unhandled type name: decimal128"); + else if (Name == "decimal32") + assert(false && "unhandled type name: decimal32"); + else if (Name == "decimal16") + assert(false && "unhandled type name: decimal16"); + else if (Name == "char32_t") + Res = ContextAST->Char32Ty; + else if (Name == "char16_t") + Res = ContextAST->Char16Ty; + else if (Name == "char8_t") + Res = ContextAST->Char8Ty; + else if (Name == "_Float16") + Res = ContextAST->Float16Ty; + else if (Name == "half") + Res = ContextAST->HalfTy; + else if (Name == "auto") + Res = ContextAST->AutoDeductTy; + else if (Name == "decltype(auto)") + assert(false && "unhandled type name: decltype(auto)"); + else if (Name == "std::nullptr_t") + Res = ContextAST->NullPtrTy; + else if (Name == "_BitInt") + assert(false && "unhandled type name: _BitInt"); + else { + StringRef const N{Name.begin(), Name.size()}; + auto &II = ContextAST->Idents.get(N); + auto *DC = ContextAST->getTranslationUnitDecl(); + auto *ED = + EnumDecl::Create(*ContextAST, DC, SourceLocation(), SourceLocation(), + &II, nullptr, false, false, true); + Res = ContextAST->getEnumType(ED); + } - remangleOpenCLCName(encoding->getName(), OB, /* Substitutable= */ false); + // then apply gathered information to that `QualType`. + + // Handle `KNestedName` first, as it will create a new `QualType`. + auto KNNMatcher = [](NodeKindInfo &NKI) { + return NKI.K == Node::Kind::KNestedName; + }; + auto *KNN = + std::find_if(PossibleKinds.begin(), PossibleKinds.end(), KNNMatcher); + if (KNN != PossibleKinds.end()) { + assert(PossibleKinds.end() == std::find_if(std::next(KNN), + PossibleKinds.end(), + KNNMatcher) && + "Expected only one KNestedName kind."); + + // Construct the full name to check if it has already been handled. + std::string const N{KNN->DataStr + " " + + Res.getBaseTypeIdentifier()->getName().str()}; + if (NestedNamesQTMap.count(N) == 0) { + assert(KNN->DataStr.rfind("__spv::", 0) == 0 && + "Unexpected nested prefix"); + SourceLocation const SL{}; + RecordDecl *RD = nullptr; + if (!SpvNamespace) + SpvNamespace = NamespaceDecl::Create( + *ContextAST, ContextAST->getTranslationUnitDecl(), false, SL, SL, + &ContextAST->Idents.get("__spv", tok::TokenKind::identifier), + nullptr, false); + std::string StructName{KNN->DataStr}; + StructName.erase(0, StructName.find_first_not_of("__spv::")); + auto *II = + &ContextAST->Idents.get(StructName, tok::TokenKind::identifier); + RD = RecordDecl::Create(*ContextAST, TTK_Struct, SpvNamespace, SL, SL, + II); + auto *NNS = + NestedNameSpecifier::Create(*ContextAST, nullptr, SpvNamespace); + auto RecordQT = ContextAST->getRecordType(RD); + NNS = NestedNameSpecifier::Create(*ContextAST, NNS, false, + RecordQT.getTypePtr()); + auto &EnumName = + ContextAST->Idents.get(Res.getBaseTypeIdentifier()->getName()); + // We need to recreate the enum, now that we have access to all the + // namespace/class info. + auto *ED = EnumDecl::Create(*ContextAST, RD, SourceLocation(), + SourceLocation(), &EnumName, nullptr, false, + false, true); + Res = ContextAST->getEnumType(ED); + Res = ContextAST->getElaboratedType(ETK_None, NNS, Res); + // Store the elaborated type for reuse, this is important as clang uses + // substitutions for ET based on the object not the name enclosed in. + NestedNamesQTMap[N] = Res; + } else + Res = NestedNamesQTMap[N]; + } - if (encoding->getReturnType()) - remangleOpenCLCType(encoding->getReturnType(), OB); + // Iterate in reversed order to preserve the semantics. + for (auto I = PossibleKinds.rbegin(); I != PossibleKinds.rend(); ++I) { + switch (I->K) { + case Node::Kind::KPointerType: { + Res = ContextAST->getPointerType(Res); + break; + } + case Node::Kind::KVectorType: { + Res = ContextAST->getVectorType( + Res, I->Data, clang::VectorType::VectorKind::GenericVector); + break; + } + case Node::Kind::KQualType: { + auto Quals = clang::Qualifiers::fromFastMask(I->Data); + Res = ContextAST->getQualifiedType(Res, Quals); + break; + } + case Node::Kind::KVendorExtQualType: { + auto AS = getLangASFromTargetAS(I->Data); + Res = ContextAST->getAddrSpaceQualType(Res, AS); + break; + } + case Node::Kind::KNestedName: { + // Handled already. + break; + } + default: { + llvm_unreachable("Unexpected Node Kind."); + } + } + } - for (const Node *paramType : encoding->getParams()) - remangleOpenCLCType(paramType, OB); + return std::make_pair(Res, IsVariadic); + } - if (encoding->getParams().size() == 0) - OB << 'v'; + // Traverse the itanium_demangle node and generate QualTypes corresponding to + // the function's return type, input arguments and template params. + void nodeToQualTypes(clang::QualType &RetTy, + SmallVector &TemplateArgTys, + SmallVector &ArgTys, bool &IsVariadic) { + const FunctionEncoding *Encoding = + static_cast(Root); + + SmallVector PossibleKinds; + if (Encoding->getReturnType()) { + RetTy = + std::get<0>(handleTypeNode(Encoding->getReturnType(), PossibleKinds)); + } else + RetTy = ContextAST->VoidTy; + + if (Encoding->getName()->getKind() == Node::Kind::KNameWithTemplateArgs) { + const NameWithTemplateArgs *NWTA = + static_cast(Encoding->getName()); + assert(NWTA->getKind() == Node::Kind::KNameWithTemplateArgs); + const TemplateArgs *TA = + static_cast(NWTA->TemplateArgs); + for (auto *TPT : TA->getParams()) { + PossibleKinds.clear(); + auto Res = handleTypeNode(TPT, PossibleKinds); + assert(!std::get<1>(Res) && "Variadic in template params."); + TemplateArgTys.push_back(std::get<0>(Res)); + } + } + + for (auto *PT : Encoding->getParams()) { + PossibleKinds.clear(); + auto Res = handleTypeNode(PT, PossibleKinds); + if (std::get<1>(Res)) { + IsVariadic = true; + continue; + } + ArgTys.push_back(std::get<0>(Res)); + } } -public: - Remangler(const Node *root, - SmallDenseMap typeReplacements) - : Root(root), TypeReplacements(typeReplacements) {} +private: + ASTContext *ContextAST = nullptr; + std::unique_ptr MangleContext{}; + const Node *Root = nullptr; + SmallDenseMap TypeReplacements{}; - bool hasFailed() { return Failed; } + bool Failed = false; - std::string remangle() { - Subs.clear(); - OutputBuffer remanglingStream; - remangleOpenCLCFunction(Root, remanglingStream); - std::string remangled = std::string(remanglingStream.getBuffer(), - remanglingStream.getCurrentPosition()); - free(remanglingStream.getBuffer()); - return remangled; - } + std::map NestedNamesQTMap{}; + NamespaceDecl *SpvNamespace = nullptr; }; class TargetTypeReplacements { @@ -479,8 +717,9 @@ class TargetTypeReplacements { SmallDenseMap CloneTypeReplacements; SmallDenseMap RemangledCloneTypeReplacements; - void CreateRemangledTypeReplacements() { - // RemangleTypes which are not aliases or not the exact same alias type + void createRemangledTypeReplacements() { + // RemangleTypes which are not aliases or not the exact same alias + // type for (auto &TypeReplacementPair : ParameterTypeReplacements) if (CloneTypeReplacements.find(TypeReplacementPair.getFirst()) == CloneTypeReplacements.end()) @@ -501,8 +740,8 @@ class TargetTypeReplacements { // Replace char with signed char ParameterTypeReplacements["char"] = "signed char"; - // Make replaced long functions clones of either integer or long long - // variant + // Make replaced long functions clones of either integer or long + // long variant if (LongWidth == SupportedLongWidth::L32) { CloneTypeReplacements["long"] = "int"; CloneTypeReplacements["unsigned long"] = "unsigned int"; @@ -511,15 +750,15 @@ class TargetTypeReplacements { CloneTypeReplacements["unsigned long"] = "unsigned long long"; } - // Make replaced char functions clones of either integer or long long - // variant + // Make replaced char functions clones of either integer or long + // long variant if (CharSignedness == Signedness::Signed) { CloneTypeReplacements["char"] = "signed char"; } else { CloneTypeReplacements["char"] = "unsigned char"; } - CreateRemangledTypeReplacements(); + createRemangledTypeReplacements(); } SmallDenseMap getParameterTypeReplacements() { @@ -536,157 +775,204 @@ class TargetTypeReplacements { } }; -bool createCloneFromMap( - Module *M, std::string originalName, - const itanium_demangle::Node *functionTree, - SmallDenseMap TypeReplacements, - bool CloneeTypeReplacement = false) { - Remangler ATR{functionTree, TypeReplacements}; - std::string RemangledName = ATR.remangle(); +class LibCLCRemangler : public ASTConsumer { +public: + LibCLCRemangler() : ContextAST(nullptr), ContextLLVM(), Replacements() {} - if (ATR.hasFailed()) - return false; + void Initialize(ASTContext &C) override { + ContextAST = &C; - // Name has not changed from the original name. - if (RemangledName == originalName) - return true; + std::unique_ptr const Buff = ExitOnErr( + errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputIRFilename))); + std::unique_ptr const M = + ExitOnErr(parseBitcodeFile(Buff.get()->getMemBufferRef(), ContextLLVM)); - StringRef CloneName, CloneeName; - if (CloneeTypeReplacement) { - CloneName = originalName; - CloneeName = RemangledName; - } else { - CloneName = RemangledName; - CloneeName = originalName; + handleModule(M.get()); } - Function *Clonee = M->getFunction(CloneeName); - if (Clonee) { - ValueToValueMapTy Dummy; - Function *NewF = CloneFunction(Clonee, Dummy); - NewF->setName(std::string(CloneName)); - } else if (Verbose) { - std::cout << "Could not create copy " << CloneName.data() << " : missing " - << CloneeName.data() << std::endl; +private: + bool createClones(llvm::Module *M, std::string OriginalMangledName, + std::string RemangledName, + const itanium_demangle::Node *FunctionTree, + TargetTypeReplacements Replacements) { + // create clone of original function + if (!createCloneFromMap(M, OriginalMangledName, FunctionTree, + Replacements.getCloneTypeReplacements(), + /* CloneeTypeReplacement= */ true)) + return false; + + // create clone of remangled function + if (!createCloneFromMap(M, RemangledName, FunctionTree, + Replacements.getRemangledCloneTypeReplacements())) + return false; + + return true; } - return true; -} + bool + createCloneFromMap(llvm::Module *M, std::string OriginalName, + const itanium_demangle::Node *FunctionTree, + SmallDenseMap TypeReplacements, + bool CloneeTypeReplacement = false) { + Remangler ATR{ContextAST, FunctionTree, TypeReplacements}; -bool createClones(Module *M, std::string originalMangledName, - std::string remangledName, - const itanium_demangle::Node *functionTree, - TargetTypeReplacements replacements) { - // create clone of original function - if (!createCloneFromMap(M, originalMangledName, functionTree, - replacements.getCloneTypeReplacements(), - /* CloneeTypeReplacement= */ true)) - return false; - - // create clone of remangled function - if (!createCloneFromMap(M, remangledName, functionTree, - replacements.getRemangledCloneTypeReplacements())) - return false; - - return true; -} + std::string const RemangledName = ATR.remangle(); -bool remangleFunction(Function &func, Module *M, - TargetTypeReplacements replacements) { - if (!func.getName().startswith("_Z")) - return true; + if (ATR.hasFailed()) + return false; - std::string MangledName = func.getName().str(); - Demangler D{MangledName.c_str(), MangledName.c_str() + MangledName.length()}; - const itanium_demangle::Node *FunctionTree = D.parse(); - if (FunctionTree == nullptr) { - errs() << "Unable to demangle name: " << MangledName << '\n'; - return false; - } + // Name has not changed from the original name. + if (RemangledName == OriginalName) + return true; - // Try to change the parameter types in the function name using the mappings. - Remangler R{FunctionTree, replacements.getParameterTypeReplacements()}; - std::string RemangledName = R.remangle(); + StringRef CloneName, CloneeName; + if (CloneeTypeReplacement) { + CloneName = OriginalName; + CloneeName = RemangledName; + } else { + CloneName = RemangledName; + CloneeName = OriginalName; + } - if (R.hasFailed()) - return false; + Function *Clonee = M->getFunction(CloneeName); + if (Clonee) { + ValueToValueMapTy Dummy; + Function *NewF = CloneFunction(Clonee, Dummy); + NewF->setName(std::string(CloneName)); + } else if (Verbose) { + std::cout << "Could not create copy " << CloneName.data() << " : missing " + << CloneeName.data() << std::endl; + } - if (RemangledName != MangledName) { - if (Verbose) { - std::cout << "Mangling changed:" - << "\n" - << "Original: " << MangledName << "\n" - << "New: " << RemangledName << "\n" - << std::endl; + return true; + } + + bool remangleFunction(Function &Func, llvm::Module *M) { + if (!Func.getName().startswith("_Z")) + return true; + + std::string const MangledName = Func.getName().str(); + Demangler D{MangledName.c_str(), + MangledName.c_str() + MangledName.length()}; + const itanium_demangle::Node *FunctionTree = D.parse(); + if (FunctionTree == nullptr) { + errs() << "Unable to demangle name: " << MangledName << '\n'; + return false; } - func.setName(RemangledName); - // Make a clone of a suitable function using the old name if there is a - // type-mapping and the corresponding clonee function exists. - if (!createClones(M, MangledName, RemangledName, FunctionTree, - replacements)) + // Try to change the parameter types in the function name using the + // mappings. + Remangler R{ContextAST, FunctionTree, + Replacements.getParameterTypeReplacements()}; + + std::string const RemangledName = R.remangle(); + + if (R.hasFailed()) return false; + + if (RemangledName != MangledName) { + if (Verbose || TestRun) { + std::cout << "Mangling changed:" + << "\n" + << "Original: " << MangledName << "\n" + << "New: " << RemangledName << "\n" + << std::endl; + } + // In test run mode, where no substitution is made, change in mangling + // name represents a failure. Report an error. + if (TestRun) { + std::cout << "Test run failure!" << std::endl; + return false; + } + Func.setName(RemangledName); + + // Make a clone of a suitable function using the old name if there is a + // type-mapping and the corresponding clonee function exists. + if (!createClones(M, MangledName, RemangledName, FunctionTree, + Replacements)) + return false; + } + + return true; } - return true; -} + void handleModule(llvm::Module *M) { + std::error_code EC; + std::unique_ptr Out( + new ToolOutputFile(OutputFilename, EC, sys::fs::OF_None)); + if (EC) { + errs() << EC.message() << '\n'; + exit(1); + } -int main(int argc, const char **argv) { - LLVMContext Context; + // This module is built explicitly for linking with any .bc compiled with + // the "nvptx64-nvidia-cuda" (CUDA) or "amdgcn-amd-amdhsa" (HIP AMD) + // triples. Therefore we update the module triple. + if (M->getTargetTriple() == "nvptx64-unknown-nvidiacl") { + M->setTargetTriple("nvptx64-nvidia-cuda"); + } else if (M->getTargetTriple() == "amdgcn-unknown-amdhsa") { + M->setTargetTriple("amdgcn-amd-amdhsa"); + } - ExitOnErr.setBanner(std::string(argv[0]) + ": error: "); - sys::PrintStackTraceOnErrorSignal(argv[0]); + std::vector FuncList; + for (auto &Func : M->getFunctionList()) + FuncList.push_back(&Func); - cl::ParseCommandLineOptions( - argc, argv, "Tool for remangling libclc bitcode to fit a target\n"); + bool Success = true; + unsigned NumProcessed = 0; + for (auto *Func : FuncList) { + Success = remangleFunction(*Func, M) && Success; + ++NumProcessed; + } + // Only fail after all to give as much context as possible. + if (!Success) { + errs() << "Failed to remangle all mangled functions in module.\n"; + exit(1); + } - if (OutputFilename.empty()) { - errs() << "No output file.\n"; - return 1; - } + if (TestRun) { + if (Verbose) + std::cout << "Successfully processed: " << NumProcessed << " functions." + << std::endl; + return; + } - TargetTypeReplacements Replacements; + WriteBitcodeToFile(*M, Out->os()); - std::string ErrorMessage; - std::unique_ptr BufferPtr = - ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFilename))); - std::unique_ptr M = - ExitOnErr(parseBitcodeFile(BufferPtr.get()->getMemBufferRef(), Context)); - - // This module is built explicitly for linking with any .bc compiled with the - // "nvptx64-nvidia-cuda" (CUDA) or "amdgcn-amd-amdhsa" (HIP AMD) triples. - // Therefore we update the module triple. - if (M.get()->getTargetTriple() == "nvptx64-unknown-nvidiacl") { - M.get()->setTargetTriple("nvptx64-nvidia-cuda"); - } - else if (M.get()->getTargetTriple() == "amdgcn-unknown-amdhsa") { - M.get()->setTargetTriple("amdgcn-amd-amdhsa"); - } - std::error_code EC; - std::unique_ptr Out( - new ToolOutputFile(OutputFilename, EC, sys::fs::OF_None)); - if (EC) { - errs() << EC.message() << '\n'; - return 1; + // Declare success. + Out->keep(); } - std::vector FuncList; - for (auto &Func : M->getFunctionList()) - FuncList.push_back(&Func); +private: + ASTContext *ContextAST; + LLVMContext ContextLLVM; + TargetTypeReplacements Replacements; +}; + +class LibCLCRemanglerActionFactory { +public: + LibCLCRemanglerActionFactory() {} - bool Success = true; - for (auto Func : FuncList) { - Success = remangleFunction(*Func, M.get(), Replacements) && Success; + std::unique_ptr newASTConsumer() { + return std::make_unique(); } - // Only fail after all to give as much context as possible. - if (!Success) { - errs() << "Failed to remangle all mangled functions in module.\n"; +}; + +int main(int argc, const char **argv) { + auto ExpectedParser = CommonOptionsParser::create( + argc, argv, LibCLCRemanglerToolCategory, cl::ZeroOrMore); + if (!ExpectedParser) { + // Fail gracefully for unsupported options. + errs() << ExpectedParser.takeError(); return 1; } - WriteBitcodeToFile(*M, Out->os()); + CommonOptionsParser &OptionsParser = ExpectedParser.get(); + ClangTool Tool(OptionsParser.getCompilations(), + OptionsParser.getSourcePathList()); - // Declare success. - Out->keep(); - return 0; + LibCLCRemanglerActionFactory LRAF{}; + std::unique_ptr FrontendFactory; + FrontendFactory = newFrontendActionFactory(&LRAF); + return Tool.run(FrontendFactory.get()); }