| //===- 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 <algorithm> |
| #include <cassert> |
| |
| 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<ConstantInt>(VersionMD->getOperand(0)); |
| auto VersionMajor = CMajor->getZExtValue(); |
| auto CMinor = mdconst::extract<ConstantInt>(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<Metadata *, 4> 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; |
| } |