1*5f757f3fSDimitry Andric //===------ BPFPreserveStaticOffset.cpp -----------------------------------===// 2*5f757f3fSDimitry Andric // 3*5f757f3fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*5f757f3fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*5f757f3fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*5f757f3fSDimitry Andric // 7*5f757f3fSDimitry Andric //===----------------------------------------------------------------------===// 8*5f757f3fSDimitry Andric // 9*5f757f3fSDimitry Andric // TLDR: replaces llvm.preserve.static.offset + GEP + load / store 10*5f757f3fSDimitry Andric // with llvm.bpf.getelementptr.and.load / store 11*5f757f3fSDimitry Andric // 12*5f757f3fSDimitry Andric // This file implements BPFPreserveStaticOffsetPass transformation. 13*5f757f3fSDimitry Andric // This transformation address two BPF verifier specific issues: 14*5f757f3fSDimitry Andric // 15*5f757f3fSDimitry Andric // (a) Access to the fields of some structural types is allowed only 16*5f757f3fSDimitry Andric // using load and store instructions with static immediate offsets. 17*5f757f3fSDimitry Andric // 18*5f757f3fSDimitry Andric // Examples of such types are `struct __sk_buff` and `struct 19*5f757f3fSDimitry Andric // bpf_sock_ops`. This is so because offsets of the fields of 20*5f757f3fSDimitry Andric // these structures do not match real offsets in the running 21*5f757f3fSDimitry Andric // kernel. During BPF program load LDX and STX instructions 22*5f757f3fSDimitry Andric // referring to the fields of these types are rewritten so that 23*5f757f3fSDimitry Andric // offsets match real offsets. For this rewrite to happen field 24*5f757f3fSDimitry Andric // offsets have to be encoded as immediate operands of the 25*5f757f3fSDimitry Andric // instructions. 26*5f757f3fSDimitry Andric // 27*5f757f3fSDimitry Andric // See kernel/bpf/verifier.c:convert_ctx_access function in the 28*5f757f3fSDimitry Andric // Linux kernel source tree for details. 29*5f757f3fSDimitry Andric // 30*5f757f3fSDimitry Andric // (b) Pointers to context parameters of BPF programs must not be 31*5f757f3fSDimitry Andric // modified before access. 32*5f757f3fSDimitry Andric // 33*5f757f3fSDimitry Andric // During BPF program verification a tag PTR_TO_CTX is tracked for 34*5f757f3fSDimitry Andric // register values. In case if register with such tag is modified 35*5f757f3fSDimitry Andric // BPF program is not allowed to read or write memory using this 36*5f757f3fSDimitry Andric // register. See kernel/bpf/verifier.c:check_mem_access function 37*5f757f3fSDimitry Andric // in the Linux kernel source tree for details. 38*5f757f3fSDimitry Andric // 39*5f757f3fSDimitry Andric // The following sequence of the IR instructions: 40*5f757f3fSDimitry Andric // 41*5f757f3fSDimitry Andric // %x = getelementptr %ptr, %constant_offset 42*5f757f3fSDimitry Andric // %y = load %x 43*5f757f3fSDimitry Andric // 44*5f757f3fSDimitry Andric // Is translated as a single machine instruction: 45*5f757f3fSDimitry Andric // 46*5f757f3fSDimitry Andric // LDW %ptr, %constant_offset 47*5f757f3fSDimitry Andric // 48*5f757f3fSDimitry Andric // In order for cases (a) and (b) to work the sequence %x-%y above has 49*5f757f3fSDimitry Andric // to be preserved by the IR passes. 50*5f757f3fSDimitry Andric // 51*5f757f3fSDimitry Andric // However, several optimization passes might sink `load` instruction 52*5f757f3fSDimitry Andric // or hoist `getelementptr` instruction so that the instructions are 53*5f757f3fSDimitry Andric // no longer in sequence. Examples of such passes are: 54*5f757f3fSDimitry Andric // SimplifyCFGPass, InstCombinePass, GVNPass. 55*5f757f3fSDimitry Andric // After such modification the verifier would reject the BPF program. 56*5f757f3fSDimitry Andric // 57*5f757f3fSDimitry Andric // To avoid this issue the patterns like (load/store (getelementptr ...)) 58*5f757f3fSDimitry Andric // are replaced by calls to BPF specific intrinsic functions: 59*5f757f3fSDimitry Andric // - llvm.bpf.getelementptr.and.load 60*5f757f3fSDimitry Andric // - llvm.bpf.getelementptr.and.store 61*5f757f3fSDimitry Andric // 62*5f757f3fSDimitry Andric // These calls are lowered back to (load/store (getelementptr ...)) 63*5f757f3fSDimitry Andric // by BPFCheckAndAdjustIR pass right before the translation from IR to 64*5f757f3fSDimitry Andric // machine instructions. 65*5f757f3fSDimitry Andric // 66*5f757f3fSDimitry Andric // The transformation is split into the following steps: 67*5f757f3fSDimitry Andric // - When IR is generated from AST the calls to intrinsic function 68*5f757f3fSDimitry Andric // llvm.preserve.static.offset are inserted. 69*5f757f3fSDimitry Andric // - BPFPreserveStaticOffsetPass is executed as early as possible 70*5f757f3fSDimitry Andric // with AllowPatial set to true, this handles marked GEP chains 71*5f757f3fSDimitry Andric // with constant offsets. 72*5f757f3fSDimitry Andric // - BPFPreserveStaticOffsetPass is executed at ScalarOptimizerLateEPCallback 73*5f757f3fSDimitry Andric // with AllowPatial set to false, this handles marked GEP chains 74*5f757f3fSDimitry Andric // with offsets that became constant after loop unrolling, e.g. 75*5f757f3fSDimitry Andric // to handle the following code: 76*5f757f3fSDimitry Andric // 77*5f757f3fSDimitry Andric // struct context { int x[4]; } __attribute__((preserve_static_offset)); 78*5f757f3fSDimitry Andric // 79*5f757f3fSDimitry Andric // struct context *ctx = ...; 80*5f757f3fSDimitry Andric // #pragma clang loop unroll(full) 81*5f757f3fSDimitry Andric // for (int i = 0; i < 4; ++i) 82*5f757f3fSDimitry Andric // foo(ctx->x[i]); 83*5f757f3fSDimitry Andric // 84*5f757f3fSDimitry Andric // The early BPFPreserveStaticOffsetPass run is necessary to allow 85*5f757f3fSDimitry Andric // additional GVN / CSE opportunities after functions inlining. 86*5f757f3fSDimitry Andric // The relative order of optimization applied to function: 87*5f757f3fSDimitry Andric // - early stage (1) 88*5f757f3fSDimitry Andric // - ... 89*5f757f3fSDimitry Andric // - function inlining (2) 90*5f757f3fSDimitry Andric // - ... 91*5f757f3fSDimitry Andric // - loop unrolling 92*5f757f3fSDimitry Andric // - ... 93*5f757f3fSDimitry Andric // - ScalarOptimizerLateEPCallback (3) 94*5f757f3fSDimitry Andric // 95*5f757f3fSDimitry Andric // When function A is inlined into function B all optimizations for A 96*5f757f3fSDimitry Andric // are already done, while some passes remain for B. In case if 97*5f757f3fSDimitry Andric // BPFPreserveStaticOffsetPass is done at (3) but not done at (1) 98*5f757f3fSDimitry Andric // the code after (2) would contain a mix of 99*5f757f3fSDimitry Andric // (load (gep %p)) and (get.and.load %p) usages: 100*5f757f3fSDimitry Andric // - the (load (gep %p)) would come from the calling function; 101*5f757f3fSDimitry Andric // - the (get.and.load %p) would come from the callee function. 102*5f757f3fSDimitry Andric // Thus clobbering CSE / GVN passes done after inlining. 103*5f757f3fSDimitry Andric 104*5f757f3fSDimitry Andric #include "BPF.h" 105*5f757f3fSDimitry Andric #include "BPFCORE.h" 106*5f757f3fSDimitry Andric #include "llvm/ADT/SmallPtrSet.h" 107*5f757f3fSDimitry Andric #include "llvm/ADT/SmallVector.h" 108*5f757f3fSDimitry Andric #include "llvm/IR/Argument.h" 109*5f757f3fSDimitry Andric #include "llvm/IR/Attributes.h" 110*5f757f3fSDimitry Andric #include "llvm/IR/BasicBlock.h" 111*5f757f3fSDimitry Andric #include "llvm/IR/Constants.h" 112*5f757f3fSDimitry Andric #include "llvm/IR/DebugInfoMetadata.h" 113*5f757f3fSDimitry Andric #include "llvm/IR/DiagnosticInfo.h" 114*5f757f3fSDimitry Andric #include "llvm/IR/IRBuilder.h" 115*5f757f3fSDimitry Andric #include "llvm/IR/InstIterator.h" 116*5f757f3fSDimitry Andric #include "llvm/IR/Instructions.h" 117*5f757f3fSDimitry Andric #include "llvm/IR/Intrinsics.h" 118*5f757f3fSDimitry Andric #include "llvm/IR/IntrinsicsBPF.h" 119*5f757f3fSDimitry Andric #include "llvm/Support/Debug.h" 120*5f757f3fSDimitry Andric #include "llvm/Support/ErrorHandling.h" 121*5f757f3fSDimitry Andric 122*5f757f3fSDimitry Andric #define DEBUG_TYPE "bpf-preserve-static-offset" 123*5f757f3fSDimitry Andric 124*5f757f3fSDimitry Andric using namespace llvm; 125*5f757f3fSDimitry Andric 126*5f757f3fSDimitry Andric static const unsigned GepAndLoadFirstIdxArg = 6; 127*5f757f3fSDimitry Andric static const unsigned GepAndStoreFirstIdxArg = 7; 128*5f757f3fSDimitry Andric 129*5f757f3fSDimitry Andric static bool isIntrinsicCall(Value *I, Intrinsic::ID Id) { 130*5f757f3fSDimitry Andric if (auto *Call = dyn_cast<CallInst>(I)) 131*5f757f3fSDimitry Andric if (Function *Func = Call->getCalledFunction()) 132*5f757f3fSDimitry Andric return Func->getIntrinsicID() == Id; 133*5f757f3fSDimitry Andric return false; 134*5f757f3fSDimitry Andric } 135*5f757f3fSDimitry Andric 136*5f757f3fSDimitry Andric static bool isPreserveStaticOffsetCall(Value *I) { 137*5f757f3fSDimitry Andric return isIntrinsicCall(I, Intrinsic::preserve_static_offset); 138*5f757f3fSDimitry Andric } 139*5f757f3fSDimitry Andric 140*5f757f3fSDimitry Andric static CallInst *isGEPAndLoad(Value *I) { 141*5f757f3fSDimitry Andric if (isIntrinsicCall(I, Intrinsic::bpf_getelementptr_and_load)) 142*5f757f3fSDimitry Andric return cast<CallInst>(I); 143*5f757f3fSDimitry Andric return nullptr; 144*5f757f3fSDimitry Andric } 145*5f757f3fSDimitry Andric 146*5f757f3fSDimitry Andric static CallInst *isGEPAndStore(Value *I) { 147*5f757f3fSDimitry Andric if (isIntrinsicCall(I, Intrinsic::bpf_getelementptr_and_store)) 148*5f757f3fSDimitry Andric return cast<CallInst>(I); 149*5f757f3fSDimitry Andric return nullptr; 150*5f757f3fSDimitry Andric } 151*5f757f3fSDimitry Andric 152*5f757f3fSDimitry Andric template <class T = Instruction> 153*5f757f3fSDimitry Andric static DILocation *mergeDILocations(SmallVector<T *> &Insns) { 154*5f757f3fSDimitry Andric DILocation *Merged = (*Insns.begin())->getDebugLoc(); 155*5f757f3fSDimitry Andric for (T *I : Insns) 156*5f757f3fSDimitry Andric Merged = DILocation::getMergedLocation(Merged, I->getDebugLoc()); 157*5f757f3fSDimitry Andric return Merged; 158*5f757f3fSDimitry Andric } 159*5f757f3fSDimitry Andric 160*5f757f3fSDimitry Andric static CallInst *makeIntrinsicCall(Module *M, 161*5f757f3fSDimitry Andric Intrinsic::BPFIntrinsics Intrinsic, 162*5f757f3fSDimitry Andric ArrayRef<Type *> Types, 163*5f757f3fSDimitry Andric ArrayRef<Value *> Args) { 164*5f757f3fSDimitry Andric 165*5f757f3fSDimitry Andric Function *Fn = Intrinsic::getDeclaration(M, Intrinsic, Types); 166*5f757f3fSDimitry Andric return CallInst::Create(Fn, Args); 167*5f757f3fSDimitry Andric } 168*5f757f3fSDimitry Andric 169*5f757f3fSDimitry Andric static void setParamElementType(CallInst *Call, unsigned ArgNo, Type *Type) { 170*5f757f3fSDimitry Andric LLVMContext &C = Call->getContext(); 171*5f757f3fSDimitry Andric Call->addParamAttr(ArgNo, Attribute::get(C, Attribute::ElementType, Type)); 172*5f757f3fSDimitry Andric } 173*5f757f3fSDimitry Andric 174*5f757f3fSDimitry Andric static void setParamReadNone(CallInst *Call, unsigned ArgNo) { 175*5f757f3fSDimitry Andric LLVMContext &C = Call->getContext(); 176*5f757f3fSDimitry Andric Call->addParamAttr(ArgNo, Attribute::get(C, Attribute::ReadNone)); 177*5f757f3fSDimitry Andric } 178*5f757f3fSDimitry Andric 179*5f757f3fSDimitry Andric static void setParamReadOnly(CallInst *Call, unsigned ArgNo) { 180*5f757f3fSDimitry Andric LLVMContext &C = Call->getContext(); 181*5f757f3fSDimitry Andric Call->addParamAttr(ArgNo, Attribute::get(C, Attribute::ReadOnly)); 182*5f757f3fSDimitry Andric } 183*5f757f3fSDimitry Andric 184*5f757f3fSDimitry Andric static void setParamWriteOnly(CallInst *Call, unsigned ArgNo) { 185*5f757f3fSDimitry Andric LLVMContext &C = Call->getContext(); 186*5f757f3fSDimitry Andric Call->addParamAttr(ArgNo, Attribute::get(C, Attribute::WriteOnly)); 187*5f757f3fSDimitry Andric } 188*5f757f3fSDimitry Andric 189*5f757f3fSDimitry Andric namespace { 190*5f757f3fSDimitry Andric struct GEPChainInfo { 191*5f757f3fSDimitry Andric bool InBounds; 192*5f757f3fSDimitry Andric Type *SourceElementType; 193*5f757f3fSDimitry Andric SmallVector<Value *> Indices; 194*5f757f3fSDimitry Andric SmallVector<GetElementPtrInst *> Members; 195*5f757f3fSDimitry Andric 196*5f757f3fSDimitry Andric GEPChainInfo() { reset(); } 197*5f757f3fSDimitry Andric 198*5f757f3fSDimitry Andric void reset() { 199*5f757f3fSDimitry Andric InBounds = true; 200*5f757f3fSDimitry Andric SourceElementType = nullptr; 201*5f757f3fSDimitry Andric Indices.clear(); 202*5f757f3fSDimitry Andric Members.clear(); 203*5f757f3fSDimitry Andric } 204*5f757f3fSDimitry Andric }; 205*5f757f3fSDimitry Andric } // Anonymous namespace 206*5f757f3fSDimitry Andric 207*5f757f3fSDimitry Andric template <class T = std::disjunction<LoadInst, StoreInst>> 208*5f757f3fSDimitry Andric static void fillCommonArgs(LLVMContext &C, SmallVector<Value *> &Args, 209*5f757f3fSDimitry Andric GEPChainInfo &GEP, T *Insn) { 210*5f757f3fSDimitry Andric Type *Int8Ty = Type::getInt8Ty(C); 211*5f757f3fSDimitry Andric Type *Int1Ty = Type::getInt1Ty(C); 212*5f757f3fSDimitry Andric // Implementation of Align guarantees that ShiftValue < 64 213*5f757f3fSDimitry Andric unsigned AlignShiftValue = Log2_64(Insn->getAlign().value()); 214*5f757f3fSDimitry Andric Args.push_back(GEP.Members[0]->getPointerOperand()); 215*5f757f3fSDimitry Andric Args.push_back(ConstantInt::get(Int1Ty, Insn->isVolatile())); 216*5f757f3fSDimitry Andric Args.push_back(ConstantInt::get(Int8Ty, (unsigned)Insn->getOrdering())); 217*5f757f3fSDimitry Andric Args.push_back(ConstantInt::get(Int8Ty, (unsigned)Insn->getSyncScopeID())); 218*5f757f3fSDimitry Andric Args.push_back(ConstantInt::get(Int8Ty, AlignShiftValue)); 219*5f757f3fSDimitry Andric Args.push_back(ConstantInt::get(Int1Ty, GEP.InBounds)); 220*5f757f3fSDimitry Andric Args.append(GEP.Indices.begin(), GEP.Indices.end()); 221*5f757f3fSDimitry Andric } 222*5f757f3fSDimitry Andric 223*5f757f3fSDimitry Andric static Instruction *makeGEPAndLoad(Module *M, GEPChainInfo &GEP, 224*5f757f3fSDimitry Andric LoadInst *Load) { 225*5f757f3fSDimitry Andric SmallVector<Value *> Args; 226*5f757f3fSDimitry Andric fillCommonArgs(M->getContext(), Args, GEP, Load); 227*5f757f3fSDimitry Andric CallInst *Call = makeIntrinsicCall(M, Intrinsic::bpf_getelementptr_and_load, 228*5f757f3fSDimitry Andric {Load->getType()}, Args); 229*5f757f3fSDimitry Andric setParamElementType(Call, 0, GEP.SourceElementType); 230*5f757f3fSDimitry Andric Call->applyMergedLocation(mergeDILocations(GEP.Members), Load->getDebugLoc()); 231*5f757f3fSDimitry Andric Call->setName((*GEP.Members.rbegin())->getName()); 232*5f757f3fSDimitry Andric if (Load->isUnordered()) { 233*5f757f3fSDimitry Andric Call->setOnlyReadsMemory(); 234*5f757f3fSDimitry Andric Call->setOnlyAccessesArgMemory(); 235*5f757f3fSDimitry Andric setParamReadOnly(Call, 0); 236*5f757f3fSDimitry Andric } 237*5f757f3fSDimitry Andric for (unsigned I = GepAndLoadFirstIdxArg; I < Args.size(); ++I) 238*5f757f3fSDimitry Andric Call->addParamAttr(I, Attribute::ImmArg); 239*5f757f3fSDimitry Andric Call->setAAMetadata(Load->getAAMetadata()); 240*5f757f3fSDimitry Andric return Call; 241*5f757f3fSDimitry Andric } 242*5f757f3fSDimitry Andric 243*5f757f3fSDimitry Andric static Instruction *makeGEPAndStore(Module *M, GEPChainInfo &GEP, 244*5f757f3fSDimitry Andric StoreInst *Store) { 245*5f757f3fSDimitry Andric SmallVector<Value *> Args; 246*5f757f3fSDimitry Andric Args.push_back(Store->getValueOperand()); 247*5f757f3fSDimitry Andric fillCommonArgs(M->getContext(), Args, GEP, Store); 248*5f757f3fSDimitry Andric CallInst *Call = 249*5f757f3fSDimitry Andric makeIntrinsicCall(M, Intrinsic::bpf_getelementptr_and_store, 250*5f757f3fSDimitry Andric {Store->getValueOperand()->getType()}, Args); 251*5f757f3fSDimitry Andric setParamElementType(Call, 1, GEP.SourceElementType); 252*5f757f3fSDimitry Andric if (Store->getValueOperand()->getType()->isPointerTy()) 253*5f757f3fSDimitry Andric setParamReadNone(Call, 0); 254*5f757f3fSDimitry Andric Call->applyMergedLocation(mergeDILocations(GEP.Members), 255*5f757f3fSDimitry Andric Store->getDebugLoc()); 256*5f757f3fSDimitry Andric if (Store->isUnordered()) { 257*5f757f3fSDimitry Andric Call->setOnlyWritesMemory(); 258*5f757f3fSDimitry Andric Call->setOnlyAccessesArgMemory(); 259*5f757f3fSDimitry Andric setParamWriteOnly(Call, 1); 260*5f757f3fSDimitry Andric } 261*5f757f3fSDimitry Andric for (unsigned I = GepAndStoreFirstIdxArg; I < Args.size(); ++I) 262*5f757f3fSDimitry Andric Call->addParamAttr(I, Attribute::ImmArg); 263*5f757f3fSDimitry Andric Call->setAAMetadata(Store->getAAMetadata()); 264*5f757f3fSDimitry Andric return Call; 265*5f757f3fSDimitry Andric } 266*5f757f3fSDimitry Andric 267*5f757f3fSDimitry Andric static unsigned getOperandAsUnsigned(CallInst *Call, unsigned ArgNo) { 268*5f757f3fSDimitry Andric if (auto *Int = dyn_cast<ConstantInt>(Call->getOperand(ArgNo))) 269*5f757f3fSDimitry Andric return Int->getValue().getZExtValue(); 270*5f757f3fSDimitry Andric std::string Report; 271*5f757f3fSDimitry Andric raw_string_ostream ReportS(Report); 272*5f757f3fSDimitry Andric ReportS << "Expecting ConstantInt as argument #" << ArgNo << " of " << *Call 273*5f757f3fSDimitry Andric << "\n"; 274*5f757f3fSDimitry Andric report_fatal_error(StringRef(Report)); 275*5f757f3fSDimitry Andric } 276*5f757f3fSDimitry Andric 277*5f757f3fSDimitry Andric static GetElementPtrInst *reconstructGEP(CallInst *Call, int Delta) { 278*5f757f3fSDimitry Andric SmallVector<Value *> Indices; 279*5f757f3fSDimitry Andric Indices.append(Call->data_operands_begin() + 6 + Delta, 280*5f757f3fSDimitry Andric Call->data_operands_end()); 281*5f757f3fSDimitry Andric Type *GEPPointeeType = Call->getParamElementType(Delta); 282*5f757f3fSDimitry Andric auto *GEP = 283*5f757f3fSDimitry Andric GetElementPtrInst::Create(GEPPointeeType, Call->getOperand(Delta), 284*5f757f3fSDimitry Andric ArrayRef<Value *>(Indices), Call->getName()); 285*5f757f3fSDimitry Andric GEP->setIsInBounds(getOperandAsUnsigned(Call, 5 + Delta)); 286*5f757f3fSDimitry Andric return GEP; 287*5f757f3fSDimitry Andric } 288*5f757f3fSDimitry Andric 289*5f757f3fSDimitry Andric template <class T = std::disjunction<LoadInst, StoreInst>> 290*5f757f3fSDimitry Andric static void reconstructCommon(CallInst *Call, GetElementPtrInst *GEP, T *Insn, 291*5f757f3fSDimitry Andric int Delta) { 292*5f757f3fSDimitry Andric Insn->setVolatile(getOperandAsUnsigned(Call, 1 + Delta)); 293*5f757f3fSDimitry Andric Insn->setOrdering((AtomicOrdering)getOperandAsUnsigned(Call, 2 + Delta)); 294*5f757f3fSDimitry Andric Insn->setSyncScopeID(getOperandAsUnsigned(Call, 3 + Delta)); 295*5f757f3fSDimitry Andric unsigned AlignShiftValue = getOperandAsUnsigned(Call, 4 + Delta); 296*5f757f3fSDimitry Andric Insn->setAlignment(Align(1ULL << AlignShiftValue)); 297*5f757f3fSDimitry Andric GEP->setDebugLoc(Call->getDebugLoc()); 298*5f757f3fSDimitry Andric Insn->setDebugLoc(Call->getDebugLoc()); 299*5f757f3fSDimitry Andric Insn->setAAMetadata(Call->getAAMetadata()); 300*5f757f3fSDimitry Andric } 301*5f757f3fSDimitry Andric 302*5f757f3fSDimitry Andric std::pair<GetElementPtrInst *, LoadInst *> 303*5f757f3fSDimitry Andric BPFPreserveStaticOffsetPass::reconstructLoad(CallInst *Call) { 304*5f757f3fSDimitry Andric GetElementPtrInst *GEP = reconstructGEP(Call, 0); 305*5f757f3fSDimitry Andric Type *ReturnType = Call->getFunctionType()->getReturnType(); 306*5f757f3fSDimitry Andric auto *Load = new LoadInst(ReturnType, GEP, "", 307*5f757f3fSDimitry Andric /* These would be set in reconstructCommon */ 308*5f757f3fSDimitry Andric false, Align(1)); 309*5f757f3fSDimitry Andric reconstructCommon(Call, GEP, Load, 0); 310*5f757f3fSDimitry Andric return std::pair{GEP, Load}; 311*5f757f3fSDimitry Andric } 312*5f757f3fSDimitry Andric 313*5f757f3fSDimitry Andric std::pair<GetElementPtrInst *, StoreInst *> 314*5f757f3fSDimitry Andric BPFPreserveStaticOffsetPass::reconstructStore(CallInst *Call) { 315*5f757f3fSDimitry Andric GetElementPtrInst *GEP = reconstructGEP(Call, 1); 316*5f757f3fSDimitry Andric auto *Store = new StoreInst(Call->getOperand(0), GEP, 317*5f757f3fSDimitry Andric /* These would be set in reconstructCommon */ 318*5f757f3fSDimitry Andric false, Align(1)); 319*5f757f3fSDimitry Andric reconstructCommon(Call, GEP, Store, 1); 320*5f757f3fSDimitry Andric return std::pair{GEP, Store}; 321*5f757f3fSDimitry Andric } 322*5f757f3fSDimitry Andric 323*5f757f3fSDimitry Andric static bool isZero(Value *V) { 324*5f757f3fSDimitry Andric auto *CI = dyn_cast<ConstantInt>(V); 325*5f757f3fSDimitry Andric return CI && CI->isZero(); 326*5f757f3fSDimitry Andric } 327*5f757f3fSDimitry Andric 328*5f757f3fSDimitry Andric // Given a chain of GEP instructions collect information necessary to 329*5f757f3fSDimitry Andric // merge this chain as a single GEP instruction of form: 330*5f757f3fSDimitry Andric // getelementptr %<type>, ptr %p, i32 0, <field_idx1>, <field_idx2>, ... 331*5f757f3fSDimitry Andric static bool foldGEPChainAsStructAccess(SmallVector<GetElementPtrInst *> &GEPs, 332*5f757f3fSDimitry Andric GEPChainInfo &Info) { 333*5f757f3fSDimitry Andric if (GEPs.empty()) 334*5f757f3fSDimitry Andric return false; 335*5f757f3fSDimitry Andric 336*5f757f3fSDimitry Andric if (!all_of(GEPs, [=](GetElementPtrInst *GEP) { 337*5f757f3fSDimitry Andric return GEP->hasAllConstantIndices(); 338*5f757f3fSDimitry Andric })) 339*5f757f3fSDimitry Andric return false; 340*5f757f3fSDimitry Andric 341*5f757f3fSDimitry Andric GetElementPtrInst *First = GEPs[0]; 342*5f757f3fSDimitry Andric Info.InBounds = First->isInBounds(); 343*5f757f3fSDimitry Andric Info.SourceElementType = First->getSourceElementType(); 344*5f757f3fSDimitry Andric Type *ResultElementType = First->getResultElementType(); 345*5f757f3fSDimitry Andric Info.Indices.append(First->idx_begin(), First->idx_end()); 346*5f757f3fSDimitry Andric Info.Members.push_back(First); 347*5f757f3fSDimitry Andric 348*5f757f3fSDimitry Andric for (auto *Iter = GEPs.begin() + 1; Iter != GEPs.end(); ++Iter) { 349*5f757f3fSDimitry Andric GetElementPtrInst *GEP = *Iter; 350*5f757f3fSDimitry Andric if (!isZero(*GEP->idx_begin())) { 351*5f757f3fSDimitry Andric Info.reset(); 352*5f757f3fSDimitry Andric return false; 353*5f757f3fSDimitry Andric } 354*5f757f3fSDimitry Andric if (!GEP->getSourceElementType() || 355*5f757f3fSDimitry Andric GEP->getSourceElementType() != ResultElementType) { 356*5f757f3fSDimitry Andric Info.reset(); 357*5f757f3fSDimitry Andric return false; 358*5f757f3fSDimitry Andric } 359*5f757f3fSDimitry Andric Info.InBounds &= GEP->isInBounds(); 360*5f757f3fSDimitry Andric Info.Indices.append(GEP->idx_begin() + 1, GEP->idx_end()); 361*5f757f3fSDimitry Andric Info.Members.push_back(GEP); 362*5f757f3fSDimitry Andric ResultElementType = GEP->getResultElementType(); 363*5f757f3fSDimitry Andric } 364*5f757f3fSDimitry Andric 365*5f757f3fSDimitry Andric return true; 366*5f757f3fSDimitry Andric } 367*5f757f3fSDimitry Andric 368*5f757f3fSDimitry Andric // Given a chain of GEP instructions collect information necessary to 369*5f757f3fSDimitry Andric // merge this chain as a single GEP instruction of form: 370*5f757f3fSDimitry Andric // getelementptr i8, ptr %p, i64 %offset 371*5f757f3fSDimitry Andric static bool foldGEPChainAsU8Access(SmallVector<GetElementPtrInst *> &GEPs, 372*5f757f3fSDimitry Andric GEPChainInfo &Info) { 373*5f757f3fSDimitry Andric if (GEPs.empty()) 374*5f757f3fSDimitry Andric return false; 375*5f757f3fSDimitry Andric 376*5f757f3fSDimitry Andric GetElementPtrInst *First = GEPs[0]; 377*5f757f3fSDimitry Andric const DataLayout &DL = First->getModule()->getDataLayout(); 378*5f757f3fSDimitry Andric LLVMContext &C = First->getContext(); 379*5f757f3fSDimitry Andric Type *PtrTy = First->getType()->getScalarType(); 380*5f757f3fSDimitry Andric APInt Offset(DL.getIndexTypeSizeInBits(PtrTy), 0); 381*5f757f3fSDimitry Andric for (GetElementPtrInst *GEP : GEPs) { 382*5f757f3fSDimitry Andric if (!GEP->accumulateConstantOffset(DL, Offset)) { 383*5f757f3fSDimitry Andric Info.reset(); 384*5f757f3fSDimitry Andric return false; 385*5f757f3fSDimitry Andric } 386*5f757f3fSDimitry Andric Info.InBounds &= GEP->isInBounds(); 387*5f757f3fSDimitry Andric Info.Members.push_back(GEP); 388*5f757f3fSDimitry Andric } 389*5f757f3fSDimitry Andric Info.SourceElementType = Type::getInt8Ty(C); 390*5f757f3fSDimitry Andric Info.Indices.push_back(ConstantInt::get(C, Offset)); 391*5f757f3fSDimitry Andric 392*5f757f3fSDimitry Andric return true; 393*5f757f3fSDimitry Andric } 394*5f757f3fSDimitry Andric 395*5f757f3fSDimitry Andric static void reportNonStaticGEPChain(Instruction *Insn) { 396*5f757f3fSDimitry Andric auto Msg = DiagnosticInfoUnsupported( 397*5f757f3fSDimitry Andric *Insn->getFunction(), 398*5f757f3fSDimitry Andric Twine("Non-constant offset in access to a field of a type marked " 399*5f757f3fSDimitry Andric "with preserve_static_offset might be rejected by BPF verifier") 400*5f757f3fSDimitry Andric .concat(Insn->getDebugLoc() 401*5f757f3fSDimitry Andric ? "" 402*5f757f3fSDimitry Andric : " (pass -g option to get exact location)"), 403*5f757f3fSDimitry Andric Insn->getDebugLoc(), DS_Warning); 404*5f757f3fSDimitry Andric Insn->getContext().diagnose(Msg); 405*5f757f3fSDimitry Andric } 406*5f757f3fSDimitry Andric 407*5f757f3fSDimitry Andric static bool allZeroIndices(SmallVector<GetElementPtrInst *> &GEPs) { 408*5f757f3fSDimitry Andric return GEPs.empty() || all_of(GEPs, [=](GetElementPtrInst *GEP) { 409*5f757f3fSDimitry Andric return GEP->hasAllZeroIndices(); 410*5f757f3fSDimitry Andric }); 411*5f757f3fSDimitry Andric } 412*5f757f3fSDimitry Andric 413*5f757f3fSDimitry Andric static bool tryToReplaceWithGEPBuiltin(Instruction *LoadOrStoreTemplate, 414*5f757f3fSDimitry Andric SmallVector<GetElementPtrInst *> &GEPs, 415*5f757f3fSDimitry Andric Instruction *InsnToReplace) { 416*5f757f3fSDimitry Andric GEPChainInfo GEPChain; 417*5f757f3fSDimitry Andric if (!foldGEPChainAsStructAccess(GEPs, GEPChain) && 418*5f757f3fSDimitry Andric !foldGEPChainAsU8Access(GEPs, GEPChain)) { 419*5f757f3fSDimitry Andric return false; 420*5f757f3fSDimitry Andric } 421*5f757f3fSDimitry Andric Module *M = InsnToReplace->getModule(); 422*5f757f3fSDimitry Andric if (auto *Load = dyn_cast<LoadInst>(LoadOrStoreTemplate)) { 423*5f757f3fSDimitry Andric Instruction *Replacement = makeGEPAndLoad(M, GEPChain, Load); 424*5f757f3fSDimitry Andric Replacement->insertBefore(InsnToReplace); 425*5f757f3fSDimitry Andric InsnToReplace->replaceAllUsesWith(Replacement); 426*5f757f3fSDimitry Andric } 427*5f757f3fSDimitry Andric if (auto *Store = dyn_cast<StoreInst>(LoadOrStoreTemplate)) { 428*5f757f3fSDimitry Andric Instruction *Replacement = makeGEPAndStore(M, GEPChain, Store); 429*5f757f3fSDimitry Andric Replacement->insertBefore(InsnToReplace); 430*5f757f3fSDimitry Andric } 431*5f757f3fSDimitry Andric return true; 432*5f757f3fSDimitry Andric } 433*5f757f3fSDimitry Andric 434*5f757f3fSDimitry Andric // Check if U->getPointerOperand() == I 435*5f757f3fSDimitry Andric static bool isPointerOperand(Value *I, User *U) { 436*5f757f3fSDimitry Andric if (auto *L = dyn_cast<LoadInst>(U)) 437*5f757f3fSDimitry Andric return L->getPointerOperand() == I; 438*5f757f3fSDimitry Andric if (auto *S = dyn_cast<StoreInst>(U)) 439*5f757f3fSDimitry Andric return S->getPointerOperand() == I; 440*5f757f3fSDimitry Andric if (auto *GEP = dyn_cast<GetElementPtrInst>(U)) 441*5f757f3fSDimitry Andric return GEP->getPointerOperand() == I; 442*5f757f3fSDimitry Andric if (auto *Call = isGEPAndLoad(U)) 443*5f757f3fSDimitry Andric return Call->getArgOperand(0) == I; 444*5f757f3fSDimitry Andric if (auto *Call = isGEPAndStore(U)) 445*5f757f3fSDimitry Andric return Call->getArgOperand(1) == I; 446*5f757f3fSDimitry Andric return false; 447*5f757f3fSDimitry Andric } 448*5f757f3fSDimitry Andric 449*5f757f3fSDimitry Andric static bool isInlineableCall(User *U) { 450*5f757f3fSDimitry Andric if (auto *Call = dyn_cast<CallInst>(U)) 451*5f757f3fSDimitry Andric return Call->hasFnAttr(Attribute::InlineHint); 452*5f757f3fSDimitry Andric return false; 453*5f757f3fSDimitry Andric } 454*5f757f3fSDimitry Andric 455*5f757f3fSDimitry Andric static void rewriteAccessChain(Instruction *Insn, 456*5f757f3fSDimitry Andric SmallVector<GetElementPtrInst *> &GEPs, 457*5f757f3fSDimitry Andric SmallVector<Instruction *> &Visited, 458*5f757f3fSDimitry Andric bool AllowPatial, bool &StillUsed); 459*5f757f3fSDimitry Andric 460*5f757f3fSDimitry Andric static void rewriteUses(Instruction *Insn, 461*5f757f3fSDimitry Andric SmallVector<GetElementPtrInst *> &GEPs, 462*5f757f3fSDimitry Andric SmallVector<Instruction *> &Visited, bool AllowPatial, 463*5f757f3fSDimitry Andric bool &StillUsed) { 464*5f757f3fSDimitry Andric for (User *U : Insn->users()) { 465*5f757f3fSDimitry Andric auto *UI = dyn_cast<Instruction>(U); 466*5f757f3fSDimitry Andric if (UI && (isPointerOperand(Insn, UI) || isPreserveStaticOffsetCall(UI) || 467*5f757f3fSDimitry Andric isInlineableCall(UI))) 468*5f757f3fSDimitry Andric rewriteAccessChain(UI, GEPs, Visited, AllowPatial, StillUsed); 469*5f757f3fSDimitry Andric else 470*5f757f3fSDimitry Andric LLVM_DEBUG({ 471*5f757f3fSDimitry Andric llvm::dbgs() << "unsupported usage in BPFPreserveStaticOffsetPass:\n"; 472*5f757f3fSDimitry Andric llvm::dbgs() << " Insn: " << *Insn << "\n"; 473*5f757f3fSDimitry Andric llvm::dbgs() << " User: " << *U << "\n"; 474*5f757f3fSDimitry Andric }); 475*5f757f3fSDimitry Andric } 476*5f757f3fSDimitry Andric } 477*5f757f3fSDimitry Andric 478*5f757f3fSDimitry Andric // A DFS traversal of GEP chain trees starting from Root. 479*5f757f3fSDimitry Andric // 480*5f757f3fSDimitry Andric // Recursion descends through GEP instructions and 481*5f757f3fSDimitry Andric // llvm.preserve.static.offset calls. Recursion stops at any other 482*5f757f3fSDimitry Andric // instruction. If load or store instruction is reached it is replaced 483*5f757f3fSDimitry Andric // by a call to `llvm.bpf.getelementptr.and.load` or 484*5f757f3fSDimitry Andric // `llvm.bpf.getelementptr.and.store` intrinsic. 485*5f757f3fSDimitry Andric // If `llvm.bpf.getelementptr.and.load/store` is reached the accumulated 486*5f757f3fSDimitry Andric // GEPs are merged into the intrinsic call. 487*5f757f3fSDimitry Andric // If nested calls to `llvm.preserve.static.offset` are encountered these 488*5f757f3fSDimitry Andric // calls are marked for deletion. 489*5f757f3fSDimitry Andric // 490*5f757f3fSDimitry Andric // Parameters description: 491*5f757f3fSDimitry Andric // - Insn - current position in the tree 492*5f757f3fSDimitry Andric // - GEPs - GEP instructions for the current branch 493*5f757f3fSDimitry Andric // - Visited - a list of visited instructions in DFS order, 494*5f757f3fSDimitry Andric // order is important for unused instruction deletion. 495*5f757f3fSDimitry Andric // - AllowPartial - when true GEP chains that can't be folded are 496*5f757f3fSDimitry Andric // not reported, otherwise diagnostic message is show for such chains. 497*5f757f3fSDimitry Andric // - StillUsed - set to true if one of the GEP chains could not be 498*5f757f3fSDimitry Andric // folded, makes sense when AllowPartial is false, means that root 499*5f757f3fSDimitry Andric // preserve.static.offset call is still in use and should remain 500*5f757f3fSDimitry Andric // until the next run of this pass. 501*5f757f3fSDimitry Andric static void rewriteAccessChain(Instruction *Insn, 502*5f757f3fSDimitry Andric SmallVector<GetElementPtrInst *> &GEPs, 503*5f757f3fSDimitry Andric SmallVector<Instruction *> &Visited, 504*5f757f3fSDimitry Andric bool AllowPatial, bool &StillUsed) { 505*5f757f3fSDimitry Andric auto MarkAndTraverseUses = [&]() { 506*5f757f3fSDimitry Andric Visited.push_back(Insn); 507*5f757f3fSDimitry Andric rewriteUses(Insn, GEPs, Visited, AllowPatial, StillUsed); 508*5f757f3fSDimitry Andric }; 509*5f757f3fSDimitry Andric auto TryToReplace = [&](Instruction *LoadOrStore) { 510*5f757f3fSDimitry Andric // Do nothing for (preserve.static.offset (load/store ..)) or for 511*5f757f3fSDimitry Andric // GEPs with zero indices. Such constructs lead to zero offset and 512*5f757f3fSDimitry Andric // are simplified by other passes. 513*5f757f3fSDimitry Andric if (allZeroIndices(GEPs)) 514*5f757f3fSDimitry Andric return; 515*5f757f3fSDimitry Andric if (tryToReplaceWithGEPBuiltin(LoadOrStore, GEPs, Insn)) { 516*5f757f3fSDimitry Andric Visited.push_back(Insn); 517*5f757f3fSDimitry Andric return; 518*5f757f3fSDimitry Andric } 519*5f757f3fSDimitry Andric if (!AllowPatial) 520*5f757f3fSDimitry Andric reportNonStaticGEPChain(Insn); 521*5f757f3fSDimitry Andric StillUsed = true; 522*5f757f3fSDimitry Andric }; 523*5f757f3fSDimitry Andric if (isa<LoadInst>(Insn) || isa<StoreInst>(Insn)) { 524*5f757f3fSDimitry Andric TryToReplace(Insn); 525*5f757f3fSDimitry Andric } else if (isGEPAndLoad(Insn)) { 526*5f757f3fSDimitry Andric auto [GEP, Load] = 527*5f757f3fSDimitry Andric BPFPreserveStaticOffsetPass::reconstructLoad(cast<CallInst>(Insn)); 528*5f757f3fSDimitry Andric GEPs.push_back(GEP); 529*5f757f3fSDimitry Andric TryToReplace(Load); 530*5f757f3fSDimitry Andric GEPs.pop_back(); 531*5f757f3fSDimitry Andric delete Load; 532*5f757f3fSDimitry Andric delete GEP; 533*5f757f3fSDimitry Andric } else if (isGEPAndStore(Insn)) { 534*5f757f3fSDimitry Andric // This case can't be merged with the above because 535*5f757f3fSDimitry Andric // `delete Load` / `delete Store` wants a concrete type, 536*5f757f3fSDimitry Andric // destructor of Instruction is protected. 537*5f757f3fSDimitry Andric auto [GEP, Store] = 538*5f757f3fSDimitry Andric BPFPreserveStaticOffsetPass::reconstructStore(cast<CallInst>(Insn)); 539*5f757f3fSDimitry Andric GEPs.push_back(GEP); 540*5f757f3fSDimitry Andric TryToReplace(Store); 541*5f757f3fSDimitry Andric GEPs.pop_back(); 542*5f757f3fSDimitry Andric delete Store; 543*5f757f3fSDimitry Andric delete GEP; 544*5f757f3fSDimitry Andric } else if (auto *GEP = dyn_cast<GetElementPtrInst>(Insn)) { 545*5f757f3fSDimitry Andric GEPs.push_back(GEP); 546*5f757f3fSDimitry Andric MarkAndTraverseUses(); 547*5f757f3fSDimitry Andric GEPs.pop_back(); 548*5f757f3fSDimitry Andric } else if (isPreserveStaticOffsetCall(Insn)) { 549*5f757f3fSDimitry Andric MarkAndTraverseUses(); 550*5f757f3fSDimitry Andric } else if (isInlineableCall(Insn)) { 551*5f757f3fSDimitry Andric // Preserve preserve.static.offset call for parameters of 552*5f757f3fSDimitry Andric // functions that might be inlined. These would be removed on a 553*5f757f3fSDimitry Andric // second pass after inlining. 554*5f757f3fSDimitry Andric // Might happen when a pointer to a preserve_static_offset 555*5f757f3fSDimitry Andric // structure is passed as parameter of a function that would be 556*5f757f3fSDimitry Andric // inlined inside a loop that would be unrolled. 557*5f757f3fSDimitry Andric if (AllowPatial) 558*5f757f3fSDimitry Andric StillUsed = true; 559*5f757f3fSDimitry Andric } else { 560*5f757f3fSDimitry Andric SmallString<128> Buf; 561*5f757f3fSDimitry Andric raw_svector_ostream BufStream(Buf); 562*5f757f3fSDimitry Andric BufStream << *Insn; 563*5f757f3fSDimitry Andric report_fatal_error( 564*5f757f3fSDimitry Andric Twine("Unexpected rewriteAccessChain Insn = ").concat(Buf)); 565*5f757f3fSDimitry Andric } 566*5f757f3fSDimitry Andric } 567*5f757f3fSDimitry Andric 568*5f757f3fSDimitry Andric static void removeMarkerCall(Instruction *Marker) { 569*5f757f3fSDimitry Andric Marker->replaceAllUsesWith(Marker->getOperand(0)); 570*5f757f3fSDimitry Andric Marker->eraseFromParent(); 571*5f757f3fSDimitry Andric } 572*5f757f3fSDimitry Andric 573*5f757f3fSDimitry Andric static bool rewriteAccessChain(Instruction *Marker, bool AllowPatial, 574*5f757f3fSDimitry Andric SmallPtrSetImpl<Instruction *> &RemovedMarkers) { 575*5f757f3fSDimitry Andric SmallVector<GetElementPtrInst *> GEPs; 576*5f757f3fSDimitry Andric SmallVector<Instruction *> Visited; 577*5f757f3fSDimitry Andric bool StillUsed = false; 578*5f757f3fSDimitry Andric rewriteUses(Marker, GEPs, Visited, AllowPatial, StillUsed); 579*5f757f3fSDimitry Andric // Check if Visited instructions could be removed, iterate in 580*5f757f3fSDimitry Andric // reverse to unblock instructions higher in the chain. 581*5f757f3fSDimitry Andric for (auto V = Visited.rbegin(); V != Visited.rend(); ++V) { 582*5f757f3fSDimitry Andric if (isPreserveStaticOffsetCall(*V)) { 583*5f757f3fSDimitry Andric removeMarkerCall(*V); 584*5f757f3fSDimitry Andric RemovedMarkers.insert(*V); 585*5f757f3fSDimitry Andric } else if ((*V)->use_empty()) { 586*5f757f3fSDimitry Andric (*V)->eraseFromParent(); 587*5f757f3fSDimitry Andric } 588*5f757f3fSDimitry Andric } 589*5f757f3fSDimitry Andric return StillUsed; 590*5f757f3fSDimitry Andric } 591*5f757f3fSDimitry Andric 592*5f757f3fSDimitry Andric static std::vector<Instruction *> 593*5f757f3fSDimitry Andric collectPreserveStaticOffsetCalls(Function &F) { 594*5f757f3fSDimitry Andric std::vector<Instruction *> Calls; 595*5f757f3fSDimitry Andric for (Instruction &Insn : instructions(F)) 596*5f757f3fSDimitry Andric if (isPreserveStaticOffsetCall(&Insn)) 597*5f757f3fSDimitry Andric Calls.push_back(&Insn); 598*5f757f3fSDimitry Andric return Calls; 599*5f757f3fSDimitry Andric } 600*5f757f3fSDimitry Andric 601*5f757f3fSDimitry Andric bool isPreserveArrayIndex(Value *V) { 602*5f757f3fSDimitry Andric return isIntrinsicCall(V, Intrinsic::preserve_array_access_index); 603*5f757f3fSDimitry Andric } 604*5f757f3fSDimitry Andric 605*5f757f3fSDimitry Andric bool isPreserveStructIndex(Value *V) { 606*5f757f3fSDimitry Andric return isIntrinsicCall(V, Intrinsic::preserve_struct_access_index); 607*5f757f3fSDimitry Andric } 608*5f757f3fSDimitry Andric 609*5f757f3fSDimitry Andric bool isPreserveUnionIndex(Value *V) { 610*5f757f3fSDimitry Andric return isIntrinsicCall(V, Intrinsic::preserve_union_access_index); 611*5f757f3fSDimitry Andric } 612*5f757f3fSDimitry Andric 613*5f757f3fSDimitry Andric static void removePAICalls(Instruction *Marker) { 614*5f757f3fSDimitry Andric auto IsPointerOperand = [](Value *Op, User *U) { 615*5f757f3fSDimitry Andric if (auto *GEP = dyn_cast<GetElementPtrInst>(U)) 616*5f757f3fSDimitry Andric return GEP->getPointerOperand() == Op; 617*5f757f3fSDimitry Andric if (isPreserveStaticOffsetCall(U) || isPreserveArrayIndex(U) || 618*5f757f3fSDimitry Andric isPreserveStructIndex(U) || isPreserveUnionIndex(U)) 619*5f757f3fSDimitry Andric return cast<CallInst>(U)->getArgOperand(0) == Op; 620*5f757f3fSDimitry Andric return false; 621*5f757f3fSDimitry Andric }; 622*5f757f3fSDimitry Andric 623*5f757f3fSDimitry Andric SmallVector<Value *, 32> WorkList; 624*5f757f3fSDimitry Andric WorkList.push_back(Marker); 625*5f757f3fSDimitry Andric do { 626*5f757f3fSDimitry Andric Value *V = WorkList.pop_back_val(); 627*5f757f3fSDimitry Andric for (User *U : V->users()) 628*5f757f3fSDimitry Andric if (IsPointerOperand(V, U)) 629*5f757f3fSDimitry Andric WorkList.push_back(U); 630*5f757f3fSDimitry Andric auto *Call = dyn_cast<CallInst>(V); 631*5f757f3fSDimitry Andric if (!Call) 632*5f757f3fSDimitry Andric continue; 633*5f757f3fSDimitry Andric if (isPreserveArrayIndex(V)) 634*5f757f3fSDimitry Andric BPFCoreSharedInfo::removeArrayAccessCall(Call); 635*5f757f3fSDimitry Andric else if (isPreserveStructIndex(V)) 636*5f757f3fSDimitry Andric BPFCoreSharedInfo::removeStructAccessCall(Call); 637*5f757f3fSDimitry Andric else if (isPreserveUnionIndex(V)) 638*5f757f3fSDimitry Andric BPFCoreSharedInfo::removeUnionAccessCall(Call); 639*5f757f3fSDimitry Andric } while (!WorkList.empty()); 640*5f757f3fSDimitry Andric } 641*5f757f3fSDimitry Andric 642*5f757f3fSDimitry Andric // Look for sequences: 643*5f757f3fSDimitry Andric // - llvm.preserve.static.offset -> getelementptr... -> load 644*5f757f3fSDimitry Andric // - llvm.preserve.static.offset -> getelementptr... -> store 645*5f757f3fSDimitry Andric // And replace those with calls to intrinsics: 646*5f757f3fSDimitry Andric // - llvm.bpf.getelementptr.and.load 647*5f757f3fSDimitry Andric // - llvm.bpf.getelementptr.and.store 648*5f757f3fSDimitry Andric static bool rewriteFunction(Function &F, bool AllowPartial) { 649*5f757f3fSDimitry Andric LLVM_DEBUG(dbgs() << "********** BPFPreserveStaticOffsetPass (AllowPartial=" 650*5f757f3fSDimitry Andric << AllowPartial << ") ************\n"); 651*5f757f3fSDimitry Andric 652*5f757f3fSDimitry Andric auto MarkerCalls = collectPreserveStaticOffsetCalls(F); 653*5f757f3fSDimitry Andric SmallPtrSet<Instruction *, 16> RemovedMarkers; 654*5f757f3fSDimitry Andric 655*5f757f3fSDimitry Andric LLVM_DEBUG(dbgs() << "There are " << MarkerCalls.size() 656*5f757f3fSDimitry Andric << " preserve.static.offset calls\n"); 657*5f757f3fSDimitry Andric 658*5f757f3fSDimitry Andric if (MarkerCalls.empty()) 659*5f757f3fSDimitry Andric return false; 660*5f757f3fSDimitry Andric 661*5f757f3fSDimitry Andric for (auto *Call : MarkerCalls) 662*5f757f3fSDimitry Andric removePAICalls(Call); 663*5f757f3fSDimitry Andric 664*5f757f3fSDimitry Andric for (auto *Call : MarkerCalls) { 665*5f757f3fSDimitry Andric if (RemovedMarkers.contains(Call)) 666*5f757f3fSDimitry Andric continue; 667*5f757f3fSDimitry Andric bool StillUsed = rewriteAccessChain(Call, AllowPartial, RemovedMarkers); 668*5f757f3fSDimitry Andric if (!StillUsed || !AllowPartial) 669*5f757f3fSDimitry Andric removeMarkerCall(Call); 670*5f757f3fSDimitry Andric } 671*5f757f3fSDimitry Andric 672*5f757f3fSDimitry Andric return true; 673*5f757f3fSDimitry Andric } 674*5f757f3fSDimitry Andric 675*5f757f3fSDimitry Andric PreservedAnalyses 676*5f757f3fSDimitry Andric llvm::BPFPreserveStaticOffsetPass::run(Function &F, 677*5f757f3fSDimitry Andric FunctionAnalysisManager &AM) { 678*5f757f3fSDimitry Andric return rewriteFunction(F, AllowPartial) ? PreservedAnalyses::none() 679*5f757f3fSDimitry Andric : PreservedAnalyses::all(); 680*5f757f3fSDimitry Andric } 681