-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[BPF] Convert 'addrcast M->N -> GEP -> addrcast N->M' to just GEP
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
Showing
9 changed files
with
218 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |