Skip to content

Commit

Permalink
BPF address space insn (#84410)
Browse files Browse the repository at this point in the history
This commit aims to support BPF arena kernel side
[feature](https://lore.kernel.org/bpf/20240209040608.98927-1-alexei.starovoitov@gmail.com/):
- arena is a memory region accessible from both BPF program and
userspace;
- base pointers for this memory region differ between kernel and user
spaces;
- `dst_reg = addr_space_cast(src_reg, dst_addr_space, src_addr_space)`
translates src_reg, a pointer in src_addr_space to dst_reg, equivalent
pointer in dst_addr_space, {src,dst}_addr_space are immediate constants;
- number 0 is assigned to kernel address space;
- number 1 is assigned to user address space.

On the LLVM side, the goal is to make load and store operations on arena
pointers "transparent" for BPF programs:
- assume that pointers with non-zero address space are pointers to
  arena memory;
- assume that arena is identified by address space number;
- assume that address space zero corresponds to kernel address space;
- assume that every BPF-side load or store from arena is done via
pointer in user address space, thus convert base pointers using
`addr_space_cast(src_reg, 0, 1)`;

Only load, store, cmpxchg and atomicrmw IR instructions are handled by
this transformation.

For example, the following C code:

```c
   #define __as __attribute__((address_space(1)))
   void copy(int __as *from, int __as *to) { *to = *from; }
```

Compiled to the following IR:

```llvm
    define void @copy(ptr addrspace(1) %from, ptr addrspace(1) %to) {
    entry:
      %0 = load i32, ptr addrspace(1) %from, align 4
      store i32 %0, ptr addrspace(1) %to, align 4
      ret void
    }
```

Is transformed to:

```llvm
    %to2 = addrspacecast ptr addrspace(1) %to to ptr     ;; !
    %from1 = addrspacecast ptr addrspace(1) %from to ptr ;; !
    %0 = load i32, ptr %from1, align 4, !tbaa !3
    store i32 %0, ptr %to2, align 4, !tbaa !3
    ret void
```

And compiled as:

```asm
    r2 = addr_space_cast(r2, 0, 1)
    r1 = addr_space_cast(r1, 0, 1)
    r1 = *(u32 *)(r1 + 0)
    *(u32 *)(r2 + 0) = r1
    exit
```

Co-authored-by: Eduard Zingerman <eddyz87@gmail.com>
  • Loading branch information
4ast and eddyz87 authored Mar 13, 2024
1 parent f467cc9 commit 2aacb56
Show file tree
Hide file tree
Showing 21 changed files with 615 additions and 0 deletions.
3 changes: 3 additions & 0 deletions clang/lib/Basic/Targets/BPF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ void BPFTargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__BPF_CPU_VERSION__", "0");
return;
}

Builder.defineMacro("__BPF_FEATURE_ARENA_CAST");

if (CPU.empty() || CPU == "generic" || CPU == "v1") {
Builder.defineMacro("__BPF_CPU_VERSION__", "1");
return;
Expand Down
8 changes: 8 additions & 0 deletions clang/test/Preprocessor/bpf-predefined-macros.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ int r;
#ifdef __BPF_FEATURE_ST
int s;
#endif
#ifdef __BPF_FEATURE_ARENA_CAST
int t;
#endif

// CHECK: int b;
// CHECK: int c;
Expand Down Expand Up @@ -90,6 +93,11 @@ int s;
// CPU_V4: int r;
// CPU_V4: int s;

// CPU_V1: int t;
// CPU_V2: int t;
// CPU_V3: int t;
// CPU_V4: int t;

// CPU_GENERIC: int g;

// CPU_PROBE: int f;
1 change: 1 addition & 0 deletions llvm/lib/Target/BPF/AsmParser/BPFAsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ struct BPFOperand : public MCParsedAsmOperand {
.Case("xchg32_32", true)
.Case("cmpxchg_64", true)
.Case("cmpxchg32_32", true)
.Case("addr_space_cast", true)
.Default(false);
}
};
Expand Down
8 changes: 8 additions & 0 deletions llvm/lib/Target/BPF/BPF.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ class BPFIRPeepholePass : public PassInfoMixin<BPFIRPeepholePass> {
static bool isRequired() { return true; }
};

class BPFASpaceCastSimplifyPass
: public PassInfoMixin<BPFASpaceCastSimplifyPass> {
public:
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);

static bool isRequired() { return true; }
};

class BPFAdjustOptPass : public PassInfoMixin<BPFAdjustOptPass> {
public:
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
Expand Down
92 changes: 92 additions & 0 deletions llvm/lib/Target/BPF/BPFASpaceCastSimplifyPass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//===-- BPFASpaceCastSimplifyPass.cpp - BPF addrspacecast simplications --===//
//
// 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
//
//===----------------------------------------------------------------------===//

#include "BPF.h"
#include <optional>

#define DEBUG_TYPE "bpf-aspace-simplify"

using namespace llvm;

namespace {

struct CastGEPCast {
AddrSpaceCastInst *OuterCast;

// Match chain of instructions:
// %inner = addrspacecast N->M
// %gep = getelementptr %inner, ...
// %outer = addrspacecast M->N %gep
// Where I is %outer.
static std::optional<CastGEPCast> match(Value *I) {
auto *OuterCast = dyn_cast<AddrSpaceCastInst>(I);
if (!OuterCast)
return std::nullopt;
auto *GEP = dyn_cast<GetElementPtrInst>(OuterCast->getPointerOperand());
if (!GEP)
return std::nullopt;
auto *InnerCast = dyn_cast<AddrSpaceCastInst>(GEP->getPointerOperand());
if (!InnerCast)
return std::nullopt;
if (InnerCast->getSrcAddressSpace() != OuterCast->getDestAddressSpace())
return std::nullopt;
if (InnerCast->getDestAddressSpace() != OuterCast->getSrcAddressSpace())
return std::nullopt;
return CastGEPCast{OuterCast};
}

static PointerType *changeAddressSpace(PointerType *Ty, unsigned AS) {
return Ty->get(Ty->getContext(), AS);
}

// Assuming match(this->OuterCast) is true, convert:
// (addrspacecast M->N (getelementptr (addrspacecast N->M ptr) ...))
// To:
// (getelementptr ptr ...)
GetElementPtrInst *rewrite() {
auto *GEP = cast<GetElementPtrInst>(OuterCast->getPointerOperand());
auto *InnerCast = cast<AddrSpaceCastInst>(GEP->getPointerOperand());
unsigned AS = OuterCast->getDestAddressSpace();
auto *NewGEP = cast<GetElementPtrInst>(GEP->clone());
NewGEP->setName(GEP->getName());
NewGEP->insertAfter(OuterCast);
NewGEP->setOperand(0, InnerCast->getPointerOperand());
auto *GEPTy = cast<PointerType>(GEP->getType());
NewGEP->mutateType(changeAddressSpace(GEPTy, AS));
OuterCast->replaceAllUsesWith(NewGEP);
OuterCast->eraseFromParent();
if (GEP->use_empty())
GEP->eraseFromParent();
if (InnerCast->use_empty())
InnerCast->eraseFromParent();
return NewGEP;
}
};

} // anonymous namespace

PreservedAnalyses BPFASpaceCastSimplifyPass::run(Function &F,
FunctionAnalysisManager &AM) {
SmallVector<CastGEPCast, 16> WorkList;
bool Changed = false;
for (BasicBlock &BB : F) {
for (Instruction &I : BB)
if (auto It = CastGEPCast::match(&I))
WorkList.push_back(It.value());
Changed |= !WorkList.empty();

while (!WorkList.empty()) {
CastGEPCast InsnChain = WorkList.pop_back_val();
GetElementPtrInst *NewGEP = InsnChain.rewrite();
for (User *U : NewGEP->users())
if (auto It = CastGEPCast::match(U))
WorkList.push_back(It.value());
}
}
return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
}
116 changes: 116 additions & 0 deletions llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
// optimizations are done and those builtins can be removed.
// - remove llvm.bpf.getelementptr.and.load builtins.
// - remove llvm.bpf.getelementptr.and.store builtins.
// - for loads and stores with base addresses from non-zero address space
// cast base address to zero address space (support for BPF arenas).
//
//===----------------------------------------------------------------------===//

Expand Down Expand Up @@ -55,6 +57,7 @@ class BPFCheckAndAdjustIR final : public ModulePass {
bool removeCompareBuiltin(Module &M);
bool sinkMinMax(Module &M);
bool removeGEPBuiltins(Module &M);
bool insertASpaceCasts(Module &M);
};
} // End anonymous namespace

