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,37 @@ +// 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(ptr elementtype(%struct.bpf_sock_ops) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i64 0, i32 1, i32 0, i64 0, i32 1, i64 1) +// 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(ptr elementtype(i8) %ctx, i1 false, i8 0, i8 1, i8 1, i1 true, 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(ptr elementtype(%struct.bpf_sock_ops) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i64 0, i32 1, i32 0, i64 2, i32 1, i64 8) +// CHECK-NEXT: tail call void @consume_char(i8 noundef signext [[r0]]) 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,151 @@ +// 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, i64 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, i64 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, i64 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, i64 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-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,472 @@ +// 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 elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i64 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 elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i64 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 elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i64 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 elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i64 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, i64 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 elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i64 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, i64 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 elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i64 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 elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i64 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 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 elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i64 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: [[r0:%[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 [[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]]) + + 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, i64 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, i64 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, i64 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: [[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, i64 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) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i64 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: [[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, i64 0, i32 1) +// 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) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i64 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, i64 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, i64 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, i64 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: [[r0:%[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:4$0:1" +// 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 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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.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,1419 @@ +// 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, i64 0, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[a]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i64 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.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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 0, i32 1) + +// BACK-NEXT: [[r0:%[0-9]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i64 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, i64 0, i32 1, i32 1) + +// BACK-NEXT: [[r0:%[0-9]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i64 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: [[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, i64 0, i32 2) +// MID-NEXT: store i32 4, ptr [[r0]] + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i64 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: store i32 4, ptr [[r1]] + + 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: [[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, i64 0, i32 2) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r0]], i64 0, i32 1 +// MID-NEXT: store i32 5, ptr [[r1]] + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i64 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: store i32 5, ptr [[r2]] + + consume_ptr(ctx); +// FRONT: ret void +// MID: ret void +// BACK: ret void +} + +// 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, i64 0, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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: [[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, i64 0, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i64 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, i64 0, i32 1, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i64 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: [[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, i64 0, i32 2) +// MID-NEXT: store i32 4, ptr [[r0]] + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i64 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: store i32 4, ptr [[r1]] + + 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: [[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, i64 0, i32 2) +// 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.inner_vof) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i64 0, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i64 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: store i32 5, ptr [[r2]] + + consume_ptr(ctx); +// FRONT: ret void +// MID: ret void +// BACK: ret void +} + +// 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, i64 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, i64 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: ret void +// MID: ret void +// BACK: ret void +} + +// 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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]], 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]]) + + 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, i64 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, i64 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, i64 0, i32 2, i64 2, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i64 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, i64 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, i64 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, i64 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, i64 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, i64 0, i32 5, i64 2, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i64 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, i64 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, i64 0, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i64 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]], i64 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); + +// FRONT: ret void +// MID: ret void +// BACK: ret void +} + +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, i64 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, i64 0 +// 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 0, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i64 0 +// 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, i64 0, i32 3) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i64 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, i64 0, i32 5) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i64 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, i64 0, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i64 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); +// FRONT: ret void +// MID: ret void +// BACK: ret void +} + +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, i64 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, i64 0 +// 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, i64 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, i64 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, i64 0, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context5, ptr %ctx1, i64 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, i64 0, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context5, ptr %ctx2, i64 0, i32 1 +// BACK-NEXT: store float {{.*}}, ptr [[r0]] + + consume_ptr(ctx1); + consume_ptr(ctx2); +// FRONT: ret void +// MID: ret void +// BACK: ret void +} + +// 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, i64 0, i32 1) +// MID: @consume_int + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i64 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, i64 0, i32 1) +// MID: @consume_int + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i64 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, i64 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, i64 0, i32 1) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i64 0, i32 1 +// BACK: load i8, ptr [[r0]] +// BACK: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i64 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, i64 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, i64 0, i32 1) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i64 0, i32 1 +// BACK: load i8, ptr [[r0]] +// BACK: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i64 0, i32 1 +// BACK: store i8 {{.*}}, ptr [[r1]] + + consume_ptr(ctx); +// FRONT: ret void +// MID: ret void +// BACK: ret void +} + +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: tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.pattern_bug1) %sk, i1 false, i8 0, i8 1, i8 2, i1 true, i64 0, i32 1) +} + +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, i64 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, i64 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, i64 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, i64 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, i64 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]], i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 0, i32 1 +// BACK-NEXT: store i32 4, ptr [[r2]] +// BACK-NEXT: br label %sw.epilog + } +// FRONT: sw.epilog: +// FRONT-NEXT: ret i32 0 +// MID: sw.epilog: +// MID-NEXT: ret i32 0 +// BACK: sw.epilog: +// BACK-NEXT: ret i32 0 + 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 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, i64 0, i32 1 +// BACK-NEXT: store i32 [[call5]], ptr [[r4]] +// BACK-NEXT: br label %sw.epilog + } + return 0; +// FRONT: sw.epilog: +// FRONT-NEXT: ret i32 0 +// MID: sw.epilog: +// MID-NEXT: ret i32 0 +// BACK: sw.epilog: +// BACK-NEXT: ret i32 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,9 @@ [IntrNoMem, ImmArg>, ImmArg>]>; +def int_bpf_context_marker : DefaultAttrsIntrinsic<[llvm_anyptr_ty], + [llvm_anyptr_ty], + [IntrNoMem]>; //===------------ 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,43 @@ 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, + 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, + 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(); 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,23 @@ public: PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); }; + +class BPFRewriteContextAccessPass + : public PassInfoMixin { +public: + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); + + static bool isRequired() { return true; } +}; + +class BPFSimplifyContextMarkersPass + : public PassInfoMixin { +public: + 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,451 @@ +//===------ 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 implementations BPFRewriteContextAccessPass that address two +// BPF verifier specific issues: +// +// (a) Access to some structural types is allowed only using +// LD / ST 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/verification loads and stores to the fields of +// these types are rewritten so that offsets match real +// offsets. For this rewrite to happen static offsets have to be +// encoded in 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 programs are not allowed to read or write memory using +// register. See kernel/bpf/verifier.c:check_mem_access function in the +// Linux kernel source tree for details. +// +// Both cases limit the ways structural types could be accessed. +// After AST translation the IR for structural types access has the +// following form: +// +// %x = getelementptr %ptr, %offset +// %y = load %x +// +// Instruction selection for BPF translates such series of +// instructions as a single instruction: +// +// LDW %ptr, %offset +// +// 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 BPFRewriteContextAccessPass is responsible for this +// transformation. It uses calls to llvm.bpf.context.marker to +// identify starting points of (load/store (gep (gep ...))) chains and +// replaces these chains with calls to intrinsics. + +#include "BPF.h" +#include "BPFCORE.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/IntrinsicsBPF.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/PassManager.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include + +#define DEBUG_TYPE "bpf-context-marker" + +using namespace llvm; + +static bool isNamedFuncCall(Value *I, const char *Name) { + auto *Call = dyn_cast(I); + if (!Call) + return false; + auto *Func = Call->getCalledFunction(); + if (!Func) + return false; + return Func->getName().startswith(Name); +} + +static bool isContextMarkerCall(Value *I) { + return isNamedFuncCall(I, "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, + SmallVector &Args) { + + Function *Fn = + Intrinsic::getDeclaration(M, Intrinsic, ArrayRef(Types)); + return CallInst::Create(Fn, ArrayRef(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()); + 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; +} + +// Given a chain of GEP instructions collect information necessary to +// merge this chain as a single instruction. +// Return `true` is such merge is possible, `false` otherwise. +static bool foldGEPChainAsStructAccess(SmallVector &GEPs, + GEPChainInfo &Info) { + if (GEPs.empty()) + 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; + auto *CI = dyn_cast(GEP->idx_begin()); + if (!CI || !CI->isZero()) { + 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; +} + +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(Instruction *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 at the end +// it is replaced by the call to `llvm.bpf.getelementptr.and.load` or +// `llvm.bpf.getelementptr.and.store` intrinsic. +// +// 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. +static void rewriteAccessChain(Instruction *Root, + Instruction *Insn, + SmallVector &GEPs, + SmallVector &Visited) { + 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); + }; + if (isa(Insn) || isa(Insn)) { + GEPChainInfo GEPChain; + if (GEPs.empty()) + return; + if (!foldGEPChainAsStructAccess(GEPs, GEPChain) && + !foldGEPChainAsU8Access(GEPs, GEPChain)) { + errs() << "In function " << Root->getFunction()->getName() << ": " + << "can't generate gep and load/store for:\n" + << *Root << "\n" + << "Irreducable GEP chain\n"; + for (auto *GEP : GEPs) + errs() << *GEP << "\n"; + return; + } + Visited.push_back(Insn); + if (auto *Load = dyn_cast(Insn)) { + auto *Replacement = makeGEPAndLoad(GEPChain, Load); + Replacement->insertBefore(Load); + Load->replaceAllUsesWith(Replacement); + } + if (auto *Store = dyn_cast(Insn)) { + auto *Replacement = makeGEPAndStore(GEPChain, Store); + Replacement->insertBefore(Store); + } + } else if (auto *GEP = dyn_cast(Insn)) { + GEPs.push_back(GEP); + MarkAndTraverseUses(); + GEPs.pop_back(); + } else if (isContextMarkerCall(Insn)) { + MarkAndTraverseUses(); + } +} + +// Rewrite the GEP chains starting at Marker and delete instructions +// that are not used after rewrite. +static void rewriteAccessChain(Instruction *Marker) { + SmallVector GEPs; + SmallVector Visited; + for (auto *U : Marker->users()) + if (auto *UI = dyn_cast(U)) + if (isPointerOperand(Marker, UI)) + rewriteAccessChain(Marker, UI, GEPs, Visited); + // 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 ((*V)->use_empty()) + (*V)->eraseFromParent(); +} + +// Note: order matters, see comment in rewriteContextAccess below +static std::vector collectContextMarkers(Function &F) { + std::vector Markers; + for (auto &Insn : instructions(F)) + if (isContextMarkerCall(&Insn)) + Markers.push_back(&Insn); + return Markers; +} + +static void removeMarker(Instruction *Marker) { + Marker->replaceAllUsesWith(Marker->getOperand(0)); + Marker->eraseFromParent(); +} + +static bool rewriteContextAccess(Function &F) { + LLVM_DEBUG(dbgs() << "********** Rewrite Context Access ************\n"); + + auto ContextMarkers = collectContextMarkers(F); + + LLVM_DEBUG(dbgs() + << "There are " << ContextMarkers.size() + << " context markers\n"); + + if (ContextMarkers.empty()) + return false; + + for (auto *Marker : ContextMarkers) { + // For a series of nested accesses like `a.b.c` there would be + // multiple marker calls inserted when all accessed structures are + // marked with btf_decl_tag("ctx"), e.g.: + // + // %m0 = call @llvm.bpf.context.marker(%a) + // %b.addr = getelementptr %m0 ... + // %m1 = call @llvm.bpf.context.marker(%b.addr) + // %c.addr = getelementptr %m1 ... + // %c = load %c.addr + // + // The `collectContextMarkers` will return a vector with %m0 + // preceding the %m1. The `rewriteAccessChain` would remove %m1. + // Thus the parent check is needed to avoid removing %m1 twice. + if (!Marker->getParent()) + continue; + rewriteAccessChain(Marker); + removeMarker(Marker); + } + + return true; +} + +using MarkerSet = SmallPtrSet; + +static void collectChainedMarkers(User *Root, MarkerSet &Markers) { + if (auto *IRoot = dyn_cast(Root)) + if (isa(IRoot) || isContextMarkerCall(IRoot)) + for (auto *User : IRoot->users()) + if (isPointerOperand(IRoot, User)) + collectChainedMarkers(User, Markers); + if (isContextMarkerCall(Root)) + Markers.insert(cast(Root)); +} + +static bool simplifyContextMarkers(Function &F) { + auto ContextMarkers = collectContextMarkers(F); + auto Changed = false; + for (auto *Marker : ContextMarkers) + if (Marker->getParent()) { + MarkerSet ChainedMarkers; + for (auto *User : Marker->users()) + collectChainedMarkers(User, ChainedMarkers); + Changed |= !ChainedMarkers.empty(); + for (auto *Insn : ChainedMarkers) + removeMarker(Insn); + } + return Changed; +} + +namespace { + +class BPFRewriteContextAccessLegacyPass final : public FunctionPass { +public: + static char ID; + + BPFRewriteContextAccessLegacyPass() : FunctionPass(ID) {} + + bool runOnFunction(Function &F) override { + return rewriteContextAccess(F); + } +}; + +} // End anonymous namespace + +char BPFRewriteContextAccessLegacyPass::ID = 0; +INITIALIZE_PASS(BPFRewriteContextAccessLegacyPass, DEBUG_TYPE, + "BPF Rewrite Context Access", false, false) + +FunctionPass *llvm::createBPFRewriteContextAccessPass() { + return new BPFRewriteContextAccessLegacyPass(); +} + +PreservedAnalyses +llvm::BPFRewriteContextAccessPass::run(Function &F, FunctionAnalysisManager &AM) { + return rewriteContextAccess(F) + ? PreservedAnalyses::none() + : PreservedAnalyses::all(); +} + +PreservedAnalyses +llvm::BPFSimplifyContextMarkersPass::run(Function &F, FunctionAnalysisManager &AM) { + return simplifyContextMarkers(F) + ? 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 @@ -21,11 +21,13 @@ #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/PassManager.h" #include "llvm/MC/TargetRegistry.h" +#include "llvm/Passes/OptimizationLevel.h" #include "llvm/Passes/PassBuilder.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Target/TargetOptions.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Scalar/LoopPassManager.h" #include "llvm/Transforms/Scalar/SimplifyCFG.h" #include "llvm/Transforms/Utils/SimplifyCFGOptions.h" using namespace llvm; @@ -48,6 +50,7 @@ initializeBPFCheckAndAdjustIRPass(PR); initializeBPFMIPeepholePass(PR); initializeBPFMIPeepholeTruncElimPass(PR); + initializeBPFRewriteContextAccessLegacyPassPass(PR); } // DataLayout: little or big endian @@ -103,11 +106,12 @@ } 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(createBPFRewriteContextAccessPass()); PM.add(createBPFIRPeephole()); }); @@ -130,6 +134,7 @@ FunctionPassManager FPM; FPM.addPass(BPFAbstractMemberAccessPass(this)); FPM.addPass(BPFPreserveDITypePass()); + FPM.addPass(BPFSimplifyContextMarkersPass()); FPM.addPass(BPFIRPeepholePass()); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); }); @@ -137,6 +142,10 @@ OptimizationLevel Level) { FPM.addPass(SimplifyCFGPass(SimplifyCFGOptions().hoistCommonInsts(true))); }); + PB.registerScalarOptimizerLateEPCallback( + [=](FunctionPassManager &FPM, OptimizationLevel Level) { + FPM.addPass(BPFRewriteContextAccessPass()); + }); 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