//===- AMDGPUUnifyMetadata.cpp - Unify OpenCL metadata --------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // \file // This pass that unifies multiple OpenCL metadata due to linking. // //===----------------------------------------------------------------------===// #include "AMDGPU.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" #include "llvm/Pass.h" #include #include using namespace llvm; namespace { namespace kOCLMD { const char SpirVer[] = "opencl.spir.version"; const char OCLVer[] = "opencl.ocl.version"; const char UsedExt[] = "opencl.used.extensions"; const char UsedOptCoreFeat[] = "opencl.used.optional.core.features"; const char CompilerOptions[] = "opencl.compiler.options"; const char LLVMIdent[] = "llvm.ident"; } // end namespace kOCLMD /// Unify multiple OpenCL metadata due to linking. class AMDGPUUnifyMetadata : public ModulePass { public: static char ID; explicit AMDGPUUnifyMetadata() : ModulePass(ID) {} private: bool runOnModule(Module &M) override; /// Unify version metadata. /// \return true if changes are made. /// Assume the named metadata has operands each of which is a pair of /// integer constant, e.g. /// !Name = {!n1, !n2} /// !n1 = {i32 1, i32 2} /// !n2 = {i32 2, i32 0} /// Keep the largest version as the sole operand if PickFirst is false. /// Otherwise pick it from the first value, representing kernel module. bool unifyVersionMD(Module &M, StringRef Name, bool PickFirst) { auto NamedMD = M.getNamedMetadata(Name); if (!NamedMD || NamedMD->getNumOperands() <= 1) return false; MDNode *MaxMD = nullptr; auto MaxVer = 0U; for (auto VersionMD : NamedMD->operands()) { assert(VersionMD->getNumOperands() == 2); auto CMajor = mdconst::extract(VersionMD->getOperand(0)); auto VersionMajor = CMajor->getZExtValue(); auto CMinor = mdconst::extract(VersionMD->getOperand(1)); auto VersionMinor = CMinor->getZExtValue(); auto Ver = (VersionMajor * 100) + (VersionMinor * 10); if (Ver > MaxVer) { MaxVer = Ver; MaxMD = VersionMD; } if (PickFirst) break; } NamedMD->eraseFromParent(); NamedMD = M.getOrInsertNamedMetadata(Name); NamedMD->addOperand(MaxMD); return true; } /// Unify version metadata. /// \return true if changes are made. /// Assume the named metadata has operands each of which is a list e.g. /// !Name = {!n1, !n2} /// !n1 = !{!"cl_khr_fp16", {!"cl_khr_fp64"}} /// !n2 = !{!"cl_khr_image"} /// Combine it into a single list with unique operands. bool unifyExtensionMD(Module &M, StringRef Name) { auto NamedMD = M.getNamedMetadata(Name); if (!NamedMD || NamedMD->getNumOperands() == 1) return false; SmallVector All; for (auto MD : NamedMD->operands()) for (const auto &Op : MD->operands()) if (std::find(All.begin(), All.end(), Op.get()) == All.end()) All.push_back(Op.get()); NamedMD->eraseFromParent(); NamedMD = M.getOrInsertNamedMetadata(Name); for (const auto &MD : All) NamedMD->addOperand(MDNode::get(M.getContext(), MD)); return true; } }; } // end anonymous namespace char AMDGPUUnifyMetadata::ID = 0; char &llvm::AMDGPUUnifyMetadataID = AMDGPUUnifyMetadata::ID; INITIALIZE_PASS(AMDGPUUnifyMetadata, "amdgpu-unify-metadata", "Unify multiple OpenCL metadata due to linking", false, false) ModulePass* llvm::createAMDGPUUnifyMetadataPass() { return new AMDGPUUnifyMetadata(); } bool AMDGPUUnifyMetadata::runOnModule(Module &M) { const char* Vers[] = { kOCLMD::SpirVer, kOCLMD::OCLVer }; const char* Exts[] = { kOCLMD::UsedExt, kOCLMD::UsedOptCoreFeat, kOCLMD::CompilerOptions, kOCLMD::LLVMIdent }; bool Changed = false; for (auto &I : Vers) Changed |= unifyVersionMD(M, I, true); for (auto &I : Exts) Changed |= unifyExtensionMD(M, I); return Changed; }