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