150ea93a2SStanislav Mekhanoshin //===-- AMDGPUUnifyMetadata.cpp - Unify OpenCL metadata -------------------===//
250ea93a2SStanislav Mekhanoshin //
350ea93a2SStanislav Mekhanoshin //                     The LLVM Compiler Infrastructure
450ea93a2SStanislav Mekhanoshin //
550ea93a2SStanislav Mekhanoshin // This file is distributed under the University of Illinois Open Source
650ea93a2SStanislav Mekhanoshin // License. See LICENSE.TXT for details.
750ea93a2SStanislav Mekhanoshin //
850ea93a2SStanislav Mekhanoshin //===----------------------------------------------------------------------===//
950ea93a2SStanislav Mekhanoshin //
1050ea93a2SStanislav Mekhanoshin // \file
1150ea93a2SStanislav Mekhanoshin // \brief This pass that unifies multiple OpenCL metadata due to linking.
1250ea93a2SStanislav Mekhanoshin //
1350ea93a2SStanislav Mekhanoshin //===----------------------------------------------------------------------===//
1450ea93a2SStanislav Mekhanoshin 
1550ea93a2SStanislav Mekhanoshin #include "AMDGPU.h"
16*734bb7bbSEugene Zelenko #include "llvm/ADT/SmallVector.h"
17*734bb7bbSEugene Zelenko #include "llvm/ADT/StringRef.h"
1850ea93a2SStanislav Mekhanoshin #include "llvm/IR/Constants.h"
19*734bb7bbSEugene Zelenko #include "llvm/IR/Function.h"
2050ea93a2SStanislav Mekhanoshin #include "llvm/IR/Module.h"
2150ea93a2SStanislav Mekhanoshin #include "llvm/Pass.h"
22*734bb7bbSEugene Zelenko #include <algorithm>
23*734bb7bbSEugene Zelenko #include <cassert>
2450ea93a2SStanislav Mekhanoshin 
2550ea93a2SStanislav Mekhanoshin using namespace llvm;
2650ea93a2SStanislav Mekhanoshin 
2750ea93a2SStanislav Mekhanoshin namespace {
28*734bb7bbSEugene Zelenko 
2950ea93a2SStanislav Mekhanoshin   namespace kOCLMD {
30*734bb7bbSEugene Zelenko 
3150ea93a2SStanislav Mekhanoshin     const char SpirVer[]            = "opencl.spir.version";
3250ea93a2SStanislav Mekhanoshin     const char OCLVer[]             = "opencl.ocl.version";
3350ea93a2SStanislav Mekhanoshin     const char UsedExt[]            = "opencl.used.extensions";
3450ea93a2SStanislav Mekhanoshin     const char UsedOptCoreFeat[]    = "opencl.used.optional.core.features";
3550ea93a2SStanislav Mekhanoshin     const char CompilerOptions[]    = "opencl.compiler.options";
3650ea93a2SStanislav Mekhanoshin     const char LLVMIdent[]          = "llvm.ident";
37*734bb7bbSEugene Zelenko 
38*734bb7bbSEugene Zelenko   } // end namespace kOCLMD
3950ea93a2SStanislav Mekhanoshin 
4050ea93a2SStanislav Mekhanoshin   /// \brief Unify multiple OpenCL metadata due to linking.
4150ea93a2SStanislav Mekhanoshin   class AMDGPUUnifyMetadata : public FunctionPass {
4250ea93a2SStanislav Mekhanoshin   public:
4350ea93a2SStanislav Mekhanoshin     static char ID;
44*734bb7bbSEugene Zelenko 
45*734bb7bbSEugene Zelenko     explicit AMDGPUUnifyMetadata() : FunctionPass(ID) {}
4650ea93a2SStanislav Mekhanoshin 
4750ea93a2SStanislav Mekhanoshin   private:
4850ea93a2SStanislav Mekhanoshin     // This should really be a module pass but we have to run it as early
4950ea93a2SStanislav Mekhanoshin     // as possible, so given function passes are executed first and
5050ea93a2SStanislav Mekhanoshin     // TargetMachine::addEarlyAsPossiblePasses() expects only function passes
5150ea93a2SStanislav Mekhanoshin     // it has to be a function pass.
5250ea93a2SStanislav Mekhanoshin     virtual bool runOnModule(Module &M);
5350ea93a2SStanislav Mekhanoshin 
5450ea93a2SStanislav Mekhanoshin     // \todo: Convert to a module pass.
55*734bb7bbSEugene Zelenko     bool runOnFunction(Function &F) override;
5650ea93a2SStanislav Mekhanoshin 
5750ea93a2SStanislav Mekhanoshin     /// \brief Unify version metadata.
5850ea93a2SStanislav Mekhanoshin     /// \return true if changes are made.
5950ea93a2SStanislav Mekhanoshin     /// Assume the named metadata has operands each of which is a pair of
6050ea93a2SStanislav Mekhanoshin     /// integer constant, e.g.
6150ea93a2SStanislav Mekhanoshin     /// !Name = {!n1, !n2}
6250ea93a2SStanislav Mekhanoshin     /// !n1 = {i32 1, i32 2}
6350ea93a2SStanislav Mekhanoshin     /// !n2 = {i32 2, i32 0}
6450ea93a2SStanislav Mekhanoshin     /// Keep the largest version as the sole operand if PickFirst is false.
6550ea93a2SStanislav Mekhanoshin     /// Otherwise pick it from the first value, representing kernel module.
6650ea93a2SStanislav Mekhanoshin     bool unifyVersionMD(Module &M, StringRef Name, bool PickFirst) {
6750ea93a2SStanislav Mekhanoshin       auto NamedMD = M.getNamedMetadata(Name);
6850ea93a2SStanislav Mekhanoshin       if (!NamedMD || NamedMD->getNumOperands() <= 1)
6950ea93a2SStanislav Mekhanoshin         return false;
7050ea93a2SStanislav Mekhanoshin       MDNode *MaxMD = nullptr;
7150ea93a2SStanislav Mekhanoshin       auto MaxVer = 0U;
7250ea93a2SStanislav Mekhanoshin       for (const auto &VersionMD : NamedMD->operands()) {
7350ea93a2SStanislav Mekhanoshin         assert(VersionMD->getNumOperands() == 2);
7450ea93a2SStanislav Mekhanoshin         auto CMajor = mdconst::extract<ConstantInt>(VersionMD->getOperand(0));
7550ea93a2SStanislav Mekhanoshin         auto VersionMajor = CMajor->getZExtValue();
7650ea93a2SStanislav Mekhanoshin         auto CMinor = mdconst::extract<ConstantInt>(VersionMD->getOperand(1));
7750ea93a2SStanislav Mekhanoshin         auto VersionMinor = CMinor->getZExtValue();
7850ea93a2SStanislav Mekhanoshin         auto Ver = (VersionMajor * 100) + (VersionMinor * 10);
7950ea93a2SStanislav Mekhanoshin         if (Ver > MaxVer) {
8050ea93a2SStanislav Mekhanoshin           MaxVer = Ver;
8150ea93a2SStanislav Mekhanoshin           MaxMD = VersionMD;
8250ea93a2SStanislav Mekhanoshin         }
8350ea93a2SStanislav Mekhanoshin         if (PickFirst)
8450ea93a2SStanislav Mekhanoshin           break;
8550ea93a2SStanislav Mekhanoshin       }
8650ea93a2SStanislav Mekhanoshin       NamedMD->eraseFromParent();
8750ea93a2SStanislav Mekhanoshin       NamedMD = M.getOrInsertNamedMetadata(Name);
8850ea93a2SStanislav Mekhanoshin       NamedMD->addOperand(MaxMD);
8950ea93a2SStanislav Mekhanoshin       return true;
9050ea93a2SStanislav Mekhanoshin     }
9150ea93a2SStanislav Mekhanoshin 
9250ea93a2SStanislav Mekhanoshin   /// \brief Unify version metadata.
9350ea93a2SStanislav Mekhanoshin   /// \return true if changes are made.
9450ea93a2SStanislav Mekhanoshin   /// Assume the named metadata has operands each of which is a list e.g.
9550ea93a2SStanislav Mekhanoshin   /// !Name = {!n1, !n2}
9650ea93a2SStanislav Mekhanoshin   /// !n1 = !{!"cl_khr_fp16", {!"cl_khr_fp64"}}
9750ea93a2SStanislav Mekhanoshin   /// !n2 = !{!"cl_khr_image"}
9850ea93a2SStanislav Mekhanoshin   /// Combine it into a single list with unique operands.
9950ea93a2SStanislav Mekhanoshin   bool unifyExtensionMD(Module &M, StringRef Name) {
10050ea93a2SStanislav Mekhanoshin     auto NamedMD = M.getNamedMetadata(Name);
10150ea93a2SStanislav Mekhanoshin     if (!NamedMD || NamedMD->getNumOperands() == 1)
10250ea93a2SStanislav Mekhanoshin       return false;
10350ea93a2SStanislav Mekhanoshin 
10450ea93a2SStanislav Mekhanoshin     SmallVector<Metadata *, 4> All;
10550ea93a2SStanislav Mekhanoshin     for (const auto &MD : NamedMD->operands())
10650ea93a2SStanislav Mekhanoshin       for (const auto &Op : MD->operands())
10750ea93a2SStanislav Mekhanoshin         if (std::find(All.begin(), All.end(), Op.get()) == All.end())
10850ea93a2SStanislav Mekhanoshin           All.push_back(Op.get());
10950ea93a2SStanislav Mekhanoshin 
11050ea93a2SStanislav Mekhanoshin     NamedMD->eraseFromParent();
11150ea93a2SStanislav Mekhanoshin     NamedMD = M.getOrInsertNamedMetadata(Name);
112980688cdSKonstantin Zhuravlyov     for (const auto &MD : All)
113980688cdSKonstantin Zhuravlyov       NamedMD->addOperand(MDNode::get(M.getContext(), MD));
114980688cdSKonstantin Zhuravlyov 
11550ea93a2SStanislav Mekhanoshin     return true;
11650ea93a2SStanislav Mekhanoshin   }
11750ea93a2SStanislav Mekhanoshin };
11850ea93a2SStanislav Mekhanoshin 
11950ea93a2SStanislav Mekhanoshin } // end anonymous namespace
12050ea93a2SStanislav Mekhanoshin 
12150ea93a2SStanislav Mekhanoshin char AMDGPUUnifyMetadata::ID = 0;
12250ea93a2SStanislav Mekhanoshin 
12350ea93a2SStanislav Mekhanoshin char &llvm::AMDGPUUnifyMetadataID = AMDGPUUnifyMetadata::ID;
12450ea93a2SStanislav Mekhanoshin 
12550ea93a2SStanislav Mekhanoshin INITIALIZE_PASS(AMDGPUUnifyMetadata, "amdgpu-unify-metadata",
12650ea93a2SStanislav Mekhanoshin                 "Unify multiple OpenCL metadata due to linking",
12750ea93a2SStanislav Mekhanoshin                 false, false)
12850ea93a2SStanislav Mekhanoshin 
12950ea93a2SStanislav Mekhanoshin FunctionPass* llvm::createAMDGPUUnifyMetadataPass() {
13050ea93a2SStanislav Mekhanoshin   return new AMDGPUUnifyMetadata();
13150ea93a2SStanislav Mekhanoshin }
13250ea93a2SStanislav Mekhanoshin 
13350ea93a2SStanislav Mekhanoshin bool AMDGPUUnifyMetadata::runOnModule(Module &M) {
13450ea93a2SStanislav Mekhanoshin   const char* Vers[] = {
13550ea93a2SStanislav Mekhanoshin       kOCLMD::SpirVer,
13650ea93a2SStanislav Mekhanoshin       kOCLMD::OCLVer
13750ea93a2SStanislav Mekhanoshin   };
13850ea93a2SStanislav Mekhanoshin   const char* Exts[] = {
13950ea93a2SStanislav Mekhanoshin       kOCLMD::UsedExt,
14050ea93a2SStanislav Mekhanoshin       kOCLMD::UsedOptCoreFeat,
14150ea93a2SStanislav Mekhanoshin       kOCLMD::CompilerOptions,
14250ea93a2SStanislav Mekhanoshin       kOCLMD::LLVMIdent
14350ea93a2SStanislav Mekhanoshin   };
14450ea93a2SStanislav Mekhanoshin 
14550ea93a2SStanislav Mekhanoshin   bool Changed = false;
14650ea93a2SStanislav Mekhanoshin 
14750ea93a2SStanislav Mekhanoshin   for (auto &I : Vers)
14850ea93a2SStanislav Mekhanoshin     Changed |= unifyVersionMD(M, I, true);
14950ea93a2SStanislav Mekhanoshin 
15050ea93a2SStanislav Mekhanoshin   for (auto &I : Exts)
15150ea93a2SStanislav Mekhanoshin     Changed |= unifyExtensionMD(M, I);
15250ea93a2SStanislav Mekhanoshin 
15350ea93a2SStanislav Mekhanoshin   return Changed;
15450ea93a2SStanislav Mekhanoshin }
15550ea93a2SStanislav Mekhanoshin 
15650ea93a2SStanislav Mekhanoshin bool AMDGPUUnifyMetadata::runOnFunction(Function &F) {
15750ea93a2SStanislav Mekhanoshin   return runOnModule(*F.getParent());
15850ea93a2SStanislav Mekhanoshin }
159