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 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