1//===------ BPFPreserveStaticOffset.cpp -----------------------------------===//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//===----------------------------------------------------------------------===//
9// TLDR: replaces llvm.preserve.static.offset + GEP + load / store
10// with llvm.bpf.getelementptr.and.load / store
12// This file implements BPFPreserveStaticOffsetPass transformation.
13// This transformation address two BPF verifier specific issues:
15// (a) Access to the fields of some structural types is allowed only
16// using load and store instructions with static immediate offsets.
18// Examples of such types are `struct __sk_buff` and `struct
19// bpf_sock_ops`. This is so because offsets of the fields of
20// these structures do not match real offsets in the running
21// kernel. During BPF program load LDX and STX instructions
22// referring to the fields of these types are rewritten so that
23// offsets match real offsets. For this rewrite to happen field
24// offsets have to be encoded as immediate operands of the
27// See kernel/bpf/verifier.c:convert_ctx_access function in the
28// Linux kernel source tree for details.
30// (b) Pointers to context parameters of BPF programs must not be
31// modified before access.
33// During BPF program verification a tag PTR_TO_CTX is tracked for
34// register values. In case if register with such tag is modified
35// BPF program is not allowed to read or write memory using this
36// register. See kernel/bpf/verifier.c:check_mem_access function
37// in the Linux kernel source tree for details.
39// The following sequence of the IR instructions:
41// %x = getelementptr %ptr, %constant_offset
44// Is translated as a single machine instruction:
46// LDW %ptr, %constant_offset
48// In order for cases (a) and (b) to work the sequence %x-%y above has
49// to be preserved by the IR passes.
51// However, several optimization passes might sink `load` instruction
52// or hoist `getelementptr` instruction so that the instructions are
53// no longer in sequence. Examples of such passes are:
54// SimplifyCFGPass, InstCombinePass, GVNPass.
55// After such modification the verifier would reject the BPF program.
57// To avoid this issue the patterns like (load/store (getelementptr ...))
58// are replaced by calls to BPF specific intrinsic functions:
59// - llvm.bpf.getelementptr.and.load
60// - llvm.bpf.getelementptr.and.store
62// These calls are lowered back to (load/store (getelementptr ...))
63// by BPFCheckAndAdjustIR pass right before the translation from IR to
64// machine instructions.
66// The transformation is split into the following steps:
67// - When IR is generated from AST the calls to intrinsic function
68// llvm.preserve.static.offset are inserted.
69// - BPFPreserveStaticOffsetPass is executed as early as possible
70// with AllowPatial set to true, this handles marked GEP chains
71// with constant offsets.
72// - BPFPreserveStaticOffsetPass is executed at ScalarOptimizerLateEPCallback
73// with AllowPatial set to false, this handles marked GEP chains
74// with offsets that became constant after loop unrolling, e.g.
75// to handle the following code:
77// struct context { int x[4]; } __attribute__((preserve_static_offset));
79// struct context *ctx = ...;
80// #pragma clang loop unroll(full)
81// for (int i = 0; i < 4; ++i)
84// The early BPFPreserveStaticOffsetPass run is necessary to allow
85// additional GVN / CSE opportunities after functions inlining.
86// The relative order of optimization applied to function:
89// - function inlining (2)
93// - ScalarOptimizerLateEPCallback (3)
95// When function A is inlined into function B all optimizations for A
96// are already done, while some passes remain for B. In case if
97// BPFPreserveStaticOffsetPass is done at (3) but not done at (1)
98// the code after (2) would contain a mix of
99// (load (gep %p)) and (get.and.load %p) usages:
100// - the (load (gep %p)) would come from the calling function;
101// - the (get.and.load %p) would come from the callee function.
102// Thus clobbering CSE / GVN passes done after inlining.
116#include "llvm/IR/IntrinsicsBPF.h"
121 #define DEBUG_TYPE "bpf-preserve-static-offset"
131 return Func->getIntrinsicID() == Id;
151template <
class T = Instruction>
191 Type *SourceElementType;
195 GEPChainInfo() { reset(); }
199 SourceElementType =
nullptr;
204}
// Anonymous namespace
206template <
class T = std::disjunction<LoadInst, StoreInst>>
208 GEPChainInfo &
GEP,
T *Insn) {
211 // Implementation of Align guarantees that ShiftValue < 64
212 unsigned AlignShiftValue =
Log2_64(Insn->getAlign().value());
213 Args.push_back(
GEP.Members[0]->getPointerOperand());
214 Args.push_back(ConstantInt::get(Int1Ty, Insn->isVolatile()));
215 Args.push_back(ConstantInt::get(Int8Ty, (
unsigned)Insn->getOrdering()));
216 Args.push_back(ConstantInt::get(Int8Ty, (
unsigned)Insn->getSyncScopeID()));
217 Args.push_back(ConstantInt::get(Int8Ty, AlignShiftValue));
218 Args.push_back(ConstantInt::get(Int1Ty,
GEP.InBounds));
219 Args.append(
GEP.Indices.begin(),
GEP.Indices.end());
227 {Load->getType()}, Args);
230 Call->setName((*
GEP.Members.rbegin())->getName());
231 if (Load->isUnordered()) {
232 Call->setOnlyReadsMemory();
233 Call->setOnlyAccessesArgMemory();
237 Call->addParamAttr(
I, Attribute::ImmArg);
238 Call->setAAMetadata(Load->getAAMetadata());
245 Args.push_back(Store->getValueOperand());
249 {Store->getValueOperand()->getType()}, Args);
251 if (Store->getValueOperand()->getType()->isPointerTy())
254 if (Store->isUnordered()) {
255 Call->setOnlyWritesMemory();
256 Call->setOnlyAccessesArgMemory();
260 Call->addParamAttr(
I, Attribute::ImmArg);
261 Call->setAAMetadata(Store->getAAMetadata());
267 return Int->getValue().getZExtValue();
270 ReportS <<
"Expecting ConstantInt as argument #" << ArgNo <<
" of " << *
Call
277 Indices.
append(
Call->data_operands_begin() + 6 + Delta,
278 Call->data_operands_end());
279 Type *GEPPointeeType =
Call->getParamElementType(Delta);
287template <
class T = std::disjunction<LoadInst, StoreInst>>
294 Insn->setAlignment(
Align(1ULL << AlignShiftValue));
295 GEP->setDebugLoc(
Call->getDebugLoc());
296 Insn->setDebugLoc(
Call->getDebugLoc());
297 Insn->setAAMetadata(
Call->getAAMetadata());
300std::pair<GetElementPtrInst *, LoadInst *>
303 Type *ReturnType =
Call->getFunctionType()->getReturnType();
305 /* These would be set in reconstructCommon */
308 return std::pair{
GEP, Load};
311std::pair<GetElementPtrInst *, StoreInst *>
315 /* These would be set in reconstructCommon */
318 return std::pair{
GEP, Store};
323 return CI && CI->isZero();
326// Given a chain of GEP instructions collect information necessary to
327// merge this chain as a single GEP instruction of form:
328// getelementptr %<type>, ptr %p, i32 0, <field_idx1>, <field_idx2>, ...
330 GEPChainInfo &
Info) {
335 return GEP->hasAllConstantIndices();
341 Info.SourceElementType =
First->getSourceElementType();
342 Type *ResultElementType =
First->getResultElementType();
351 if (!
GEP->getSourceElementType() ||
352 GEP->getSourceElementType() != ResultElementType) {
356 Info.InBounds &=
GEP->isInBounds();
357 Info.Indices.append(
GEP->idx_begin() + 1,
GEP->idx_end());
359 ResultElementType =
GEP->getResultElementType();
365// Given a chain of GEP instructions collect information necessary to
366// merge this chain as a single GEP instruction of form:
367// getelementptr i8, ptr %p, i64 %offset
369 GEPChainInfo &
Info) {
376 Type *PtrTy =
First->getType()->getScalarType();
383 Info.InBounds &=
GEP->isInBounds();
387 Info.Indices.push_back(ConstantInt::get(
C,
Offset));
395 Twine(
"Non-constant offset in access to a field of a type marked "
396 "with preserve_static_offset might be rejected by BPF verifier")
399 :
" (pass -g option to get exact location)"),
405 return GEP->hasAllZeroIndices();
412 GEPChainInfo GEPChain;
430// Check if U->getPointerOperand() == I
433 return L->getPointerOperand() ==
I;
435 return S->getPointerOperand() ==
I;
437 return GEP->getPointerOperand() ==
I;
439 return Call->getArgOperand(0) ==
I;
441 return Call->getArgOperand(1) ==
I;
447 return Call->hasFnAttr(Attribute::InlineHint);
454 bool AllowPatial,
bool &StillUsed);
467 llvm::dbgs() <<
"unsupported usage in BPFPreserveStaticOffsetPass:\n";
474// A DFS traversal of GEP chain trees starting from Root.
476// Recursion descends through GEP instructions and
477// llvm.preserve.static.offset calls. Recursion stops at any other
478// instruction. If load or store instruction is reached it is replaced
479// by a call to `llvm.bpf.getelementptr.and.load` or
480// `llvm.bpf.getelementptr.and.store` intrinsic.
481// If `llvm.bpf.getelementptr.and.load/store` is reached the accumulated
482// GEPs are merged into the intrinsic call.
483// If nested calls to `llvm.preserve.static.offset` are encountered these
484// calls are marked for deletion.
486// Parameters description:
487// - Insn - current position in the tree
488// - GEPs - GEP instructions for the current branch
489// - Visited - a list of visited instructions in DFS order,
490// order is important for unused instruction deletion.
491// - AllowPartial - when true GEP chains that can't be folded are
492// not reported, otherwise diagnostic message is show for such chains.
493// - StillUsed - set to true if one of the GEP chains could not be
494// folded, makes sense when AllowPartial is false, means that root
495// preserve.static.offset call is still in use and should remain
496// until the next run of this pass.
500 bool AllowPatial,
bool &StillUsed) {
501 auto MarkAndTraverseUses = [&]() {
503 rewriteUses(Insn, GEPs, Visited, AllowPatial, StillUsed);
505 auto TryToReplace = [&](
Instruction *LoadOrStore) {
506 // Do nothing for (preserve.static.offset (load/store ..)) or for
507 // GEPs with zero indices. Such constructs lead to zero offset and
508 // are simplified by other passes.
530 // This case can't be merged with the above because
531 // `delete Load` / `delete Store` wants a concrete type,
532 // destructor of Instruction is protected.
542 MarkAndTraverseUses();
545 MarkAndTraverseUses();
547 // Preserve preserve.static.offset call for parameters of
548 // functions that might be inlined. These would be removed on a
549 // second pass after inlining.
550 // Might happen when a pointer to a preserve_static_offset
551 // structure is passed as parameter of a function that would be
552 // inlined inside a loop that would be unrolled.
560 Twine(
"Unexpected rewriteAccessChain Insn = ").
concat(Buf));
573 bool StillUsed =
false;
574 rewriteUses(Marker, GEPs, Visited, AllowPatial, StillUsed);
575 // Check if Visited instructions could be removed, iterate in
576 // reverse to unblock instructions higher in the chain.
577 for (
auto V = Visited.
rbegin(); V != Visited.
rend(); ++V) {
580 RemovedMarkers.
insert(*V);
581 }
else if ((*V)->use_empty()) {
582 (*V)->eraseFromParent();
588static std::vector<Instruction *>
590 std::vector<Instruction *> Calls;
593 Calls.push_back(&Insn);
612 return GEP->getPointerOperand() ==
Op;
624 if (IsPointerOperand(V, U))
635 }
while (!WorkList.
empty());
638// Look for sequences:
639// - llvm.preserve.static.offset -> getelementptr... -> load
640// - llvm.preserve.static.offset -> getelementptr... -> store
641// And replace those with calls to intrinsics:
642// - llvm.bpf.getelementptr.and.load
643// - llvm.bpf.getelementptr.and.store
645 LLVM_DEBUG(
dbgs() <<
"********** BPFPreserveStaticOffsetPass (AllowPartial="
646 << AllowPartial <<
") ************\n");
652 <<
" preserve.static.offset calls\n");
654 if (MarkerCalls.empty())
657 for (
auto *
Call : MarkerCalls)
660 for (
auto *
Call : MarkerCalls) {
664 if (!StillUsed || !AllowPartial)
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
Expand Atomic instructions
This file contains the simple types necessary to represent the attributes associated with functions a...
static CallInst * isGEPAndLoad(Value *I)
bool isPreserveUnionIndex(Value *V)
bool isPreserveArrayIndex(Value *V)
static bool isPreserveStaticOffsetCall(Value *I)
static void setParamElementType(CallInst *Call, unsigned ArgNo, Type *Type)
static bool foldGEPChainAsU8Access(SmallVector< GetElementPtrInst * > &GEPs, GEPChainInfo &Info)
static void fillCommonArgs(LLVMContext &C, SmallVector< Value * > &Args, GEPChainInfo &GEP, T *Insn)
static void removePAICalls(Instruction *Marker)
static void reportNonStaticGEPChain(Instruction *Insn)
static bool foldGEPChainAsStructAccess(SmallVector< GetElementPtrInst * > &GEPs, GEPChainInfo &Info)
static const unsigned GepAndStoreFirstIdxArg
static void removeMarkerCall(Instruction *Marker)
static Instruction * makeGEPAndStore(Module *M, GEPChainInfo &GEP, StoreInst *Store)
static void rewriteUses(Instruction *Insn, SmallVector< GetElementPtrInst * > &GEPs, SmallVector< Instruction * > &Visited, bool AllowPatial, bool &StillUsed)
static void setParamReadNone(CallInst *Call, unsigned ArgNo)
static Instruction * makeGEPAndLoad(Module *M, GEPChainInfo &GEP, LoadInst *Load)
static unsigned getOperandAsUnsigned(CallInst *Call, unsigned ArgNo)
bool isPreserveStructIndex(Value *V)
static void setParamReadOnly(CallInst *Call, unsigned ArgNo)
static void rewriteAccessChain(Instruction *Insn, SmallVector< GetElementPtrInst * > &GEPs, SmallVector< Instruction * > &Visited, bool AllowPatial, bool &StillUsed)
static bool isInlineableCall(User *U)
static const unsigned GepAndLoadFirstIdxArg
static DebugLoc mergeDebugLocs(SmallVector< T * > &Insns)
static GetElementPtrInst * reconstructGEP(CallInst *Call, int Delta)
static CallInst * makeIntrinsicCall(Module *M, Intrinsic::BPFIntrinsics Intrinsic, ArrayRef< Type * > Types, ArrayRef< Value * > Args)
static bool allZeroIndices(SmallVector< GetElementPtrInst * > &GEPs)
static std::vector< Instruction * > collectPreserveStaticOffsetCalls(Function &F)
static bool rewriteFunction(Function &F, bool AllowPartial)
static bool tryToReplaceWithGEPBuiltin(Instruction *LoadOrStoreTemplate, SmallVector< GetElementPtrInst * > &GEPs, Instruction *InsnToReplace)
static void reconstructCommon(CallInst *Call, GetElementPtrInst *GEP, T *Insn, int Delta)
static CallInst * isGEPAndStore(Value *I)
static void setParamWriteOnly(CallInst *Call, unsigned ArgNo)
static bool isIntrinsicCall(Value *I, Intrinsic::ID Id)
static bool isPointerOperand(Value *I, User *U)
static bool isIntrinsicCall(const CallBase *Call, Intrinsic::ID IID)
Analysis containing CSE Info
This file contains the declarations for the subclasses of Constant, which represent the different fla...
Module.h This file contains the declarations for the Module class.
static bool isZero(Value *V, const DataLayout &DL, DominatorTree *DT, AssumptionCache *AC)
This file defines the SmallPtrSet class.
This file defines the SmallVector class.
Class for arbitrary precision integers.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
static LLVM_ABI Attribute get(LLVMContext &Context, AttrKind Kind, uint64_t Val=0)
Return a uniquified Attribute object.
static void removeArrayAccessCall(CallInst *Call)
static void removeStructAccessCall(CallInst *Call)
static void removeUnionAccessCall(CallInst *Call)
static std::pair< GetElementPtrInst *, StoreInst * > reconstructStore(CallInst *Call)
static std::pair< GetElementPtrInst *, LoadInst * > reconstructLoad(CallInst *Call)
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM)
This class represents a function call, abstracting a target machine's calling convention.
static CallInst * Create(FunctionType *Ty, Value *F, const Twine &NameStr="", InsertPosition InsertBefore=nullptr)
A parsed version of the target data layout string in and methods for querying it.
static LLVM_ABI DebugLoc getMergedLocation(DebugLoc LocA, DebugLoc LocB)
When two instructions are combined into a single instruction we also need to combine the original loc...
Diagnostic information for unsupported feature in backend.
an instruction for type-safe pointer arithmetic to access elements of arrays and structs
static GetElementPtrInst * Create(Type *PointeeType, Value *Ptr, ArrayRef< Value * > IdxList, const Twine &NameStr="", InsertPosition InsertBefore=nullptr)
const DebugLoc & getDebugLoc() const
Return the debug location for this node as a DebugLoc.
LLVM_ABI const Module * getModule() const
Return the module owning the function this instruction belongs to or nullptr it the function does not...
LLVM_ABI void insertBefore(InstListType::iterator InsertPos)
Insert an unlinked instruction into a basic block immediately before the specified position.
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
LLVM_ABI const Function * getFunction() const
Return the function this instruction belongs to.
This is an important class for using LLVM in a threaded context.
LLVM_ABI void diagnose(const DiagnosticInfo &DI)
Report a message to the currently installed diagnostic handler.
An instruction for reading from memory.
A Module instance is used to store all the information related to an LLVM module.
A set of analyses that are preserved following a run of a transformation pass.
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
A templated base class for SmallPtrSet which provides the typesafe interface that is common across al...
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
bool contains(ConstPtrType Ptr) const
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
void push_back(const T &Elt)
reverse_iterator rbegin()
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An instruction for storing to memory.
StringRef - Represent a constant reference to a string, i.e.
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
The instances of the Type class are immutable: once they are created, they are never changed.
static LLVM_ABI IntegerType * getInt8Ty(LLVMContext &C)
static LLVM_ABI IntegerType * getInt1Ty(LLVMContext &C)
Value * getOperand(unsigned i) const
LLVM Value Representation.
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
iterator_range< user_iterator > users()
LLVM_ABI LLVMContext & getContext() const
All values hold a context through their type.
self_iterator getIterator()
A raw_ostream that writes to an std::string.
A raw_ostream that writes to an SmallVector or SmallString.
@ C
The default llvm calling convention, compatible with C.
This namespace contains an enum with a value for every intrinsic/builtin function known by LLVM.
LLVM_ABI Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > Tys={})
Look up the Function declaration of the intrinsic id in the Module M.
This is an optimization pass for GlobalISel generic memory operations.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
unsigned Log2_64(uint64_t Value)
Return the floor log base 2 of the specified value, -1 if the value is zero.
detail::concat_range< ValueT, RangeTs... > concat(RangeTs &&...Ranges)
Returns a concatenated range across two or more ranges.
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
AtomicOrdering
Atomic ordering for LLVM's memory model.
@ First
Helpers to iterate all locations in the MemoryEffectsBase class.
DWARFExpression::Operation Op
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
This struct is a compact representation of a valid (non-zero power of two) alignment.