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