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.
removeGlobalCtors(GlobalVariable * GCL,const BitVector & CtorsToRemove)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 *>>
parseGlobalCtors(GlobalVariable * GV)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.
findGlobalCtors(Module & M)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.
optimizeGlobalCtorsList(Module & M,function_ref<bool (uint32_t,Function *)> ShouldRemove)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