aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Target/AMDGPU/AMDGPUUnifyMetadata.cpp
blob: e400491c3860e36954443b700e5eef19c222b1ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//===- AMDGPUUnifyMetadata.cpp - Unify OpenCL metadata --------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// \file
// This pass that unifies multiple OpenCL metadata due to linking.
//
//===----------------------------------------------------------------------===//

#include "AMDGPU.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"

using namespace llvm;

namespace {

  namespace kOCLMD {

    const char SpirVer[]            = "opencl.spir.version";
    const char OCLVer[]             = "opencl.ocl.version";
    const char UsedExt[]            = "opencl.used.extensions";
    const char UsedOptCoreFeat[]    = "opencl.used.optional.core.features";
    const char CompilerOptions[]    = "opencl.compiler.options";
    const char LLVMIdent[]          = "llvm.ident";

  } // end namespace kOCLMD

    /// Unify version metadata.
    /// \return true if changes are made.
    /// Assume the named metadata has operands each of which is a pair of
    /// integer constant, e.g.
    /// !Name = {!n1, !n2}
    /// !n1 = {i32 1, i32 2}
    /// !n2 = {i32 2, i32 0}
    /// Keep the largest version as the sole operand if PickFirst is false.
    /// Otherwise pick it from the first value, representing kernel module.
    bool unifyVersionMD(Module &M, StringRef Name, bool PickFirst) {
      auto *NamedMD = M.getNamedMetadata(Name);
      if (!NamedMD || NamedMD->getNumOperands() <= 1)
        return false;
      MDNode *MaxMD = nullptr;
      auto MaxVer = 0U;
      for (auto *VersionMD : NamedMD->operands()) {
        assert(VersionMD->getNumOperands() == 2);
        auto *CMajor = mdconst::extract<ConstantInt>(VersionMD->getOperand(0));
        auto VersionMajor = CMajor->getZExtValue();
        auto *CMinor = mdconst::extract<ConstantInt>(VersionMD->getOperand(1));
        auto VersionMinor = CMinor->getZExtValue();
        auto Ver = (VersionMajor * 100) + (VersionMinor * 10);
        if (Ver > MaxVer) {
          MaxVer = Ver;
          MaxMD = VersionMD;
        }
        if (PickFirst)
          break;
      }
      NamedMD->eraseFromParent();
      NamedMD = M.getOrInsertNamedMetadata(Name);
      NamedMD->addOperand(MaxMD);
      return true;
    }

  /// Unify version metadata.
  /// \return true if changes are made.
  /// Assume the named metadata has operands each of which is a list e.g.
  /// !Name = {!n1, !n2}
  /// !n1 = !{!"cl_khr_fp16", {!"cl_khr_fp64"}}
  /// !n2 = !{!"cl_khr_image"}
  /// Combine it into a single list with unique operands.
  bool unifyExtensionMD(Module &M, StringRef Name) {
    auto *NamedMD = M.getNamedMetadata(Name);
    if (!NamedMD || NamedMD->getNumOperands() == 1)
      return false;

    SmallVector<Metadata *, 4> All;
    for (auto *MD : NamedMD->operands())
      for (const auto &Op : MD->operands())
        if (!llvm::is_contained(All, Op.get()))
          All.push_back(Op.get());

    NamedMD->eraseFromParent();
    NamedMD = M.getOrInsertNamedMetadata(Name);
    for (const auto &MD : All)
      NamedMD->addOperand(MDNode::get(M.getContext(), MD));

    return true;
  }

  /// Unify multiple OpenCL metadata due to linking.
  bool unifyMetadataImpl(Module &M) {
    const char *Vers[] = {kOCLMD::SpirVer, kOCLMD::OCLVer};
    const char *Exts[] = {kOCLMD::UsedExt, kOCLMD::UsedOptCoreFeat,
                          kOCLMD::CompilerOptions, kOCLMD::LLVMIdent};

    bool Changed = false;

    for (auto &I : Vers)
      Changed |= unifyVersionMD(M, I, true);

    for (auto &I : Exts)
      Changed |= unifyExtensionMD(M, I);

    return Changed;
  }

  } // end anonymous namespace

  PreservedAnalyses AMDGPUUnifyMetadataPass::run(Module &M,
                                                 ModuleAnalysisManager &AM) {
    return unifyMetadataImpl(M) ? PreservedAnalyses::none()
                                : PreservedAnalyses::all();
  }