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