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 
AMDGPUUnifyMetadata()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.
unifyVersionMD(Module & M,StringRef Name,bool PickFirst)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.
unifyExtensionMD(Module & M,StringRef Name)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 (!llvm::is_contained(All, Op.get()))
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 
createAMDGPUUnifyMetadataPass()121 ModulePass* llvm::createAMDGPUUnifyMetadataPass() {
122   return new AMDGPUUnifyMetadata();
123 }
124 
runOnModule(Module & M)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