1 //===-- NVPTXCtorDtorLowering.cpp - Handle global ctors and dtors --------===//
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 creates a unified init and fini kernel with the required metadata
11 //===----------------------------------------------------------------------===//
12 
13 #include "NVPTXCtorDtorLowering.h"
14 #include "NVPTX.h"
15 #include "llvm/ADT/StringExtras.h"
16 #include "llvm/IR/Constants.h"
17 #include "llvm/IR/Function.h"
18 #include "llvm/IR/GlobalVariable.h"
19 #include "llvm/IR/IRBuilder.h"
20 #include "llvm/IR/Module.h"
21 #include "llvm/IR/Value.h"
22 #include "llvm/Pass.h"
23 #include "llvm/Support/CommandLine.h"
24 #include "llvm/Transforms/Utils/ModuleUtils.h"
25 
26 using namespace llvm;
27 
28 #define DEBUG_TYPE "nvptx-lower-ctor-dtor"
29 
30 static cl::opt<std::string>
31     GlobalStr("nvptx-lower-global-ctor-dtor-id",
32               cl::desc("Override unique ID of ctor/dtor globals."),
33               cl::init(""), cl::Hidden);
34 
35 namespace {
36 
37 static std::string getHash(StringRef Str) {
38   llvm::MD5 Hasher;
39   llvm::MD5::MD5Result Hash;
40   Hasher.update(Str);
41   Hasher.final(Hash);
42   return llvm::utohexstr(Hash.low(), /*LowerCase=*/true);
43 }
44 
45 static bool createInitOrFiniGlobls(Module &M, StringRef GlobalName,
46                                    bool IsCtor) {
47   GlobalVariable *GV = M.getGlobalVariable(GlobalName);
48   if (!GV || !GV->hasInitializer())
49     return false;
50   ConstantArray *GA = dyn_cast<ConstantArray>(GV->getInitializer());
51   if (!GA || GA->getNumOperands() == 0)
52     return false;
53 
54   // NVPTX has no way to emit variables at specific sections or support for
55   // the traditional constructor sections. Instead, we emit mangled global
56   // names so the runtime can build the list manually.
57   for (Value *V : GA->operands()) {
58     auto *CS = cast<ConstantStruct>(V);
59     auto *F = cast<Constant>(CS->getOperand(1));
60     uint64_t Priority = cast<ConstantInt>(CS->getOperand(0))->getSExtValue();
61     std::string PriorityStr = "." + std::to_string(Priority);
62     // We append a semi-unique hash and the priority to the global name.
63     std::string GlobalID =
64         !GlobalStr.empty() ? GlobalStr : getHash(M.getSourceFileName());
65     std::string NameStr =
66         ((IsCtor ? "__init_array_object_" : "__fini_array_object_") +
67          F->getName() + "_" + GlobalID + "_" + std::to_string(Priority))
68             .str();
69     // PTX does not support exported names with '.' in them.
70     llvm::transform(NameStr, NameStr.begin(),
71                     [](char c) { return c == '.' ? '_' : c; });
72 
73     auto *GV = new GlobalVariable(M, F->getType(), /*IsConstant=*/true,
74                                   GlobalValue::ExternalLinkage, F, NameStr,
75                                   nullptr, GlobalValue::NotThreadLocal,
76                                   /*AddressSpace=*/4);
77     // This isn't respected by Nvidia, simply put here for clarity.
78     GV->setSection(IsCtor ? ".init_array" + PriorityStr
79                           : ".fini_array" + PriorityStr);
80     GV->setVisibility(GlobalVariable::ProtectedVisibility);
81     appendToUsed(M, {GV});
82   }
83 
84   GV->eraseFromParent();
85   return true;
86 }
87 
88 static bool lowerCtorsAndDtors(Module &M) {
89   bool Modified = false;
90   Modified |= createInitOrFiniGlobls(M, "llvm.global_ctors", /*IsCtor =*/true);
91   Modified |= createInitOrFiniGlobls(M, "llvm.global_dtors", /*IsCtor =*/false);
92   return Modified;
93 }
94 
95 class NVPTXCtorDtorLoweringLegacy final : public ModulePass {
96 public:
97   static char ID;
98   NVPTXCtorDtorLoweringLegacy() : ModulePass(ID) {}
99   bool runOnModule(Module &M) override { return lowerCtorsAndDtors(M); }
100 };
101 
102 } // End anonymous namespace
103 
104 PreservedAnalyses NVPTXCtorDtorLoweringPass::run(Module &M,
105                                                  ModuleAnalysisManager &AM) {
106   return lowerCtorsAndDtors(M) ? PreservedAnalyses::none()
107                                : PreservedAnalyses::all();
108 }
109 
110 char NVPTXCtorDtorLoweringLegacy::ID = 0;
111 char &llvm::NVPTXCtorDtorLoweringLegacyPassID = NVPTXCtorDtorLoweringLegacy::ID;
112 INITIALIZE_PASS(NVPTXCtorDtorLoweringLegacy, DEBUG_TYPE,
113                 "Lower ctors and dtors for NVPTX", false, false)
114 
115 ModulePass *llvm::createNVPTXCtorDtorLoweringLegacyPass() {
116   return new NVPTXCtorDtorLoweringLegacy();
117 }
118