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