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