aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Target/AMDGPU/AMDGPUUnifyMetadata.cpp
blob: 240b6c2ff46259099b3ca48498ae483551757971 (plain) (blame)
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//===- 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 multiple OpenCL metadata due to linking.
  class AMDGPUUnifyMetadata : public ModulePass {
  public:
    static char ID;

    explicit AMDGPUUnifyMetadata() : ModulePass(ID) {}

  private:
    bool runOnModule(Module &M) override;
  };

    /// 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;
  }

  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

  char AMDGPUUnifyMetadata::ID = 0;

  char &llvm::AMDGPUUnifyMetadataID = AMDGPUUnifyMetadata::ID;

  INITIALIZE_PASS(AMDGPUUnifyMetadata, "amdgpu-unify-metadata",
                  "Unify multiple OpenCL metadata due to linking", false, false)

  ModulePass *llvm::createAMDGPUUnifyMetadataPass() {
    return new AMDGPUUnifyMetadata();
  }

  bool AMDGPUUnifyMetadata::runOnModule(Module &M) {
    return unifyMetadataImpl(M);
  }

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