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
20using namespace llvm;
21
22namespace {
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 version metadata.
36 /// \return true if changes are made.
37 /// Assume the named metadata has operands each of which is a pair of
38 /// integer constant, e.g.
39 /// !Name = {!n1, !n2}
40 /// !n1 = {i32 1, i32 2}
41 /// !n2 = {i32 2, i32 0}
42 /// Keep the largest version as the sole operand if PickFirst is false.
43 /// Otherwise pick it from the first value, representing kernel module.
44 bool unifyVersionMD(Module &M, StringRef Name, bool PickFirst) {
45 auto *NamedMD = M.getNamedMetadata(Name);
46 if (!NamedMD || NamedMD->getNumOperands() <= 1)
47 return false;
48 MDNode *MaxMD = nullptr;
49 auto MaxVer = 0U;
50 for (auto *VersionMD : NamedMD->operands()) {
51 assert(VersionMD->getNumOperands() == 2);
52 auto *CMajor = mdconst::extract<ConstantInt>(MD: VersionMD->getOperand(I: 0));
53 auto VersionMajor = CMajor->getZExtValue();
54 auto *CMinor = mdconst::extract<ConstantInt>(MD: VersionMD->getOperand(I: 1));
55 auto VersionMinor = CMinor->getZExtValue();
56 auto Ver = (VersionMajor * 100) + (VersionMinor * 10);
57 if (Ver > MaxVer) {
58 MaxVer = Ver;
59 MaxMD = VersionMD;
60 }
61 if (PickFirst)
62 break;
63 }
64 NamedMD->eraseFromParent();
65 NamedMD = M.getOrInsertNamedMetadata(Name);
66 NamedMD->addOperand(M: MaxMD);
67 return true;
68 }
69
70 /// Unify version metadata.
71 /// \return true if changes are made.
72 /// Assume the named metadata has operands each of which is a list e.g.
73 /// !Name = {!n1, !n2}
74 /// !n1 = !{!"cl_khr_fp16", {!"cl_khr_fp64"}}
75 /// !n2 = !{!"cl_khr_image"}
76 /// Combine it into a single list with unique operands.
77 bool unifyExtensionMD(Module &M, StringRef Name) {
78 auto *NamedMD = M.getNamedMetadata(Name);
79 if (!NamedMD || NamedMD->getNumOperands() == 1)
80 return false;
81
82 SmallVector<Metadata *, 4> All;
83 for (auto *MD : NamedMD->operands())
84 for (const auto &Op : MD->operands())
85 if (!llvm::is_contained(Range&: All, Element: Op.get()))
86 All.push_back(Elt: Op.get());
87
88 NamedMD->eraseFromParent();
89 NamedMD = M.getOrInsertNamedMetadata(Name);
90 for (const auto &MD : All)
91 NamedMD->addOperand(M: MDNode::get(Context&: M.getContext(), MDs: MD));
92
93 return true;
94 }
95
96 /// Unify multiple OpenCL metadata due to linking.
97 bool unifyMetadataImpl(Module &M) {
98 const char *Vers[] = {kOCLMD::SpirVer, kOCLMD::OCLVer};
99 const char *Exts[] = {kOCLMD::UsedExt, kOCLMD::UsedOptCoreFeat,
100 kOCLMD::CompilerOptions, kOCLMD::LLVMIdent};
101
102 bool Changed = false;
103
104 for (auto &I : Vers)
105 Changed |= unifyVersionMD(M, Name: I, PickFirst: true);
106
107 for (auto &I : Exts)
108 Changed |= unifyExtensionMD(M, Name: I);
109
110 return Changed;
111 }
112
113 } // end anonymous namespace
114
115 PreservedAnalyses AMDGPUUnifyMetadataPass::run(Module &M,
116 ModuleAnalysisManager &AM) {
117 return unifyMetadataImpl(M) ? PreservedAnalyses::none()
118 : PreservedAnalyses::all();
119 }
120