xref: /freebsd/contrib/llvm-project/llvm/lib/Transforms/Utils/CtorUtils.cpp (revision 37f1f2684f2670b204080ef2d6c303becd28545f)
1  //===- CtorUtils.cpp - Helpers for working with global_ctors ----*- C++ -*-===//
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  // This file defines functions that are used to process llvm.global_ctors.
10  //
11  //===----------------------------------------------------------------------===//
12  
13  #include "llvm/Transforms/Utils/CtorUtils.h"
14  #include "llvm/ADT/BitVector.h"
15  #include "llvm/IR/Constants.h"
16  #include "llvm/IR/Function.h"
17  #include "llvm/IR/GlobalVariable.h"
18  #include "llvm/IR/Module.h"
19  #include "llvm/Support/Debug.h"
20  #include "llvm/Support/raw_ostream.h"
21  
22  #define DEBUG_TYPE "ctor_utils"
23  
24  using namespace llvm;
25  
26  /// Given a specified llvm.global_ctors list, remove the listed elements.
27  static void removeGlobalCtors(GlobalVariable *GCL, const BitVector &CtorsToRemove) {
28    // Filter out the initializer elements to remove.
29    ConstantArray *OldCA = cast<ConstantArray>(GCL->getInitializer());
30    SmallVector<Constant *, 10> CAList;
31    for (unsigned I = 0, E = OldCA->getNumOperands(); I < E; ++I)
32      if (!CtorsToRemove.test(I))
33        CAList.push_back(OldCA->getOperand(I));
34  
35    // Create the new array initializer.
36    ArrayType *ATy =
37        ArrayType::get(OldCA->getType()->getElementType(), CAList.size());
38    Constant *CA = ConstantArray::get(ATy, CAList);
39  
40    // If we didn't change the number of elements, don't create a new GV.
41    if (CA->getType() == OldCA->getType()) {
42      GCL->setInitializer(CA);
43      return;
44    }
45  
46    // Create the new global and insert it next to the existing list.
47    GlobalVariable *NGV =
48        new GlobalVariable(CA->getType(), GCL->isConstant(), GCL->getLinkage(),
49                           CA, "", GCL->getThreadLocalMode());
50    GCL->getParent()->getGlobalList().insert(GCL->getIterator(), NGV);
51    NGV->takeName(GCL);
52  
53    // Nuke the old list, replacing any uses with the new one.
54    if (!GCL->use_empty()) {
55      Constant *V = NGV;
56      if (V->getType() != GCL->getType())
57        V = ConstantExpr::getBitCast(V, GCL->getType());
58      GCL->replaceAllUsesWith(V);
59    }
60    GCL->eraseFromParent();
61  }
62  
63  /// Given a llvm.global_ctors list that we can understand,
64  /// return a list of the functions and null terminator as a vector.
65  static std::vector<Function *> parseGlobalCtors(GlobalVariable *GV) {
66    if (GV->getInitializer()->isNullValue())
67      return std::vector<Function *>();
68    ConstantArray *CA = cast<ConstantArray>(GV->getInitializer());
69    std::vector<Function *> Result;
70    Result.reserve(CA->getNumOperands());
71    for (auto &V : CA->operands()) {
72      ConstantStruct *CS = cast<ConstantStruct>(V);
73      Result.push_back(dyn_cast<Function>(CS->getOperand(1)));
74    }
75    return Result;
76  }
77  
78  /// Find the llvm.global_ctors list, verifying that all initializers have an
79  /// init priority of 65535.
80  static GlobalVariable *findGlobalCtors(Module &M) {
81    GlobalVariable *GV = M.getGlobalVariable("llvm.global_ctors");
82    if (!GV)
83      return nullptr;
84  
85    // Verify that the initializer is simple enough for us to handle. We are
86    // only allowed to optimize the initializer if it is unique.
87    if (!GV->hasUniqueInitializer())
88      return nullptr;
89  
90    if (isa<ConstantAggregateZero>(GV->getInitializer()))
91      return GV;
92    ConstantArray *CA = cast<ConstantArray>(GV->getInitializer());
93  
94    for (auto &V : CA->operands()) {
95      if (isa<ConstantAggregateZero>(V))
96        continue;
97      ConstantStruct *CS = cast<ConstantStruct>(V);
98      if (isa<ConstantPointerNull>(CS->getOperand(1)))
99        continue;
100  
101      // Must have a function or null ptr.
102      if (!isa<Function>(CS->getOperand(1)))
103        return nullptr;
104  
105      // Init priority must be standard.
106      ConstantInt *CI = cast<ConstantInt>(CS->getOperand(0));
107      if (CI->getZExtValue() != 65535)
108        return nullptr;
109    }
110  
111    return GV;
112  }
113  
114  /// Call "ShouldRemove" for every entry in M's global_ctor list and remove the
115  /// entries for which it returns true.  Return true if anything changed.
116  bool llvm::optimizeGlobalCtorsList(
117      Module &M, function_ref<bool(Function *)> ShouldRemove) {
118    GlobalVariable *GlobalCtors = findGlobalCtors(M);
119    if (!GlobalCtors)
120      return false;
121  
122    std::vector<Function *> Ctors = parseGlobalCtors(GlobalCtors);
123    if (Ctors.empty())
124      return false;
125  
126    bool MadeChange = false;
127  
128    // Loop over global ctors, optimizing them when we can.
129    unsigned NumCtors = Ctors.size();
130    BitVector CtorsToRemove(NumCtors);
131    for (unsigned i = 0; i != Ctors.size() && NumCtors > 0; ++i) {
132      Function *F = Ctors[i];
133      // Found a null terminator in the middle of the list, prune off the rest of
134      // the list.
135      if (!F)
136        continue;
137  
138      LLVM_DEBUG(dbgs() << "Optimizing Global Constructor: " << *F << "\n");
139  
140      // We cannot simplify external ctor functions.
141      if (F->empty())
142        continue;
143  
144      // If we can evaluate the ctor at compile time, do.
145      if (ShouldRemove(F)) {
146        Ctors[i] = nullptr;
147        CtorsToRemove.set(i);
148        NumCtors--;
149        MadeChange = true;
150        continue;
151      }
152    }
153  
154    if (!MadeChange)
155      return false;
156  
157    removeGlobalCtors(GlobalCtors, CtorsToRemove);
158    return true;
159  }
160