//===- 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/IR/Constants.h" #include "llvm/IR/Module.h" #include "llvm/IR/PassManager.h" #include "llvm/Pass.h" 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 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 (!llvm::is_contained(All, Op.get())) 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; } /// Unify multiple OpenCL metadata due to linking. bool unifyMetadataImpl(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; } } // end anonymous namespace PreservedAnalyses AMDGPUUnifyMetadataPass::run(Module &M, ModuleAnalysisManager &AM) { return unifyMetadataImpl(M) ? PreservedAnalyses::none() : PreservedAnalyses::all(); }