[llvm] [BPF] add cast_{user,kern} instructions (PR #79902)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jan 29 13:26:05 PST 2024
https://github.com/eddyz87 created https://github.com/llvm/llvm-project/pull/79902
This commit aims to support BPF arena kernel side feature:
- 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 = cast_user(src_reg, addr_space_no)` translates `src_reg`, kernel space pointer within arena, to `dst_reg`, equivalent user space pointer within arena (both pointers have identical offset from arena start), `addr_space_no` is an immediate constant, used to identify the particular arena;
- `dst_reg = cast_kern(src_reg, addr_space_no)` is similar but in opposite direction: converts user space arena pointer to kernel space arena pointer.
On the LLVM side, the goal is to have all arena pointers stored in arena memory in user space format:
- assume that pointers with non-zero address space are pointers to arena memory;
- assume that arena is identified by address space number;
- assume that every BPF-side load or store from arena is done via pointer in user address space, thus convert base pointers using cast_kern;
- assume that every BPF-side store of arena pointer value is in kernel address space, thus convert stored pointers with cast_user.
Only load and store IR instructions are handled at the moment.
For example, the following C code:
```c
struct list {
struct list __as *next;
int i;
};
extern struct list __as *mklist(void);
struct list __as *push(int i, struct list __as *list) {
struct list __as *elt = mklist();
elt->i = i;
elt->next = list;
return elt;
}
```
Compiled to the following IR:
```llvm
%call = tail call ptr addrspace(272) @mklist() #2
%i1 = getelementptr inbounds %struct.list, ptr addrspace(272) %call, i64 0, i32 1
store i32 %i, ptr addrspace(272) %i1, align 8
store ptr addrspace(272) %list, ptr addrspace(272) %call, align 8
ret ptr addrspace(272) %call
```
Is transformed to:
```llvm
%list6 = call ptr addrspace(272) @llvm.bpf.addr.space.p272.p272(ptr addrspace(272) %list, i32 2) ;; cast_user
%call = tail call ptr addrspace(272) @mklist() #3
%call4 = call ptr addrspace(272) @llvm.bpf.addr.space.p272.p272(ptr addrspace(272) %call, i32 1) ;; cast_kern
%i15 = getelementptr inbounds %struct.list, ptr addrspace(272) %call4, i64 0, i32 1
store i32 %i, ptr addrspace(272) %i15, align 8, !tbaa !3
store ptr addrspace(272) %list6, ptr addrspace(272) %call4, align 8
ret ptr addrspace(272) %call
```
And compiled as:
```asm
r6 = r2
r7 = r1
call mklist
r1 = cast_kern(r0, 272)
*(u32 *)(r1 + 8) = r7
r2 = cast_user(r6, 272)
*(u64 *)(r1 + 0) = r2
exit
```
Internally:
- use a new intrinsic function to mark the conversions: `llvm.bpf.addr.space(<pointer>, <cast_direction>)`, where `cast_diretion` is an immediate describing whether operation is `cast_kern` (1) or `cast_user` (2);
- piggy-back `BPFCheckAndAdjustIR` pass to insert the above intrinsic calls for load and store instructions;
- modify `BPFInstrInfo.td` and `BPFIselLowering.cpp` to allow translation of new intrinsic:
- define and SDNode type `BPFAddrSpace` to represent new intrinsic: - override `BPFTargetLowering::CollectTargetIntrinsicOperands()` method to add pointer address space as a parameter of intrinsic SDNode; - define `BPFTargetLowering::LowerINTRINSIC_WO_CHAIN()` called from `BPFTargetLowering::LowerOperation()` to lower intrinsic call to an SDNode;
- define new instructions: `ADDR_SPACE_K`, `ADDR_SPACE_U`;
- define patterns to lower `BPFAddrSpace` as `ADDR_SPACE_{KU}`.
>From f235b4021c58e12cae44cad1e2f1e468ea4be17d Mon Sep 17 00:00:00 2001
From: Eduard Zingerman <eddyz87 at gmail.com>
Date: Fri, 26 Jan 2024 04:18:32 +0200
Subject: [PATCH] [BPF] add cast_{user,kern} instructions
This commit aims to support BPF arena kernel side feature:
- 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 = cast_user(src_reg, addr_space_no)`
translates src_reg, kernel space pointer within arena,
to dst_reg, equivalent user space pointer within arena
(both pointers have identical offset from arena start),
addr_space_no is an immediate constant, used to identify
the particular arena;
- `dst_reg = cast_kern(src_reg, addr_space_no)`
is similar but in opposite direction: converts user space arena
pointer to kernel space arena pointer.
On the LLVM side, the goal is to have all arena pointers stored in
arena memory in user space format:
- assume that pointers with non-zero address space are pointers to
arena memory;
- assume that arena is identified by address space number;
- assume that every BPF-side load or store from arena is done via
pointer in user address space, thus convert base pointers using
cast_kern;
- assume that every BPF-side store of arena pointer value is in kernel
address space, thus convert stored pointers with cast_user.
Only load and store IR instructions are handled at the moment.
For example, the following C code:
```c
struct list {
struct list __as *next;
int i;
};
extern struct list __as *mklist(void);
struct list __as *push(int i, struct list __as *list) {
struct list __as *elt = mklist();
elt->i = i;
elt->next = list;
return elt;
}
```
Compiled to the following IR:
```llvm
%call = tail call ptr addrspace(272) @mklist() #2
%i1 = getelementptr inbounds %struct.list, ptr addrspace(272) %call, i64 0, i32 1
store i32 %i, ptr addrspace(272) %i1, align 8, !tbaa !3
store ptr addrspace(272) %list, ptr addrspace(272) %call, align 8
ret ptr addrspace(272) %call
```
Is transformed to:
```llvm
%list6 = call ptr addrspace(272) @llvm.bpf.addr.space.p272.p272(ptr addrspace(272) %list, i32 2) ;; cast_user
%call = tail call ptr addrspace(272) @mklist() #3
%call4 = call ptr addrspace(272) @llvm.bpf.addr.space.p272.p272(ptr addrspace(272) %call, i32 1) ;; cast_kern
%i15 = getelementptr inbounds %struct.list, ptr addrspace(272) %call4, i64 0, i32 1
store i32 %i, ptr addrspace(272) %i15, align 8, !tbaa !3
store ptr addrspace(272) %list6, ptr addrspace(272) %call4, align 8
ret ptr addrspace(272) %call
```
And compiled as:
```asm
r6 = r2
r7 = r1
call mklist
r1 = cast_kern(r0, 272)
*(u32 *)(r1 + 8) = r7
r2 = cast_user(r6, 272)
*(u64 *)(r1 + 0) = r2
exit
```
Internally:
- use a new intrinsic function to mark the conversions:
`llvm.bpf.addr.space(<pointer>, <cast_direction>)`,
where `cast_diretion` is an immediate describing whether
operation is `cast_kern` (1) or `cast_user` (2);
- piggy-back `BPFCheckAndAdjustIR` pass to insert the above intrinsic
calls for load and store instructions;
- modify `BPFInstrInfo.td` and `BPFIselLowering.cpp` to allow
translation of new intrinsic:
- define and SDNode type `BPFAddrSpace` to represent new intrinsic:
- override `BPFTargetLowering::CollectTargetIntrinsicOperands()`
method to add pointer address space as a parameter of intrinsic
SDNode;
- define `BPFTargetLowering::LowerINTRINSIC_WO_CHAIN()` called
from `BPFTargetLowering::LowerOperation()` to lower intrinsic
call to an SDNode;
- define new instructions: `ADDR_SPACE_K`, `ADDR_SPACE_U`;
- define patterns to lower `BPFAddrSpace` as `ADDR_SPACE_{KU}`.
---
llvm/include/llvm/IR/IntrinsicsBPF.td | 5 +
llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp | 88 ++++++++++
llvm/lib/Target/BPF/BPFISelLowering.cpp | 32 ++++
llvm/lib/Target/BPF/BPFISelLowering.h | 6 +-
llvm/lib/Target/BPF/BPFInstrInfo.td | 20 +++
llvm/test/CodeGen/BPF/addr-space-builtin.ll | 153 ++++++++++++++++++
llvm/test/CodeGen/BPF/addr-space-gep-chain.ll | 34 ++++
llvm/test/CodeGen/BPF/addr-space-insn.ll | 15 ++
llvm/test/CodeGen/BPF/addr-space-ku-chain.ll | 56 +++++++
.../BPF/addr-space-ku-for-same-base.ll | 61 +++++++
llvm/test/CodeGen/BPF/addr-space-phi.ll | 68 ++++++++
11 files changed, 537 insertions(+), 1 deletion(-)
create mode 100644 llvm/test/CodeGen/BPF/addr-space-builtin.ll
create mode 100644 llvm/test/CodeGen/BPF/addr-space-gep-chain.ll
create mode 100644 llvm/test/CodeGen/BPF/addr-space-insn.ll
create mode 100644 llvm/test/CodeGen/BPF/addr-space-ku-chain.ll
create mode 100644 llvm/test/CodeGen/BPF/addr-space-ku-for-same-base.ll
create mode 100644 llvm/test/CodeGen/BPF/addr-space-phi.ll
diff --git a/llvm/include/llvm/IR/IntrinsicsBPF.td b/llvm/include/llvm/IR/IntrinsicsBPF.td
index c7ec0916f1d1f8f..b1d00dd94e147f9 100644
--- a/llvm/include/llvm/IR/IntrinsicsBPF.td
+++ b/llvm/include/llvm/IR/IntrinsicsBPF.td
@@ -76,4 +76,9 @@ let TargetPrefix = "bpf" in { // All intrinsics start with "llvm.bpf."
ImmArg <ArgIndex<5>>, // alignment
ImmArg <ArgIndex<6>>, // inbounds
]>;
+ def int_bpf_addr_space : ClangBuiltin<"__builtin_bpf_addr_space">,
+ Intrinsic<[llvm_anyptr_ty], [llvm_anyptr_ty, llvm_i32_ty],
+ [IntrSpeculatable, IntrNoMem,
+ NoCapture <ArgIndex<0>>,
+ ImmArg <ArgIndex<1>>]>;
}
diff --git a/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp b/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp
index 81effc9b1db46cb..d350d4f81657949 100644
--- a/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp
+++ b/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp
@@ -55,6 +55,7 @@ class BPFCheckAndAdjustIR final : public ModulePass {
bool removeCompareBuiltin(Module &M);
bool sinkMinMax(Module &M);
bool removeGEPBuiltins(Module &M);
+ bool insertASpaceBuiltins(Module &M);
};
} // End anonymous namespace
@@ -416,11 +417,98 @@ bool BPFCheckAndAdjustIR::removeGEPBuiltins(Module &M) {
return Changed;
}
+static Instruction *aspaceWrapValue(DenseMap<Value *, Instruction *> &Cache,
+ Function *F,
+ Value *ToWrap,
+ unsigned Code) {
+ 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, Code);
+ auto *NewGEP = GEP->clone();
+ NewGEP->insertAfter(GEP);
+ NewGEP->setOperand(GEP->getPointerOperandIndex(), WrappedPtr);
+ NewGEP->setName(GEP->getName());
+ Cache[ToWrap] = NewGEP;
+ return NewGEP;
+ }
+
+ Module *M = F->getParent();
+ IRBuilder IB(F->getContext());
+ if (Instruction *InsnPtr = dyn_cast<Instruction>(ToWrap))
+ IB.SetInsertPoint(*InsnPtr->getInsertionPointAfterDef());
+ else
+ IB.SetInsertPoint(F->getEntryBlock().getFirstInsertionPt());
+ Type *PtrTy = ToWrap->getType();
+ Function *ASpaceFn =
+ Intrinsic::getDeclaration(M, Intrinsic::bpf_addr_space, {PtrTy, PtrTy});
+ auto *Call = IB.CreateCall(ASpaceFn, {ToWrap, IB.getInt32(Code)}, ToWrap->getName());
+ Cache[ToWrap] = Call;
+ return Call;
+}
+
+// Wrap operand with a call to bpf.addr.space() builtin
+static void aspaceWrapOperand(DenseMap<Value *, Instruction *> &Cache,
+ Instruction *I, unsigned OpNum, unsigned Code) {
+ Value *OldOp = I->getOperand(OpNum);
+ if (OldOp->getType()->getPointerAddressSpace() == 0)
+ return;
+
+ Value *NewOp = aspaceWrapValue(Cache, I->getFunction(), OldOp, Code);
+ I->setOperand(OpNum, NewOp);
+ for (;;) {
+ auto *OldGEP = dyn_cast<GetElementPtrInst>(OldOp);
+ if (!OldGEP)
+ break;
+ if (!OldGEP->use_empty())
+ break;
+ OldOp = OldGEP->getPointerOperand();
+ OldGEP->eraseFromParent();
+ }
+}
+
+enum {
+ ASPACE_TO_KERNEL = 1,
+ ASPACE_TO_USER = 2,
+};
+
+bool BPFCheckAndAdjustIR::insertASpaceBuiltins(Module &M) {
+ bool Changed = false;
+ for (Function &F : M) {
+ DenseMap<Value *, Instruction *> ToKernelCache;
+ DenseMap<Value *, Instruction *> ToUserCache;
+ for (BasicBlock &BB : F) {
+ for (Instruction &I : BB) {
+ if (auto *LD = dyn_cast<LoadInst>(&I)) {
+ aspaceWrapOperand(ToKernelCache,
+ LD, LD->getPointerOperandIndex(), ASPACE_TO_KERNEL);
+ continue;
+ }
+ if (auto *ST = dyn_cast<StoreInst>(&I)) {
+ aspaceWrapOperand(ToKernelCache,
+ ST, ST->getPointerOperandIndex(), ASPACE_TO_KERNEL);
+ Value *VO = ST->getValueOperand();
+ PointerType *VOTy = dyn_cast<PointerType>(VO->getType());
+ if (VOTy && VOTy->getAddressSpace() != 0)
+ aspaceWrapOperand(ToUserCache, ST, 0, ASPACE_TO_USER);
+ continue;
+ }
+ }
+ }
+ Changed |= !ToKernelCache.empty() || !ToUserCache.empty();
+ }
+ return Changed;
+}
+
bool BPFCheckAndAdjustIR::adjustIR(Module &M) {
bool Changed = removePassThroughBuiltin(M);
Changed = removeCompareBuiltin(M) || Changed;
Changed = sinkMinMax(M) || Changed;
Changed = removeGEPBuiltins(M) || Changed;
+ Changed = insertASpaceBuiltins(M) || Changed;
return Changed;
}
diff --git a/llvm/lib/Target/BPF/BPFISelLowering.cpp b/llvm/lib/Target/BPF/BPFISelLowering.cpp
index 4d8ace7c1ece02a..40a0fa7d542d847 100644
--- a/llvm/lib/Target/BPF/BPFISelLowering.cpp
+++ b/llvm/lib/Target/BPF/BPFISelLowering.cpp
@@ -24,6 +24,7 @@
#include "llvm/CodeGen/ValueTypes.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/IntrinsicsBPF.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
@@ -137,6 +138,8 @@ BPFTargetLowering::BPFTargetLowering(const TargetMachine &TM,
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i32, Expand);
}
+ setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom);
+
// Extended load operations for i1 types must be promoted
for (MVT VT : MVT::integer_valuetypes()) {
setLoadExtAction(ISD::EXTLOAD, VT, MVT::i1, Promote);
@@ -315,6 +318,8 @@ SDValue BPFTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
return LowerSDIVSREM(Op, DAG);
case ISD::DYNAMIC_STACKALLOC:
return LowerDYNAMIC_STACKALLOC(Op, DAG);
+ case ISD::INTRINSIC_WO_CHAIN:
+ return LowerINTRINSIC_WO_CHAIN(Op, DAG);
}
}
@@ -638,6 +643,31 @@ SDValue BPFTargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op,
return DAG.getMergeValues(Ops, SDLoc());
}
+void BPFTargetLowering::CollectTargetIntrinsicOperands(
+ const CallInst &I, SmallVectorImpl<SDValue> &Ops, SelectionDAG &DAG) const {
+ Function *Func = I.getCalledFunction();
+ if (!Func)
+ return;
+ if (Func->getIntrinsicID() == Intrinsic::bpf_addr_space) {
+ unsigned ASpace = I.getOperand(0)->getType()->getPointerAddressSpace();
+ Ops.push_back(DAG.getTargetConstant(ASpace, SDLoc(), MVT::i64));
+ }
+ return;
+}
+
+SDValue BPFTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op,
+ SelectionDAG &DAG) const {
+ SDLoc DL(Op);
+ unsigned IntNo = Op.getConstantOperandVal(0);
+ if (IntNo == Intrinsic::bpf_addr_space) {
+ SDValue Ptr = Op.getOperand(1);
+ SDValue Code = Op.getOperand(2);
+ SDValue ASpace = Op.getOperand(3);
+ return DAG.getNode(BPFISD::ADDR_SPACE, DL, Op.getValueType(), Ptr, Code, ASpace);
+ }
+ return SDValue();
+}
+
SDValue BPFTargetLowering::LowerBR_CC(SDValue Op, SelectionDAG &DAG) const {
SDValue Chain = Op.getOperand(0);
ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(1))->get();
@@ -687,6 +717,8 @@ const char *BPFTargetLowering::getTargetNodeName(unsigned Opcode) const {
return "BPFISD::Wrapper";
case BPFISD::MEMCPY:
return "BPFISD::MEMCPY";
+ case BPFISD::ADDR_SPACE:
+ return "BPFISD::ADDR_SPACE";
}
return nullptr;
}
diff --git a/llvm/lib/Target/BPF/BPFISelLowering.h b/llvm/lib/Target/BPF/BPFISelLowering.h
index 819711b650c15f3..f84d8a4e390f8be 100644
--- a/llvm/lib/Target/BPF/BPFISelLowering.h
+++ b/llvm/lib/Target/BPF/BPFISelLowering.h
@@ -28,7 +28,8 @@ enum NodeType : unsigned {
SELECT_CC,
BR_CC,
Wrapper,
- MEMCPY
+ MEMCPY,
+ ADDR_SPACE,
};
}
@@ -65,6 +66,8 @@ class BPFTargetLowering : public TargetLowering {
EVT VT) const override;
MVT getScalarShiftAmountTy(const DataLayout &, EVT) const override;
+ void CollectTargetIntrinsicOperands(
+ const CallInst &I, SmallVectorImpl<SDValue> &Ops, SelectionDAG &DAG) const override;
private:
// Control Instruction Selection Features
@@ -75,6 +78,7 @@ class BPFTargetLowering : public TargetLowering {
SDValue LowerSDIVSREM(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerDYNAMIC_STACKALLOC(SDValue Op, SelectionDAG &DAG) const;
+ SDValue LowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
diff --git a/llvm/lib/Target/BPF/BPFInstrInfo.td b/llvm/lib/Target/BPF/BPFInstrInfo.td
index 7d443a34490146a..b44ae35dd33f19a 100644
--- a/llvm/lib/Target/BPF/BPFInstrInfo.td
+++ b/llvm/lib/Target/BPF/BPFInstrInfo.td
@@ -31,6 +31,9 @@ def SDT_BPFMEMCPY : SDTypeProfile<0, 4, [SDTCisVT<0, i64>,
SDTCisVT<1, i64>,
SDTCisVT<2, i64>,
SDTCisVT<3, i64>]>;
+def SDT_BPFAddrSpace : SDTypeProfile<0, 3, [SDTCisPtrTy<0>,
+ SDTCisVT<1, i64>,
+ SDTCisVT<2, i64>]>;
def BPFcall : SDNode<"BPFISD::CALL", SDT_BPFCall,
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue,
@@ -49,6 +52,7 @@ def BPFWrapper : SDNode<"BPFISD::Wrapper", SDT_BPFWrapper>;
def BPFmemcpy : SDNode<"BPFISD::MEMCPY", SDT_BPFMEMCPY,
[SDNPHasChain, SDNPInGlue, SDNPOutGlue,
SDNPMayStore, SDNPMayLoad]>;
+def BPFAddrSpace : SDNode<"BPFISD::ADDR_SPACE", SDT_BPFAddrSpace>;
def BPFIsLittleEndian : Predicate<"Subtarget->isLittleEndian()">;
def BPFIsBigEndian : Predicate<"!Subtarget->isLittleEndian()">;
def BPFHasALU32 : Predicate<"Subtarget->getHasAlu32()">;
@@ -418,8 +422,24 @@ let Predicates = [BPFHasMovsx] in {
"$dst = (s16)$src",
[(set GPR32:$dst, (sext_inreg GPR32:$src, i16))]>;
}
+
+class ADDR_SPACE<int Code, string AsmPattern>
+ : ALU_RR<BPF_ALU64, BPF_MOV, 64,
+ (outs GPR:$dst),
+ (ins GPR:$src, i64imm:$imm),
+ AsmPattern,
+ []> {
+ bits<64> imm;
+ let Inst{47-32} = Code;
+ let Inst{31-0} = imm{31-0};
+}
+def ADDR_SPACE_K : ADDR_SPACE<1, "$dst = cast_kern($src, $imm)">;
+def ADDR_SPACE_U : ADDR_SPACE<2, "$dst = cast_user($src, $imm)">;
}
+def : Pat<(BPFAddrSpace GPR:$src, 1, i64:$as), (ADDR_SPACE_K $src, $as)>;
+def : Pat<(BPFAddrSpace GPR:$src, 2, i64:$as), (ADDR_SPACE_U $src, $as)>;
+
def FI_ri
: TYPE_LD_ST<BPF_IMM.Value, BPF_DW.Value,
(outs GPR:$dst),
diff --git a/llvm/test/CodeGen/BPF/addr-space-builtin.ll b/llvm/test/CodeGen/BPF/addr-space-builtin.ll
new file mode 100644
index 000000000000000..145696feb56a594
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/addr-space-builtin.ll
@@ -0,0 +1,153 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt --bpf-check-and-opt-ir -S -mtriple=bpf-pc-linux < %s | FileCheck %s
+
+; Generated from the following C code:
+
+; #define __uptr __attribute__((address_space(272)))
+;
+; void simple_store(void __uptr *foo) {
+; *((volatile int __uptr *)(foo + 16)) = 0xdead;
+; *((volatile int __uptr *)(foo + 12)) = 0xbeef;
+; }
+;
+; void separate_addr_store(void __uptr *foo, void __uptr *bar) {
+; *((volatile int __uptr *)(foo + 16)) = 0xdead;
+; *((volatile int __uptr *)(bar + 12)) = 0xbeef;
+; }
+;
+; void ptr_store(void __uptr *foo, void __uptr *bar) {
+; *((volatile void __uptr * __uptr*)(foo + 16)) = bar + 16;
+; *((volatile void __uptr * __uptr*)(foo + 8)) = bar + 8;
+; }
+;
+; void separate_ptr_store(void __uptr *foo, void __uptr *bar, void __uptr *buz) {
+; *((volatile void __uptr * __uptr*)(foo + 16)) = bar;
+; *((volatile void __uptr * __uptr*)(foo + 8)) = buz;
+; }
+;
+; int simple_load(int __uptr *foo) {
+; return *foo;
+; }
+;
+; Using the following command:
+;
+; clang --target=bpf -O2 -S -emit-llvm -o t.ll t.c
+
+; Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite)
+define dso_local void @simple_store(ptr addrspace(272) noundef %foo) local_unnamed_addr #0 {
+; CHECK-LABEL: define dso_local void @simple_store(
+; CHECK-SAME: ptr addrspace(272) noundef [[FOO:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = call ptr addrspace(272) @llvm.bpf.addr.space.p272.p272(ptr addrspace(272) [[FOO]], i32 1)
+; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr addrspace(272) [[TMP0]], i64 16
+; CHECK-NEXT: store volatile i32 57005, ptr addrspace(272) [[ADD_PTR]], align 4, !tbaa [[TBAA3:![0-9]+]]
+; CHECK-NEXT: [[ADD_PTR1:%.*]] = getelementptr inbounds i8, ptr addrspace(272) [[TMP0]], i64 12
+; CHECK-NEXT: store volatile i32 48879, ptr addrspace(272) [[ADD_PTR1]], align 4, !tbaa [[TBAA3]]
+; CHECK-NEXT: ret void
+;
+entry:
+ %add.ptr = getelementptr inbounds i8, ptr addrspace(272) %foo, i64 16
+ store volatile i32 57005, ptr addrspace(272) %add.ptr, align 4, !tbaa !3
+ %add.ptr1 = getelementptr inbounds i8, ptr addrspace(272) %foo, i64 12
+ store volatile i32 48879, ptr addrspace(272) %add.ptr1, align 4, !tbaa !3
+ ret void
+}
+
+; Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite)
+define dso_local void @separate_addr_store(ptr addrspace(272) noundef %foo, ptr addrspace(272) noundef %bar) local_unnamed_addr #0 {
+; CHECK-LABEL: define dso_local void @separate_addr_store(
+; CHECK-SAME: ptr addrspace(272) noundef [[FOO:%.*]], ptr addrspace(272) noundef [[BAR:%.*]]) local_unnamed_addr #[[ATTR0]] {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = call ptr addrspace(272) @llvm.bpf.addr.space.p272.p272(ptr addrspace(272) [[BAR]], i32 1)
+; CHECK-NEXT: [[TMP1:%.*]] = call ptr addrspace(272) @llvm.bpf.addr.space.p272.p272(ptr addrspace(272) [[FOO]], i32 1)
+; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr addrspace(272) [[TMP1]], i64 16
+; CHECK-NEXT: store volatile i32 57005, ptr addrspace(272) [[ADD_PTR]], align 4, !tbaa [[TBAA3]]
+; CHECK-NEXT: [[ADD_PTR1:%.*]] = getelementptr inbounds i8, ptr addrspace(272) [[TMP0]], i64 12
+; CHECK-NEXT: store volatile i32 48879, ptr addrspace(272) [[ADD_PTR1]], align 4, !tbaa [[TBAA3]]
+; CHECK-NEXT: ret void
+;
+entry:
+ %add.ptr = getelementptr inbounds i8, ptr addrspace(272) %foo, i64 16
+ store volatile i32 57005, ptr addrspace(272) %add.ptr, align 4, !tbaa !3
+ %add.ptr1 = getelementptr inbounds i8, ptr addrspace(272) %bar, i64 12
+ store volatile i32 48879, ptr addrspace(272) %add.ptr1, align 4, !tbaa !3
+ ret void
+}
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+define dso_local void @ptr_store(ptr addrspace(272) nocapture noundef writeonly %foo, ptr addrspace(272) noundef %bar) local_unnamed_addr #1 {
+; CHECK-LABEL: define dso_local void @ptr_store(
+; CHECK-SAME: ptr addrspace(272) nocapture noundef writeonly [[FOO:%.*]], ptr addrspace(272) noundef [[BAR:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = call ptr addrspace(272) @llvm.bpf.addr.space.p272.p272(ptr addrspace(272) [[BAR]], i32 2)
+; CHECK-NEXT: [[TMP1:%.*]] = call ptr addrspace(272) @llvm.bpf.addr.space.p272.p272(ptr addrspace(272) [[FOO]], i32 1)
+; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr addrspace(272) [[TMP0]], i64 16
+; CHECK-NEXT: [[ADD_PTR1:%.*]] = getelementptr inbounds i8, ptr addrspace(272) [[TMP1]], i64 16
+; CHECK-NEXT: store ptr addrspace(272) [[ADD_PTR]], ptr addrspace(272) [[ADD_PTR1]], align 8, !tbaa [[TBAA7:![0-9]+]]
+; CHECK-NEXT: [[ADD_PTR2:%.*]] = getelementptr inbounds i8, ptr addrspace(272) [[TMP0]], i64 8
+; CHECK-NEXT: [[ADD_PTR3:%.*]] = getelementptr inbounds i8, ptr addrspace(272) [[TMP1]], i64 8
+; CHECK-NEXT: store ptr addrspace(272) [[ADD_PTR2]], ptr addrspace(272) [[ADD_PTR3]], align 8, !tbaa [[TBAA7]]
+; CHECK-NEXT: ret void
+;
+entry:
+ %add.ptr = getelementptr inbounds i8, ptr addrspace(272) %bar, i64 16
+ %add.ptr1 = getelementptr inbounds i8, ptr addrspace(272) %foo, i64 16
+ store ptr addrspace(272) %add.ptr, ptr addrspace(272) %add.ptr1, align 8, !tbaa !7
+ %add.ptr2 = getelementptr inbounds i8, ptr addrspace(272) %bar, i64 8
+ %add.ptr3 = getelementptr inbounds i8, ptr addrspace(272) %foo, i64 8
+ store ptr addrspace(272) %add.ptr2, ptr addrspace(272) %add.ptr3, align 8, !tbaa !7
+ ret void
+}
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+define dso_local void @separate_ptr_store(ptr addrspace(272) nocapture noundef writeonly %foo, ptr addrspace(272) noundef %bar, ptr addrspace(272) noundef %buz) local_unnamed_addr #1 {
+; CHECK-LABEL: define dso_local void @separate_ptr_store(
+; CHECK-SAME: ptr addrspace(272) nocapture noundef writeonly [[FOO:%.*]], ptr addrspace(272) noundef [[BAR:%.*]], ptr addrspace(272) noundef [[BUZ:%.*]]) local_unnamed_addr #[[ATTR1]] {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = call ptr addrspace(272) @llvm.bpf.addr.space.p272.p272(ptr addrspace(272) [[BUZ]], i32 2)
+; CHECK-NEXT: [[TMP1:%.*]] = call ptr addrspace(272) @llvm.bpf.addr.space.p272.p272(ptr addrspace(272) [[BAR]], i32 2)
+; CHECK-NEXT: [[TMP2:%.*]] = call ptr addrspace(272) @llvm.bpf.addr.space.p272.p272(ptr addrspace(272) [[FOO]], i32 1)
+; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr addrspace(272) [[TMP2]], i64 16
+; CHECK-NEXT: store ptr addrspace(272) [[TMP1]], ptr addrspace(272) [[ADD_PTR]], align 8, !tbaa [[TBAA7]]
+; CHECK-NEXT: [[ADD_PTR1:%.*]] = getelementptr inbounds i8, ptr addrspace(272) [[TMP2]], i64 8
+; CHECK-NEXT: store ptr addrspace(272) [[TMP0]], ptr addrspace(272) [[ADD_PTR1]], align 8, !tbaa [[TBAA7]]
+; CHECK-NEXT: ret void
+;
+entry:
+ %add.ptr = getelementptr inbounds i8, ptr addrspace(272) %foo, i64 16
+ store ptr addrspace(272) %bar, ptr addrspace(272) %add.ptr, align 8, !tbaa !7
+ %add.ptr1 = getelementptr inbounds i8, ptr addrspace(272) %foo, i64 8
+ store ptr addrspace(272) %buz, ptr addrspace(272) %add.ptr1, align 8, !tbaa !7
+ ret void
+}
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
+define dso_local i32 @simple_load(ptr addrspace(272) nocapture noundef readonly %foo) local_unnamed_addr #2 {
+; CHECK-LABEL: define dso_local i32 @simple_load(
+; CHECK-SAME: ptr addrspace(272) nocapture noundef readonly [[FOO:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = call ptr addrspace(272) @llvm.bpf.addr.space.p272.p272(ptr addrspace(272) [[FOO]], i32 1)
+; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(272) [[TMP0]], align 4, !tbaa [[TBAA3]]
+; CHECK-NEXT: ret i32 [[TMP1]]
+;
+entry:
+ %0 = load i32, ptr addrspace(272) %foo, align 4, !tbaa !3
+ ret i32 %0
+}
+
+attributes #0 = { nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #1 = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #2 = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 7, !"frame-pointer", i32 2}
+!2 = !{!"some clang version"}
+!3 = !{!4, !4, i64 0}
+!4 = !{!"int", !5, i64 0}
+!5 = !{!"omnipotent char", !6, i64 0}
+!6 = !{!"Simple C/C++ TBAA"}
+!7 = !{!8, !8, i64 0}
+!8 = !{!"any pointer", !5, i64 0}
diff --git a/llvm/test/CodeGen/BPF/addr-space-gep-chain.ll b/llvm/test/CodeGen/BPF/addr-space-gep-chain.ll
new file mode 100644
index 000000000000000..6811d177900ff0e
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/addr-space-gep-chain.ll
@@ -0,0 +1,34 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt --bpf-check-and-opt-ir -S -mtriple=bpf-pc-linux < %s | FileCheck %s
+
+define dso_local void @test(ptr addrspace(1) noundef %p) local_unnamed_addr #0 {
+; CHECK-LABEL: define dso_local void @test(
+; CHECK-SAME: ptr addrspace(1) noundef [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[P1:%.*]] = call ptr addrspace(1) @llvm.bpf.addr.space.p1.p1(ptr addrspace(1) [[P]], i32 1)
+; CHECK-NEXT: [[A2:%.*]] = getelementptr inbounds i8, ptr addrspace(1) [[P1]], i64 8
+; CHECK-NEXT: [[B3:%.*]] = getelementptr inbounds i8, ptr addrspace(1) [[A2]], i64 16
+; CHECK-NEXT: [[C4:%.*]] = getelementptr inbounds i8, ptr addrspace(1) [[B3]], i64 24
+; CHECK-NEXT: [[D5:%.*]] = getelementptr inbounds i8, ptr addrspace(1) [[C4]], i64 32
+; CHECK-NEXT: store i64 11, ptr addrspace(1) [[C4]], align 8
+; CHECK-NEXT: store i64 22, ptr addrspace(1) [[D5]], align 8
+; CHECK-NEXT: ret void
+;
+entry:
+ %a = getelementptr inbounds i8, ptr addrspace(1) %p, i64 8
+ %b = getelementptr inbounds i8, ptr addrspace(1) %a, i64 16
+ %c = getelementptr inbounds i8, ptr addrspace(1) %b, i64 24
+ %d = getelementptr inbounds i8, ptr addrspace(1) %c, i64 32
+ store i64 11, ptr addrspace(1) %c, align 8
+ store i64 22, ptr addrspace(1) %d, align 8
+ ret void
+}
+
+attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 7, !"frame-pointer", i32 2}
+!2 = !{!"some clan version"}
diff --git a/llvm/test/CodeGen/BPF/addr-space-insn.ll b/llvm/test/CodeGen/BPF/addr-space-insn.ll
new file mode 100644
index 000000000000000..cbf68e0f019b91d
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/addr-space-insn.ll
@@ -0,0 +1,15 @@
+; RUN: llc -march=bpfel -mcpu=v4 -filetype=asm -show-mc-encoding < %s | FileCheck %s
+
+define dso_local void @test_fn(ptr addrspace(272) noundef %a, ptr addrspace(272) noundef %b) {
+; CHECK-LABEL: test_fn:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: r2 = cast_kern(r2, 272) # encoding: [0xbf,0x22,0x01,0x00,0x10,0x01,0x00,0x00]
+; CHECK-NEXT: r1 = cast_user(r1, 272) # encoding: [0xbf,0x11,0x02,0x00,0x10,0x01,0x00,0x00]
+; CHECK-NEXT: *(u64 *)(r2 + 0) = r1
+; CHECK-NEXT: exit
+entry:
+ store volatile ptr addrspace(272) %a, ptr addrspace(272) %b, align 8
+ ret void
+}
+
+declare ptr addrspace(272) @llvm.bpf.addr.space.p272.p272(ptr addrspace(272) nocapture, i32 immarg)
diff --git a/llvm/test/CodeGen/BPF/addr-space-ku-chain.ll b/llvm/test/CodeGen/BPF/addr-space-ku-chain.ll
new file mode 100644
index 000000000000000..33aad5cce8c8e78
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/addr-space-ku-chain.ll
@@ -0,0 +1,56 @@
+; RUN: opt --bpf-check-and-opt-ir -S -mtriple=bpf-pc-linux < %s | FileCheck %s
+
+; Generated from the following C code:
+;
+; #define __uptr __attribute__((address_space(272)))
+;
+; void test(void __uptr *q, void __uptr *p) {
+; void __uptr * __uptr *a;
+; void __uptr * __uptr *b;
+;
+; a = q + 8;
+; *a = p;
+; b = p + 16;
+; *b = a;
+; }
+;
+; Using the following command:
+;
+; clang --target=bpf -O2 -S -emit-llvm -o t.ll t.c
+
+; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
+define dso_local void @test(ptr addrspace(1) noundef %q, ptr addrspace(1) noundef %p) local_unnamed_addr #0 {
+; CHECK-LABEL: define dso_local void @test
+; CHECK-SAME: (ptr addrspace(1) noundef [[Q:%.*]], ptr addrspace(1) noundef [[P:%.*]])
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[QU:%.*]] = call ptr addrspace(1) @llvm.bpf.addr.space.p1.p1(ptr addrspace(1) [[Q]], i32 2)
+; CHECK-NEXT: [[PK:%.*]] = call ptr addrspace(1) @llvm.bpf.addr.space.p1.p1(ptr addrspace(1) [[P]], i32 1)
+; CHECK-NEXT: [[PU:%.*]] = call ptr addrspace(1) @llvm.bpf.addr.space.p1.p1(ptr addrspace(1) [[P]], i32 2)
+; CHECK-NEXT: [[QK:%.*]] = call ptr addrspace(1) @llvm.bpf.addr.space.p1.p1(ptr addrspace(1) [[Q]], i32 1)
+; CHECK-NEXT: [[AU:%.*]] = getelementptr inbounds i8, ptr addrspace(1) [[QU]], i64 8
+; CHECK-NEXT: [[AK:%.*]] = getelementptr inbounds i8, ptr addrspace(1) [[QK]], i64 8
+; CHECK-NEXT: store ptr addrspace(1) [[PU]], ptr addrspace(1) [[AK]], align 8
+; CHECK-NEXT: [[BK:%.*]] = getelementptr inbounds i8, ptr addrspace(1) [[PK]], i64 16
+; CHECK-NEXT: store ptr addrspace(1) [[AU]], ptr addrspace(1) [[BK]], align 8
+; CHECK-NEXT: ret void
+;
+entry:
+ %add.ptr = getelementptr inbounds i8, ptr addrspace(1) %q, i64 8
+ store ptr addrspace(1) %p, ptr addrspace(1) %add.ptr, align 8, !tbaa !3
+ %add.ptr1 = getelementptr inbounds i8, ptr addrspace(1) %p, i64 16
+ store ptr addrspace(1) %add.ptr, ptr addrspace(1) %add.ptr1, align 8, !tbaa !3
+ ret void
+}
+
+attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 7, !"frame-pointer", i32 2}
+!2 = !{!"some clan version"}
+!3 = !{!4, !4, i64 0}
+!4 = !{!"any pointer", !5, i64 0}
+!5 = !{!"omnipotent char", !6, i64 0}
+!6 = !{!"Simple C/C++ TBAA"}
diff --git a/llvm/test/CodeGen/BPF/addr-space-ku-for-same-base.ll b/llvm/test/CodeGen/BPF/addr-space-ku-for-same-base.ll
new file mode 100644
index 000000000000000..3ce7fc55b1bf9e8
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/addr-space-ku-for-same-base.ll
@@ -0,0 +1,61 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt --bpf-check-and-opt-ir -S -mtriple=bpf-pc-linux < %s | FileCheck %s
+
+; Generated from the following C code:
+;
+; #define __uptr __attribute__((address_space(272)))
+;
+; struct htab;
+; void __uptr *htab_for_user;
+; extern void __uptr* bpf_alloc(void);
+; void test(void) {
+; long __uptr* p = bpf_alloc();
+; p[2] = 2;
+; htab_for_user = &p[2];
+; }
+;
+; Using the following command:
+;
+; clang --target=bpf -O2 -S -emit-llvm -o t.ll t.c
+
+ at htab_for_user = dso_local local_unnamed_addr global ptr addrspace(1) null, align 8
+
+; Function Attrs: nounwind
+define dso_local void @test() local_unnamed_addr #0 {
+; CHECK-LABEL: define dso_local void @test
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CALL:%.*]] = tail call ptr addrspace(1) @bpf_alloc()
+; CHECK-NEXT: [[TMP0:%.*]] = call ptr addrspace(1) @llvm.bpf.addr.space.p1.p1(ptr addrspace(1) [[CALL]], i32 2)
+; CHECK-NEXT: [[TMP1:%.*]] = call ptr addrspace(1) @llvm.bpf.addr.space.p1.p1(ptr addrspace(1) [[CALL]], i32 1)
+; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i64, ptr addrspace(1) [[TMP0]], i64 2
+; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i64, ptr addrspace(1) [[TMP1]], i64 2
+; CHECK-NEXT: store i64 2, ptr addrspace(1) [[TMP3]], align 8
+; CHECK-NEXT: store ptr addrspace(1) [[TMP2]], ptr @htab_for_user, align 8
+; CHECK-NEXT: ret void
+;
+entry:
+ %call = tail call ptr addrspace(1) @bpf_alloc() #2
+ %arrayidx = getelementptr inbounds i64, ptr addrspace(1) %call, i64 2
+ store i64 2, ptr addrspace(1) %arrayidx, align 8, !tbaa !3
+ store ptr addrspace(1) %arrayidx, ptr @htab_for_user, align 8, !tbaa !7
+ ret void
+}
+
+declare dso_local ptr addrspace(1) @bpf_alloc() local_unnamed_addr #1
+
+attributes #0 = { nounwind "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #1 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #2 = { nounwind }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 7, !"frame-pointer", i32 2}
+!2 = !{!"some clang version"}
+!3 = !{!4, !4, i64 0}
+!4 = !{!"long", !5, i64 0}
+!5 = !{!"omnipotent char", !6, i64 0}
+!6 = !{!"Simple C/C++ TBAA"}
+!7 = !{!8, !8, i64 0}
+!8 = !{!"any pointer", !5, i64 0}
diff --git a/llvm/test/CodeGen/BPF/addr-space-phi.ll b/llvm/test/CodeGen/BPF/addr-space-phi.ll
new file mode 100644
index 000000000000000..f61428af4f5ff87
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/addr-space-phi.ll
@@ -0,0 +1,68 @@
+; RUN: opt --bpf-check-and-opt-ir -S -mtriple=bpf-pc-linux < %s | FileCheck %s
+
+; Generated from the following C code:
+;
+; extern int __uptr *magic1();
+; extern int __uptr *magic2();
+;
+; void test(long i) {
+; int __uptr *a;
+;
+; if (i > 42)
+; a = magic1();
+; else
+; a = magic2();
+; a[5] = 7;
+; }
+;
+; Using the following command:
+;
+; clang --target=bpf -O2 -S -emit-llvm -o t.ll t.c
+
+; Function Attrs: nounwind
+define dso_local void @test(i64 noundef %i) local_unnamed_addr #0 {
+; CHECK-NOT: @llvm.bpf.addr.space
+; CHECK: if.end:
+; CHECK-NEXT: [[A_0:%.*]] = phi
+; CHECK-NEXT: [[TMP0:%.*]] = call ptr addrspace(1) @llvm.bpf.addr.space.p1.p1(ptr addrspace(1) [[A_0]], i32 1)
+; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr addrspace(1) [[TMP0]], i64 5
+; CHECK-NEXT: store i32 7, ptr addrspace(1) [[TMP1]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %cmp = icmp sgt i64 %i, 42
+ br i1 %cmp, label %if.then, label %if.else
+
+if.then: ; preds = %entry
+ %call = tail call ptr addrspace(1) @magic1() #2
+ br label %if.end
+
+if.else: ; preds = %entry
+ %call1 = tail call ptr addrspace(1) @magic2() #2
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ %a.0 = phi ptr addrspace(1) [ %call, %if.then ], [ %call1, %if.else ]
+ %arrayidx = getelementptr inbounds i32, ptr addrspace(1) %a.0, i64 5
+ store i32 7, ptr addrspace(1) %arrayidx, align 4, !tbaa !3
+ ret void
+}
+
+declare dso_local ptr addrspace(1) @magic1(...) local_unnamed_addr #1
+
+declare dso_local ptr addrspace(1) @magic2(...) local_unnamed_addr #1
+
+attributes #0 = { nounwind "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #1 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #2 = { nounwind }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 7, !"frame-pointer", i32 2}
+!2 = !{!"some clang version"}
+!3 = !{!4, !4, i64 0}
+!4 = !{!"int", !5, i64 0}
+!5 = !{!"omnipotent char", !6, i64 0}
+!6 = !{!"Simple C/C++ TBAA"}
More information about the llvm-commits
mailing list