Skip to content

Commit

Permalink
[BPF] Convert 'addrcast M->N -> GEP -> addrcast N->M' to just GEP
Browse files Browse the repository at this point in the history
For BPF GEP would adjust pointer using same offset in any address
space, thus transformation of form:

    %inner = addrspacecast N->M %ptr
    %gep   = getelementptr %inner, ...
    %outer = addrspacecast M->N %gep

to just:

   %gep = getelementptr %ptr, ...

is valid.

Applying such transformation helps with C patterns that use e.g.
(void *) casts to offsets w/o actual memory access:

    #define container_of(ptr, type, member)                 \
        ({                                                  \
            void __arena *__mptr = (void *)(ptr);           \
            ((type *)(__mptr - offsetof(type, member)));    \
        })

(Note the address space cast on first body line)
  • Loading branch information
eddyz87 committed Mar 8, 2024
1 parent 7903dfa commit e8b41ad
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 0 deletions.
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();
}
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
19 changes: 19 additions & 0 deletions llvm/test/CodeGen/BPF/addr-space-simplify-1.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
; RUN: opt -passes=bpf-aspace-simplify -mtriple=bpf-pc-linux -S < %s | FileCheck %s

; Check that bpf-aspace-simplify pass removes unnecessary (for BPF)
; address space casts for cast M->N -> GEP -> cast N->M chain.

define dso_local ptr addrspace(1) @test (ptr addrspace(1) %p) {
; CHECK-LABEL: define dso_local ptr addrspace(1) @test(
; CHECK-SAME: ptr addrspace(1) [[P:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[B1:%.*]] = getelementptr inbounds i8, ptr addrspace(1) [[P]], i64 8
; CHECK-NEXT: ret ptr addrspace(1) [[B1]]
;
entry:
%a = addrspacecast ptr addrspace(1) %p to ptr
%b = getelementptr inbounds i8, ptr %a, i64 8
%c = addrspacecast ptr %b to ptr addrspace(1)
ret ptr addrspace(1) %c
}
21 changes: 21 additions & 0 deletions llvm/test/CodeGen/BPF/addr-space-simplify-2.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
; RUN: opt -passes=bpf-aspace-simplify -mtriple=bpf-pc-linux -S < %s | FileCheck %s

; Check that bpf-aspace-simplify pass does not change
; chain 'cast M->N -> GEP -> cast N->K'.

define dso_local ptr addrspace(2) @test (ptr addrspace(1) %p) {
; CHECK-LABEL: define dso_local ptr addrspace(2) @test(
; CHECK-SAME: ptr addrspace(1) [[P:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[A:%.*]] = addrspacecast ptr addrspace(1) [[P]] to ptr
; CHECK-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 8
; CHECK-NEXT: [[C:%.*]] = addrspacecast ptr [[B]] to ptr addrspace(2)
; CHECK-NEXT: ret ptr addrspace(2) [[C]]
;
entry:
%a = addrspacecast ptr addrspace(1) %p to ptr
%b = getelementptr inbounds i8, ptr %a, i64 8
%c = addrspacecast ptr %b to ptr addrspace(2)
ret ptr addrspace(2) %c
}
26 changes: 26 additions & 0 deletions llvm/test/CodeGen/BPF/addr-space-simplify-3.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
; RUN: opt -passes=bpf-aspace-simplify -mtriple=bpf-pc-linux -S < %s | FileCheck %s

; Check that when bpf-aspace-simplify pass modifies chain
; 'cast M->N -> GEP -> cast N->M' it does not remove GEP,
; when that GEP is used by some other instruction.

define dso_local ptr addrspace(1) @test (ptr addrspace(1) %p) {
; CHECK-LABEL: define dso_local ptr addrspace(1) @test(
; CHECK-SAME: ptr addrspace(1) [[P:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[A:%.*]] = addrspacecast ptr addrspace(1) [[P]] to ptr
; CHECK-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 8
; CHECK-NEXT: [[B1:%.*]] = getelementptr inbounds i8, ptr addrspace(1) [[P]], i64 8
; CHECK-NEXT: call void @sink(ptr [[B]])
; CHECK-NEXT: ret ptr addrspace(1) [[B1]]
;
entry:
%a = addrspacecast ptr addrspace(1) %p to ptr
%b = getelementptr inbounds i8, ptr %a, i64 8
%c = addrspacecast ptr %b to ptr addrspace(1)
call void @sink(ptr %b)
ret ptr addrspace(1) %c
}

declare dso_local void @sink(ptr)
21 changes: 21 additions & 0 deletions llvm/test/CodeGen/BPF/addr-space-simplify-4.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
; RUN: opt -passes=bpf-aspace-simplify -mtriple=bpf-pc-linux -S < %s | FileCheck %s

; Check that bpf-aspace-simplify pass simplifies chain
; 'cast K->M -> cast M->N -> GEP -> cast N->M -> cast M->K'.

define dso_local ptr addrspace(2) @test (ptr addrspace(2) %p) {
; CHECK-LABEL: define dso_local ptr addrspace(2) @test(
; CHECK-SAME: ptr addrspace(2) [[P:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[C12:%.*]] = getelementptr inbounds i8, ptr addrspace(2) [[P]], i64 8
; CHECK-NEXT: ret ptr addrspace(2) [[C12]]
;
entry:
%a = addrspacecast ptr addrspace(2) %p to ptr addrspace(1)
%b = addrspacecast ptr addrspace(1) %a to ptr
%c = getelementptr inbounds i8, ptr %b, i64 8
%d = addrspacecast ptr %c to ptr addrspace(1)
%e = addrspacecast ptr addrspace (1) %d to ptr addrspace(2)
ret ptr addrspace(2) %e
}
25 changes: 25 additions & 0 deletions llvm/test/CodeGen/BPF/addr-space-simplify-5.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
; RUN: opt -passes=bpf-aspace-simplify -mtriple=bpf-pc-linux -S < %s | FileCheck %s

; Check that bpf-aspace-simplify pass removes unnecessary (for BPF)
; address space casts for cast M->N -> GEP -> cast N->M chain,
; where chain is split between several BBs.

define dso_local ptr addrspace(1) @test (ptr addrspace(1) %p) {
; CHECK-LABEL: define dso_local ptr addrspace(1) @test(
; CHECK-SAME: ptr addrspace(1) [[P:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: [[B1:%.*]] = getelementptr inbounds i8, ptr addrspace(1) [[P]], i64 8
; CHECK-NEXT: ret ptr addrspace(1) [[B1]]
;
entry:
%a = addrspacecast ptr addrspace(1) %p to ptr
%b = getelementptr inbounds i8, ptr %a, i64 8
br label %exit

exit:
%c = addrspacecast ptr %b to ptr addrspace(1)
ret ptr addrspace(1) %c
}

0 comments on commit e8b41ad

Please sign in to comment.