Expand Down Expand Up @@ -416,11 +419,124 @@ bool BPFCheckAndAdjustIR::removeGEPBuiltins(Module &M) {
return Changed;
}

// Wrap ToWrap with cast to address space zero:
// - if ToWrap is a getelementptr,
// wrap it's base pointer instead and return a copy;
// - if ToWrap is Instruction, insert address space cast
// immediately after ToWrap;
// - if ToWrap is not an Instruction (function parameter
// or a global value), insert address space cast at the
// beginning of the Function F;
// - use Cache to avoid inserting too many casts;
static Value *aspaceWrapValue(DenseMap<Value *, Value *> &Cache, Function *F,
Value *ToWrap) {
auto It = Cache.find(ToWrap);
if (It != Cache.end())
return It->getSecond();

if (auto *GEP = dyn_cast<GetElementPtrInst>(ToWrap)) {
Value *Ptr = GEP->getPointerOperand();
Value *WrappedPtr = aspaceWrapValue(Cache, F, Ptr);
auto *GEPTy = cast<PointerType>(GEP->getType());
auto *NewGEP = GEP->clone();
NewGEP->insertAfter(GEP);
NewGEP->mutateType(GEPTy->getPointerTo(0));
NewGEP->setOperand(GEP->getPointerOperandIndex(), WrappedPtr);
NewGEP->setName(GEP->getName());
Cache[ToWrap] = NewGEP;
return NewGEP;
}

IRBuilder IB(F->getContext());
if (Instruction *InsnPtr = dyn_cast<Instruction>(ToWrap))
IB.SetInsertPoint(*InsnPtr->getInsertionPointAfterDef());
else
IB.SetInsertPoint(F->getEntryBlock().getFirstInsertionPt());
auto *PtrTy = cast<PointerType>(ToWrap->getType());
auto *ASZeroPtrTy = PtrTy->getPointerTo(0);
auto *ACast = IB.CreateAddrSpaceCast(ToWrap, ASZeroPtrTy, ToWrap->getName());
Cache[ToWrap] = ACast;
return ACast;
}

// Wrap a pointer operand OpNum of instruction I
// with cast to address space zero
static void aspaceWrapOperand(DenseMap<Value *, Value *> &Cache, Instruction *I,
unsigned OpNum) {
Value *OldOp = I->getOperand(OpNum);
if (OldOp->getType()->getPointerAddressSpace() == 0)
return;

Value *NewOp = aspaceWrapValue(Cache, I->getFunction(), OldOp);
I->setOperand(OpNum, NewOp);
// Check if there are any remaining users of old GEP,
// delete those w/o users
for (;;) {
auto *OldGEP = dyn_cast<GetElementPtrInst>(OldOp);
if (!OldGEP)
break;
if (!OldGEP->use_empty())
break;
OldOp = OldGEP->getPointerOperand();
OldGEP->eraseFromParent();
}
}

