diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -3715,6 +3715,41 @@ return eltType; } +// Returns true if D is marked with __attribute__((btf_decl_tag("ctx"))) +static bool hasCtxBTFDeclTagAttr(const Decl *D) { + if (auto *Attr = D->getAttr()) + return Attr->getBTFDeclTag().equals("ctx"); + + return false; +} + +static bool pointeeHasCtxBTFDeclTagAttr(const Expr *E) { + if (auto *PtrType = dyn_cast(E->getType())) + if (auto *BaseDecl = PtrType->getPointeeType()->getAsRecordDecl()) + return hasCtxBTFDeclTagAttr(BaseDecl); + + return false; +} + +// Returns true if declaration is marked +// with __attribute__((preserve_access_index)) +// but NOT with __attribute__((btf_decl_tag("ctx"))) +static bool hasBPFPreserveAccessIndexAttr(const Decl *D) { + return D->hasAttr() && !hasCtxBTFDeclTagAttr(D); +} + +// Wraps Addr with a call to llvm.bpf.context.marker intrinsic. +// This is used for target specific rewrites, for details see +// llvm/lib/Target/BPF/BPFContextMarker.cpp +static Address wrapWithBPFContextMarker(CodeGenFunction &CGF, + Address &Addr) { + llvm::Function *Fn = + CGF.CGM.getIntrinsic(llvm::Intrinsic::bpf_context_marker, + {Addr.getType(), Addr.getType()}); + llvm::CallInst *Call = CGF.Builder.CreateCall(Fn, {Addr.getPointer()}); + return Address(Call, Addr.getElementType(), Addr.getAlignment()); +} + /// Given an array base, check whether its member access belongs to a record /// with preserve_access_index attribute or not. static bool IsPreserveAIArrayBase(CodeGenFunction &CGF, const Expr *ArrayBase) { @@ -3730,7 +3765,7 @@ // p->b[5] is a MemberExpr example. const Expr *E = ArrayBase->IgnoreImpCasts(); if (const auto *ME = dyn_cast(E)) - return ME->getMemberDecl()->hasAttr(); + return hasBPFPreserveAccessIndexAttr(ME->getMemberDecl()); if (const auto *DRE = dyn_cast(E)) { const auto *VarDef = dyn_cast(DRE->getDecl()); @@ -3744,7 +3779,7 @@ const auto *PointeeT = PtrT->getPointeeType() ->getUnqualifiedDesugaredType(); if (const auto *RecT = dyn_cast(PointeeT)) - return RecT->getDecl()->hasAttr(); + return hasBPFPreserveAccessIndexAttr(RecT->getDecl()); return false; } @@ -3776,6 +3811,9 @@ CharUnits eltAlign = getArrayElementAlign(addr.getAlignment(), indices.back(), eltSize); + if (Base && pointeeHasCtxBTFDeclTagAttr(Base)) + addr = wrapWithBPFContextMarker(CGF, addr); + llvm::Value *eltPtr; auto LastIndex = dyn_cast(indices.back()); if (!LastIndex || @@ -4386,9 +4424,11 @@ Address Addr = base.getAddress(*this); unsigned Idx = RL.getLLVMFieldNo(field); const RecordDecl *rec = field->getParent(); + if (hasCtxBTFDeclTagAttr(rec)) + Addr = wrapWithBPFContextMarker(*this, Addr); if (!UseVolatile) { if (!IsInPreservedAIRegion && - (!getDebugInfo() || !rec->hasAttr())) { + (!getDebugInfo() || !hasBPFPreserveAccessIndexAttr(rec))) { if (Idx != 0) // For structs, we GEP to the field that the record layout suggests. Addr = Builder.CreateStructGEP(Addr, Idx, field->getName()); @@ -4459,6 +4499,8 @@ } Address addr = base.getAddress(*this); + if (hasCtxBTFDeclTagAttr(rec)) + addr = wrapWithBPFContextMarker(*this, addr); if (auto *ClassDef = dyn_cast(rec)) { if (CGM.getCodeGenOpts().StrictVTablePointers && ClassDef->isDynamicClass()) { @@ -4481,7 +4523,7 @@ addr = Builder.CreateLaunderInvariantGroup(addr); if (IsInPreservedAIRegion || - (getDebugInfo() && rec->hasAttr())) { + (getDebugInfo() && hasBPFPreserveAccessIndexAttr(rec))) { // Remember the original union field index llvm::DIType *DbgInfo = getDebugInfo()->getOrCreateStandaloneType(base.getType(), rec->getLocation()); @@ -4496,7 +4538,7 @@ addr, CGM.getTypes().ConvertTypeForMem(FieldType), field->getName()); } else { if (!IsInPreservedAIRegion && - (!getDebugInfo() || !rec->hasAttr())) + (!getDebugInfo() || !hasBPFPreserveAccessIndexAttr(rec))) // For structs, we GEP to the field that the record layout suggests. addr = emitAddrOfFieldStorage(*this, addr, field); else diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7290,14 +7290,31 @@ return false; } +static bool hasCtxBTFDeclTagAttr(Decl *D) { + return hasBTFDeclTagAttr(D, "ctx"); +} + +static void handleCtxBTFDeclTagAttr(Sema &S, RecordDecl *RD) { + // Add btf_decl_tag("ctx") attribute to all fields and inner records. + for (auto *D : RD->decls()) { + if (hasCtxBTFDeclTagAttr(D) || D->getDeclContext() != RD) + continue; + + D->addAttr(BTFDeclTagAttr::CreateImplicit(S.Context, "ctx")); + if (auto *NestedRD = dyn_cast(D)) + handleCtxBTFDeclTagAttr(S, NestedRD); + } +} + static void handleBTFDeclTagAttr(Sema &S, Decl *D, const ParsedAttr &AL) { StringRef Str; if (!S.checkStringLiteralArgumentAttr(AL, 0, Str)) return; if (hasBTFDeclTagAttr(D, Str)) return; - D->addAttr(::new (S.Context) BTFDeclTagAttr(S.Context, AL, Str)); + if (Str == "ctx" && isa(D)) + handleCtxBTFDeclTagAttr(S, cast(D)); } BTFDeclTagAttr *Sema::mergeBTFDeclTagAttr(Decl *D, const BTFDeclTagAttr &AL) { diff --git a/clang/test/CodeGen/bpf-decl-tag-ctx-2.c b/clang/test/CodeGen/bpf-decl-tag-ctx-2.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/bpf-decl-tag-ctx-2.c @@ -0,0 +1,41 @@ +// Check that offsets for non-structural GEP chains could be folded. +// RUN: %clang -target bpf -O2 -S -emit-llvm -o - %s | \ +// RUN: FileCheck %s + +struct foo { + int a; + int b[10]; +}; + +struct bpf_sock_ops { + int op; + union { + int args[4]; + struct foo y[5]; + }; +} __attribute__((btf_decl_tag("ctx"))); + +extern void consume_int(int); +extern void consume_char(char); + +// CHECK-LABEL: @foofn +void foofn(struct bpf_sock_ops *ctx, int i) { + consume_int(ctx->args[2]); + consume_char(((char*)&ctx->args)[2]); + consume_char(((char*)&ctx->y[2].b[7])[1<<2]); +} + +// CHECK: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(i8) %ctx, i1 false, i8 0, i8 1, i8 2, i1 false, i64 12) +// CHECK-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// CHECK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i8 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) %ctx, i1 false, i8 0, i8 1, i8 1, i1 false, i64 6) +// CHECK-NEXT: tail call void @consume_char(i8 noundef signext [[r0]]) + +// CHECK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i8 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) %ctx, i1 false, i8 0, i8 1, i8 2, i1 false, i64 128) +// CHECK-NEXT: tail call void @consume_char(i8 noundef signext [[r0]]) diff --git a/clang/test/CodeGen/bpf-decl-tag-ctx-diagnostic.c b/clang/test/CodeGen/bpf-decl-tag-ctx-diagnostic.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/bpf-decl-tag-ctx-diagnostic.c @@ -0,0 +1,16 @@ +// RUN: %clang -target bpf -O2 -S -emit-llvm -o /dev/null %s 2>&1 | \ +// RUN: FileCheck %s + +#define __ctx__ __attribute__((btf_decl_tag("ctx"))) + +struct context { + int x[10]; +} __attribute__((btf_decl_tag("ctx"))); + +extern void consume_int(int); + +void foo(struct context *ctx, int i) { +// CHECK: warning: Non-constant offset in access to a field +// CHECK-SAME: of a type marked with btf_decl_tag("ctx") + consume_int(ctx->x[i]); +} diff --git a/clang/test/CodeGen/bpf-decl-tag-ctx-gvn.c b/clang/test/CodeGen/bpf-decl-tag-ctx-gvn.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/bpf-decl-tag-ctx-gvn.c @@ -0,0 +1,23 @@ +// Check that btf_decl_tag("ctx") does not clobber CSE analysis. +// +// RUN: %clang -target bpf -O3 -S -emit-llvm -o - %s | \ +// RUN: FileCheck %s + +struct foo { + int _; + int x; +} __attribute__((btf_decl_tag("ctx"))); + +extern void consume_int(int); + +void foofn(struct foo *ctx) { + void *p; + if (ctx->x) + p = &ctx->x; + else + p = &ctx->x + 1; + if (ctx->x) + consume_int((void*)&ctx->x - p); +} +// CHECK-LABEL: @foofn +// CHECK: @consume_int(i32 noundef 0) diff --git a/clang/test/CodeGen/bpf-decl-tag-ctx-loop-inline-1.c b/clang/test/CodeGen/bpf-decl-tag-ctx-loop-inline-1.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/bpf-decl-tag-ctx-loop-inline-1.c @@ -0,0 +1,48 @@ +// Check that loop unrolling works in combination with +// btf_decl_tag("ctx") and function inlining. +// +// RUN: %clang -target bpf -O2 -S -emit-llvm -o - %s | \ +// RUN: FileCheck %s + +struct buz { + int _; + int x; +} __attribute__((btf_decl_tag("ctx"))); + +struct bar { + int _; + struct buz buz; +} __attribute__((btf_decl_tag("ctx"))); + +struct foo { + int _; + struct bar bar[4]; +} __attribute__((btf_decl_tag("ctx"))); + +extern void consume_int(int); + +static inline void barfn(struct bar * restrict bar) __attribute__((always_inline)) { + consume_int(bar->buz.x); +} + +void foofn1(struct foo * restrict foo) { +#pragma clang loop unroll(full) + for (int i = 0; i < 2; ++i) { + barfn(&foo->bar[i]); + } +} + +// CHECK-LABEL: define {{.*}} @foofn1 + +// TODO: this GEP should be merged with GEP and load +// CHECK: [[arrayidx:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.foo, ptr %foo, i64 0, i32 1, i64 0 +// CHECK: [[x1_i:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr nonnull elementtype(%struct.bar) [[arrayidx]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 1) +// CHECK: tail call void @consume_int(i32 noundef [[x1_i]]) + +// CHECK: [[arrayidx_1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.foo, ptr %foo, i64 0, i32 1, i64 1 +// CHECK: [[x1_i_1:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr nonnull elementtype(%struct.bar) [[arrayidx_1]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 1) +// CHECK: tail call void @consume_int(i32 noundef [[x1_i_1]]) diff --git a/clang/test/CodeGen/bpf-decl-tag-ctx-loop-inline-2.c b/clang/test/CodeGen/bpf-decl-tag-ctx-loop-inline-2.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/bpf-decl-tag-ctx-loop-inline-2.c @@ -0,0 +1,62 @@ +// Check that CSE is done after loop unrolling in combination with +// btf_decl_tag("ctx"). +// +// RUN: %clang -target bpf -O2 -S -emit-llvm -o - %s | \ +// RUN: FileCheck %s + +struct __sk_buff { + unsigned len; + unsigned cb[5]; +} __attribute__((btf_decl_tag("ctx"))); + +int check_cse_inside_unrolled_loop(struct __sk_buff * skb) +{ +#pragma clang loop unroll(full) + for (int i = 0; i < 3; i++) { + if (skb->cb[i] == 1) // Verify that skb->cb[i] read here + return 1; + skb->cb[i]++; // is reused here as increment base + } + return 0; +} + +// CHECK-LABEL: define {{.*}} @check_cse_inside_unrolled_loop + +// CHECK: [[arrayidx14:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.__sk_buff) %skb, i1 false, i8 0, i8 1, i8 2, i1 true, i64 0, i32 1, i64 0) +// CHECK-NEXT: icmp eq i32 [[arrayidx14]], 1 +// CHECK-NEXT: br + +// CHECK: {{.*}}: +// CHECK-NEXT: [[inc:%[a-zA-Z0-9._]+]] = add i32 [[arrayidx14]], 1 +// CHECK-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.store.i32 +// CHECK-SAME: (i32 [[inc]], ptr elementtype(%struct.__sk_buff) %skb, i1 false, i8 0, i8 1, i8 2, i1 true, i64 0, i32 1, i64 0) +// CHECK-NEXT: [[arrayidx_116:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.__sk_buff) %skb, i1 false, i8 0, i8 1, i8 2, i1 true, i64 0, i32 1, i64 1) +// CHECK-NEXT: icmp eq i32 [[arrayidx_116]], 1 +// CHECK-NEXT: br + +// CHECK: {{.*}}: +// CHECK-NEXT: [[inc_1:%[a-zA-Z0-9._]+]] = add i32 [[arrayidx_116]], 1 +// CHECK-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.store.i32 +// CHECK-SAME: (i32 [[inc_1]], ptr elementtype(%struct.__sk_buff) %skb, i1 false, i8 0, i8 1, i8 2, i1 true, i64 0, i32 1, i64 1) +// CHECK-NEXT: [[arrayidx_218:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.__sk_buff) %skb, i1 false, i8 0, i8 1, i8 2, i1 true, i64 0, i32 1, i64 2) +// CHECK-NEXT: icmp eq i32 [[arrayidx_218]], 1 +// CHECK-NEXT: br + +// CHECK: {{.*}}: +// CHECK-NEXT: [[inc_2:%[a-zA-Z0-9._]+]] = add i32 [[arrayidx_218]], 1 +// CHECK-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.store.i32 +// CHECK-SAME: (i32 [[inc_2]], ptr elementtype(%struct.__sk_buff) %skb, i1 false, i8 0, i8 1, i8 2, i1 true, i64 0, i32 1, i64 2) +// CHECK-NEXT: br + +// CHECK: {{.*}}: +// CHECK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = phi +// CHECK-NEXT: ret i32 [[r0]] diff --git a/clang/test/CodeGen/bpf-decl-tag-ctx-loop-inline-3.c b/clang/test/CodeGen/bpf-decl-tag-ctx-loop-inline-3.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/bpf-decl-tag-ctx-loop-inline-3.c @@ -0,0 +1,33 @@ +// Check that CSE is done after loop unrolling in combination with +// btf_decl_tag("ctx") and function inlining. +// +// RUN: %clang -target bpf -O2 -S -emit-llvm -o - %s | \ +// RUN: FileCheck %s + +struct foo { + int _; + int x; + int y; +} __attribute__((btf_decl_tag("ctx"))); + +extern void consume_int(int); + +static inline void barfn(struct foo * restrict foo) { + consume_int(foo->x); +} + +void foofn(struct foo * restrict foo) { + consume_int(foo->x); +#pragma clang loop unroll(full) + for (int i = 0; i < 2; ++i) + barfn(foo); + consume_int(foo->x); +} + +// CHECK: [[x:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.foo) %foo, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// CHECK-NEXT: tail call void @consume_int(i32 noundef [[x]]) +// CHECK-NEXT: tail call void @consume_int(i32 noundef [[x]]) +// CHECK-NEXT: tail call void @consume_int(i32 noundef [[x]]) +// CHECK-NEXT: tail call void @consume_int(i32 noundef [[x]]) diff --git a/clang/test/CodeGen/bpf-decl-tag-ctx-loops.c b/clang/test/CodeGen/bpf-decl-tag-ctx-loops.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/bpf-decl-tag-ctx-loops.c @@ -0,0 +1,155 @@ +// Check that loop unrolling works in combination with +// btf_decl_tag("ctx"). Important because btf_decl_tag("ctx") only +// allows static offsets inside marked structs. +// +// RUN: %clang -target bpf -O3 -S -emit-llvm -o - %s | \ +// RUN: FileCheck %s + +#define __ctx__ __attribute__((btf_decl_tag("ctx"))) + +struct bpf_sock { + int bound_dev_if; + int family; +} __ctx__; + +struct bpf_sockopt { + int _; + struct bpf_sock *sk; + int level; + int optlen; +} __ctx__; + +extern int magic(void); +extern int magic2(char); + +void foofn(struct bpf_sockopt *ctx) { +#pragma clang loop unroll(full) + for (int i = 0; i < 4; ++i) + magic2(((char *)&ctx->level)[i]); +} + +// CHECK-LABEL: @foofn + +// CHECK: [[r0:%[a-zA-Z0-9._]+]] = tail call i8 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(%struct.bpf_sockopt) %ctx, i1 false, i8 0, i8 1, i8 0, i1 true, i64 0, i32 2) +// CHECK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = tail call i32 @magic2(i8 noundef signext [[r0]]) + +// CHECK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i8 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) %ctx, i1 false, i8 0, i8 1, i8 0, i1 true, i64 17) +// CHECK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = tail call i32 @magic2(i8 noundef signext [[r0]]) + +// CHECK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i8 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) %ctx, i1 false, i8 0, i8 1, i8 0, i1 true, i64 18) +// CHECK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = tail call i32 @magic2(i8 noundef signext [[r0]]) + +// CHECK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i8 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) %ctx, i1 false, i8 0, i8 1, i8 0, i1 true, i64 19) +// CHECK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = tail call i32 @magic2(i8 noundef signext [[r0]]) + + +int known_load_sink_example_1(struct bpf_sockopt *ctx) +{ + unsigned g = 0; +#pragma clang loop unroll(full) + for (int i = 0; i < 4; ++i) { + switch (ctx->level) { + case 10: + g = magic2(((char*)&ctx->sk->family)[i]); + break; + case 20: + g = magic2(((char*)&ctx->optlen)[i]); + break; + } + } + return g % 2; +} + +// CHECK-LABEL: @known_load_sink_example_1 + +// First loop iteration + +// CHECK: [[level13:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.bpf_sockopt) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// CHECK-NEXT: switch i32 [[level13]], label {{.*}} [ +// CHECK-NEXT: i32 10, label +// CHECK-NEXT: i32 20, label +// CHECK-NEXT: ] + +// CHECK: {{.*}}: +// CHECK-NEXT: [[sk14:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.p0 +// CHECK-SAME: (ptr elementtype(%struct.bpf_sockopt) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 1) +// CHECK-NEXT: [[family15:%[a-zA-Z0-9._]+]] = tail call i8 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(%struct.bpf_sock) [[sk14]], i1 false, i8 0, i8 1, i8 0, i1 true, i64 0, i32 1) +// CHECK-NEXT: br label + +// CHECK: {{.*}}: +// CHECK-NEXT: [[optlen16:%[a-zA-Z0-9._]+]] = tail call i8 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(%struct.bpf_sockopt) %ctx, i1 false, i8 0, i8 1, i8 0, i1 true, i64 0, i32 3) +// CHECK-NEXT: br label + +// CHECK: {{.*}}: +// CHECK-NEXT: [[family15_sink:%[a-zA-Z0-9._]+]] = phi +// CHECK-NEXT: [[call:%[a-zA-Z0-9._]+]] = tail call i32 @magic2(i8 noundef signext [[family15_sink]]) +// CHECK-NEXT: br label + +// CHECK: {{.*}}: +// CHECK-NEXT: [[g_1:%[a-zA-Z0-9._]+]] = phi +// CHECK-NEXT: [[level_117:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.bpf_sockopt) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) + + +struct __sk_buff { + int _; + int priority; + int mark; + int tc_index; +} __ctx__; + +int known_store_sink_example_1(struct __sk_buff *ctx) { +#pragma clang loop unroll(full) + for (int i = 0; i < 4; ++i) { + switch (ctx->priority) { + case 10: + ((char*)&ctx->mark)[i] = 3; + break; + case 20: + ((char*)&ctx->priority)[i] = 4; + break; + } + } + return 0; +} + +// CHECK-LABEL: @known_store_sink_example_1 + +// This is the last loop iteration + +// CHECK: for.inc.2: +// CHECK-NEXT: [[priority_315:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.__sk_buff) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// CHECK-NEXT: switch i32 [[priority_315]], label {{.*}} [ +// CHECK-NEXT: i32 10, label +// CHECK-NEXT: i32 20, label +// CHECK-NEXT: ] + +// CHECK: {{.*}}: +// CHECK-NEXT: tail call void (i8, ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.store.i8 +// CHECK-SAME: (i8 4, ptr elementtype(i8) %ctx, i1 false, i8 0, i8 1, i8 0, i1 true, i64 7) +// CHECK-NEXT: br label + +// CHECK: {{.*}}: +// CHECK-NEXT: tail call void (i8, ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.store.i8 +// CHECK-SAME: (i8 3, ptr elementtype(i8) %ctx, i1 false, i8 0, i8 1, i8 0, i1 true, i64 11) +// CHECK-NEXT: br label diff --git a/clang/test/CodeGen/bpf-decl-tag-ctx-o0-gep-merge.c b/clang/test/CodeGen/bpf-decl-tag-ctx-o0-gep-merge.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/bpf-decl-tag-ctx-o0-gep-merge.c @@ -0,0 +1,268 @@ +// When -O0 optimization level is enabled BPFRewriteContextAccessPass +// handles GEP merging itself, check that it is done correctly. + +// RUN: %clang -target bpf -O0 -S -emit-llvm -o - %s | \ +// RUN: FileCheck %s + +#define __ctx__ __attribute__((btf_decl_tag("ctx"))) + +extern void ci(int); +extern void cc(int); + +struct context1 { + int first; + int int_var; + char char_var; +} __ctx__; + +void testfn1(struct context1 *ctx) { + ci(ctx->first); + ci(ctx->int_var); + cc(ctx->char_var); + + cc(((char*)&ctx->first)[1]); + cc(((char*)&ctx->int_var)[1]); + cc(((char*)&ctx->char_var)[1]); +} + +// CHECK-LABEL: @testfn1 + +// CHECK: getelementptr inbounds %struct.context1, ptr {{.*}}, i32 0, i32 0 +// CHECK-NEXT: load i32 + +// CHECK: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.context1) {{.*}} i1 true, i32 0, i32 1) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(%struct.context1) {{.*}} i1 true, i32 0, i32 2) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 1) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 5) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 9) + +struct context2 { + int _; + int int_arr[10]; +} __ctx__; + +void testfn2(struct context2 *ctx) { + ci(ctx->int_arr[2]); + ci(ctx->int_arr[3]); + + cc(((char*)&ctx->int_arr[2])[1]); + cc(((char*)&ctx->int_arr[3])[1]); +} + +// CHECK-LABEL: @testfn2 + +// CHECK: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.context2) {{.*}} i1 true, i32 0, i32 1, i64 2) + +// CHECK: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.context2) {{.*}} i1 true, i32 0, i32 1, i64 3) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 13) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 17) + + +struct context3 { + int _; + int int_arr[10][5]; +} __ctx__; + +void testfn3(struct context3 *ctx) { + ci(ctx->int_arr[2][3]); + cc(((char*)&ctx->int_arr[2][3])[1]); +} + +// CHECK-LABEL: @testfn3 + +// CHECK: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.context3) {{.*}} i1 true, i32 0, i32 1, i64 2, i64 3) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 57) + +struct inner4 { + int a; + int b; +} __ctx__; + +struct context4 { + int _; + struct inner4 arr[10]; +} __ctx__; + +void testfn4(struct context4 *ctx) { + ci(ctx->arr[5].a); + ci(ctx->arr[5].b); + + cc(((char*)&ctx->arr[5].a)[1]); + cc(((char*)&ctx->arr[5].b)[1]); +} + +// CHECK-LABEL: @testfn4 + +// CHECK: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.context4) {{.*}} i1 true, i32 0, i32 1, i64 5, i32 0) + +// CHECK: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.context4) {{.*}} i1 true, i32 0, i32 1, i64 5, i32 1) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 45) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 49) + +struct inner5 { + int a; + int b[5]; +} __ctx__; + +struct context5 { + int _; + struct inner5 arr[10]; +} __ctx__; + +void testfn5(struct context5 *ctx) { + ci(ctx->arr[5].a); + ci(ctx->arr[5].b[2]); + + cc(((char*)&ctx->arr[5].a)[1]); + cc(((char*)&ctx->arr[5].b[2])[1]); +} + +// CHECK-LABEL: @testfn5 + +// CHECK: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.context5) {{.*}} i1 true, i32 0, i32 1, i64 5, i32 0) + +// CHECK: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.context5) {{.*}} i1 true, i32 0, i32 1, i64 5, i32 1, i64 2) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 125) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 137) + +struct context6 { + int _; + union { + int a; + struct inner4 inner; + }; +} __ctx__; + +void testfn6(struct context6 *ctx) { + ci(ctx->a); + ci(ctx->inner.a); + ci(ctx->inner.b); + + cc(((char*)&ctx->a)[1]); + cc(((char*)&ctx->inner.a)[1]); + cc(((char*)&ctx->inner.b)[1]); +} + +// CHECK-LABEL: @testfn6 + +// CHECK: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.context6) {{.*}} i1 true, i32 0, i32 1) + +// CHECK: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 4) + +// CHECK: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 8) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 5) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 5) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 9) + +struct context7 { + int _; + struct { + int a; + struct inner4 inner; + }; +} __ctx__; + +void testfn7(struct context7 *ctx) { + ci(ctx->a); + ci(ctx->inner.a); + ci(ctx->inner.b); + + cc(((char*)&ctx->a)[1]); + cc(((char*)&ctx->inner.a)[1]); + cc(((char*)&ctx->inner.b)[1]); +} + +// CHECK-LABEL: @testfn7 + +// CHECK: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.context7) {{.*}} i1 true, i32 0, i32 1, i32 0) + +// CHECK: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.context7) {{.*}} i1 true, i32 0, i32 1, i32 1, i32 0) + +// CHECK: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.context7) {{.*}} i1 true, i32 0, i32 1, i32 1, i32 1) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 5) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 9) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 13) + +struct context8 { + int a; + int b __attribute__((aligned(128))); + int c; +} __ctx__; + +void testfn8(struct context8 *ctx) { + ci(ctx->a); + ci(ctx->b); + ci(ctx->c); + + cc(((char*)&ctx->a)[1]); + cc(((char*)&ctx->b)[1]); + cc(((char*)&ctx->c)[1]); +} + +// CHECK-LABEL: @testfn8 + +// CHECK: getelementptr inbounds %struct.context8, ptr {{.*}}, i32 0, i32 0 +// CHECK-NEXT: load i32 + +// CHECK: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.context8) {{.*}} i1 true, i32 0, i32 2) + +// CHECK: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.context8) {{.*}} i1 true, i32 0, i32 3) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 1) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 129) + +// CHECK: @llvm.bpf.getelementptr.and.load.i8 +// CHECK-SAME: (ptr elementtype(i8) {{.*}} i1 false, i64 133) diff --git a/clang/test/CodeGen/bpf-decl-tag-ctx-pai.c b/clang/test/CodeGen/bpf-decl-tag-ctx-pai.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/bpf-decl-tag-ctx-pai.c @@ -0,0 +1,469 @@ +// Check interaction between two attributes: +// __attribute__((btf_decl_tag("ctx"))) +// __attribute__((preserve_access_index)) +// +// First, check that calls to context.marker are inserted where expected: +// +// RUN: %clang -g -target bpf -Xclang -disable-llvm-passes -S -emit-llvm -o - %s | \ +// RUN: FileCheck -check-prefix=FRONT %s +// +// Next, check that gep.and.load/store calls were generated: +// +// RUN: %clang -g -target bpf -O2 -S -emit-llvm -o - %s | \ +// RUN: FileCheck -check-prefix=MID %s +// +// For gep.and.load/store calls lowering refer to test the case +// bpf-decl-tag-ctx.c + +#define __ctx__ __attribute__((btf_decl_tag("ctx"))) +#define __preserve_access_index__ __attribute__((preserve_access_index)) + +extern void consume_int(int); +extern void consume_ptr(void *); + +struct context1_inner2 { + int a; + int b; +} __preserve_access_index__; + +struct context1_inner { + int a; + int b; + int c[5]; + struct context1_inner2 *inner; + struct context1_inner2 inner_inline; +} __ctx__ __preserve_access_index__; + +struct context1 { + int first; + struct context1_inner *inner; + struct context1_inner inner_inline; + int arr[4]; + int *ptr; + int last; +} __ctx__ __preserve_access_index__; + +// FRONT-LABEL: define {{.*}} @preserve_access_index1 +// MID-LABEL: define {{.*}} @preserve_access_index1 +void preserve_access_index1(struct context1 *ctx) { + consume_int(ctx->first); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 0 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[r2]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r3]]) + +// MID: [[r0:%[a-zA-Z0-9._]+]] = load i32, ptr %ctx +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + + consume_int(ctx->arr[2]); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 3 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds [4 x i32], ptr [[r2]], i64 0, i64 2 +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load i32, ptr [[r3]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r4]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 3, i64 2) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + + consume_ptr(ctx->ptr); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 4 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: call void @consume_ptr(ptr noundef [[r3]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 4) +// MID-NEXT: tail call void @consume_ptr(ptr noundef [[r0]]) + + consume_int(ctx->last); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 5 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[r2]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r3]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 5) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + + consume_int(ctx->inner->b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r4]], i32 0, i32 1 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 1) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context1_inner) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->inner->c[3]); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r4]], i32 0, i32 2 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = getelementptr inbounds [5 x i32], ptr [[r5]], i64 0, i64 3 +// FRONT-NEXT: [[r7:%[a-zA-Z0-9._]+]] = load i32, ptr [[r6]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r7]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 1) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context1_inner) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2, i64 3) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->inner_inline.b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load i32, ptr [[r4]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r5]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + + consume_int(ctx->inner_inline.c[3]); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r3]], i32 0, i32 2 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds [5 x i32], ptr [[r4]], i64 0, i64 3 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2, i32 2, i64 3) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + + consume_int(ctx[17].arr[2]); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i64 17 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r3]], i32 0, i32 3 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds [4 x i32], ptr [[r4]], i64 0, i64 2 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i64 17, i32 3, i64 2) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + + consume_int(ctx->inner_inline.inner->a); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r3]], i32 0, i32 3 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r4]] +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = call ptr @llvm.preserve.struct.access.index.p0.p0(ptr elementtype(%struct.context1_inner2) [[r5]], i32 0, i32 0) +// FRONT-NEXT: [[r7:%[a-zA-Z0-9._]+]] = load i32, ptr [[r6]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r7]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2, i32 3) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i64, ptr @"llvm.context1_inner2:0:0$0:0" +// MID-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr i8, ptr [[r0]], i64 [[r1]] +// MID-NEXT: [[r3:%[a-zA-Z0-9._]+]] = tail call ptr @llvm.bpf.passthrough.p0.p0({{.*}}, ptr [[r2]]) +// MID-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load i32, ptr [[r3]] +// MID-NEXT: tail call void @consume_int(i32 noundef [[r4]]) + + consume_int(ctx->inner_inline.inner_inline.a); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r3]], i32 0, i32 4 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = call ptr @llvm.preserve.struct.access.index.p0.p0(ptr elementtype(%struct.context1_inner2) [[r4]], i32 0, i32 0) +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// Note: because @"llvm.context1_inner2:0:0$0:0" represents a dynamic offset in bytes +// there is no way to reduce it to bpf.llvm.getelementptr.and.load as it's indices +// represent logical offsets (array element number, struct field index). +// MID-NEXT: [[inner_inline:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i64 0, i32 2, i32 4 +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i64, ptr @"llvm.context1_inner2:0:0$0:0" +// MID-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr i8, ptr [[inner_inline]], i64 [[r1]] +// MID-NEXT: [[r3:%[a-zA-Z0-9._]+]] = tail call ptr @llvm.bpf.passthrough.p0.p0({{.*}}, ptr [[r2]]) +// MID-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load i32, ptr [[r3]] +// MID-NEXT: tail call void @consume_int(i32 noundef [[r4]]) + + ctx->first = 1; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 0 +// FRONT-NEXT: store i32 1, ptr [[r2]] + +// MID-NEXT: store i32 1, ptr %ctx + + ctx->arr[2] = 2; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 3 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds [4 x i32], ptr [[r2]], i64 0, i64 2 +// FRONT-NEXT: store i32 2, ptr [[r3]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 2, ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 3, i64 2) + + ctx->ptr = (void*) 0; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 4 +// FRONT-NEXT: store ptr null, ptr [[r2]] + +// MID-NEXT: tail call void (ptr, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.p0(ptr readnone null, ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 4) + + ctx->last = 3; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 5 +// FRONT-NEXT: store i32 3, ptr [[r2]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 3, ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 5) + + ctx->inner->b = 4; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r4]], i32 0, i32 1 +// FRONT-NEXT: store i32 4, ptr [[r5]] + +// MID-NEXT: [[inner_ptr:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 1) +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 4, ptr elementtype(%struct.context1_inner) [[inner_ptr]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) + + ctx->inner->c[3] = 5; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r4]], i32 0, i32 2 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = getelementptr inbounds [5 x i32], ptr [[r5]], i64 0, i64 3 +// FRONT-NEXT: store i32 5, ptr [[r6]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 5, ptr elementtype(%struct.context1_inner) [[inner_ptr]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2, i64 3) + + ctx->inner_inline.b = 6; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: store i32 6, ptr [[r4]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 6, ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2, i32 1) + + ctx->inner_inline.c[3] = 7; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r3]], i32 0, i32 2 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds [5 x i32], ptr [[r4]], i64 0, i64 3 +// FRONT-NEXT: store i32 7, ptr [[r5]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 7, ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2, i32 2, i64 3) + + ctx[19].arr[2] = 55; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i64 19 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r3]], i32 0, i32 3 +// Note: there is no marker call here, but this is fine: every array of a primitive +// type would have a base used as a member expression, thus a marker call always would be +// somewhere above. +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds [4 x i32], ptr [[r4]], i64 0, i64 2 +// FRONT-NEXT: store i32 55, ptr [[r5]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 55, ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i64 19, i32 3, i64 2) + + ctx->inner_inline.inner->a = 34; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r3]], i32 0, i32 3 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r4]] +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = call ptr @llvm.preserve.struct.access.index.p0.p0(ptr elementtype(%struct.context1_inner2) [[r5]], i32 0, i32 0) +// FRONT-NEXT: store i32 34, ptr [[r6]] + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2, i32 3) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i64, ptr @"llvm.context1_inner2:0:0$0:0" +// MID-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr i8, ptr [[r0]], i64 [[r1]] +// MID-NEXT: [[r3:%[a-zA-Z0-9._]+]] = tail call ptr @llvm.bpf.passthrough.p0.p0({{.*}}, ptr [[r2]]) +// MID-NEXT: store i32 34, ptr [[r3]] + + ctx->inner_inline.inner_inline.b = 76; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r3]], i32 0, i32 4 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = call ptr @llvm.preserve.struct.access.index.p0.p0(ptr elementtype(%struct.context1_inner2) [[r4]], i32 1, i32 1) +// FRONT-NEXT: store i32 76, ptr [[r5]] + +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i64, ptr @"llvm.context1_inner2:0:4$0:1" +// MID-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr i8, ptr [[inner_inline]], i64 [[r1]] +// MID-NEXT: [[r3:%[a-zA-Z0-9._]+]] = tail call ptr @llvm.bpf.passthrough.p0.p0({{.*}}, ptr [[r2]]) +// MID-NEXT: store i32 76, ptr [[r3]] + + consume_ptr(ctx); +} + +struct context2 { + int _; + struct { + int _; + int b[10]; + } anon_struct; + struct { + int _; + int b[10]; + } anon_struct_arr[7]; + struct { int _1; int injected_field; }; + union { int _2; int injected_union_field; }; +} __ctx__ __preserve_access_index__; + +// FRONT-LABEL: define {{.*}} @nested_structs +// MID-LABEL: define {{.*}} @nested_structs +void nested_structs(struct context2 *ctx) { + consume_int(ctx->anon_struct.b[8]); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.anon, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds [10 x i32], ptr [[r4]], i64 0, i64 8 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 1, i64 8) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + + consume_int(ctx->anon_struct_arr[5].b[7]); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds [7 x %struct.anon.0], ptr [[r3]], i64 0, i64 5 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r4]]) +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.anon.0, ptr [[r5]], i32 0, i32 1 +// FRONT-NEXT: [[r7:%[a-zA-Z0-9._]+]] = getelementptr inbounds [10 x i32], ptr [[r6]], i64 0, i64 7 +// FRONT-NEXT: [[r8:%[a-zA-Z0-9._]+]] = load i32, ptr [[r7]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r8]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2, i64 5, i32 1, i64 7) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + + consume_int(ctx->injected_field); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 3 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.anon.1, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load i32, ptr [[r4]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r5]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 3, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + + consume_int(ctx->injected_union_field); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 4 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load i32, ptr [[r3]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r4]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 4) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) +} + +extern int magic2(int); + +struct bpf_sock { + int bound_dev_if; + int family; +} __ctx__ __preserve_access_index__; + +struct bpf_sockopt { + int _; + struct bpf_sock *sk; + int level; + int optlen; +} __ctx__ __preserve_access_index__; + +// FRONT-LABEL: define {{.*}} @known_load_sink_example_1 +// MID-LABEL: define {{.*}} @known_load_sink_example_1 +int known_load_sink_example_1(struct bpf_sockopt *ctx) +{ + unsigned g = 0; + switch (ctx->level) { +// MID: [[level3:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.bpf_sockopt) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// MID-NEXT: switch i32 [[level3]], label {{.*}} [ +// MID-NEXT: i32 10, label +// MID-NEXT: i32 20, label +// MID-NEXT: ] + + case 10: + g = magic2(ctx->sk->family); + break; +// MID: {{.*}}: +// MID-NEXT: [[sk4:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.bpf_sockopt) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 1) +// MID-NEXT: [[family5:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.bpf_sock) [[sk4]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID: br label + + case 20: + g = magic2(ctx->optlen); + break; +// MID: {{.*}}: +// MID-NEXT: [[optlen6:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.bpf_sockopt) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 3) +// MID: br label + } +// MID: {{.*}}: +// MID-NEXT: [[optlen6_sink:%[a-zA-Z0-9._]+]] = phi i32 +// MID-NEXT: [[call2:%[a-zA-Z0-9._]+]] = tail call i32 @magic2(i32 noundef [[optlen6_sink]]) +// MID-NEXT: [[phi_bo:%[a-zA-Z0-9._]+]] = and i32 [[call2]], 1 +// MID-NEXT: br label + + return g % 2; +// MID: {{.*}}: +// MID-NEXT: [[g_0:%[a-zA-Z0-9._]+]] = phi i32 +// MID: ret i32 [[g_0]] +} diff --git a/clang/test/CodeGen/bpf-decl-tag-ctx-required.c b/clang/test/CodeGen/bpf-decl-tag-ctx-required.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/bpf-decl-tag-ctx-required.c @@ -0,0 +1,32 @@ +// Check that passes handling __attribute__((btf_decl_tag("ctx"))) are +// executed when -O0 is specified + +// RUN: %clang -target bpf -O0 -S -emit-llvm %s -o - | \ +// RUN: FileCheck %s + +#define __ctx__ __attribute__((btf_decl_tag("ctx"))) + +extern void consume_int(int); + +struct foo { + int a; + int b; +} __ctx__; + +struct bar { + int _; + struct foo foo; +} __ctx__; + +void testfn(struct bar *ctx) { + consume_int(ctx->foo.b); +// CHECK: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// CHECK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call i32 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.bar) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 1) +// CHECK-NEXT: call void @consume_int(i32 noundef [[r1]]) + + ctx->foo.b = 7; +// CHECK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// CHECK-NEXT: call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 7, ptr elementtype(%struct.bar) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 1) +} diff --git a/clang/test/CodeGen/bpf-decl-tag-ctx-tbaa.c b/clang/test/CodeGen/bpf-decl-tag-ctx-tbaa.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/bpf-decl-tag-ctx-tbaa.c @@ -0,0 +1,33 @@ +// Check that btf_decl_tag("ctx") does not clobber CSE analysis. +// For this particular test foo->x would not be reused if TBAA information +// is not preserved when gep.and.load call is done. +// +// RUN: %clang -target bpf -O2 -S -emit-llvm -o - %s | \ +// RUN: FileCheck %s + +struct foo { + int _; + int x; + int y; +} __attribute__((btf_decl_tag("ctx"))); + +extern void consume_int(int); + +void foofn(struct foo * restrict foo) { + consume_int(foo->x); + foo->y = 1; + consume_int(foo->x); +} + +// CHECK: [[x:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.load.i32 +// CHECK-SAME: (ptr elementtype(%struct.foo) %foo, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// CHECK-NEXT: tail call void @consume_int(i32 noundef [[x]]) +// CHECK-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// CHECK-SAME: @llvm.bpf.getelementptr.and.store.i32 +// CHECK-SAME: (i32 1, ptr elementtype(%struct.foo) %foo, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2) + +// Note that foo->x is not re-read here + +// CHECK-NEXT: tail call void @consume_int(i32 noundef [[x]]) +// CHECK-NEXT: ret void diff --git a/clang/test/CodeGen/bpf-decl-tag-ctx.c b/clang/test/CodeGen/bpf-decl-tag-ctx.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/bpf-decl-tag-ctx.c @@ -0,0 +1,1376 @@ +// This test covers various usages of __attribute__((btf_decl_tag("ctx"))). +// This attribute is handled in three stages: +// - context.marker intrinsic call is added while IR is generated from AST +// - context.marker call is replaced with calls to either: +// llvm.bpf.getelementptr.and.load intrinsic, or +// llvm.bpf.getelementptr.and.store intrinsic +// at the early IR processing stage +// - get and load/store intrinsics are lowered at the late IR processing stage +// +// The test verifies all three stages using three separate RUN commands. +// +// First, check that calls to context.marker intrinsic are inserted +// where expected: +// +// RUN: %clang -target bpf -Xclang -disable-llvm-passes -S -emit-llvm -o - %s | \ +// RUN: FileCheck -check-prefix=FRONT %s +// +// Next, check that gep_and_load/store calls were generated: +// +// RUN: %clang -target bpf -O2 -S -emit-llvm -o - %s | \ +// RUN: FileCheck -check-prefix=MID %s +// +// Finally, verify that gep_and_load/store calls were rewritten: +// +// RUN: %clang -mllvm --print-after=bpf-check-and-opt-ir \ +// RUN: -mllvm --stop-after=bpf-check-and-opt-ir \ +// RUN: -target bpf -S -O2 -o /dev/null 2>&1 %s | \ +// RUN: FileCheck -check-prefix=BACK %s + +#define __ctx__ __attribute__((btf_decl_tag("ctx"))) +#define __preserve_access_index__ __attribute__((preserve_access_index)) + +struct inner_no_vof { + int a; + int b; +}; + +struct inner_vof { + int a; + int b; +} __ctx__; + +struct context1 { + int first; + struct inner_no_vof inner_inline; + struct inner_no_vof *inner_ptr; + int butlast; + int last; +} __ctx__; + +struct context2 { + int first; + struct inner_vof inner_inline; + struct inner_vof *inner_ptr; + int butlast; + int last; +} __ctx__; + +extern void consume_int(int); +extern void consume_float(float); +extern void consume_ptr(void *); + +// FRONT-LABEL: define dso_local void @fields_access_1 +// MID-LABEL: define dso_local void @fields_access_1 +// BACK-LABEL: define dso_local void @fields_access_1 +void fields_access_1(struct context1 *ctx) { + consume_int(ctx->first); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 0 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[r2]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r3]]) + +// MID: [[r0:%[a-zA-Z0-9._]+]] = load i32, ptr %ctx +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr %ctx +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->inner_inline.a); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r2]], i32 0, i32 0 +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load i32, ptr [[r3]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r4]]) + +// MID-NEXT: [[a:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 0) +// MID-NEXT: tail call void @consume_int(i32 noundef [[a]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 1, i32 0 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->inner_inline.b); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r2]], i32 0, i32 1 +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load i32, ptr [[r3]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r4]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 1, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->inner_ptr->a); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r3]], i32 0, i32 0 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load i32, ptr [[r4]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r5]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// MID-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = load i32, ptr [[r1]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r2]]) + + consume_int(ctx->inner_ptr->b); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load i32, ptr [[r4]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r5]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r0]], i64 0, i32 1 +// MID-NEXT: [[r2:%[a-zA-Z0-9._]+]] = load i32, ptr [[r1]] +// MID-NEXT: tail call void @consume_int(i32 noundef [[r2]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r1]], i64 0, i32 1 +// BACK-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[r2]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r3]]) + + ctx->first = 1; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 0 +// FRONT-NEXT: store i32 1, ptr [[r2]] + +// MID-NEXT: store i32 1, ptr %ctx + +// BACK-NEXT: store i32 1, ptr %ctx + + ctx->inner_inline.a = 2; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r2]], i32 0 +// FRONT-NEXT: store i32 2, ptr [[r3]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 2, ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 0) + +// BACK-NEXT: [[r0:%[0-9]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 1 +// BACK-NEXT: store i32 2, ptr [[r0]] + + ctx->inner_inline.b = 3; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r2]], i32 0, i32 1 +// FRONT-NEXT: store i32 3, ptr [[r3]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 3, ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 1) + +// BACK-NEXT: [[r0:%[0-9]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 1, i32 1 +// BACK-NEXT: store i32 3, ptr [[r0]] + + ctx->inner_ptr->a = 4; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r3]], i32 0, i32 0 +// FRONT-NEXT: store i32 4, ptr [[r4]] + +// MID-NEXT: [[inner_ptr:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr nonnull elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// MID-NEXT: store i32 4, ptr [[inner_ptr]] + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[inner_ptr:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: store i32 4, ptr [[inner_ptr]] + + ctx->inner_ptr->b = 5; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r3]], i32 0, i32 1 + +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[inner_ptr]], i64 0, i32 1 +// MID-NEXT: store i32 5, ptr [[r1]] + +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[inner_ptr]], i64 0, i32 1 +// BACK-NEXT: store i32 5, ptr [[r2]] + + consume_ptr(ctx); +} + +// FRONT-LABEL: define dso_local void @fields_access_2 +// MID-LABEL: define dso_local void @fields_access_2 +// BACK-LABEL-LABEL: define dso_local void @fields_access_2 +void fields_access_2(struct context2 *ctx) { + consume_int(ctx->inner_inline.a); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r3]], i32 0, i32 0 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load i32, ptr [[r4]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r5]]) + +// MID: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 0) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i32 0, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->inner_inline.b); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load i32, ptr [[r4]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r5]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i32 0, i32 1, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->inner_ptr->a); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r4]], i32 0, i32 0 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// MID-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = load i32, ptr [[r1]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r2]]) + + consume_int(ctx->inner_ptr->b); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r4]], i32 0, i32 1 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.inner_vof) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r1]], i32 0, i32 1 +// BACK-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[r2]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r3]]) + + ctx->inner_inline.a = 2; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r3]], i32 0, i32 0 +// FRONT-NEXT: store i32 2, ptr [[r4]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 2, ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 0) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i32 0, i32 1 +// BACK-NEXT: store i32 2, ptr [[r0]] + + ctx->inner_inline.b = 3; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: store i32 3, ptr [[r4]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 3, ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i32 0, i32 1, i32 1 +// BACK-NEXT: store i32 3, ptr [[r0]] + + ctx->inner_ptr->a = 4; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r4]], i32 0, i32 0 +// FRONT-NEXT: store i32 4, ptr [[r5]] + +// MID-NEXT: [[inner_ptr:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// MID-NEXT: store i32 4, ptr [[inner_ptr]] + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[inner_ptr:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: store i32 4, ptr [[inner_ptr]] + + ctx->inner_ptr->b = 5; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r4]], i32 0, i32 1 +// FRONT-NEXT: store i32 5, ptr [[r5]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 5, ptr nonnull elementtype(%struct.inner_vof) [[inner_ptr]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) + +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[inner_ptr]], i32 0, i32 1 +// BACK-NEXT: store i32 5, ptr [[r2]] + + consume_ptr(ctx); +} + +// FRONT-LABEL: define dso_local void @pointer_access_not_affected_1 +// MID-LABEL: define dso_local void @pointer_access_not_affected_1 +// BACK-LABEL: define dso_local void @pointer_access_not_affected_1 +void pointer_access_not_affected_1(struct context1 *ctx) { + consume_ptr(&ctx->inner_inline); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: call void @consume_ptr(ptr noundef [[r2]]) + +// MID: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i64 0, i32 1 +// MID-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r0]]) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i64 0, i32 1 +// BACK-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r0]]) + + consume_ptr(&ctx->inner_inline.b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r2]], i32 0, i32 1 +// FRONT-NEXT: call void @consume_ptr(ptr noundef [[r3]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i64 0, i32 1, i32 1 +// MID-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i64 0, i32 1, i32 1 +// BACK-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r0]]) + + consume_ptr(&ctx->inner_ptr); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: call void @consume_ptr(ptr noundef [[r2]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i64 0, i32 2 +// MID-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i64 0, i32 2 +// BACK-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r0]]) + + consume_ptr(&ctx->inner_ptr->b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: call void @consume_ptr(ptr noundef [[r4]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r0]], i64 0, i32 1 +// MID-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r1]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r1]], i64 0, i32 1 +// BACK-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r2]]) + + consume_ptr(&ctx->last); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 4 + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i64 0, i32 4 +// MID-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i64 0, i32 4 +// BACK-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r0]]) +} + +// FRONT-LABEL: define dso_local void @pointer_access_not_affected_2 +// MID-LABEL: define dso_local void @pointer_access_not_affected_2 +// BACK-LABEL: define dso_local void @pointer_access_not_affected_2 +void pointer_access_not_affected_2(struct context2 *ctx) { + consume_ptr(&ctx->inner_inline); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: call void @consume_ptr(ptr noundef [[r2]]) + +// MID: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i64 0, i32 1 +// MID-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r0]]) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i64 0, i32 1 +// BACK-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r0]]) + + consume_ptr(&ctx->inner_inline.b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: call void @consume_ptr(ptr noundef [[r4]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i64 0, i32 1, i32 1 +// MID-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i64 0, i32 1, i32 1 +// BACK-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r0]]) + + consume_ptr(&ctx->inner_ptr); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: call void @consume_ptr(ptr noundef [[r2]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i64 0, i32 2 +// MID-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i64 0, i32 2 +// BACK-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r0]]) + + consume_ptr(&ctx->inner_ptr->b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r4]], i32 0, i32 1 + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r0]], i64 0, i32 1 +// MID-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r1]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r1]], i64 0, i32 1 +// BACK-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r2]]) + +// FRONT: ret void +// MID: ret void +// BACK: ret void +} + +struct context3 { + int first; + struct inner_no_vof *no_vof_ptr; + struct inner_no_vof no_vof_arr[5]; + struct inner_no_vof *no_vof_arr_ptr[7]; + struct inner_vof *vof_ptr; + struct inner_vof vof_arr[5]; + struct inner_vof *vof_arr_ptr[7]; + int arr[4]; +} __ctx__; + +// FRONT-LABEL: define dso_local void @array_access +// MID-LABEL: define dso_local void @array_access +// BACK-LABEL: define dso_local void @array_access +void array_access(struct context3 *ctx) { + consume_int(ctx->no_vof_ptr[1].b); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r3]], i64 1 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r4]], i32 0, i32 1 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 1) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r0]], i64 1, i32 1 +// MID-NEXT: [[r2:%[a-zA-Z0-9._]+]] = load i32, ptr [[r1]] +// MID-NEXT: tail call void @consume_int(i32 noundef [[r2]]) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r1]], i64 1, i32 1 +// BACK-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[r2]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r3]]) + + consume_int(ctx->no_vof_arr[2].b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds [5 x %struct.inner_no_vof], ptr [[r2]], i64 0, i64 2 +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load i32, ptr [[r4]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r5]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2, i64 2, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 2, i64 2, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->no_vof_arr_ptr[3]->a); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 3 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds [7 x ptr], ptr [[r2]], i64 0, i64 3 +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r3]] +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r4]], i32 0, i32 0 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 3, i64 3) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// MID-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 3, i64 3 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = load i32, ptr [[r1]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r2]]) + + consume_int(ctx->vof_ptr[1].b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 4 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r4]], i64 1 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r5]]) +// FRONT-NEXT: [[r7:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r6]], i32 0, i32 1 +// FRONT-NEXT: [[r8:%[a-zA-Z0-9._]+]] = load i32, ptr [[r7]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r8]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 4) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.inner_vof) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i64 1, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 4 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r1]], i64 1, i32 1 +// BACK-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[r2]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r3]]) + + consume_int(ctx->vof_arr[2].b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 5 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds [5 x %struct.inner_vof], ptr [[r3]], i64 0, i64 2 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r4]]) +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r5]], i32 0, i32 1 +// FRONT-NEXT: [[r7:%[a-zA-Z0-9._]+]] = load i32, ptr [[r6]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r7]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 5, i64 2, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 5, i64 2, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->vof_arr_ptr[3]->b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 6 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds [7 x ptr], ptr [[r2]], i64 0, i64 3 +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r3]] +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r4]]) +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r5]], i32 0, i32 1 +// FRONT-NEXT: [[r7:%[a-zA-Z0-9._]+]] = load i32, ptr [[r6]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r7]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 6, i64 3) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.inner_vof) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 6, i64 3 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r1]], i32 0, i32 1 +// BACK-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[r2]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r3]]) + + consume_int(ctx[12].arr[3]); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i64 12 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r3]], i32 0, i32 7 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds [4 x i32], ptr [[r4]], i64 0, i64 3 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i64 12, i32 7, i64 3) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i64 12, i32 7, i64 3 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + ctx->no_vof_ptr[1].b = 2; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r3]], i64 1 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r4]], i32 0, i32 1 +// FRONT-NEXT: store i32 2, ptr [[r5]] + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 1) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r0]], i64 1, i32 1 +// MID-NEXT: store i32 2, ptr [[r1]] + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r1]], i64 1, i32 1 +// BACK-NEXT: store i32 2, ptr [[r2]] + + ctx->no_vof_arr[2].b = 4; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds [5 x %struct.inner_no_vof], ptr [[r2]], i64 0, i64 2 +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: store i32 4, ptr [[r4]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 4, ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2, i64 2, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 2, i64 2, i32 1 +// BACK-NEXT: store i32 4, ptr [[r0]] + + ctx->no_vof_arr_ptr[3]->b = 6; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 3 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds [7 x ptr], ptr [[r2]], i64 0, i64 3 +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r3]] +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r4]], i32 0, i32 1 +// FRONT-NEXT: store i32 6, ptr [[r5]] + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 3, i64 3) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r0]], i64 0, i32 1 +// MID-NEXT: store i32 6, ptr [[r1]] + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 3, i64 3 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r1]], i64 0, i32 1 +// BACK-NEXT: store i32 6, ptr [[r2]] + + ctx->vof_ptr[1].b = 2; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 4 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r4]], i64 1 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r5]]) +// FRONT-NEXT: [[r7:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r6]], i32 0, i32 1 +// FRONT-NEXT: store i32 2, ptr [[r7]] + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 4) +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 2, ptr elementtype(%struct.inner_vof) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i64 1, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 4 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r1]], i64 1, i32 1 +// BACK-NEXT: store i32 2, ptr [[r2]] + + ctx->vof_arr[2].b = 4; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 5 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds [5 x %struct.inner_vof], ptr [[r3]], i64 0, i64 2 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r4]]) +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r5]], i32 0, i32 1 +// FRONT-NEXT: store i32 4, ptr [[r6]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 4, ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 5, i64 2, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 5, i64 2, i32 1 +// BACK-NEXT: store i32 4, ptr [[r0]] + + ctx->vof_arr_ptr[3]->b = 6; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 6 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds [7 x ptr], ptr [[r2]], i64 0, i64 3 +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r3]] +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r4]]) +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r5]], i32 0, i32 1 +// FRONT-NEXT: store i32 6, ptr [[r6]] + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 6, i64 3) +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 6, ptr elementtype(%struct.inner_vof) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 6, i64 3 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r1]], i32 0, i32 1 +// BACK-NEXT: store i32 6, ptr [[r2]] + + ctx[15].arr[3] = 22; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i64 15 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r3]], i32 0, i32 7 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds [4 x i32], ptr [[r4]], i64 0, i64 3 +// FRONT-NEXT: store i32 22, ptr [[r5]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 22, ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i64 15, i32 7, i64 3) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i64 15, i32 7, i64 3 +// BACK-NEXT: store i32 22, ptr [[r0]] + + consume_ptr(ctx); +} + +typedef int aligned_int __attribute__((aligned(128))); + +struct context4 { + int _; + volatile int a; + aligned_int b; + volatile aligned_int c; +} __ctx__; + +// FRONT-LABEL: define dso_local void @load_and_store_attrs +// MID-LABEL: define dso_local void @load_and_store_attrs +// BACK-LABEL: define dso_local void @load_and_store_attrs +void load_and_store_attrs(struct context4 *ctx) { + int r; + consume_int(ctx->a); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load volatile i32, ptr [[r2]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r3]]) + +// MID: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context4) %ctx, i1 true, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i32 0, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load volatile i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr [[r1]], i32 0, i32 3 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[r2]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r3]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context4) %ctx, i1 false, i8 0, i8 1, i8 7, i1 true, i32 0, i32 3) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i32 0, i32 3 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->c); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr [[r1]], i32 0, i32 5 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load volatile i32, ptr [[r2]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r3]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context4) %ctx, i1 true, i8 0, i8 1, i8 7, i1 true, i32 0, i32 5) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i32 0, i32 5 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load volatile i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + __atomic_load(&ctx->a, &r, 2); + consume_int(r); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load atomic volatile i32, ptr [[r2]] acquire +// FRONT: call void @consume_int + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context4) %ctx, i1 true, i8 4, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i32 0, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load atomic volatile i32, ptr [[r0]] acquire +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + ctx->a = 1; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: store volatile i32 1, ptr [[r2]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 1, ptr elementtype(%struct.context4) %ctx, i1 true, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i32 0, i32 1 +// BACK-NEXT: store volatile i32 1, ptr [[r0]] + + ctx->b = 2; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr [[r1]], i32 0, i32 3 +// FRONT-NEXT: store i32 2, ptr [[r2]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 2, ptr elementtype(%struct.context4) %ctx, i1 false, i8 0, i8 1, i8 7, i1 true, i32 0, i32 3) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i32 0, i32 3 +// BACK-NEXT: store i32 2, ptr [[r0]] + + ctx->c = 3; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr [[r1]], i32 0, i32 5 +// FRONT-NEXT: store volatile i32 3, ptr [[r2]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 3, ptr elementtype(%struct.context4) %ctx, i1 true, i8 0, i8 1, i8 7, i1 true, i32 0, i32 5) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i32 0, i32 5 +// BACK-NEXT: store volatile i32 3, ptr [[r0]] + + r = 7; + __atomic_store(&ctx->a, &r, 3); +// FRONT-NEXT: store i32 7, ptr %r +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr %r +// FRONT-NEXT: store atomic volatile i32 [[r3]], ptr [[r2]] release + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 7, ptr elementtype(%struct.context4) %ctx, i1 true, i8 5, i8 1, i8 2, i1 true, i32 0, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i32 0, i32 1 +// BACK-NEXT: store atomic volatile i32 7, ptr [[r0]] release +// BACK-NEXT: tail call void @consume_ptr(ptr noundef %ctx) + + consume_ptr(ctx); +} + +struct context5 { + int _; + union { + int a; + float b; + } u; +} __ctx__; + +// FRONT-LABEL: define dso_local void @union_access +// MID-LABEL: define dso_local void @union_access +// BACK-LABEL: define dso_local void @union_access +void union_access(struct context5 *ctx1, struct context5 *ctx2) { + consume_int(ctx1->u.a); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr %ctx1.addr +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context5, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load i32, ptr [[r3]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r4]]) + +// MID: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context5) %ctx1, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context5, ptr %ctx1, i32 0, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_float(ctx2->u.b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr %ctx2.addr +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context5, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load float, ptr [[r3]] +// FRONT-NEXT: call void @consume_float(float noundef [[r4]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call float (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.f32(ptr elementtype(%struct.context5) %ctx2, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: tail call void @consume_float(float noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context5, ptr %ctx2, i32 0, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load float, ptr [[r0]] +// BACK-NEXT: tail call void @consume_float(float noundef [[r1]]) + + ctx1->u.a = 2; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr %ctx1.addr +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context5, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: store i32 2, ptr [[r3]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 2, ptr elementtype(%struct.context5) %ctx1, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context5, ptr %ctx1, i32 0, i32 1 +// BACK-NEXT: store i32 2, ptr [[r0]] + + ctx2->u.b = 3.4f; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr %ctx2.addr +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context5, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: store float {{.*}}, ptr [[r3]] + +// MID-NEXT: tail call void (float, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.f32(float {{.*}}, ptr elementtype(%struct.context5) %ctx2, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context5, ptr %ctx2, i32 0, i32 1 +// BACK-NEXT: store float {{.*}}, ptr [[r0]] + + consume_ptr(ctx1); + consume_ptr(ctx2); +} + +// TODO: add an offset at the start! +struct context6 { + unsigned _; + unsigned a:1; + unsigned b:2; +} __ctx__; + +// FRONT-LABEL: define dso_local void @bitfield_access +// MID-LABEL: define dso_local void @bitfield_access +// BACK-LABEL: define dso_local void @bitfield_access +void bitfield_access(struct context6 *ctx) { + consume_int(ctx->a); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr {{.*}}) +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr [[r0]], i32 0, i32 1 +// FRONT-NEXT: load i8, ptr [[r1]] +// FRONT: @consume_int + +// MID: tail call i8 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i8(ptr elementtype(%struct.context6) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID: @consume_int + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i32 0, i32 1 +// BACK: load i8, ptr [[r0]] +// BACK: @consume_int + + consume_int(ctx->b); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr {{.*}}) +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr [[r0]], i32 0, i32 1 +// FRONT-NEXT: load i8, ptr [[r1]] +// FRONT: @consume_int + +// MID: [[r0:%[a-zA-Z0-9._]+]] = tail call i8 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i8(ptr elementtype(%struct.context6) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID: @consume_int + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i32 0, i32 1 +// BACK: load i8, ptr [[r0]] +// BACK: tail call void @consume_int + + ctx->a = 1; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr {{.*}}) +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr [[r0]], i32 0, i32 1 +// FRONT-NEXT: load i8, ptr [[r1]] +// FRONT: store {{.*}}, ptr [[r1]] + +// MID-NEXT: tail call i8 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i8(ptr elementtype(%struct.context6) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID: tail call void (i8, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i8(i8 {{.*}}, ptr elementtype(%struct.context6) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i32 0, i32 1 +// BACK: load i8, ptr [[r0]] +// BACK: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i32 0, i32 1 +// BACK: store i8 {{.*}}, ptr [[r1]] + + ctx->b = 2; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr {{.*}}) +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr [[r0]], i32 0, i32 1 +// FRONT-NEXT: load i8, ptr [[r1]] +// FRONT: store {{.*}}, ptr [[r1]] + +// MID: tail call i8 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i8(ptr elementtype(%struct.context6) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID: tail call void (i8, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i8(i8 {{.*}}, ptr elementtype(%struct.context6) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i32 0, i32 1 +// BACK: load i8, ptr [[r0]] +// BACK: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i32 0, i32 1 +// BACK: store i8 {{.*}}, ptr [[r1]] + + consume_ptr(ctx); +} + +struct pattern_bug1 { + int src_port; + int dst_port; +} __ctx__; + +// No need to check FRONT or BACK +// MID-LABEL: define dso_local i32 @pattern_bug1 +int pattern_bug1(struct pattern_bug1 *sk) { + int *port = &sk->dst_port; + return port[0] == 0x777; +// MID: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.pattern_bug1, ptr %sk, i64 0, i32 1 +// MID-NEXT: load i32, ptr [[r0]] +} + +struct bpf_sock { + int bound_dev_if; + int family; +} __ctx__; + +struct bpf_sockopt { + int _; + struct bpf_sock *sk; + int level; + int optlen; +} __ctx__; + +extern int magic(void); +extern int magic2(int); + +// The next test case is related to the issue described in the following thread: +// https://lore.kernel.org/bpf/CAA-VZPmxh8o8EBcJ=m-DH4ytcxDFmo0JKsm1p1gf40kS0CE3NQ@mail.gmail.com/T/#m4b9ce2ce73b34f34172328f975235fc6f19841b6 + +// FRONT-LABEL: define dso_local i32 @known_load_sink_example_1 +// MID-LABEL: define dso_local i32 @known_load_sink_example_1 +// BACK-LABEL: define dso_local i32 @known_load_sink_example_1 +int known_load_sink_example_1(struct bpf_sockopt *ctx) +{ + unsigned g = 0; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[level:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.bpf_sockopt, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = load i32, ptr [[level]] +// FRONT-NEXT: switch i32 [[r2]], label [[sw_epilog:%[a-zA-Z0-9._]+]] [ +// FRONT-NEXT: i32 10, label [[sw_bb:%[a-zA-Z0-9._]+]] +// FRONT-NEXT: i32 20, label [[sw_bb1:%[a-zA-Z0-9._]+]] +// FRONT-NEXT: ] + +// MID: [[level3:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.bpf_sockopt) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// MID-NEXT: switch i32 [[level3]], label [[sw_epilog:%[a-zA-Z0-9._]+]] [ +// MID-NEXT: i32 10, label [[sw_bb:%[a-zA-Z0-9._]+]] +// MID-NEXT: i32 20, label [[sw_bb1:%[a-zA-Z0-9._]+]] +// MID-NEXT: ] + +// BACK: [[level39:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.bpf_sockopt, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load i32, ptr [[level39]] +// BACK-NEXT: switch i32 [[r0]], label [[sw_epilog:%[a-zA-Z0-9._]+]] [ +// BACK-NEXT: i32 10, label [[sw_bb:%[a-zA-Z0-9._]+]] +// BACK-NEXT: i32 20, label [[sw_bb1:%[a-zA-Z0-9._]+]] +// BACK-NEXT: ] + switch (ctx->level) { + case 10: + g = magic2(ctx->sk->family); + break; +// FRONT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[sk:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.bpf_sockopt, ptr [[r4]], i32 0, i32 1 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load ptr, ptr [[sk]] +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r5]]) +// FRONT-NEXT: [[family:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.bpf_sock, ptr [[r6]], i32 0, i32 1 +// FRONT-NEXT: [[r7:%[a-zA-Z0-9._]+]] = load i32, ptr [[family]] +// FRONT-NEXT: [[call:%[a-zA-Z0-9._]+]] = call i32 @magic2(i32 noundef [[r7]]) +// FRONT-NEXT: store i32 [[call]], ptr %g +// FRONT-NEXT: br label %sw.epilog + +// MID: sw.bb: +// MID-NEXT: [[sk4:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.bpf_sockopt) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 1) +// MID-NEXT: [[family5:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.bpf_sock) [[sk4]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: br label [[sw_epilog_sink_split:%[a-zA-Z0-9._]+]] + +// BACK: sw.{{.*}}: +// BACK-NEXT: [[sk410:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.bpf_sockopt, ptr %ctx, i32 0 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[sk410]] +// BACK-NEXT: [[family511:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.bpf_sock, ptr [[r1]], i32 0, i32 1 +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = load i32, ptr [[family511]] +// BACK-NEXT: br label %sw.epilog.sink.split + + case 20: + g = magic2(ctx->optlen); + break; +// FRONT: [[r8:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r9:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r8]]) +// FRONT-NEXT: [[optlen:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.bpf_sockopt, ptr [[r9]], i32 0, i32 3 +// FRONT-NEXT: [[r10:%[a-zA-Z0-9._]+]] = load i32, ptr [[optlen]] +// FRONT-NEXT: [[call2:%[a-zA-Z0-9._]+]] = call i32 @magic2(i32 noundef [[r10]]) +// FRONT-NEXT: store i32 [[call2]], ptr %g +// FRONT-NEXT: br label %sw.epilog + +// MID: sw.{{.*}}: +// MID-NEXT: [[optlen6:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.bpf_sockopt) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 3) +// MID-NEXT: br label %sw.epilog.sink.split + +// BACK: sw.{{.*}}: +// BACK-NEXT: [[optlen612:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.bpf_sockopt, ptr %ctx, i32 0, i32 3 +// BACK-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[optlen612]] +// BACK-NEXT: br label %sw.epilog.sink.split + } +// FRONT: sw.epilog: +// FRONT-NEXT: [[r11:%[a-zA-Z0-9._]+]] = load i32, ptr %g +// FRONT-NEXT: [[rem:%[a-zA-Z0-9._]+]] = urem i32 [[r11]], 2 +// FRONT: ret i32 [[rem]] + +// MID: sw.epilog.sink.split: +// MID-NEXT: [[optlen6_sink:%[a-zA-Z0-9._]+]] = phi i32 [ [[optlen6]], [[sw_bb1]] ], [ [[family5]], [[sw_bb]] ] +// MID-NEXT: [[call2:%[a-zA-Z0-9._]+]] = tail call i32 @magic2(i32 noundef [[optlen6_sink]]) +// MID-NEXT: [[phi_bo:%[a-zA-Z0-9._]+]] = and i32 [[call2]], 1 +// MID-NEXT: br label %sw.epilog + +// MID: sw.epilog: +// MID-NEXT: [[g_0:%[a-zA-Z0-9._]+]] = phi i32 [ 0, %entry ], [ %phi.bo, %sw.epilog.sink.split ] +// MID-NEXT: ret i32 [[g_0]] + +// BACK: sw.epilog.sink.split: +// BACK-NEXT: [[optlen6_sink:%[a-zA-Z0-9._]+]] = phi i32 [ [[r3]], [[sw_bb1]] ], [ [[r2]], [[sw_bb]] ] +// BACK-NEXT: [[call2:%[a-zA-Z0-9._]+]] = tail call i32 @magic2(i32 noundef [[optlen6_sink]]) +// BACK-NEXT: [[phi_bo:%[a-zA-Z0-9._]+]] = and i32 [[call2]], 1 +// BACK-NEXT: br label %sw.epilog + +// BACK: sw.epilog: +// BACK-NEXT: [[g_0:%[a-zA-Z0-9._]+]] = phi +// BACK-NEXT: ret i32 [[g_0]] + + return g % 2; +} + +struct __sk_buff { + int _; + int priority; + int mark; + int tc_index; +} __ctx__; + +// FRONT-LABEL: define dso_local i32 @known_store_sink_example_1 +// MID-LABEL: define dso_local i32 @known_store_sink_example_1 +// BACK-LABEL: define dso_local i32 @known_store_sink_example_1 +int known_store_sink_example_1(struct __sk_buff *ctx) { + switch (ctx->priority) { +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[priority:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = load i32, ptr [[priority]] +// FRONT-NEXT: switch i32 [[r2]], label [[sw_epilog:%[a-zA-Z0-9._]+]] [ +// FRONT-NEXT: i32 10, label [[sw_bb:%[a-zA-Z0-9._]+]] +// FRONT-NEXT: i32 20, label [[sw_bb1:%[a-zA-Z0-9._]+]] +// FRONT-NEXT: ] + +// MID: [[priority3:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.__sk_buff) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: switch i32 [[priority3]], label [[sw_epilog:%[a-zA-Z0-9._]+]] [ +// MID-NEXT: i32 10, label [[sw_bb:%[a-zA-Z0-9._]+]] +// MID-NEXT: i32 20, label [[sw_bb1:%[a-zA-Z0-9._]+]] +// MID-NEXT: ] + +// BACK: [[priority36:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr %ctx, i32 0, i32 1 +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load i32, ptr [[priority36]] +// BACK-NEXT: switch i32 [[r0]], label [[sw_epilog:%[a-zA-Z0-9._]+]] [ +// BACK-NEXT: i32 10, label [[sw_bb:%[a-zA-Z0-9._]+]] +// BACK-NEXT: i32 20, label [[sw_bb1:%[a-zA-Z0-9._]+]] +// BACK-NEXT: ] + case 10: + ctx->mark = 3; + break; +// FRONT: sw.bb: +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[mark:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr [[r4]], i32 0, i32 2 +// FRONT-NEXT: store i32 3, ptr [[mark]] +// FRONT-NEXT: br label %sw.epilog + +// MID: sw.bb: +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 3, ptr elementtype(%struct.__sk_buff) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2) +// MID-NEXT: br label %sw.epilog + +// BACK: sw.{{.*}}: +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: store i32 3, ptr [[r1]] +// BACK-NEXT: br label %sw.epilog + + case 20: + ctx->priority = 4; + break; +// FRONT: sw.bb1: +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r5]]) +// FRONT-NEXT: [[priority2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr [[r6]], i32 0, i32 1 +// FRONT-NEXT: store i32 4, ptr [[priority2]] +// FRONT-NEXT: br label %sw.epilog + +// MID: sw.bb1: +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 4, ptr elementtype(%struct.__sk_buff) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: br label %sw.epilog + +// BACK: sw.{{.*}}: +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr %ctx, i32 0, i32 1 +// BACK-NEXT: store i32 4, ptr [[r2]] +// BACK-NEXT: br label %sw.epilog + } + return 0; +} + +// FRONT-LABEL: define dso_local i32 @known_store_sink_example_2 +// MID-LABEL: define dso_local i32 @known_store_sink_example_2 +// BACK-LABEL: define dso_local i32 @known_store_sink_example_2 +int known_store_sink_example_2(struct __sk_buff *ctx) { + switch (ctx->tc_index) { +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[tc_index:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr [[r1]], i32 0, i32 3 +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = load i32, ptr [[tc_index]] +// FRONT-NEXT: switch i32 [[r2]], label [[sw_epilog:%[a-zA-Z0-9._]+]] [ +// FRONT-NEXT: i32 10, label [[sw_bb:%[a-zA-Z0-9._]+]] +// FRONT-NEXT: i32 20, label [[sw_bb3:%[a-zA-Z0-9._]+]] +// FRONT-NEXT: ] + +// MID: [[tc_index8:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.__sk_buff) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 3) +// MID-NEXT: switch i32 [[tc_index8]], label [[sw_epilog:%[a-zA-Z0-9._]+]] [ +// MID-NEXT: i32 10, label [[sw_bb:%[a-zA-Z0-9._]+]] +// MID-NEXT: i32 20, label [[sw_bb3:%[a-zA-Z0-9._]+]] +// MID-NEXT: ] + +// BACK: [[tc_index814:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr %ctx, i32 0, i32 3 +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load i32, ptr [[tc_index814]] +// BACK-NEXT: switch i32 [[r0]], label [[sw_epilog:%[a-zA-Z0-9._]+]] [ +// BACK-NEXT: i32 10, label [[sw_bb:%[a-zA-Z0-9._]+]] +// BACK-NEXT: i32 20, label [[sw_bb3:%[a-zA-Z0-9._]+]] +// BACK-NEXT: ] + + case 10: + magic2(ctx->mark); + ctx->mark = magic(); + break; +// FRONT: sw.bb: +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[mark:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr [[r4]], i32 0, i32 2 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load i32, ptr [[mark]] +// FRONT-NEXT: [[call:%[a-zA-Z0-9._]+]] = call i32 @magic2(i32 noundef [[r5]]) +// FRONT-NEXT: [[call1:%[a-zA-Z0-9._]+]] = call i32 @magic() +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r7:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r6]]) +// FRONT-NEXT: [[mark2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr [[r7]], i32 0, i32 2 +// FRONT-NEXT: store i32 [[call1]], ptr [[mark2]] +// FRONT-NEXT: br label %sw.epilog + +// MID: sw.bb: +// MID-NEXT: [[mark7:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.__sk_buff) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2) +// MID-NEXT: [[call:%[a-zA-Z0-9._]+]] = tail call i32 @magic2(i32 noundef [[mark7]]) +// MID-NEXT: [[call1:%[a-zA-Z0-9._]+]] = tail call i32 @magic() +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 [[call1]], ptr elementtype(%struct.__sk_buff) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2) +// MID-NEXT: br label %sw.epilog + +// BACK: sw.{{.*}}: +// BACK-NEXT: [[mark715:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[mark715]] +// BACK-NEXT: [[call:%[a-zA-Z0-9._]+]] = tail call i32 @magic2(i32 noundef [[r1]]) +// BACK-NEXT: [[call1:%[a-zA-Z0-9._]+]] = tail call i32 @magic() +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: store i32 [[call1]], ptr [[r2]] +// BACK-NEXT: br label %sw.epilog + + case 20: + magic2(ctx->priority); + ctx->priority = magic(); + break; +// FRONT: sw.{{.*}}: +// FRONT-NEXT: [[r8:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r9:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r8]]) +// FRONT-NEXT: [[priority:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr [[r9]], i32 0, i32 1 +// FRONT-NEXT: [[r10:%[a-zA-Z0-9._]+]] = load i32, ptr [[priority]] +// FRONT-NEXT: [[call4:%[a-zA-Z0-9._]+]] = call i32 @magic2(i32 noundef [[r10]]) +// FRONT-NEXT: [[call5:%[a-zA-Z0-9._]+]] = call i32 @magic() +// FRONT-NEXT: [[r11:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r12:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r11]]) +// FRONT-NEXT: [[priority6:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr [[r12]], i32 0, i32 1 +// FRONT-NEXT: store i32 [[call5]], ptr [[priority6]] +// FRONT-NEXT: br label %sw.epilog + +// MID: sw.{{.*}}: +// MID-NEXT: [[priority9:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.__sk_buff) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: [[call4:%[a-zA-Z0-9._]+]] = tail call i32 @magic2(i32 noundef [[priority9]]) +// MID-NEXT: [[call5:%[a-zA-Z0-9._]+]] = tail call i32 @magic() +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 [[call5]], ptr elementtype(%struct.__sk_buff) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: br label %sw.epilog + +// BACK: sw.{{.*}}: +// BACK-NEXT: [[priority916:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr %ctx, i32 0, i32 1 +// BACK-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[priority916]] +// BACK-NEXT: [[call4:%[a-zA-Z0-9._]+]] = tail call i32 @magic2(i32 noundef [[r3]]) +// BACK-NEXT: [[call5:%[a-zA-Z0-9._]+]] = tail call i32 @magic() +// BACK-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr %ctx, i32 0, i32 1 +// BACK-NEXT: store i32 [[call5]], ptr [[r4]] +// BACK-NEXT: br label %sw.epilog + } + return 0; +} diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -1967,6 +1967,13 @@ [IntrNoMem, ImmArg>, ImmArg>]>; +def int_bpf_context_marker : Intrinsic<[llvm_anyptr_ty], + [llvm_anyptr_ty], + [IntrNoMem, IntrSpeculatable, IntrWillReturn, + IntrNoCallback, IntrNoFree, IntrNoSync, + NoCapture >, + ReadNone >, + ]>; //===------------ Intrinsics to perform common vector shuffles ------------===// diff --git a/llvm/include/llvm/IR/IntrinsicsBPF.td b/llvm/include/llvm/IR/IntrinsicsBPF.td --- a/llvm/include/llvm/IR/IntrinsicsBPF.td +++ b/llvm/include/llvm/IR/IntrinsicsBPF.td @@ -37,4 +37,49 @@ def int_bpf_compare : ClangBuiltin<"__builtin_bpf_compare">, Intrinsic<[llvm_i1_ty], [llvm_i32_ty, llvm_anyint_ty, llvm_anyint_ty], [IntrNoMem]>; + def int_bpf_getelementptr_and_load : ClangBuiltin<"__builtin_bpf_getelementptr_and_load">, + Intrinsic<[llvm_any_ty], + [llvm_ptr_ty, // base ptr for getelementptr + llvm_i1_ty, // volatile + llvm_i8_ty, // atomic order + llvm_i8_ty, // synscope id + llvm_i8_ty, // alignment + llvm_i1_ty, // inbounds + llvm_vararg_ty], // indices for getelementptr insn + [IntrReadMem, + IntrArgMemOnly, + IntrNoCallback, + IntrNoFree, + IntrWillReturn, + NoCapture >, + ReadOnly >, + ImmArg >, // volatile + ImmArg >, // atomic order + ImmArg >, // synscope id + ImmArg >, // alignment + ImmArg >, // inbounds + ]>; + def int_bpf_getelementptr_and_store : ClangBuiltin<"__builtin_bpf_getelementptr_and_store">, + Intrinsic<[], + [llvm_any_ty, // value to store + llvm_ptr_ty, // base ptr for getelementptr + llvm_i1_ty, // volatile + llvm_i8_ty, // atomic order + llvm_i8_ty, // syncscope id + llvm_i8_ty, // alignment + llvm_i1_ty, // inbounds + llvm_vararg_ty], // indexes for getelementptr insn + [IntrWriteMem, + IntrArgMemOnly, + IntrNoCallback, + IntrNoFree, + IntrWillReturn, + NoCapture >, + WriteOnly >, + ImmArg >, // volatile + ImmArg >, // atomic order + ImmArg >, // syncscope id + ImmArg >, // alignment + ImmArg >, // inbounds + ]>; } diff --git a/llvm/lib/Target/BPF/BPF.h b/llvm/lib/Target/BPF/BPF.h --- a/llvm/lib/Target/BPF/BPF.h +++ b/llvm/lib/Target/BPF/BPF.h @@ -30,6 +30,7 @@ FunctionPass *createBPFMIPeepholeTruncElimPass(); FunctionPass *createBPFMIPreEmitPeepholePass(); FunctionPass *createBPFMIPreEmitCheckingPass(); +FunctionPass *createBPFRewriteContextAccessPass(bool AllowPartial); void initializeBPFAdjustOptPass(PassRegistry&); void initializeBPFCheckAndAdjustIRPass(PassRegistry&); @@ -42,6 +43,7 @@ void initializeBPFMIPeepholeTruncElimPass(PassRegistry&); void initializeBPFMIPreEmitPeepholePass(PassRegistry&); void initializeBPFMIPreEmitCheckingPass(PassRegistry&); +void initializeBPFRewriteContextAccessLegacyPassPass(PassRegistry &); class BPFAbstractMemberAccessPass : public PassInfoMixin { @@ -72,6 +74,18 @@ public: PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); }; + +class BPFRewriteContextAccessPass + : public PassInfoMixin { + bool AllowPartial; + +public: + BPFRewriteContextAccessPass(bool AllowPartial) : AllowPartial(AllowPartial) {} + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); + + static bool isRequired() { return true; } +}; + } // namespace llvm #endif diff --git a/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp b/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp --- a/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp +++ b/llvm/lib/Target/BPF/BPFCheckAndAdjustIR.cpp @@ -12,6 +12,8 @@ // The following are done for IR adjustment: // - remove __builtin_bpf_passthrough builtins. Target independent IR // optimizations are done and those builtins can be removed. +// - remove llvm.bpf.getelementptr.and.load builtins. +// - remove llvm.bpf.getelementptr.and.store builtins. // //===----------------------------------------------------------------------===// @@ -22,6 +24,7 @@ #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicsBPF.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" #include "llvm/IR/User.h" @@ -47,6 +50,7 @@ bool adjustIR(Module &M); bool removePassThroughBuiltin(Module &M); bool removeCompareBuiltin(Module &M); + bool removeGEPBuiltins(Module &M); }; } // End anonymous namespace @@ -161,9 +165,95 @@ return Changed; } +static unsigned getOperandAsUnsigned(CallInst *Call, unsigned ArgNo) { + if (auto *Int = dyn_cast(Call->getOperand(ArgNo))) + return Int->getValue().getZExtValue(); + std::string Report; + raw_string_ostream ReportS(Report); + ReportS << "Expecting ConstantInt as argument #" << ArgNo << " of " << *Call + << "\n"; + report_fatal_error(StringRef(Report)); +} + +static GetElementPtrInst *reconstructGEP(CallInst *Call, int Delta) { + SmallVector Indices; + Indices.append(Call->data_operands_begin() + 6 + Delta, + Call->data_operands_end()); + auto *GEPPointeeType = Call->getParamElementType(Delta); + auto *GEP = GetElementPtrInst::Create(GEPPointeeType, Call->getOperand(Delta), + ArrayRef(Indices), + Call->getName(), Call); + GEP->setIsInBounds(getOperandAsUnsigned(Call, 5 + Delta)); + return GEP; +} + +template > +static void unrollCommon(CallInst *Call, GetElementPtrInst *GEP, T *Insn, + int Delta) { + Insn->setVolatile(getOperandAsUnsigned(Call, 1 + Delta)); + Insn->setOrdering((AtomicOrdering)getOperandAsUnsigned(Call, 2 + Delta)); + Insn->setSyncScopeID(getOperandAsUnsigned(Call, 3 + Delta)); + auto AlignShiftValue = getOperandAsUnsigned(Call, 4 + Delta); + Insn->setAlignment(Align(uint64_t(1) << AlignShiftValue)); + GEP->setDebugLoc(Call->getDebugLoc()); + Insn->setDebugLoc(Call->getDebugLoc()); + Call->replaceAllUsesWith(Insn); + Call->eraseFromParent(); +} + +static void unrollGEPLoad(CallInst *Call) { + auto *GEP = reconstructGEP(Call, 0); + auto *ReturnType = Call->getFunctionType()->getReturnType(); + auto *Load = new LoadInst(ReturnType, GEP, "", Call); + unrollCommon(Call, GEP, Load, 0); +} + +static void unrollGEPStore(CallInst *Call) { + auto *GEP = reconstructGEP(Call, 1); + auto *Store = new StoreInst(Call->getOperand(0), GEP, Call); + unrollCommon(Call, GEP, Store, 1); +} + +static bool removeGEPBuiltinsInFunc(Function &F) { + SmallVector GEPLoads; + SmallVector GEPStores; + for (auto &BB : F) + for (auto &Insn : BB) + if (auto *Call = dyn_cast(&Insn)) + if (auto *Called = Call->getCalledFunction()) + switch (Called->getIntrinsicID()) { + case Intrinsic::bpf_getelementptr_and_load: + GEPLoads.push_back(Call); + break; + case Intrinsic::bpf_getelementptr_and_store: + GEPStores.push_back(Call); + break; + } + + if (GEPLoads.empty() && GEPStores.empty()) + return false; + + for_each(GEPLoads, unrollGEPLoad); + for_each(GEPStores, unrollGEPStore); + + return true; +} + +// Rewrites the following builtins: +// - llvm.bpf.getelementptr.and.load +// - llvm.bpf.getelementptr.and.store +// As (load (getelementptr ...)) or (store (getelementptr ...)). +bool BPFCheckAndAdjustIR::removeGEPBuiltins(Module &M) { + bool Changed = false; + for (auto &F : M) + Changed = removeGEPBuiltinsInFunc(F) || Changed; + return Changed; +} + bool BPFCheckAndAdjustIR::adjustIR(Module &M) { bool Changed = removePassThroughBuiltin(M); Changed = removeCompareBuiltin(M) || Changed; + Changed = removeGEPBuiltins(M) || Changed; return Changed; } diff --git a/llvm/lib/Target/BPF/BPFContextMarker.cpp b/llvm/lib/Target/BPF/BPFContextMarker.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/BPF/BPFContextMarker.cpp @@ -0,0 +1,491 @@ +//===------ BPFContextMarker.cpp ------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// TLDR: replaces llvm.bpf.context.marker + GEP + load / store +// with llvm.bpf.getelementptr.and.load / store +// +// This file implements BPFRewriteContextAccessPass transformation. +// This transformation address two BPF verifier specific issues: +// +// (a) Access to the fields of some structural types is allowed only +// using load and store instructions with static immediate offsets. +// +// Examples of such types are `struct __sk_buff` and `struct +// bpf_sock_ops`. This is so because offsets of the fields of +// these structures do not match real offsets in the running +// kernel. During BPF program load LDX and STX instructions +// referring to the fields of these types are rewritten so that +// offsets match real offsets. For this rewrite to happen field +// offsets have to be encoded as immediate operands of the +// instructions. +// +// See kernel/bpf/verifier.c:convert_ctx_access function in the +// Linux kernel source tree for details. +// +// (b) Pointers to context parameters of BPF programs must not be +// modified before access. +// +// During BPF program verification a tag PTR_TO_CTX is tracked for +// register values. In case if register with such tag is modified +// BPF program is not allowed to read or write memory using this +// register. See kernel/bpf/verifier.c:check_mem_access function +// in the Linux kernel source tree for details. +// +// The following sequence of the IR instructions: +// +// %x = getelementptr %ptr, %constant_offset +// %y = load %x +// +// Is translated as a single machine instruction: +// +// LDW %ptr, %constant_offset +// +// In order for cases (a) and (b) to work the sequence %x-%y above has +// to be preserved by the IR passes. +// +// However, several optimization passes might sink `load` instruction +// or hoist `getelementptr` instruction so that the instructions are +// no longer in sequence. Examples of such passes are: +// SimplifyCFGPass, InstCombinePass, GVNPass. +// After such modification the verifier would reject the BPF program. +// +// To avoid this issue the patterns like (load/store (getelementptr ...)) +// are replaced by calls to BPF specific intrinsic functions: +// - llvm.bpf.getelementptr.and.load +// - llvm.bpf.getelementptr.and.store +// +// These calls are lowered back to (load/store (getelementptr ...)) +// by BPFCheckAndAdjustIR pass right before the translation from IR to +// machine instructions. +// +// The transformation is split into the following steps: +// - When IR is generated from AST the calls to intrinsic function +// llvm.bpf.context.marker are inserted. +// - BPFRewriteContextAccessPass is executed as early as possible +// with AllowPatial set to true, this handles marked GEP chains +// with constant offsets. +// - BPFRewriteContextAccessPass is executed at ScalarOptimizerLateEPCallback +// with AllowPatial set to false, this handles marked GEP chains +// with offsets that became constant after loop unrolling, e.g. +// to handle the following code: +// +// struct context { int x[4]; } __attribute__((btf_decl_tag("ctx"))); +// +// struct context *ctx = ...; +// #pragma clang loop unroll(full) +// for (int i = 0; i < 4; ++i) +// foo(ctx->x[i]); +// +// The early BPFRewriteContextAccessPass run is necessary to allow +// additional GVN / CSE opportunities after functions inlining. +// The relative order of optimization applied to function: +// - early stage (1) +// - ... +// - function inlining (2) +// - ... +// - loop unrolling +// - ... +// - ScalarOptimizerLateEPCallback (3) +// +// When function A is inlined into function B all optimizations for A +// are already done, while some passes remain for B. In case if +// BPFRewriteContextAccessPass is done at (3) but not done at (1) +// the code after (2) would contain a mix of +// (load (gep %p)) and (get.and.load %p) usages: +// - the (load (gep %p)) would come from the calling function; +// - the (get.and.load %p) would come from the callee function. +// Thus clobbering CSE / GVN passes done after inlining. + +#include "BPF.h" +#include "BPFCORE.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/Argument.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/IntrinsicsBPF.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "bpf-context-marker" + +using namespace llvm; + +static bool isContextMarkerCall(Value *I) { + auto *Call = dyn_cast(I); + if (!Call) + return false; + auto *Func = Call->getCalledFunction(); + if (!Func) + return false; + // For a mysterious reason ID match does not work at this point. + return Func->getName().startswith("llvm.bpf.context.marker"); +} + +template +static const DILocation *mergeDILocations(SmallVector &Insns) { + const DILocation *Merged = (*Insns.begin())->getDebugLoc(); + for (auto *I : Insns) + Merged = DILocation::getMergedLocation(Merged, I->getDebugLoc()); + return Merged; +} + +static CallInst *makeIntrinsicCall(Module *M, + Intrinsic::BPFIntrinsics Intrinsic, + ArrayRef Types, + ArrayRef Args) { + + Function *Fn = Intrinsic::getDeclaration(M, Intrinsic, Types); + return CallInst::Create(Fn, Args); +} + +static void setParamElementType(CallInst *Call, unsigned ArgNo, Type *Type) { + auto &C = Call->getContext(); + Call->addParamAttr(ArgNo, Attribute::get(C, Attribute::ElementType, Type)); +} + +static void setParamReadNone(CallInst *Call, unsigned ArgNo) { + auto &C = Call->getContext(); + Call->addParamAttr(ArgNo, Attribute::get(C, Attribute::ReadNone)); +} + +namespace { +struct GEPChainInfo { + bool InBounds = true; + Type *SourceElementType; + SmallVector Indices; + SmallVector Members; + + void reset() { + InBounds = false; + SourceElementType = nullptr; + Indices.clear(); + Members.clear(); + } +}; +} // Anonymous namespace + +template > +static void fillCommonArgs(SmallVector &Args, + GEPChainInfo &GEP, + T *Insn) { + auto &C = Insn->getContext(); + auto *Int8Ty = Type::getInt8Ty(C); + auto *Int1Ty = Type::getInt1Ty(C); + // Implementation of Align guarantees that ShiftValue < 64 + auto AlignShiftValue = Log2_64(Insn->getAlign().value()); + Args.push_back(GEP.Members[0]->getPointerOperand()); + Args.push_back(ConstantInt::get(Int1Ty, Insn->isVolatile())); + Args.push_back(ConstantInt::get(Int8Ty, (unsigned) Insn->getOrdering())); + Args.push_back(ConstantInt::get(Int8Ty, (unsigned) Insn->getSyncScopeID())); + Args.push_back(ConstantInt::get(Int8Ty, AlignShiftValue)); + Args.push_back(ConstantInt::get(Int1Ty, GEP.InBounds)); + Args.append(GEP.Indices.begin(), GEP.Indices.end()); +} + +static Instruction *makeGEPAndLoad(GEPChainInfo &GEP, + LoadInst *Load) { + auto *M = Load->getModule(); + SmallVector Args; + fillCommonArgs(Args, GEP, Load); + auto *Call = makeIntrinsicCall(M, Intrinsic::bpf_getelementptr_and_load, + {Load->getType()}, Args); + setParamElementType(Call, 0, GEP.SourceElementType); + Call->applyMergedLocation(mergeDILocations(GEP.Members), + Load->getDebugLoc()); + Call->setName((*GEP.Members.rbegin())->getName()); + if (!Call->getCalledFunction()->onlyReadsMemory()) + Call->getCalledFunction()->setOnlyReadsMemory(); + return Call; +} + +static Instruction *makeGEPAndStore(GEPChainInfo &GEP, + StoreInst *Store) { + auto *M = Store->getModule(); + SmallVector Args; + Args.push_back(Store->getValueOperand()); + fillCommonArgs(Args, GEP, Store); + auto *Call = makeIntrinsicCall(M, Intrinsic::bpf_getelementptr_and_store, + {Store->getValueOperand()->getType()}, Args); + setParamElementType(Call, 1, GEP.SourceElementType); + if (Store->getValueOperand()->getType()->isPointerTy()) + setParamReadNone(Call, 0); + Call->applyMergedLocation(mergeDILocations(GEP.Members), + Store->getDebugLoc()); + return Call; +} + +static bool isZero(Value *V) { + auto *CI = dyn_cast(V); + return CI && CI->isZero(); +} + +// Given a chain of GEP instructions collect information necessary to +// merge this chain as a single GEP instruction of form: +// getelementptr %, ptr %p, i32 0, , , ... +static bool foldGEPChainAsStructAccess(SmallVector &GEPs, + GEPChainInfo &Info) { + if (GEPs.empty()) + return false; + + if (!all_of(GEPs, [=](auto *GEP) { return GEP->hasAllConstantIndices(); })) + return false; + + auto *First = GEPs[0]; + Info.InBounds = First->isInBounds(); + Info.SourceElementType = First->getSourceElementType(); + auto *ResultElementType = First->getResultElementType(); + Info.Indices.append(First->idx_begin(), First->idx_end()); + Info.Members.push_back(First); + + for (auto *Iter = GEPs.begin() + 1; Iter != GEPs.end(); ++Iter) { + auto *GEP = *Iter; + if (!isZero(*GEP->idx_begin())) { + Info.reset(); + return false; + } + if (!GEP->getSourceElementType() || + GEP->getSourceElementType() != ResultElementType) { + Info.reset(); + return false; + } + Info.InBounds &= GEP->isInBounds(); + Info.Indices.append(GEP->idx_begin() + 1, GEP->idx_end()); + Info.Members.push_back(GEP); + ResultElementType = GEP->getResultElementType(); + } + + return true; +} + +// Given a chain of GEP instructions collect information necessary to +// merge this chain as a single GEP instruction of form: +// getelementptr i8, ptr %p, i64 %offset +static bool foldGEPChainAsU8Access(SmallVector &GEPs, + GEPChainInfo &Info) { + if (GEPs.empty()) + return false; + + auto *First = GEPs[0]; + auto &DL = First->getModule()->getDataLayout(); + auto &C = First->getContext(); + Type *PtrTy = First->getType()->getScalarType(); + APInt Offset(DL.getIndexTypeSizeInBits(PtrTy), 0); + for (auto *GEP : GEPs) { + if (!GEP->accumulateConstantOffset(DL, Offset)) { + Info.reset(); + return false; + } + Info.InBounds &= GEP->isInBounds(); + Info.Members.push_back(GEP); + } + Info.SourceElementType = Type::getInt8Ty(C); + Info.Indices.push_back(ConstantInt::get(C, Offset)); + + return true; +} + +static bool isPointerOperand(Value *I, User *U) { + if (auto *L = dyn_cast(U)) + return L->getPointerOperand() == I; + if (auto *S = dyn_cast(U)) + return S->getPointerOperand() == I; + if (auto *GEP = dyn_cast(U)) + return GEP->getPointerOperand() == I; + return true; +} + +// A DFS traversal of GEP chain trees starting from Root. +// +// Recursion descends through GEP instructions and +// llvm.bpf.context.marker calls. Recursion stops at any other +// instruction. If load or store instruction is reached it is replaced +// by a call to `llvm.bpf.getelementptr.and.load` or +// `llvm.bpf.getelementptr.and.store` intrinsic. +// If nested calls to `llvm.bpf.context.marker` are encountered these +// calls are marked for deletion. +// +// Parameters description: +// - Root - a call to llvm.bpf.context.marker, the root of the tree +// - Insn - current position in the tree +// - GEPs - GEP instructions for the current branch +// - Visited - a list of visited instructions in DFS order, +// order is important for unused instruction deletion. +// - AllowPartial - when true GEP chains that can't be folded are +// not reported, otherwise diagnostic message is show for such chains. +// - StillUsed - set to true if one of the GEP chains could not be folded, +// makes sense when AllowPartial is false, means that root marker is still +// in use and should remain until the next run of this pass. +static void rewriteAccessChain(Value *Root, + Instruction *Insn, + SmallVector &GEPs, + SmallVector &Visited, + bool AllowPatial, + bool &StillUsed) { + auto MarkAndTraverseUses = [&]() { + Visited.push_back(Insn); + for (auto *U : Insn->users()) + if (auto *UI = dyn_cast(U)) + if (isPointerOperand(Insn, UI)) + rewriteAccessChain(Root, UI, GEPs, Visited, AllowPatial, StillUsed); + }; + if (isa(Insn) || isa(Insn)) { + GEPChainInfo GEPChain; + // Do nothing for (marker (load/store ..)) or for GEPs with zero indices. + // Such constructs lead to zero offset and would be simplified + // by other passes. + if (GEPs.empty() || + all_of(GEPs, [=](auto *GEP) { return GEP->hasAllZeroIndices(); })) + return; + if (!foldGEPChainAsStructAccess(GEPs, GEPChain) && + !foldGEPChainAsU8Access(GEPs, GEPChain)) { + if (!AllowPatial) { + auto Msg = DiagnosticInfoUnsupported + (*Insn->getFunction(), + Twine("Non-constant offset in access to a field of a type marked " + "with btf_decl_tag(\"ctx\") tag might be rejected by BPF " + "verifier") + .concat(Insn->getDebugLoc() + ? "" + : " (pass -g option to get exact location)"), + Insn->getDebugLoc(), + DS_Warning); + Root->getContext().diagnose(Msg); + } + StillUsed = true; + return; + } + Visited.push_back(Insn); + if (auto *Load = dyn_cast(Insn)) { + auto *Replacement = makeGEPAndLoad(GEPChain, Load); + Replacement->insertBefore(Load); + Replacement->setAAMetadata(Load->getAAMetadata()); + Load->replaceAllUsesWith(Replacement); + } + if (auto *Store = dyn_cast(Insn)) { + auto *Replacement = makeGEPAndStore(GEPChain, Store); + Replacement->insertBefore(Store); + Replacement->setAAMetadata(Store->getAAMetadata()); + } + } else if (auto *GEP = dyn_cast(Insn)) { + GEPs.push_back(GEP); + MarkAndTraverseUses(); + GEPs.pop_back(); + } else if (isContextMarkerCall(Insn)) { + MarkAndTraverseUses(); + } +} + +static void removeMarker(Instruction *Marker) { + Marker->replaceAllUsesWith(Marker->getOperand(0)); + Marker->eraseFromParent(); +} + +using InsnSet = std::set; + +static bool rewriteAccessChain(Instruction *Marker, + bool AllowPatial, + InsnSet &RemovedMarkers) { + SmallVector GEPs; + SmallVector Visited; + bool StillUsed; + for (auto *U : Marker->users()) + if (auto *UI = dyn_cast(U)) + if (isPointerOperand(Marker, UI)) + rewriteAccessChain(Marker, UI, GEPs, Visited, AllowPatial, StillUsed); + // Check if Visited instructions could be removed, iterate in + // reverse to unblock instructions higher in the chain. + for (auto V = Visited.rbegin(); V != Visited.rend(); ++V) { + if (isContextMarkerCall(*V)) { + removeMarker(*V); + RemovedMarkers.insert(*V); + } else if ((*V)->use_empty()) { + (*V)->eraseFromParent(); + } + } + return StillUsed; +} + +static std::vector collectContextMarkers(Function &F) { + std::vector Markers; + for (auto &Insn : instructions(F)) + if (isContextMarkerCall(&Insn)) + Markers.push_back(&Insn); + return Markers; +} + +// Look for sequences: +// - llvm.bpf.context.marker -> getelementptr... -> load +// - llvm.bpf.context.marker -> getelementptr... -> store +// And replace those with calls to intrinsics: +// - llvm.bpf.getelementptr.and.load +// - llvm.bpf.getelementptr.and.store +static bool rewriteContextAccess(Function &F, bool AllowPartial) { + LLVM_DEBUG(dbgs() + << "********** Rewrite Context Access (AllowPartial=" + << AllowPartial + << ") ************\n"); + + auto ContextMarkers = collectContextMarkers(F); + InsnSet RemovedMarkers; + + LLVM_DEBUG(dbgs() + << "There are " << ContextMarkers.size() + << " context markers\n"); + + if (ContextMarkers.empty()) + return false; + + for (auto *Marker : ContextMarkers) { + if (RemovedMarkers.find(Marker) != RemovedMarkers.end()) + continue; + bool StillUsed = rewriteAccessChain(Marker, AllowPartial, RemovedMarkers); + if (!StillUsed || !AllowPartial) + removeMarker(Marker); + } + + return true; +} + +namespace { + +class BPFRewriteContextAccessLegacyPass final : public FunctionPass { + bool AllowPartial; +public: + static char ID; + + BPFRewriteContextAccessLegacyPass(bool AllowPartial = false) + : FunctionPass(ID), AllowPartial(AllowPartial) {} + + bool runOnFunction(Function &F) override { + return rewriteContextAccess(F, AllowPartial); + } +}; + +} // End anonymous namespace + +char BPFRewriteContextAccessLegacyPass::ID = 0; +INITIALIZE_PASS(BPFRewriteContextAccessLegacyPass, DEBUG_TYPE, + "BPF Rewrite Context Access", false, false) + +FunctionPass *llvm::createBPFRewriteContextAccessPass(bool AllowPartial) { + return new BPFRewriteContextAccessLegacyPass(AllowPartial); +} + +PreservedAnalyses +llvm::BPFRewriteContextAccessPass::run(Function &F, FunctionAnalysisManager &AM) { + return rewriteContextAccess(F, AllowPartial) + ? PreservedAnalyses::none() + : PreservedAnalyses::all(); +} diff --git a/llvm/lib/Target/BPF/BPFTargetMachine.cpp b/llvm/lib/Target/BPF/BPFTargetMachine.cpp --- a/llvm/lib/Target/BPF/BPFTargetMachine.cpp +++ b/llvm/lib/Target/BPF/BPFTargetMachine.cpp @@ -26,6 +26,7 @@ #include "llvm/Target/TargetOptions.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Scalar/EarlyCSE.h" #include "llvm/Transforms/Scalar/SimplifyCFG.h" #include "llvm/Transforms/Utils/SimplifyCFGOptions.h" using namespace llvm; @@ -48,6 +49,7 @@ initializeBPFCheckAndAdjustIRPass(PR); initializeBPFMIPeepholePass(PR); initializeBPFMIPeepholeTruncElimPass(PR); + initializeBPFRewriteContextAccessLegacyPassPass(PR); } // DataLayout: little or big endian @@ -103,12 +105,13 @@ } void BPFTargetMachine::adjustPassManager(PassManagerBuilder &Builder) { - Builder.addExtension( + Builder.addExtension( PassManagerBuilder::EP_EarlyAsPossible, [&](const PassManagerBuilder &, legacy::PassManagerBase &PM) { PM.add(createBPFAbstractMemberAccess(this)); PM.add(createBPFPreserveDIType()); PM.add(createBPFIRPeephole()); + PM.add(createBPFRewriteContextAccessPass(true)); }); Builder.addExtension( @@ -117,6 +120,13 @@ PM.add(createCFGSimplificationPass( SimplifyCFGOptions().hoistCommonInsts(true))); }); + // See comments below + Builder.addExtension( + PassManagerBuilder::EP_ScalarOptimizerLate, + [&](const PassManagerBuilder &Builder, legacy::PassManagerBase &PM) { + PM.add(createBPFRewriteContextAccessPass(false)); + PM.add(createEarlyCSEPass(true)); + }); Builder.addExtension( PassManagerBuilder::EP_ModuleOptimizerEarly, [&](const PassManagerBuilder &, legacy::PassManagerBase &PM) { @@ -131,12 +141,22 @@ FPM.addPass(BPFAbstractMemberAccessPass(this)); FPM.addPass(BPFPreserveDITypePass()); FPM.addPass(BPFIRPeepholePass()); + FPM.addPass(BPFRewriteContextAccessPass(true)); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); }); PB.registerPeepholeEPCallback([=](FunctionPassManager &FPM, OptimizationLevel Level) { FPM.addPass(SimplifyCFGPass(SimplifyCFGOptions().hoistCommonInsts(true))); }); + PB.registerScalarOptimizerLateEPCallback( + [=](FunctionPassManager &FPM, OptimizationLevel Level) { + // Run this after loop unrolling but before + // SimplifyCFGPass(... .sinkCommonInsts(true)) + FPM.addPass(BPFRewriteContextAccessPass(false)); + // CSE is necessary to optimize some of the patterns occurring + // after function inlining inside unrolled loops. + FPM.addPass(EarlyCSEPass(true)); + }); PB.registerPipelineEarlySimplificationEPCallback( [=](ModulePassManager &MPM, OptimizationLevel) { MPM.addPass(BPFAdjustOptPass()); diff --git a/llvm/lib/Target/BPF/CMakeLists.txt b/llvm/lib/Target/BPF/CMakeLists.txt --- a/llvm/lib/Target/BPF/CMakeLists.txt +++ b/llvm/lib/Target/BPF/CMakeLists.txt @@ -19,6 +19,7 @@ BPFAdjustOpt.cpp BPFAsmPrinter.cpp BPFCheckAndAdjustIR.cpp + BPFContextMarker.cpp BPFFrameLowering.cpp BPFInstrInfo.cpp BPFIRPeephole.cpp diff --git a/llvm/test/CodeGen/BPF/btf-decl-tag-ctx.ll b/llvm/test/CodeGen/BPF/btf-decl-tag-ctx.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/btf-decl-tag-ctx.ll @@ -0,0 +1,200 @@ +; This is a canary test to check that llvm.bpf.context.marker +; intrinsic is processed by opt. +; +; RUN: opt -O2 %s | llvm-dis +; Compiler flag to generate IR: +; +; struct __sk_buff { +; int _; +; int priority; +; int mark; +; int tc_index; +; } __attribute__((btf_decl_tag("ctx"))); +; +; int known_store_sink_example_1(struct __sk_buff *ctx) { +; switch (ctx->priority) { +; case 10: +; ctx->mark = 3; +; break; +; case 20: +; ctx->priority = 4; +; break; +; } +; return 0; +; } +; +; struct foo { +; int _; +; int x; +; } __attribute__((btf_decl_tag("ctx"))); +; +; extern void consume_int(int); +; +; void foofn(struct foo *ctx) { +; void *p; +; if (ctx->x) +; p = &ctx->x; +; else +; p = &ctx->x + 1; +; if (ctx->x) +; consume_int((void*)&ctx->x - p); +; } +; +; Compiler command: +; +; clang -target bpf -S -O2 -emit-llvm -Xclang -disable-llvm-passes -o - test.c +; + +source_filename = "t4.c" +target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128" +target triple = "bpf" + +%struct.__sk_buff = type { i32, i32, i32, i32 } +%struct.foo = type { i32, i32 } + +; Function Attrs: nounwind +define dso_local i32 @known_store_sink_example_1(ptr noundef %ctx) #0 { +entry: + %ctx.addr = alloca ptr, align 8 + store ptr %ctx, ptr %ctx.addr, align 8, !tbaa !3 + %0 = load ptr, ptr %ctx.addr, align 8, !tbaa !3 + %1 = call ptr @llvm.bpf.context.marker.p0.p0(ptr %0) + %priority = getelementptr inbounds %struct.__sk_buff, ptr %1, i32 0, i32 1 + %2 = load i32, ptr %priority, align 4, !tbaa !7 + switch i32 %2, label %sw.epilog [ + i32 10, label %sw.bb + i32 20, label %sw.bb1 + ] + +sw.bb: ; preds = %entry + %3 = load ptr, ptr %ctx.addr, align 8, !tbaa !3 + %4 = call ptr @llvm.bpf.context.marker.p0.p0(ptr %3) + %mark = getelementptr inbounds %struct.__sk_buff, ptr %4, i32 0, i32 2 + store i32 3, ptr %mark, align 4, !tbaa !10 + br label %sw.epilog + +sw.bb1: ; preds = %entry + %5 = load ptr, ptr %ctx.addr, align 8, !tbaa !3 + %6 = call ptr @llvm.bpf.context.marker.p0.p0(ptr %5) + %priority2 = getelementptr inbounds %struct.__sk_buff, ptr %6, i32 0, i32 1 + store i32 4, ptr %priority2, align 4, !tbaa !7 + br label %sw.epilog + +sw.epilog: ; preds = %entry, %sw.bb1, %sw.bb + ret i32 0 +} + +; CHECK-LABEL: @known_store_sink_example_1 + +; CHECK: [[priority:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +; CHECK-SAME: @llvm.bpf.getelementptr.and.load.i32 +; CHECK-SAME: (ptr elementtype(%struct.__sk_buff) %ctx, +; CHECK-SAME: i1 false, i8 0, i8 1, i8 2, i1 true, i64 0, i32 1) +; CHECK-NEXT: switch i32 [[priority]], label {{.*}} [ +; CHECK-NEXT: i32 10, label {{.*}} +; CHECK-NEXT: i32 20, label {{.*}} +; CHECK-NEXT: ] + +; CHECK: {{.*}}: +; CHECK-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +; CHECK-SAME: @llvm.bpf.getelementptr.and.store.i32 +; CHECK-SAME: (i32 3, ptr elementtype(%struct.__sk_buff) %ctx, +; CHECK-SAME: i1 false, i8 0, i8 1, i8 2, i1 true, i64 0, i32 2) + +; CHECK: {{.*}}: +; CHECK-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +; CHECK-SAME: @llvm.bpf.getelementptr.and.store.i32 +; CHECK-SAME: (i32 4, ptr elementtype(%struct.__sk_buff) %ctx, +; CHECK-SAME: i1 false, i8 0, i8 1, i8 2, i1 true, i64 0, i32 1) + +declare ptr @llvm.bpf.context.marker.p0.p0(ptr) + +; Function Attrs: nounwind +define dso_local void @foofn(ptr noundef %ctx) #0 { +entry: + %ctx.addr = alloca ptr, align 8 + %p = alloca ptr, align 8 + store ptr %ctx, ptr %ctx.addr, align 8, !tbaa !3 + call void @llvm.lifetime.start.p0(i64 8, ptr %p) #3 + %0 = load ptr, ptr %ctx.addr, align 8, !tbaa !3 + %1 = call ptr @llvm.bpf.context.marker.p0.p0(ptr %0) + %x = getelementptr inbounds %struct.foo, ptr %1, i32 0, i32 1 + %2 = load i32, ptr %x, align 4, !tbaa !11 + %tobool = icmp ne i32 %2, 0 + br i1 %tobool, label %if.then, label %if.else + +if.then: ; preds = %entry + %3 = load ptr, ptr %ctx.addr, align 8, !tbaa !3 + %4 = call ptr @llvm.bpf.context.marker.p0.p0(ptr %3) + %x1 = getelementptr inbounds %struct.foo, ptr %4, i32 0, i32 1 + store ptr %x1, ptr %p, align 8, !tbaa !3 + br label %if.end + +if.else: ; preds = %entry + %5 = load ptr, ptr %ctx.addr, align 8, !tbaa !3 + %6 = call ptr @llvm.bpf.context.marker.p0.p0(ptr %5) + %x2 = getelementptr inbounds %struct.foo, ptr %6, i32 0, i32 1 + %add.ptr = getelementptr inbounds i32, ptr %x2, i64 1 + store ptr %add.ptr, ptr %p, align 8, !tbaa !3 + br label %if.end + +if.end: ; preds = %if.else, %if.then + %7 = load ptr, ptr %ctx.addr, align 8, !tbaa !3 + %8 = call ptr @llvm.bpf.context.marker.p0.p0(ptr %7) + %x3 = getelementptr inbounds %struct.foo, ptr %8, i32 0, i32 1 + %9 = load i32, ptr %x3, align 4, !tbaa !11 + %tobool4 = icmp ne i32 %9, 0 + br i1 %tobool4, label %if.then5, label %if.end7 + +if.then5: ; preds = %if.end + %10 = load ptr, ptr %ctx.addr, align 8, !tbaa !3 + %11 = call ptr @llvm.bpf.context.marker.p0.p0(ptr %10) + %x6 = getelementptr inbounds %struct.foo, ptr %11, i32 0, i32 1 + %12 = load ptr, ptr %p, align 8, !tbaa !3 + %sub.ptr.lhs.cast = ptrtoint ptr %x6 to i64 + %sub.ptr.rhs.cast = ptrtoint ptr %12 to i64 + %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast + %conv = trunc i64 %sub.ptr.sub to i32 + call void @consume_int(i32 noundef %conv) + br label %if.end7 + +if.end7: ; preds = %if.then5, %if.end + call void @llvm.lifetime.end.p0(i64 8, ptr %p) #3 + ret void +} + +; Verify that GVN pass was executed by opt, see also +; clang/test/CodeGen/bpf-decl-tag-ctx-gvn.c + +; CHECK-LABEL: @foofn +; CHECK: tail call void @consume_int(i32 noundef 0) + +; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #1 + +declare dso_local void @consume_int(i32 noundef) #2 + +; Function Attrs: argmemonly nocallback nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #1 + +attributes #0 = { nounwind "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +attributes #1 = { argmemonly nocallback nofree nosync nounwind willreturn } +attributes #2 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +attributes #3 = { nounwind } + +!llvm.module.flags = !{!0, !1} +!llvm.ident = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"frame-pointer", i32 2} +!2 = !{!"clang version 16.0.0 (https://github.com/llvm/llvm-project.git 6ef148adf4418ead36a8109d31a0c14033bafbc6)"} +!3 = !{!4, !4, i64 0} +!4 = !{!"any pointer", !5, i64 0} +!5 = !{!"omnipotent char", !6, i64 0} +!6 = !{!"Simple C/C++ TBAA"} +!7 = !{!8, !9, i64 4} +!8 = !{!"__sk_buff", !9, i64 0, !9, i64 4, !9, i64 8, !9, i64 12} +!9 = !{!"int", !5, i64 0} +!10 = !{!8, !9, i64 8} +!11 = !{!12, !9, i64 4} +!12 = !{!"foo", !9, i64 0, !9, i64 4}