Path: blob/main/contrib/llvm-project/llvm/lib/Transforms/IPO/GlobalSplit.cpp
35269 views
//===- GlobalSplit.cpp - global variable splitter -------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This pass uses inrange annotations on GEP indices to split globals where9// beneficial. Clang currently attaches these annotations to references to10// virtual table globals under the Itanium ABI for the benefit of the11// whole-program virtual call optimization and control flow integrity passes.12//13//===----------------------------------------------------------------------===//1415#include "llvm/Transforms/IPO/GlobalSplit.h"16#include "llvm/ADT/SmallVector.h"17#include "llvm/ADT/StringExtras.h"18#include "llvm/IR/Constant.h"19#include "llvm/IR/Constants.h"20#include "llvm/IR/DataLayout.h"21#include "llvm/IR/Function.h"22#include "llvm/IR/GlobalValue.h"23#include "llvm/IR/GlobalVariable.h"24#include "llvm/IR/Intrinsics.h"25#include "llvm/IR/LLVMContext.h"26#include "llvm/IR/Metadata.h"27#include "llvm/IR/Module.h"28#include "llvm/IR/Operator.h"29#include "llvm/IR/Type.h"30#include "llvm/IR/User.h"31#include "llvm/Support/Casting.h"32#include "llvm/Transforms/IPO.h"33#include <cstdint>34#include <vector>3536using namespace llvm;3738static bool splitGlobal(GlobalVariable &GV) {39// If the address of the global is taken outside of the module, we cannot40// apply this transformation.41if (!GV.hasLocalLinkage())42return false;4344// We currently only know how to split ConstantStructs.45auto *Init = dyn_cast_or_null<ConstantStruct>(GV.getInitializer());46if (!Init)47return false;4849const DataLayout &DL = GV.getDataLayout();50const StructLayout *SL = DL.getStructLayout(Init->getType());51ArrayRef<TypeSize> MemberOffsets = SL->getMemberOffsets();52unsigned IndexWidth = DL.getIndexTypeSizeInBits(GV.getType());5354// Verify that each user of the global is an inrange getelementptr constant,55// and collect information on how it relates to the global.56struct GEPInfo {57GEPOperator *GEP;58unsigned MemberIndex;59APInt MemberRelativeOffset;6061GEPInfo(GEPOperator *GEP, unsigned MemberIndex, APInt MemberRelativeOffset)62: GEP(GEP), MemberIndex(MemberIndex),63MemberRelativeOffset(std::move(MemberRelativeOffset)) {}64};65SmallVector<GEPInfo> Infos;66for (User *U : GV.users()) {67auto *GEP = dyn_cast<GEPOperator>(U);68if (!GEP)69return false;7071std::optional<ConstantRange> InRange = GEP->getInRange();72if (!InRange)73return false;7475APInt Offset(IndexWidth, 0);76if (!GEP->accumulateConstantOffset(DL, Offset))77return false;7879// Determine source-relative inrange.80ConstantRange SrcInRange = InRange->sextOrTrunc(IndexWidth).add(Offset);8182// Check that the GEP offset is in the range (treating upper bound as83// inclusive here).84if (!SrcInRange.contains(Offset) && SrcInRange.getUpper() != Offset)85return false;8687// Find which struct member the range corresponds to.88if (SrcInRange.getLower().uge(SL->getSizeInBytes()))89return false;9091unsigned MemberIndex =92SL->getElementContainingOffset(SrcInRange.getLower().getZExtValue());93TypeSize MemberStart = MemberOffsets[MemberIndex];94TypeSize MemberEnd = MemberIndex == MemberOffsets.size() - 195? SL->getSizeInBytes()96: MemberOffsets[MemberIndex + 1];9798// Verify that the range matches that struct member.99if (SrcInRange.getLower() != MemberStart ||100SrcInRange.getUpper() != MemberEnd)101return false;102103Infos.emplace_back(GEP, MemberIndex, Offset - MemberStart);104}105106SmallVector<MDNode *, 2> Types;107GV.getMetadata(LLVMContext::MD_type, Types);108109IntegerType *Int32Ty = Type::getInt32Ty(GV.getContext());110111std::vector<GlobalVariable *> SplitGlobals(Init->getNumOperands());112for (unsigned I = 0; I != Init->getNumOperands(); ++I) {113// Build a global representing this split piece.114auto *SplitGV =115new GlobalVariable(*GV.getParent(), Init->getOperand(I)->getType(),116GV.isConstant(), GlobalValue::PrivateLinkage,117Init->getOperand(I), GV.getName() + "." + utostr(I));118SplitGlobals[I] = SplitGV;119120unsigned SplitBegin = SL->getElementOffset(I);121unsigned SplitEnd = (I == Init->getNumOperands() - 1)122? SL->getSizeInBytes()123: SL->getElementOffset(I + 1);124125// Rebuild type metadata, adjusting by the split offset.126// FIXME: See if we can use DW_OP_piece to preserve debug metadata here.127for (MDNode *Type : Types) {128uint64_t ByteOffset = cast<ConstantInt>(129cast<ConstantAsMetadata>(Type->getOperand(0))->getValue())130->getZExtValue();131// Type metadata may be attached one byte after the end of the vtable, for132// classes without virtual methods in Itanium ABI. AFAIK, it is never133// attached to the first byte of a vtable. Subtract one to get the right134// slice.135// This is making an assumption that vtable groups are the only kinds of136// global variables that !type metadata can be attached to, and that they137// are either Itanium ABI vtable groups or contain a single vtable (i.e.138// Microsoft ABI vtables).139uint64_t AttachedTo = (ByteOffset == 0) ? ByteOffset : ByteOffset - 1;140if (AttachedTo < SplitBegin || AttachedTo >= SplitEnd)141continue;142SplitGV->addMetadata(143LLVMContext::MD_type,144*MDNode::get(GV.getContext(),145{ConstantAsMetadata::get(146ConstantInt::get(Int32Ty, ByteOffset - SplitBegin)),147Type->getOperand(1)}));148}149150if (GV.hasMetadata(LLVMContext::MD_vcall_visibility))151SplitGV->setVCallVisibilityMetadata(GV.getVCallVisibility());152}153154for (const GEPInfo &Info : Infos) {155assert(Info.MemberIndex < SplitGlobals.size() && "Invalid member");156auto *NewGEP = ConstantExpr::getGetElementPtr(157Type::getInt8Ty(GV.getContext()), SplitGlobals[Info.MemberIndex],158ConstantInt::get(GV.getContext(), Info.MemberRelativeOffset),159Info.GEP->isInBounds());160Info.GEP->replaceAllUsesWith(NewGEP);161}162163// Finally, remove the original global. Any remaining uses refer to invalid164// elements of the global, so replace with poison.165if (!GV.use_empty())166GV.replaceAllUsesWith(PoisonValue::get(GV.getType()));167GV.eraseFromParent();168return true;169}170171static bool splitGlobals(Module &M) {172// First, see if the module uses either of the llvm.type.test or173// llvm.type.checked.load intrinsics, which indicates that splitting globals174// may be beneficial.175Function *TypeTestFunc =176M.getFunction(Intrinsic::getName(Intrinsic::type_test));177Function *TypeCheckedLoadFunc =178M.getFunction(Intrinsic::getName(Intrinsic::type_checked_load));179Function *TypeCheckedLoadRelativeFunc =180M.getFunction(Intrinsic::getName(Intrinsic::type_checked_load_relative));181if ((!TypeTestFunc || TypeTestFunc->use_empty()) &&182(!TypeCheckedLoadFunc || TypeCheckedLoadFunc->use_empty()) &&183(!TypeCheckedLoadRelativeFunc ||184TypeCheckedLoadRelativeFunc->use_empty()))185return false;186187bool Changed = false;188for (GlobalVariable &GV : llvm::make_early_inc_range(M.globals()))189Changed |= splitGlobal(GV);190return Changed;191}192193PreservedAnalyses GlobalSplitPass::run(Module &M, ModuleAnalysisManager &AM) {194if (!splitGlobals(M))195return PreservedAnalyses::all();196return PreservedAnalyses::none();197}198199200