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