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