| //===- GlobalSplit.cpp - global variable splitter -------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This pass uses inrange annotations on GEP indices to split globals where | |
| // beneficial. Clang currently attaches these annotations to references to | |
| // virtual table globals under the Itanium ABI for the benefit of the | |
| // whole-program virtual call optimization and control flow integrity passes. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/Transforms/IPO/GlobalSplit.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/IR/Constant.h" | |
| #include "llvm/IR/Constants.h" | |
| #include "llvm/IR/DataLayout.h" | |
| #include "llvm/IR/Function.h" | |
| #include "llvm/IR/GlobalValue.h" | |
| #include "llvm/IR/GlobalVariable.h" | |
| #include "llvm/IR/Intrinsics.h" | |
| #include "llvm/IR/LLVMContext.h" | |
| #include "llvm/IR/Metadata.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/IR/Operator.h" | |
| #include "llvm/IR/Type.h" | |
| #include "llvm/IR/User.h" | |
| #include "llvm/InitializePasses.h" | |
| #include "llvm/Pass.h" | |
| #include "llvm/Support/Casting.h" | |
| #include "llvm/Transforms/IPO.h" | |
| #include <cstdint> | |
| #include <vector> | |
| using namespace llvm; | |
| static bool splitGlobal(GlobalVariable &GV) { | |
| // If the address of the global is taken outside of the module, we cannot | |
| // apply this transformation. | |
| if (!GV.hasLocalLinkage()) | |
| return false; | |
| // We currently only know how to split ConstantStructs. | |
| auto *Init = dyn_cast_or_null<ConstantStruct>(GV.getInitializer()); | |
| if (!Init) | |
| return false; | |
| // Verify that each user of the global is an inrange getelementptr constant. | |
| // From this it follows that any loads from or stores to that global must use | |
| // a pointer derived from an inrange getelementptr constant, which is | |
| // sufficient to allow us to apply the splitting transform. | |
| for (User *U : GV.users()) { | |
| if (!isa<Constant>(U)) | |
| return false; | |
| auto *GEP = dyn_cast<GEPOperator>(U); | |
| if (!GEP || !GEP->getInRangeIndex() || *GEP->getInRangeIndex() != 1 || | |
| !isa<ConstantInt>(GEP->getOperand(1)) || | |
| !cast<ConstantInt>(GEP->getOperand(1))->isZero() || | |
| !isa<ConstantInt>(GEP->getOperand(2))) | |
| return false; | |
| } | |
| SmallVector<MDNode *, 2> Types; | |
| GV.getMetadata(LLVMContext::MD_type, Types); | |
| const DataLayout &DL = GV.getParent()->getDataLayout(); | |
| const StructLayout *SL = DL.getStructLayout(Init->getType()); | |
| IntegerType *Int32Ty = Type::getInt32Ty(GV.getContext()); | |
| std::vector<GlobalVariable *> SplitGlobals(Init->getNumOperands()); | |
| for (unsigned I = 0; I != Init->getNumOperands(); ++I) { | |
| // Build a global representing this split piece. | |
| auto *SplitGV = | |
| new GlobalVariable(*GV.getParent(), Init->getOperand(I)->getType(), | |
| GV.isConstant(), GlobalValue::PrivateLinkage, | |
| Init->getOperand(I), GV.getName() + "." + utostr(I)); | |
| SplitGlobals[I] = SplitGV; | |
| unsigned SplitBegin = SL->getElementOffset(I); | |
| unsigned SplitEnd = (I == Init->getNumOperands() - 1) | |
| ? SL->getSizeInBytes() | |
| : SL->getElementOffset(I + 1); | |
| // Rebuild type metadata, adjusting by the split offset. | |
| // FIXME: See if we can use DW_OP_piece to preserve debug metadata here. | |
| for (MDNode *Type : Types) { | |
| uint64_t ByteOffset = cast<ConstantInt>( | |
| cast<ConstantAsMetadata>(Type->getOperand(0))->getValue()) | |
| ->getZExtValue(); | |
| // Type metadata may be attached one byte after the end of the vtable, for | |
| // classes without virtual methods in Itanium ABI. AFAIK, it is never | |
| // attached to the first byte of a vtable. Subtract one to get the right | |
| // slice. | |
| // This is making an assumption that vtable groups are the only kinds of | |
| // global variables that !type metadata can be attached to, and that they | |
| // are either Itanium ABI vtable groups or contain a single vtable (i.e. | |
| // Microsoft ABI vtables). | |
| uint64_t AttachedTo = (ByteOffset == 0) ? ByteOffset : ByteOffset - 1; | |
| if (AttachedTo < SplitBegin || AttachedTo >= SplitEnd) | |
| continue; | |
| SplitGV->addMetadata( | |
| LLVMContext::MD_type, | |
| *MDNode::get(GV.getContext(), | |
| {ConstantAsMetadata::get( | |
| ConstantInt::get(Int32Ty, ByteOffset - SplitBegin)), | |
| Type->getOperand(1)})); | |
| } | |
| if (GV.hasMetadata(LLVMContext::MD_vcall_visibility)) | |
| SplitGV->setVCallVisibilityMetadata(GV.getVCallVisibility()); | |
| } | |
| for (User *U : GV.users()) { | |
| auto *GEP = cast<GEPOperator>(U); | |
| unsigned I = cast<ConstantInt>(GEP->getOperand(2))->getZExtValue(); | |
| if (I >= SplitGlobals.size()) | |
| continue; | |
| SmallVector<Value *, 4> Ops; | |
| Ops.push_back(ConstantInt::get(Int32Ty, 0)); | |
| for (unsigned I = 3; I != GEP->getNumOperands(); ++I) | |
| Ops.push_back(GEP->getOperand(I)); | |
| auto *NewGEP = ConstantExpr::getGetElementPtr( | |
| SplitGlobals[I]->getInitializer()->getType(), SplitGlobals[I], Ops, | |
| GEP->isInBounds()); | |
| GEP->replaceAllUsesWith(NewGEP); | |
| } | |
| // Finally, remove the original global. Any remaining uses refer to invalid | |
| // elements of the global, so replace with poison. | |
| if (!GV.use_empty()) | |
| GV.replaceAllUsesWith(PoisonValue::get(GV.getType())); | |
| GV.eraseFromParent(); | |
| return true; | |
| } | |
| static bool splitGlobals(Module &M) { | |
| // First, see if the module uses either of the llvm.type.test or | |
| // llvm.type.checked.load intrinsics, which indicates that splitting globals | |
| // may be beneficial. | |
| Function *TypeTestFunc = | |
| M.getFunction(Intrinsic::getName(Intrinsic::type_test)); | |
| Function *TypeCheckedLoadFunc = | |
| M.getFunction(Intrinsic::getName(Intrinsic::type_checked_load)); | |
| if ((!TypeTestFunc || TypeTestFunc->use_empty()) && | |
| (!TypeCheckedLoadFunc || TypeCheckedLoadFunc->use_empty())) | |
| return false; | |
| bool Changed = false; | |
| for (GlobalVariable &GV : llvm::make_early_inc_range(M.globals())) | |
| Changed |= splitGlobal(GV); | |
| return Changed; | |
| } | |
| namespace { | |
| struct GlobalSplit : public ModulePass { | |
| static char ID; | |
| GlobalSplit() : ModulePass(ID) { | |
| initializeGlobalSplitPass(*PassRegistry::getPassRegistry()); | |
| } | |
| bool runOnModule(Module &M) override { | |
| if (skipModule(M)) | |
| return false; | |
| return splitGlobals(M); | |
| } | |
| }; | |
| } // end anonymous namespace | |
| char GlobalSplit::ID = 0; | |
| INITIALIZE_PASS(GlobalSplit, "globalsplit", "Global splitter", false, false) | |
| ModulePass *llvm::createGlobalSplitPass() { | |
| return new GlobalSplit; | |
| } | |
| PreservedAnalyses GlobalSplitPass::run(Module &M, ModuleAnalysisManager &AM) { | |
| if (!splitGlobals(M)) | |
| return PreservedAnalyses::all(); | |
| return PreservedAnalyses::none(); | |
| } |