diff --git a/libclc/cmake/modules/AddLibclc.cmake b/libclc/cmake/modules/AddLibclc.cmake index c149673bce991..1913399f06a73 100644 --- a/libclc/cmake/modules/AddLibclc.cmake +++ b/libclc/cmake/modules/AddLibclc.cmake @@ -108,9 +108,6 @@ 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") @@ -131,11 +128,10 @@ macro(add_libclc_builtin_set arch_suffix) -o "${builtins_remangle_path}" --long-width=${long_width} --char-signedness=${signedness} - --input-ir="$" - ${dummy_in} - DEPENDS "${builtins_obj_path}" "prepare-${obj_suffix}" libclc-remangler ${dummy_in}) + "$" + DEPENDS "${builtins_obj_path}" "prepare-${obj_suffix}" libclc-remangler ) add_custom_target( "remangled-${long_width}-${signedness}_char.${obj_suffix_mangled}" ALL - DEPENDS "${builtins_remangle_path}" "${dummy_in}") + DEPENDS "${builtins_remangle_path}" ) 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 2b5af7df26872..80ec1f05d0264 100644 --- a/libclc/test/CMakeLists.txt +++ b/libclc/test/CMakeLists.txt @@ -43,27 +43,3 @@ 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 71970e715b4ef..7101129d6825f 100644 --- a/libclc/utils/libclc-remangler/CMakeLists.txt +++ b/libclc/utils/libclc-remangler/CMakeLists.txt @@ -14,11 +14,4 @@ target_include_directories(libclc-remangler PRIVATE ${CMAKE_SOURCE_DIR}/../clang/include ${CMAKE_BINARY_DIR}/tools/clang/include) -clang_target_link_libraries(libclc-remangler - PRIVATE - clangAST - clangBasic - clangFrontend - clangTooling - LLVMOption - ) +clang_target_link_libraries(libclc-remangler PRIVATE clangBasic) diff --git a/libclc/utils/libclc-remangler/LibclcRemangler.cpp b/libclc/utils/libclc-remangler/LibclcRemangler.cpp index 7fa263ec36f0f..274840ee63c9f 100644 --- a/libclc/utils/libclc-remangler/LibclcRemangler.cpp +++ b/libclc/utils/libclc-remangler/LibclcRemangler.cpp @@ -27,32 +27,45 @@ // 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 -// `_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`. +// 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`. // //===----------------------------------------------------------------------===// -#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 "clang/Basic/Diagnostic.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" +#include "llvm/ADT/STLExtras.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; @@ -61,44 +74,55 @@ enum class SupportedLongWidth { L32, L64 }; static ExitOnError ExitOnErr; -// 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 InputFilename(cl::Positional, + cl::desc("")); 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.")), - cl::cat(LibCLCRemanglerToolCategory)); + "long is 64-bit wide."))); static cl::opt CharSignedness( "char-signedness", cl::values(clEnumValN(Signedness::Signed, "signed", "char is signed."), clEnumValN(Signedness::Unsigned, "unsigned", - "char is unsigned.")), - cl::cat(LibCLCRemanglerToolCategory)); + "char is unsigned."))); static cl::opt Verbose("v", cl::desc("Enable verbose output"), - 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); + cl::init(false)); 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; @@ -123,592 +147,331 @@ 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 &&...A) { - return new (Alloc.allocate(sizeof(T))) T(std::forward(A)...); + template T *makeNode(Args &&...args) { + return new (Alloc.allocate(sizeof(T))) T(std::forward(args)...); } - void *allocateNodeArray(size_t Sz) { - return Alloc.allocate(sizeof(itanium_demangle::Node *) * Sz); + void *allocateNodeArray(size_t sz) { + return Alloc.allocate(sizeof(Node *) * sz); } - -private: - BumpPointerAllocator Alloc; }; } // unnamed namespace using Demangler = ManglingParser; class Remangler { -public: - Remangler(ASTContext *ContextAST, const Node *Root, - SmallDenseMap TypeReplacements) - : ContextAST(ContextAST), Root(Root), TypeReplacements(TypeReplacements) { - MangleContext.reset(ContextAST->createMangleContext()); - } + const Node *Root; + SmallDenseMap TypeReplacements; - bool hasFailed() { return Failed; } + SmallVector Subs; + bool Failed = 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()}; + void printNode(const Node *node, OutputBuffer &nodeOutBuffer) { + node->print(nodeOutBuffer); } -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; - } - 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; - } - NodeKindInfo(Node::Kind K, const char *NestedName, size_t NestedNameSize) - : K(K) { + 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); + } - DataStr.assign(NestedName, NestedNameSize); + 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; + } } - Node::Kind K; - size_t Data = 0; - std::string DataStr; - }; + return false; + } -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); + 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; } + return false; + } - // 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 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; } - - // 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; + 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; + } + default: { + OutputBuffer errorTypeOut; + errorTypeOut << "Unhandled name : "; + nameNode->print(errorTypeOut); + errorTypeOut << "\n"; + errs() << errorTypeOut.getBuffer(); + free(errorTypeOut.getBuffer()); + Failed = true; } - // 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; + if (Substitutable) + addSub(nameNode); } - // 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) { + 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); + } + + void remangleOpenCLCQualifiers(const itanium_demangle::Qualifiers quals, + OutputBuffer &OB) { + if (quals & QualConst) + OB << "K"; + if (quals & QualVolatile) + OB << "V"; + if (quals & QualRestrict) + OB << "r"; + } + + void remangleOpenCLCType(const Node *typeNode, OutputBuffer &OB) { + switch (typeNode->getKind()) { case Node::Kind::KPointerType: { - PossibleKinds.push_back(NodeKindInfo(Kind)); - const itanium_demangle::PointerType *PType = - static_cast(TypeNode); - return handleTypeNode(PType->getPointee(), PossibleKinds); + const itanium_demangle::PointerType *ptype = + static_cast(typeNode); + OB << 'P'; + remangleOpenCLCType(ptype->getPointee(), OB); + break; } case Node::Kind::KVectorType: { - 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); + 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; } case Node::Kind::KBinaryFPType: { - const itanium_demangle::BinaryFPType *BFPType = - static_cast(TypeNode); + if (remangleSub(typeNode, OB)) + return; + + const BinaryFPType *BFPType = static_cast(typeNode); assert(BFPType->getDimension()->getKind() == Node::Kind::KNameType); - 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); + const NameType *dims = + static_cast(BFPType->getDimension()); + + OB << "DF"; + OB << dims->getName(); + OB << '_'; + break; } case Node::Kind::KVendorExtQualType: { - 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); + 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; } case Node::Kind::KQualType: { - const itanium_demangle::QualType *QType = - static_cast(TypeNode); - auto Qual = QType->getQuals(); - PossibleKinds.push_back(NodeKindInfo(Kind, Qual)); - return handleTypeNode(QType->getChild(), PossibleKinds); + const itanium_demangle::QualType *qtype = + static_cast(typeNode); + remangleOpenCLCQualifiers(qtype->getQuals(), OB); + remangleOpenCLCType(qtype->getChild(), OB); + break; } case Node::Kind::KNameType: { - const itanium_demangle::NameType *TypeName = - static_cast(TypeNode); - return handleLeafTypeNode(TypeName, PossibleKinds); + const NameType *typeName = static_cast(typeNode); + remangleOpenCLCTypeName(typeName, OB); + break; } case Node::Kind::KNestedName: { - 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); + // Enum type with nested name + remangleOpenCLCName(typeNode, OB, /* Substitutable= */ true); + break; } 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); } - // 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); - } + void remangleOpenCLCFunction(const Node *root, OutputBuffer &OB) { + assert(root->getKind() == Node::Kind::KFunctionEncoding); + OB << "_Z"; - 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); - } + const FunctionEncoding *encoding = + static_cast(root); - // 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]; - } + remangleOpenCLCName(encoding->getName(), OB, /* Substitutable= */ false); - // 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."); - } - } - } + if (encoding->getReturnType()) + remangleOpenCLCType(encoding->getReturnType(), OB); - return std::make_pair(Res, IsVariadic); - } + for (const Node *paramType : encoding->getParams()) + remangleOpenCLCType(paramType, OB); - // 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)); - } + if (encoding->getParams().size() == 0) + OB << 'v'; } -private: - ASTContext *ContextAST = nullptr; - std::unique_ptr MangleContext{}; - const Node *Root = nullptr; - SmallDenseMap TypeReplacements{}; +public: + Remangler(const Node *root, + SmallDenseMap typeReplacements) + : Root(root), TypeReplacements(typeReplacements) {} - bool Failed = false; + bool hasFailed() { return Failed; } - std::map NestedNamesQTMap{}; - NamespaceDecl *SpvNamespace = nullptr; + std::string remangle() { + Subs.clear(); + OutputBuffer remanglingStream; + remangleOpenCLCFunction(Root, remanglingStream); + std::string remangled = std::string(remanglingStream.getBuffer(), + remanglingStream.getCurrentPosition()); + free(remanglingStream.getBuffer()); + return remangled; + } }; class TargetTypeReplacements { @@ -716,9 +479,8 @@ 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()) @@ -739,8 +501,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"; @@ -749,15 +511,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() { @@ -774,204 +536,157 @@ class TargetTypeReplacements { } }; -class LibCLCRemangler : public ASTConsumer { -public: - LibCLCRemangler() : ContextAST(nullptr), ContextLLVM(), Replacements() {} +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(); - void Initialize(ASTContext &C) override { - ContextAST = &C; + if (ATR.hasFailed()) + return false; - std::unique_ptr const Buff = ExitOnErr( - errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputIRFilename))); - std::unique_ptr const M = - ExitOnErr(parseBitcodeFile(Buff.get()->getMemBufferRef(), ContextLLVM)); + // Name has not changed from the original name. + if (RemangledName == originalName) + return true; - handleModule(M.get()); + StringRef CloneName, CloneeName; + if (CloneeTypeReplacement) { + CloneName = originalName; + CloneeName = RemangledName; + } else { + CloneName = RemangledName; + CloneeName = originalName; } -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; + 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; } - bool - createCloneFromMap(llvm::Module *M, std::string OriginalName, - const itanium_demangle::Node *FunctionTree, - SmallDenseMap TypeReplacements, - bool CloneeTypeReplacement = false) { - Remangler ATR{ContextAST, FunctionTree, TypeReplacements}; - - std::string const RemangledName = ATR.remangle(); - - if (ATR.hasFailed()) - return false; - - // Name has not changed from the original name. - if (RemangledName == OriginalName) - return true; - - StringRef CloneName, CloneeName; - if (CloneeTypeReplacement) { - CloneName = OriginalName; - CloneeName = RemangledName; - } else { - CloneName = RemangledName; - CloneeName = OriginalName; - } + return true; +} - 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; - } +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; +} +bool remangleFunction(Function &func, Module *M, + TargetTypeReplacements replacements) { + if (!func.getName().startswith("_Z")) 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; - } - - // 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(); + 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; + } - if (R.hasFailed()) - return false; + // Try to change the parameter types in the function name using the mappings. + Remangler R{FunctionTree, replacements.getParameterTypeReplacements()}; + std::string RemangledName = R.remangle(); - 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); + if (R.hasFailed()) + return false; - // 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; + if (RemangledName != MangledName) { + if (Verbose) { + std::cout << "Mangling changed:" + << "\n" + << "Original: " << MangledName << "\n" + << "New: " << RemangledName << "\n" + << std::endl; } + func.setName(RemangledName); - return true; + // 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; } - 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); - } - - // 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"); - } - - std::vector FuncList; - for (auto &Func : M->getFunctionList()) - FuncList.push_back(&Func); + return true; +} - 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); - } +int main(int argc, const char **argv) { + LLVMContext Context; - if (TestRun) { - if (Verbose) - std::cout << "Successfully processed: " << NumProcessed << " functions." - << std::endl; - return; - } + ExitOnErr.setBanner(std::string(argv[0]) + ": error: "); + sys::PrintStackTraceOnErrorSignal(argv[0]); - WriteBitcodeToFile(*M, Out->os()); + cl::ParseCommandLineOptions( + argc, argv, "Tool for remangling libclc bitcode to fit a target\n"); - // Declare success. - Out->keep(); + if (OutputFilename.empty()) { + errs() << "No output file.\n"; + return 1; } -private: - ASTContext *ContextAST; - LLVMContext ContextLLVM; TargetTypeReplacements Replacements; -}; - -class LibCLCRemanglerActionFactory { -public: - LibCLCRemanglerActionFactory() {} - std::unique_ptr newASTConsumer() { - return std::make_unique(); + 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; } -}; -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(); + std::vector FuncList; + for (auto &Func : M->getFunctionList()) + FuncList.push_back(&Func); + + bool Success = true; + for (auto Func : FuncList) { + Success = remangleFunction(*Func, M.get(), Replacements) && Success; + } + // Only fail after all to give as much context as possible. + if (!Success) { + errs() << "Failed to remangle all mangled functions in module.\n"; return 1; } - CommonOptionsParser &OptionsParser = ExpectedParser.get(); - ClangTool Tool(OptionsParser.getCompilations(), - OptionsParser.getSourcePathList()); + WriteBitcodeToFile(*M, Out->os()); - LibCLCRemanglerActionFactory LRAF{}; - std::unique_ptr FrontendFactory; - FrontendFactory = newFrontendActionFactory(&LRAF); - return Tool.run(FrontendFactory.get()); + // Declare success. + Out->keep(); + return 0; }