1 //===- AMDGPUUnifyMetadata.cpp - Unify OpenCL metadata --------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // \file 10 // This pass that unifies multiple OpenCL metadata due to linking. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "AMDGPU.h" 15 #include "llvm/ADT/SmallVector.h" 16 #include "llvm/ADT/StringRef.h" 17 #include "llvm/IR/Constants.h" 18 #include "llvm/IR/Metadata.h" 19 #include "llvm/IR/Module.h" 20 #include "llvm/Pass.h" 21 #include <algorithm> 22 #include <cassert> 23 24 using namespace llvm; 25 26 namespace { 27 28 namespace kOCLMD { 29 30 const char SpirVer[] = "opencl.spir.version"; 31 const char OCLVer[] = "opencl.ocl.version"; 32 const char UsedExt[] = "opencl.used.extensions"; 33 const char UsedOptCoreFeat[] = "opencl.used.optional.core.features"; 34 const char CompilerOptions[] = "opencl.compiler.options"; 35 const char LLVMIdent[] = "llvm.ident"; 36 37 } // end namespace kOCLMD 38 39 /// Unify multiple OpenCL metadata due to linking. 40 class AMDGPUUnifyMetadata : public ModulePass { 41 public: 42 static char ID; 43 44 explicit AMDGPUUnifyMetadata() : ModulePass(ID) {} 45 46 private: 47 bool runOnModule(Module &M) override; 48 49 /// Unify version metadata. 50 /// \return true if changes are made. 51 /// Assume the named metadata has operands each of which is a pair of 52 /// integer constant, e.g. 53 /// !Name = {!n1, !n2} 54 /// !n1 = {i32 1, i32 2} 55 /// !n2 = {i32 2, i32 0} 56 /// Keep the largest version as the sole operand if PickFirst is false. 57 /// Otherwise pick it from the first value, representing kernel module. 58 bool unifyVersionMD(Module &M, StringRef Name, bool PickFirst) { 59 auto NamedMD = M.getNamedMetadata(Name); 60 if (!NamedMD || NamedMD->getNumOperands() <= 1) 61 return false; 62 MDNode *MaxMD = nullptr; 63 auto MaxVer = 0U; 64 for (auto VersionMD : NamedMD->operands()) { 65 assert(VersionMD->getNumOperands() == 2); 66 auto CMajor = mdconst::extract<ConstantInt>(VersionMD->getOperand(0)); 67 auto VersionMajor = CMajor->getZExtValue(); 68 auto CMinor = mdconst::extract<ConstantInt>(VersionMD->getOperand(1)); 69 auto VersionMinor = CMinor->getZExtValue(); 70 auto Ver = (VersionMajor * 100) + (VersionMinor * 10); 71 if (Ver > MaxVer) { 72 MaxVer = Ver; 73 MaxMD = VersionMD; 74 } 75 if (PickFirst) 76 break; 77 } 78 NamedMD->eraseFromParent(); 79 NamedMD = M.getOrInsertNamedMetadata(Name); 80 NamedMD->addOperand(MaxMD); 81 return true; 82 } 83 84 /// Unify version metadata. 85 /// \return true if changes are made. 86 /// Assume the named metadata has operands each of which is a list e.g. 87 /// !Name = {!n1, !n2} 88 /// !n1 = !{!"cl_khr_fp16", {!"cl_khr_fp64"}} 89 /// !n2 = !{!"cl_khr_image"} 90 /// Combine it into a single list with unique operands. 91 bool unifyExtensionMD(Module &M, StringRef Name) { 92 auto NamedMD = M.getNamedMetadata(Name); 93 if (!NamedMD || NamedMD->getNumOperands() == 1) 94 return false; 95 96 SmallVector<Metadata *, 4> All; 97 for (auto MD : NamedMD->operands()) 98 for (const auto &Op : MD->operands()) 99 if (std::find(All.begin(), All.end(), Op.get()) == All.end()) 100 All.push_back(Op.get()); 101 102 NamedMD->eraseFromParent(); 103 NamedMD = M.getOrInsertNamedMetadata(Name); 104 for (const auto &MD : All) 105 NamedMD->addOperand(MDNode::get(M.getContext(), MD)); 106 107 return true; 108 } 109 }; 110 111 } // end anonymous namespace 112 113 char AMDGPUUnifyMetadata::ID = 0; 114 115 char &llvm::AMDGPUUnifyMetadataID = AMDGPUUnifyMetadata::ID; 116 117 INITIALIZE_PASS(AMDGPUUnifyMetadata, "amdgpu-unify-metadata", 118 "Unify multiple OpenCL metadata due to linking", 119 false, false) 120 121 ModulePass* llvm::createAMDGPUUnifyMetadataPass() { 122 return new AMDGPUUnifyMetadata(); 123 } 124 125 bool AMDGPUUnifyMetadata::runOnModule(Module &M) { 126 const char* Vers[] = { 127 kOCLMD::SpirVer, 128 kOCLMD::OCLVer 129 }; 130 const char* Exts[] = { 131 kOCLMD::UsedExt, 132 kOCLMD::UsedOptCoreFeat, 133 kOCLMD::CompilerOptions, 134 kOCLMD::LLVMIdent 135 }; 136 137 bool Changed = false; 138 139 for (auto &I : Vers) 140 Changed |= unifyVersionMD(M, I, true); 141 142 for (auto &I : Exts) 143 Changed |= unifyExtensionMD(M, I); 144 145 return Changed; 146 } 147