// Support for BPF arenas:
// - for each function in the module M, update pointer operand of
// each memory access instruction (load/store/cmpxchg/atomicrmw)
// by casting it from non-zero address space to zero address space, e.g:
//
// (load (ptr addrspace (N) %p) ...)
// -> (load (addrspacecast ptr addrspace (N) %p to ptr))
//
// - assign section with name .arena.N for globals defined in
// non-zero address space N
bool BPFCheckAndAdjustIR::insertASpaceCasts(Module &M) {
bool Changed = false;
for (Function &F : M) {
DenseMap<Value *, Value *> CastsCache;
for (BasicBlock &BB : F) {
for (Instruction &I : BB) {
unsigned PtrOpNum;

if (auto *LD = dyn_cast<LoadInst>(&I))
PtrOpNum = LD->getPointerOperandIndex();
else if (auto *ST = dyn_cast<StoreInst>(&I))
PtrOpNum = ST->getPointerOperandIndex();
else if (auto *CmpXchg = dyn_cast<AtomicCmpXchgInst>(&I))
PtrOpNum = CmpXchg->getPointerOperandIndex();
else if (auto *RMW = dyn_cast<AtomicRMWInst>(&I))
PtrOpNum = RMW->getPointerOperandIndex();
else
continue;

aspaceWrapOperand(CastsCache, &I, PtrOpNum);
}
}
Changed |= !CastsCache.empty();
}
// Merge all globals within same address space into single
// .arena.<addr space no> section
for (GlobalVariable &G : M.globals()) {
if (G.getAddressSpace() == 0 || G.hasSection())
continue;
SmallString<16> SecName;
raw_svector_ostream OS(SecName);
OS << ".arena." << G.getAddressSpace();
G.setSection(SecName);
// Prevent having separate section for constants
G.setConstant(false);
}
return Changed;
}

bool BPFCheckAndAdjustIR::adjustIR(Module &M) {
bool Changed = removePassThroughBuiltin(M);
Changed = removeCompareBuiltin(M) || Changed;
Changed = sinkMinMax(M) || Changed;
Changed = removeGEPBuiltins(M) || Changed;
Changed = insertASpaceCasts(M) || Changed;
return Changed;
}

Expand Down
29 changes: 29 additions & 0 deletions llvm/lib/Target/BPF/BPFInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,35 @@ let Predicates = [BPFHasMovsx] in {
}
}

def ADDR_SPACE_CAST
: ALU_RR<BPF_ALU64, BPF_MOV, 64,
(outs GPR:$dst),
(ins GPR:$src, i64imm:$dst_as, i64imm:$src_as),
"$dst = addr_space_cast($src, $dst_as, $src_as)",
[]> {
bits<64> dst_as;
bits<64> src_as;

let Inst{47-32} = 1;
let Inst{31-16} = dst_as{15-0};
let Inst{15-0} = src_as{15-0};
}

def SrcAddrSpace : SDNodeXForm<addrspacecast, [{
return CurDAG->getTargetConstant(
cast<AddrSpaceCastSDNode>(N)->getSrcAddressSpace(),
SDLoc(N), MVT::i64);
}]>;

def DstAddrSpace : SDNodeXForm<addrspacecast, [{
return CurDAG->getTargetConstant(
cast<AddrSpaceCastSDNode>(N)->getDestAddressSpace(),
SDLoc(N), MVT::i64);
}]>;

def : Pat<(addrspacecast:$this GPR:$src),
(ADDR_SPACE_CAST $src, (DstAddrSpace $this), (SrcAddrSpace $this))>;

def FI_ri
: TYPE_LD_ST<BPF_IMM.Value, BPF_DW.Value,
(outs GPR:$dst),
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Target/BPF/BPFTargetMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ void BPFTargetMachine::registerPassBuilderCallbacks(
FPM.addPass(BPFPreserveStaticOffsetPass(false));
return true;
}
if (PassName == "bpf-aspace-simplify") {
FPM.addPass(BPFASpaceCastSimplifyPass());
return true;
}
return false;
});
PB.registerPipelineStartEPCallback(
Expand All @@ -135,6 +139,7 @@ void BPFTargetMachine::registerPassBuilderCallbacks(
PB.registerPeepholeEPCallback([=](FunctionPassManager &FPM,
OptimizationLevel Level) {
FPM.addPass(SimplifyCFGPass(SimplifyCFGOptions().hoistCommonInsts(true)));
FPM.addPass(BPFASpaceCastSimplifyPass());
});
PB.registerScalarOptimizerLateEPCallback(
[=](FunctionPassManager &FPM, OptimizationLevel Level) {
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/BPF/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ add_llvm_target(BPFCodeGen
BPFAbstractMemberAccess.cpp
BPFAdjustOpt.cpp
BPFAsmPrinter.cpp
BPFASpaceCastSimplifyPass.cpp
BPFCheckAndAdjustIR.cpp
BPFFrameLowering.cpp
BPFInstrInfo.cpp
Expand Down
Loading

0 comments on commit 2aacb56

Please sign in to comment.