xref: /freebsd/contrib/llvm-project/llvm/lib/Transforms/Utils/CtorUtils.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
10b57cec5SDimitry Andric //===- CtorUtils.cpp - Helpers for working with global_ctors ----*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file defines functions that are used to process llvm.global_ctors.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include "llvm/Transforms/Utils/CtorUtils.h"
140b57cec5SDimitry Andric #include "llvm/ADT/BitVector.h"
150b57cec5SDimitry Andric #include "llvm/IR/Constants.h"
160b57cec5SDimitry Andric #include "llvm/IR/Function.h"
170b57cec5SDimitry Andric #include "llvm/IR/GlobalVariable.h"
180b57cec5SDimitry Andric #include "llvm/IR/Module.h"
190b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
200b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
2181ad6265SDimitry Andric #include <numeric>
220b57cec5SDimitry Andric 
230b57cec5SDimitry Andric #define DEBUG_TYPE "ctor_utils"
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric using namespace llvm;
260b57cec5SDimitry Andric 
270b57cec5SDimitry Andric /// Given a specified llvm.global_ctors list, remove the listed elements.
280b57cec5SDimitry Andric static void removeGlobalCtors(GlobalVariable *GCL, const BitVector &CtorsToRemove) {
290b57cec5SDimitry Andric   // Filter out the initializer elements to remove.
300b57cec5SDimitry Andric   ConstantArray *OldCA = cast<ConstantArray>(GCL->getInitializer());
310b57cec5SDimitry Andric   SmallVector<Constant *, 10> CAList;
320b57cec5SDimitry Andric   for (unsigned I = 0, E = OldCA->getNumOperands(); I < E; ++I)
330b57cec5SDimitry Andric     if (!CtorsToRemove.test(I))
340b57cec5SDimitry Andric       CAList.push_back(OldCA->getOperand(I));
350b57cec5SDimitry Andric 
360b57cec5SDimitry Andric   // Create the new array initializer.
370b57cec5SDimitry Andric   ArrayType *ATy =
380b57cec5SDimitry Andric       ArrayType::get(OldCA->getType()->getElementType(), CAList.size());
390b57cec5SDimitry Andric   Constant *CA = ConstantArray::get(ATy, CAList);
400b57cec5SDimitry Andric 
410b57cec5SDimitry Andric   // If we didn't change the number of elements, don't create a new GV.
420b57cec5SDimitry Andric   if (CA->getType() == OldCA->getType()) {
430b57cec5SDimitry Andric     GCL->setInitializer(CA);
440b57cec5SDimitry Andric     return;
450b57cec5SDimitry Andric   }
460b57cec5SDimitry Andric 
470b57cec5SDimitry Andric   // Create the new global and insert it next to the existing list.
480b57cec5SDimitry Andric   GlobalVariable *NGV =
490b57cec5SDimitry Andric       new GlobalVariable(CA->getType(), GCL->isConstant(), GCL->getLinkage(),
500b57cec5SDimitry Andric                          CA, "", GCL->getThreadLocalMode());
5106c3fb27SDimitry Andric   GCL->getParent()->insertGlobalVariable(GCL->getIterator(), NGV);
520b57cec5SDimitry Andric   NGV->takeName(GCL);
530b57cec5SDimitry Andric 
540b57cec5SDimitry Andric   // Nuke the old list, replacing any uses with the new one.
55*5f757f3fSDimitry Andric   if (!GCL->use_empty())
56*5f757f3fSDimitry Andric     GCL->replaceAllUsesWith(NGV);
57*5f757f3fSDimitry Andric 
580b57cec5SDimitry Andric   GCL->eraseFromParent();
590b57cec5SDimitry Andric }
600b57cec5SDimitry Andric 
610b57cec5SDimitry Andric /// Given a llvm.global_ctors list that we can understand,
620b57cec5SDimitry Andric /// return a list of the functions and null terminator as a vector.
6381ad6265SDimitry Andric static std::vector<std::pair<uint32_t, Function *>>
6481ad6265SDimitry Andric parseGlobalCtors(GlobalVariable *GV) {
650b57cec5SDimitry Andric   ConstantArray *CA = cast<ConstantArray>(GV->getInitializer());
6681ad6265SDimitry Andric   std::vector<std::pair<uint32_t, Function *>> Result;
670b57cec5SDimitry Andric   Result.reserve(CA->getNumOperands());
680b57cec5SDimitry Andric   for (auto &V : CA->operands()) {
690b57cec5SDimitry Andric     ConstantStruct *CS = cast<ConstantStruct>(V);
7081ad6265SDimitry Andric     Result.emplace_back(cast<ConstantInt>(CS->getOperand(0))->getZExtValue(),
7181ad6265SDimitry Andric                         dyn_cast<Function>(CS->getOperand(1)));
720b57cec5SDimitry Andric   }
730b57cec5SDimitry Andric   return Result;
740b57cec5SDimitry Andric }
750b57cec5SDimitry Andric 
7681ad6265SDimitry Andric /// Find the llvm.global_ctors list.
770b57cec5SDimitry Andric static GlobalVariable *findGlobalCtors(Module &M) {
780b57cec5SDimitry Andric   GlobalVariable *GV = M.getGlobalVariable("llvm.global_ctors");
790b57cec5SDimitry Andric   if (!GV)
800b57cec5SDimitry Andric     return nullptr;
810b57cec5SDimitry Andric 
820b57cec5SDimitry Andric   // Verify that the initializer is simple enough for us to handle. We are
830b57cec5SDimitry Andric   // only allowed to optimize the initializer if it is unique.
840b57cec5SDimitry Andric   if (!GV->hasUniqueInitializer())
850b57cec5SDimitry Andric     return nullptr;
860b57cec5SDimitry Andric 
8781ad6265SDimitry Andric   // If there are no ctors, then the initializer might be null/undef/poison.
8881ad6265SDimitry Andric   // Ignore anything but an array.
8981ad6265SDimitry Andric   ConstantArray *CA = dyn_cast<ConstantArray>(GV->getInitializer());
9081ad6265SDimitry Andric   if (!CA)
9181ad6265SDimitry Andric     return nullptr;
920b57cec5SDimitry Andric 
930b57cec5SDimitry Andric   for (auto &V : CA->operands()) {
940b57cec5SDimitry Andric     if (isa<ConstantAggregateZero>(V))
950b57cec5SDimitry Andric       continue;
960b57cec5SDimitry Andric     ConstantStruct *CS = cast<ConstantStruct>(V);
970b57cec5SDimitry Andric     if (isa<ConstantPointerNull>(CS->getOperand(1)))
980b57cec5SDimitry Andric       continue;
990b57cec5SDimitry Andric 
10081ad6265SDimitry Andric     // Can only handle global constructors with no arguments.
10181ad6265SDimitry Andric     Function *F = dyn_cast<Function>(CS->getOperand(1));
10281ad6265SDimitry Andric     if (!F || F->arg_size() != 0)
1030b57cec5SDimitry Andric       return nullptr;
1040b57cec5SDimitry Andric   }
1050b57cec5SDimitry Andric   return GV;
1060b57cec5SDimitry Andric }
1070b57cec5SDimitry Andric 
1080b57cec5SDimitry Andric /// Call "ShouldRemove" for every entry in M's global_ctor list and remove the
1090b57cec5SDimitry Andric /// entries for which it returns true.  Return true if anything changed.
1100b57cec5SDimitry Andric bool llvm::optimizeGlobalCtorsList(
11181ad6265SDimitry Andric     Module &M, function_ref<bool(uint32_t, Function *)> ShouldRemove) {
1120b57cec5SDimitry Andric   GlobalVariable *GlobalCtors = findGlobalCtors(M);
1130b57cec5SDimitry Andric   if (!GlobalCtors)
1140b57cec5SDimitry Andric     return false;
1150b57cec5SDimitry Andric 
11681ad6265SDimitry Andric   std::vector<std::pair<uint32_t, Function *>> Ctors =
11781ad6265SDimitry Andric       parseGlobalCtors(GlobalCtors);
1180b57cec5SDimitry Andric   if (Ctors.empty())
1190b57cec5SDimitry Andric     return false;
1200b57cec5SDimitry Andric 
1210b57cec5SDimitry Andric   bool MadeChange = false;
1220b57cec5SDimitry Andric   // Loop over global ctors, optimizing them when we can.
12381ad6265SDimitry Andric   BitVector CtorsToRemove(Ctors.size());
12481ad6265SDimitry Andric   std::vector<size_t> CtorsByPriority(Ctors.size());
12581ad6265SDimitry Andric   std::iota(CtorsByPriority.begin(), CtorsByPriority.end(), 0);
12681ad6265SDimitry Andric   stable_sort(CtorsByPriority, [&](size_t LHS, size_t RHS) {
12781ad6265SDimitry Andric     return Ctors[LHS].first < Ctors[RHS].first;
12881ad6265SDimitry Andric   });
12981ad6265SDimitry Andric   for (unsigned CtorIndex : CtorsByPriority) {
13081ad6265SDimitry Andric     const uint32_t Priority = Ctors[CtorIndex].first;
13181ad6265SDimitry Andric     Function *F = Ctors[CtorIndex].second;
1320b57cec5SDimitry Andric     if (!F)
1330b57cec5SDimitry Andric       continue;
1340b57cec5SDimitry Andric 
1350b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "Optimizing Global Constructor: " << *F << "\n");
1360b57cec5SDimitry Andric 
1370b57cec5SDimitry Andric     // If we can evaluate the ctor at compile time, do.
13881ad6265SDimitry Andric     if (ShouldRemove(Priority, F)) {
13981ad6265SDimitry Andric       Ctors[CtorIndex].second = nullptr;
14081ad6265SDimitry Andric       CtorsToRemove.set(CtorIndex);
1410b57cec5SDimitry Andric       MadeChange = true;
1420b57cec5SDimitry Andric       continue;
1430b57cec5SDimitry Andric     }
1440b57cec5SDimitry Andric   }
1450b57cec5SDimitry Andric 
1460b57cec5SDimitry Andric   if (!MadeChange)
1470b57cec5SDimitry Andric     return false;
1480b57cec5SDimitry Andric 
1490b57cec5SDimitry Andric   removeGlobalCtors(GlobalCtors, CtorsToRemove);
1500b57cec5SDimitry Andric   return true;
1510b57cec5SDimitry Andric }
152