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