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 @@ -3717,6 +3717,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) { @@ -3732,7 +3767,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()); @@ -3746,7 +3781,7 @@ const auto *PointeeT = PtrT->getPointeeType() ->getUnqualifiedDesugaredType(); if (const auto *RecT = dyn_cast(PointeeT)) - return RecT->getDecl()->hasAttr(); + return hasBPFPreserveAccessIndexAttr(RecT->getDecl()); return false; } @@ -3778,6 +3813,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 || @@ -4388,9 +4426,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()); @@ -4461,6 +4501,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()) { @@ -4483,7 +4525,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()); @@ -4498,7 +4540,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 @@ -7280,14 +7280,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-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,471 @@ +// 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; +} __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._]+]] = 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, i32 0, i32 0) +// 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, i32 0, i32 3, i64 2) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + + consume_ptr(ctx->ptr); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 4 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: call void @consume_ptr(ptr noundef [[r3]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 4) +// MID-NEXT: tail call void @consume_ptr(ptr noundef [[r0]]) + + consume_int(ctx->last); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 5 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[r2]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r3]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 5) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + + consume_int(ctx->inner->b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r4]], i32 0, i32 1 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 1) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context1_inner) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->inner->c[3]); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r4]], i32 0, i32 2 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = getelementptr inbounds [5 x i32], ptr [[r5]], i64 0, i64 3 +// FRONT-NEXT: [[r7:%[a-zA-Z0-9._]+]] = load i32, ptr [[r6]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r7]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 1) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context1_inner) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2, i64 3) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->inner_inline.b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load i32, ptr [[r4]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r5]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + + consume_int(ctx->inner_inline.c[3]); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r3]], i32 0, i32 2 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds [5 x i32], ptr [[r4]], i64 0, i64 3 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2, i32 2, i64 3) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + + consume_int(ctx[17].arr[2]); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i64 17 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r3]], i32 0, i32 3 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds [4 x i32], ptr [[r4]], i64 0, i64 2 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr 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, i32 0, i32 2, i32 3) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i64, ptr @"llvm.context1_inner2:0:0$0:0" +// MID-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr i8, ptr [[r0]], i64 [[r1]] +// MID-NEXT: [[r3:%[a-zA-Z0-9._]+]] = tail call ptr @llvm.bpf.passthrough.p0.p0({{.*}}, ptr [[r2]]) +// MID-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load i32, ptr [[r3]] +// MID-NEXT: tail call void @consume_int(i32 noundef [[r4]]) + + consume_int(ctx->inner_inline.inner_inline.a); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r3]], i32 0, i32 4 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = call ptr @llvm.preserve.struct.access.index.p0.p0(ptr elementtype(%struct.context1_inner2) [[r4]], i32 0, i32 0) +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// Note: because @"llvm.context1_inner2:0:0$0:0" represents a dynamic offset in bytes +// there is no way to reduce it to bpf.llvm.getelementptr.and.load as it's indices +// represent logical offsets (array element number, struct field index). +// MID-NEXT: [[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: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 1, ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 0) + + 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 elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 3, i64 2) + + ctx->ptr = (void*) 0; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 4 +// FRONT-NEXT: store ptr null, ptr [[r2]] + +// MID-NEXT: tail call void (ptr, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.p0(ptr readnone null, ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 4) + + ctx->last = 3; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 5 +// FRONT-NEXT: store i32 3, ptr [[r2]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 3, ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 5) + + ctx->inner->b = 4; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r4]], i32 0, i32 1 +// FRONT-NEXT: store i32 4, ptr [[r5]] + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 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, i32 0, i32 1) + + ctx->inner->c[3] = 5; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r4]], i32 0, i32 2 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = getelementptr inbounds [5 x i32], ptr [[r5]], i64 0, i64 3 +// FRONT-NEXT: store i32 5, ptr [[r6]] + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 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, i32 0, i32 2, i64 3) + + ctx->inner_inline.b = 6; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: store i32 6, ptr [[r4]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 6, ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2, i32 1) + + ctx->inner_inline.c[3] = 7; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1_inner, ptr [[r3]], i32 0, i32 2 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds [5 x i32], ptr [[r4]], i64 0, i64 3 +// FRONT-NEXT: store i32 7, ptr [[r5]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 7, ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2, i32 2, i64 3) + + ctx[19].arr[2] = 55; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i64 19 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r3]], i32 0, i32 3 +// Note: there is no marker call here, but this is fine: every array of a primitive +// type would have a base used as a member expression, thus a marker call always would be +// somewhere above. +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds [4 x i32], ptr [[r4]], i64 0, i64 2 +// FRONT-NEXT: store i32 55, ptr [[r5]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 55, ptr 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 elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2, i32 3) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i64, ptr @"llvm.context1_inner2:0:0$0:0" +// MID-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr i8, ptr [[r0]], i64 [[r1]] +// MID-NEXT: [[r3:%[a-zA-Z0-9._]+]] = tail call ptr @llvm.bpf.passthrough.p0.p0({{.*}}, ptr [[r2]]) +// MID-NEXT: store i32 34, ptr [[r3]] + + ctx->inner_inline.inner_inline.a = 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 0, i32 0) +// FRONT-NEXT: store i32 76, ptr [[r5]] + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load i64, ptr @"llvm.context1_inner2:0:0$0:0" +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr i8, ptr {{.*}}, i64 [[r0]] +// MID-NEXT: [[r2:%[a-zA-Z0-9._]+]] = tail call ptr @llvm.bpf.passthrough.p0.p0({{.*}}, ptr [[r1]]) +// MID-NEXT: store i32 76, ptr [[r2]] + + consume_ptr(ctx); +} + +struct context2 { + int _; + struct { + int _; + int b[10]; + } anon_struct; + struct { + int _; + int b[10]; + } anon_struct_arr[7]; + struct { int _1; int injected_field; }; + union { int _2; int injected_union_field; }; +} __ctx__ __preserve_access_index__; + +// FRONT-LABEL: define {{.*}} @nested_structs +// MID-LABEL: define {{.*}} @nested_structs +void nested_structs(struct context2 *ctx) { + consume_int(ctx->anon_struct.b[8]); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.anon, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds [10 x i32], ptr [[r4]], i64 0, i64 8 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 1, i64 8) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + + consume_int(ctx->anon_struct_arr[5].b[7]); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds [7 x %struct.anon.0], ptr [[r3]], i64 0, i64 5 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r4]]) +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.anon.0, ptr [[r5]], i32 0, i32 1 +// FRONT-NEXT: [[r7:%[a-zA-Z0-9._]+]] = getelementptr inbounds [10 x i32], ptr [[r6]], i64 0, i64 7 +// FRONT-NEXT: [[r8:%[a-zA-Z0-9._]+]] = load i32, ptr [[r7]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r8]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2, i64 5, i32 1, i64 7) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + + consume_int(ctx->injected_field); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 3 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.anon.1, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load i32, ptr [[r4]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r5]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 3, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + + consume_int(ctx->injected_union_field); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 4 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load i32, ptr [[r3]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r4]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 4) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) +} + +extern int magic2(int); + +struct bpf_sock { + int bound_dev_if; + int family; +} __ctx__ __preserve_access_index__; + +struct bpf_sockopt { + struct bpf_sock *sk; + int level; + int optlen; +} __ctx__ __preserve_access_index__; + +// FRONT-LABEL: define {{.*}} @known_load_sink_example_1 +// MID-LABEL: define {{.*}} @known_load_sink_example_1 +int known_load_sink_example_1(struct bpf_sockopt *ctx) +{ + unsigned g = 0; + switch (ctx->level) { +// MID: [[level3:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.bpf_sockopt) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 1) +// MID-NEXT: switch i32 [[level3]], label {{.*}} [ +// MID-NEXT: i32 10, label +// MID-NEXT: i32 20, label +// MID-NEXT: ] + + case 10: + g = magic2(ctx->sk->family); + break; +// MID: {{.*}}: +// MID-NEXT: [[sk4:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.bpf_sockopt) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 0) +// MID-NEXT: [[family5:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.bpf_sock) [[sk4]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID: br label + + case 20: + g = magic2(ctx->optlen); + break; +// MID: {{.*}}: +// MID-NEXT: [[optlen6:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.bpf_sockopt) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2) +// 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,1426 @@ +// 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._]+]] = 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, i32 0, i32 0) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 0 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->inner_inline.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 elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 0) +// MID-NEXT: tail call void @consume_int(i32 noundef [[a]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 1, i32 0 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->inner_inline.b); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r2]], i32 0, i32 1 +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load i32, ptr [[r3]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r4]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 1, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->inner_ptr->a); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r3]], i32 0, i32 0 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load i32, ptr [[r4]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r5]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// MID-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = load i32, ptr [[r1]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r2]]) + + consume_int(ctx->inner_ptr->b); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load i32, ptr [[r4]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r5]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r0]], i64 0, i32 1 +// MID-NEXT: [[r2:%[a-zA-Z0-9._]+]] = load i32, ptr [[r1]] +// MID-NEXT: tail call void @consume_int(i32 noundef [[r2]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r1]], i64 0, i32 1 +// BACK-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[r2]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r3]]) + + ctx->first = 1; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 0 +// FRONT-NEXT: store i32 1, ptr [[r2]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 1, ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 0) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 0 +// BACK-NEXT: store i32 1, ptr [[r0]] + + 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, 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 elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 0) + +// BACK-NEXT: [[r0:%[0-9]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 1, i32 0 +// 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 elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 1) + +// BACK-NEXT: [[r0:%[0-9]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 1, i32 1 +// BACK-NEXT: store i32 3, ptr [[r0]] + + ctx->inner_ptr->a = 4; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r3]], i32 0, i32 0 +// FRONT-NEXT: store i32 4, ptr [[r4]] + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// MID-NEXT: store i32 4, ptr [[r0]] + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: 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 elementtype(%struct.context1) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r0]], i64 0, i32 1 +// MID-NEXT: store i32 5, ptr [[r1]] + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r1]], i64 0, i32 1 +// BACK-NEXT: 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, i32 0, i32 1, i32 0) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i32 0, i32 1, i32 0 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->inner_inline.b); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load i32, ptr [[r4]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r5]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i32 0, i32 1, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->inner_ptr->a); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r4]], i32 0, i32 0 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.inner_vof) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 0) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r1]], i32 0, i32 0 +// BACK-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[r2]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r3]]) + + consume_int(ctx->inner_ptr->b); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r4]], i32 0, i32 1 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.inner_vof) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r1]], i32 0, i32 1 +// BACK-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[r2]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r3]]) + + ctx->inner_inline.a = 2; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r3]], i32 0, i32 0 +// FRONT-NEXT: store i32 2, ptr [[r4]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 2, ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 0) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i32 0, i32 1, i32 0 +// BACK-NEXT: store i32 2, ptr [[r0]] + + ctx->inner_inline.b = 3; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: store i32 3, ptr [[r4]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 3, ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i32 0, i32 1, i32 1 +// BACK-NEXT: store i32 3, ptr [[r0]] + + ctx->inner_ptr->a = 4; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r4]], i32 0, i32 0 +// FRONT-NEXT: store i32 4, ptr [[r5]] + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context2) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 2) +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 4, ptr elementtype(%struct.inner_vof) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 0) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r1]], i32 0, i32 0 +// BACK-NEXT: store i32 4, ptr [[r2]] + + 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, i32 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, i32 0, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r1]], i32 0, i32 1 +// BACK-NEXT: 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, i32 0, i32 2) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r0]], i64 0, i32 1 +// MID-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r1]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r1]], i64 0, i32 1 +// BACK-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r2]]) + + consume_ptr(&ctx->last); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr [[r1]], i32 0, i32 4 + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i64 0, i32 4 +// MID-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context1, ptr %ctx, i64 0, i32 4 +// BACK-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r0]]) + +// FRONT: 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, i32 0, i32 2) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r0]], i64 0, i32 1 +// MID-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r1]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context2, ptr %ctx, i32 0, i32 2 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r1]], i64 0, i32 1 +// BACK-NEXT: tail call void @consume_ptr(ptr noundef nonnull [[r2]]) + +// FRONT: ret void +// MID: ret void +// BACK: ret void +} + +struct context3 { + int first; + struct inner_no_vof *no_vof_ptr; + struct inner_no_vof no_vof_arr[5]; + struct inner_no_vof *no_vof_arr_ptr[7]; + struct inner_vof *vof_ptr; + struct inner_vof vof_arr[5]; + struct inner_vof *vof_arr_ptr[7]; + int arr[4]; +} __ctx__; + +// FRONT-LABEL: define dso_local void @array_access +// MID-LABEL: define dso_local void @array_access +// BACK-LABEL: define dso_local void @array_access +void array_access(struct context3 *ctx) { + consume_int(ctx->no_vof_ptr[1].b); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r3]], i64 1 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r4]], i32 0, i32 1 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 1) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r0]], i64 1, i32 1 +// MID-NEXT: [[r2:%[a-zA-Z0-9._]+]] = load i32, ptr [[r1]] +// MID-NEXT: tail call void @consume_int(i32 noundef [[r2]]) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r1]], i64 1, i32 1 +// BACK-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[r2]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r3]]) + + consume_int(ctx->no_vof_arr[2].b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds [5 x %struct.inner_no_vof], ptr [[r2]], i64 0, i64 2 +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load i32, ptr [[r4]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r5]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2, i64 2, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 2, i64 2, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->no_vof_arr_ptr[3]->a); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 3 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds [7 x ptr], ptr [[r2]], i64 0, i64 3 +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r3]] +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r4]], i32 0, i32 0 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 3, i64 3) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// MID-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 3, i64 3 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = load i32, ptr [[r1]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r2]]) + + consume_int(ctx->vof_ptr[1].b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 4 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r4]], i64 1 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r5]]) +// FRONT-NEXT: [[r7:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r6]], i32 0, i32 1 +// FRONT-NEXT: [[r8:%[a-zA-Z0-9._]+]] = load i32, ptr [[r7]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r8]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 4) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.inner_vof) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i64 1, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 4 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r1]], i64 1, i32 1 +// BACK-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[r2]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r3]]) + + consume_int(ctx->vof_arr[2].b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 5 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds [5 x %struct.inner_vof], ptr [[r3]], i64 0, i64 2 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r4]]) +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r5]], i32 0, i32 1 +// FRONT-NEXT: [[r7:%[a-zA-Z0-9._]+]] = load i32, ptr [[r6]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r7]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 5, i64 2, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 5, i64 2, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + consume_int(ctx->vof_arr_ptr[3]->b); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 6 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds [7 x ptr], ptr [[r2]], i64 0, i64 3 +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r3]] +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r4]]) +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r5]], i32 0, i32 1 +// FRONT-NEXT: [[r7:%[a-zA-Z0-9._]+]] = load i32, ptr [[r6]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r7]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 6, i64 3) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.inner_vof) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 6, i64 3 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r1]], i32 0, i32 1 +// BACK-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[r2]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r3]]) + + consume_int(ctx[12].arr[3]); +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i64 12 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r3]], i32 0, i32 7 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds [4 x i32], ptr [[r4]], i64 0, i64 3 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = load i32, ptr [[r5]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r6]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i64 12, i32 7, i64 3) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i64 12, i32 7, i64 3 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[r0]] +// BACK-NEXT: tail call void @consume_int(i32 noundef [[r1]]) + + ctx->no_vof_ptr[1].b = 2; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 1 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r3]], i64 1 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r4]], i32 0, i32 1 +// FRONT-NEXT: store i32 2, ptr [[r5]] + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 1) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r0]], i64 1, i32 1 +// MID-NEXT: store i32 2, ptr [[r1]] + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r1]], i64 1, i32 1 +// BACK-NEXT: store i32 2, ptr [[r2]] + + ctx->no_vof_arr[2].b = 4; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds [5 x %struct.inner_no_vof], ptr [[r2]], i64 0, i64 2 +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r3]], i32 0, i32 1 +// FRONT-NEXT: store i32 4, ptr [[r4]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 4, ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2, i64 2, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 2, i64 2, i32 1 +// BACK-NEXT: store i32 4, ptr [[r0]] + + ctx->no_vof_arr_ptr[3]->b = 6; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 3 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds [7 x ptr], ptr [[r2]], i64 0, i64 3 +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r3]] +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r4]], i32 0, i32 1 +// FRONT-NEXT: store i32 6, ptr [[r5]] + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 3, i64 3) +// MID-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r0]], i64 0, i32 1 +// MID-NEXT: store i32 6, ptr [[r1]] + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 3, i64 3 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_no_vof, ptr [[r1]], i64 0, i32 1 +// BACK-NEXT: store i32 6, ptr [[r2]] + + ctx->vof_ptr[1].b = 2; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 4 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r2]] +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r3]]) +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r4]], i64 1 +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r5]]) +// FRONT-NEXT: [[r7:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r6]], i32 0, i32 1 +// FRONT-NEXT: store i32 2, ptr [[r7]] + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 4) +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 2, ptr elementtype(%struct.inner_vof) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i64 1, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 4 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r1]], i64 1, i32 1 +// BACK-NEXT: store i32 2, ptr [[r2]] + + ctx->vof_arr[2].b = 4; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 5 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds [5 x %struct.inner_vof], ptr [[r3]], i64 0, i64 2 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r4]]) +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r5]], i32 0, i32 1 +// FRONT-NEXT: store i32 4, ptr [[r6]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 4, ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 5, i64 2, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 5, i64 2, i32 1 +// BACK-NEXT: store i32 4, ptr [[r0]] + + ctx->vof_arr_ptr[3]->b = 6; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i32 0, i32 6 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = getelementptr inbounds [7 x ptr], ptr [[r2]], i64 0, i64 3 +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r3]] +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r4]]) +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r5]], i32 0, i32 1 +// FRONT-NEXT: store i32 6, ptr [[r6]] + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 6, i64 3) +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 6, ptr elementtype(%struct.inner_vof) [[r0]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i32 0, i32 6, i64 3 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[r0]] +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.inner_vof, ptr [[r1]], i32 0, i32 1 +// BACK-NEXT: store i32 6, ptr [[r2]] + + ctx[15].arr[3] = 22; +// FRONT-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r0]]) +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r1]], i64 15 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr [[r3]], i32 0, i32 7 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = getelementptr inbounds [4 x i32], ptr [[r4]], i64 0, i64 3 +// FRONT-NEXT: store i32 22, ptr [[r5]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 22, ptr elementtype(%struct.context3) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i64 15, i32 7, i64 3) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context3, ptr %ctx, i64 15, i32 7, i64 3 +// BACK-NEXT: store i32 22, ptr [[r0]] + + consume_ptr(ctx); + +// FRONT: ret void +// MID: ret void +// BACK: ret void +} + +typedef int aligned_int __attribute__((aligned(128))); + +struct context4 { + 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 0 +// 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 7, i1 true, i32 0, i32 0) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i32 0, i32 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 2 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[r2]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r3]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context4) %ctx, i1 false, i8 0, i8 1, i8 7, i1 true, i32 0, i32 2) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i32 0, i32 2 +// 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 4 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load volatile i32, ptr [[r2]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r3]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context4) %ctx, i1 true, i8 0, i8 1, i8 7, i1 true, i32 0, i32 4) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i32 0, i32 4 +// 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 0 +// 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 7, i1 true, i32 0, i32 0) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i32 0, i32 0 +// 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 0 +// 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 7, i1 true, i32 0, i32 0) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i32 0, i32 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 2 +// FRONT-NEXT: store i32 2, ptr [[r2]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 2, ptr elementtype(%struct.context4) %ctx, i1 false, i8 0, i8 1, i8 7, i1 true, i32 0, i32 2) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i32 0, i32 2 +// 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 4 +// FRONT-NEXT: store volatile i32 3, ptr [[r2]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 3, ptr elementtype(%struct.context4) %ctx, i1 true, i8 0, i8 1, i8 7, i1 true, i32 0, i32 4) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i32 0, i32 4 +// 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 0 +// 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 7, i1 true, i32 0, i32 0) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context4, ptr %ctx, i32 0, i32 0 +// 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 { + 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 0 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load i32, ptr [[r3]] +// FRONT-NEXT: call void @consume_int(i32 noundef [[r4]]) + +// MID: [[r0:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.context5) %ctx1, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 0) +// MID-NEXT: tail call void @consume_int(i32 noundef [[r0]]) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context5, ptr %ctx1, i32 0, i32 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 0 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: [[r4:%[a-zA-Z0-9._]+]] = load float, ptr [[r3]] +// FRONT-NEXT: call void @consume_float(float noundef [[r4]]) + +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = tail call float (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.f32(ptr elementtype(%struct.context5) %ctx2, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 0) +// MID-NEXT: tail call void @consume_float(float noundef [[r0]]) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context5, ptr %ctx2, i32 0, i32 0 +// 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 0 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: store i32 2, ptr [[r3]] + +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 2, ptr elementtype(%struct.context5) %ctx1, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 0) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context5, ptr %ctx1, i32 0, i32 0 +// 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 0 +// FRONT-NEXT: [[r3:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r2]]) +// FRONT-NEXT: store float {{.*}}, ptr [[r3]] + +// MID-NEXT: tail call void (float, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.f32(float {{.*}}, ptr elementtype(%struct.context5) %ctx2, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 0) + +// BACK-NEXT: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context5, ptr %ctx2, i32 0, i32 0 +// 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, i32 0, i32 1) +// MID: @consume_int + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i32 0, i32 1 +// BACK: load i8, ptr [[r0]] +// BACK: @consume_int + + consume_int(ctx->b); +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr {{.*}}) +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr [[r0]], i32 0, i32 1 +// FRONT-NEXT: load i8, ptr [[r1]] +// FRONT: @consume_int + +// MID: [[r0:%[a-zA-Z0-9._]+]] = tail call i8 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i8(ptr elementtype(%struct.context6) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID: @consume_int + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i32 0, i32 1 +// BACK: load i8, ptr [[r0]] +// BACK: tail call void @consume_int + + ctx->a = 1; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr {{.*}}) +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr [[r0]], i32 0, i32 1 +// FRONT-NEXT: load i8, ptr [[r1]] +// FRONT: store {{.*}}, ptr [[r1]] + +// MID-NEXT: tail call i8 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i8(ptr elementtype(%struct.context6) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID: tail call void (i8, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i8(i8 {{.*}}, ptr elementtype(%struct.context6) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i32 0, i32 1 +// BACK: load i8, ptr [[r0]] +// BACK: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i32 0, i32 1 +// BACK: store i8 {{.*}}, ptr [[r1]] + + ctx->b = 2; +// FRONT: [[r0:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr {{.*}}) +// FRONT-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr [[r0]], i32 0, i32 1 +// FRONT-NEXT: load i8, ptr [[r1]] +// FRONT: store {{.*}}, ptr [[r1]] + +// MID: tail call i8 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i8(ptr elementtype(%struct.context6) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID: tail call void (i8, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i8(i8 {{.*}}, ptr elementtype(%struct.context6) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) + +// BACK: [[r0:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i32 0, i32 1 +// BACK: load i8, ptr [[r0]] +// BACK: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.context6, ptr %ctx, i32 0, i32 1 +// BACK: store i8 {{.*}}, ptr [[r1]] + + consume_ptr(ctx); +// 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: [[dst_port:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.pattern_bug1, ptr %sk, i64 0, i32 1 +// MID-NEXT: [[r0:%[a-zA-Z0-9._]+]] = load i32, ptr [[dst_port]] +// MID-NEXT: [[cmp:%[a-zA-Z0-9._]+]] = icmp eq i32 [[r0]], 1911 +// MID-NEXT: [[conv:%[a-zA-Z0-9._]+]] = zext i1 [[cmp]] to i32 +// MID-NEXT: ret i32 [[conv]] +} + +struct bpf_sock { + int bound_dev_if; + int family; +} __ctx__; + +struct bpf_sockopt { + 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 1 +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = load i32, ptr [[level]] +// FRONT-NEXT: switch i32 [[r2]], label [[sw_epilog:%[a-zA-Z0-9._]+]] [ +// FRONT-NEXT: i32 10, label [[sw_bb:%[a-zA-Z0-9._]+]] +// FRONT-NEXT: i32 20, label [[sw_bb1:%[a-zA-Z0-9._]+]] +// FRONT-NEXT: ] + +// MID: [[level3:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.bpf_sockopt) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 1) +// MID-NEXT: switch i32 [[level3]], label [[sw_epilog:%[a-zA-Z0-9._]+]] [ +// MID-NEXT: i32 10, label [[sw_bb:%[a-zA-Z0-9._]+]] +// MID-NEXT: i32 20, label [[sw_bb1:%[a-zA-Z0-9._]+]] +// MID-NEXT: ] + +// BACK: [[level39:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.bpf_sockopt, ptr %ctx, i32 0, i32 1 +// 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 0 +// FRONT-NEXT: [[r5:%[a-zA-Z0-9._]+]] = load ptr, ptr [[sk]] +// FRONT-NEXT: [[r6:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r5]]) +// FRONT-NEXT: [[family:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.bpf_sock, ptr [[r6]], i32 0, i32 1 +// FRONT-NEXT: [[r7:%[a-zA-Z0-9._]+]] = load i32, ptr [[family]] +// FRONT-NEXT: [[call:%[a-zA-Z0-9._]+]] = call i32 @magic2(i32 noundef [[r7]]) +// FRONT-NEXT: store i32 [[call]], ptr %g +// FRONT-NEXT: br label %sw.epilog + +// MID: sw.bb: +// MID-NEXT: [[sk4:%[a-zA-Z0-9._]+]] = tail call ptr (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.p0(ptr elementtype(%struct.bpf_sockopt) %ctx, i1 false, i8 0, i8 1, i8 3, i1 true, i32 0, i32 0) +// MID-NEXT: [[family5:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.bpf_sock) [[sk4]], i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: br label [[sw_epilog_sink_split:%[a-zA-Z0-9._]+]] + +// BACK: sw.{{.*}}: +// BACK-NEXT: [[sk410:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.bpf_sockopt, ptr %ctx, i32 0, i32 0 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load ptr, ptr [[sk410]] +// BACK-NEXT: [[family511:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.bpf_sock, ptr [[r1]], i32 0, i32 1 +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = load i32, ptr [[family511]] +// BACK-NEXT: br label %sw.epilog.sink.split + + case 20: + g = magic2(ctx->optlen); + break; +// FRONT: [[r8:%[a-zA-Z0-9._]+]] = load ptr, ptr {{.*}} +// FRONT-NEXT: [[r9:%[a-zA-Z0-9._]+]] = call ptr @llvm.bpf.context.marker.p0.p0(ptr [[r8]]) +// FRONT-NEXT: [[optlen:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.bpf_sockopt, ptr [[r9]], i32 0, i32 2 +// FRONT-NEXT: [[r10:%[a-zA-Z0-9._]+]] = load i32, ptr [[optlen]] +// FRONT-NEXT: [[call2:%[a-zA-Z0-9._]+]] = call i32 @magic2(i32 noundef [[r10]]) +// FRONT-NEXT: store i32 [[call2]], ptr %g +// FRONT-NEXT: br label %sw.epilog + +// MID: sw.{{.*}}: +// MID-NEXT: [[optlen6:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.bpf_sockopt) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2) +// MID-NEXT: br label %sw.epilog.sink.split + +// BACK: sw.{{.*}}: +// BACK-NEXT: [[optlen612:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.bpf_sockopt, ptr %ctx, i32 0, i32 2 +// 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 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 0 +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = load i32, ptr [[priority]] +// FRONT-NEXT: switch i32 [[r2]], label [[sw_epilog:%[a-zA-Z0-9._]+]] [ +// FRONT-NEXT: i32 10, label [[sw_bb:%[a-zA-Z0-9._]+]] +// FRONT-NEXT: i32 20, label [[sw_bb1:%[a-zA-Z0-9._]+]] +// FRONT-NEXT: ] + +// MID: [[priority3:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.__sk_buff) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 0) +// MID-NEXT: switch i32 [[priority3]], label [[sw_epilog:%[a-zA-Z0-9._]+]] [ +// MID-NEXT: i32 10, label [[sw_bb:%[a-zA-Z0-9._]+]] +// MID-NEXT: i32 20, label [[sw_bb1:%[a-zA-Z0-9._]+]] +// MID-NEXT: ] + +// BACK: [[priority36:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr %ctx, i32 0, i32 0 +// 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 1 +// FRONT-NEXT: store i32 3, ptr [[mark]] +// FRONT-NEXT: br label %sw.epilog + +// MID: sw.bb: +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 3, ptr elementtype(%struct.__sk_buff) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: br label %sw.epilog + +// BACK: sw.{{.*}}: +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr %ctx, i32 0, i32 1 +// 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 0 +// FRONT-NEXT: store i32 4, ptr [[priority2]] +// FRONT-NEXT: br label %sw.epilog + +// MID: sw.bb1: +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 4, ptr elementtype(%struct.__sk_buff) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 0) +// MID-NEXT: br label %sw.epilog + +// BACK: sw.{{.*}}: +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr %ctx, i32 0, i32 0 +// 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 2 +// FRONT-NEXT: [[r2:%[a-zA-Z0-9._]+]] = load i32, ptr [[tc_index]] +// FRONT-NEXT: switch i32 [[r2]], label [[sw_epilog:%[a-zA-Z0-9._]+]] [ +// FRONT-NEXT: i32 10, label [[sw_bb:%[a-zA-Z0-9._]+]] +// FRONT-NEXT: i32 20, label [[sw_bb3:%[a-zA-Z0-9._]+]] +// FRONT-NEXT: ] + +// MID: [[tc_index8:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.__sk_buff) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 2) +// MID-NEXT: switch i32 [[tc_index8]], label [[sw_epilog:%[a-zA-Z0-9._]+]] [ +// MID-NEXT: i32 10, label [[sw_bb:%[a-zA-Z0-9._]+]] +// MID-NEXT: i32 20, label [[sw_bb3:%[a-zA-Z0-9._]+]] +// MID-NEXT: ] + +// BACK: [[tc_index814:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr %ctx, i32 0, i32 2 +// 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 1 +// 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 1 +// FRONT-NEXT: store i32 [[call1]], ptr [[mark2]] +// FRONT-NEXT: br label %sw.epilog + +// MID: sw.bb: +// MID-NEXT: [[mark7:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.__sk_buff) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: [[call:%[a-zA-Z0-9._]+]] = tail call i32 @magic2(i32 noundef [[mark7]]) +// MID-NEXT: [[call1:%[a-zA-Z0-9._]+]] = tail call i32 @magic() +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 [[call1]], ptr elementtype(%struct.__sk_buff) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 1) +// MID-NEXT: br label %sw.epilog + +// BACK: sw.{{.*}}: +// BACK-NEXT: [[mark715:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr %ctx, i32 0, i32 1 +// BACK-NEXT: [[r1:%[a-zA-Z0-9._]+]] = load i32, ptr [[mark715]] +// BACK-NEXT: [[call:%[a-zA-Z0-9._]+]] = tail call i32 @magic2(i32 noundef [[r1]]) +// BACK-NEXT: [[call1:%[a-zA-Z0-9._]+]] = tail call i32 @magic() +// BACK-NEXT: [[r2:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr %ctx, i32 0, i32 1 +// 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 0 +// 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 0 +// FRONT-NEXT: store i32 [[call5]], ptr [[priority6]] +// FRONT-NEXT: br label %sw.epilog + +// MID: sw.{{.*}}: +// MID-NEXT: [[priority9:%[a-zA-Z0-9._]+]] = tail call i32 (ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.load.i32(ptr elementtype(%struct.__sk_buff) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 0) +// MID-NEXT: [[call4:%[a-zA-Z0-9._]+]] = tail call i32 @magic2(i32 noundef [[priority9]]) +// MID-NEXT: [[call5:%[a-zA-Z0-9._]+]] = tail call i32 @magic() +// MID-NEXT: tail call void (i32, ptr, i1, i8, i8, i8, i1, ...) +// MID-SAME: @llvm.bpf.getelementptr.and.store.i32(i32 [[call5]], ptr elementtype(%struct.__sk_buff) %ctx, i1 false, i8 0, i8 1, i8 2, i1 true, i32 0, i32 0) +// MID-NEXT: br label %sw.epilog + +// BACK: sw.{{.*}}: +// BACK-NEXT: [[priority916:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr %ctx, i32 0, i32 0 +// BACK-NEXT: [[r3:%[a-zA-Z0-9._]+]] = load i32, ptr [[priority916]] +// BACK-NEXT: [[call4:%[a-zA-Z0-9._]+]] = tail call i32 @magic2(i32 noundef [[r3]]) +// BACK-NEXT: [[call5:%[a-zA-Z0-9._]+]] = tail call i32 @magic() +// BACK-NEXT: [[r4:%[a-zA-Z0-9._]+]] = getelementptr inbounds %struct.__sk_buff, ptr %ctx, i32 0, i32 0 +// 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 @@ -1963,6 +1963,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,15 @@ public: PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); }; + +class BPFRewriteContextAccessPass + : 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,376 @@ +//===------ 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; + Type *ResultElementType; + SmallVector Indices; + SmallVector Members; +}; +} // 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 collectGEPChainInfo(SmallVector &GEPs, + GEPChainInfo &Info) { + if (GEPs.empty()) + return false; + + auto *First = GEPs[0]; + Info.InBounds = First->isInBounds(); + Info.SourceElementType = First->getSourceElementType(); + Info.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()) + return false; + if (!GEP->getSourceElementType() || + GEP->getSourceElementType() != Info.ResultElementType) + return false; + Info.InBounds &= GEP->isInBounds(); + Info.Indices.append(GEP->idx_begin() + 1, GEP->idx_end()); + Info.Members.push_back(GEP); + Info.ResultElementType = GEP->getResultElementType(); + } + + 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 (!collectGEPChainInfo(GEPs, GEPChain)) { + errs() << "Can't generate gep and load/store for " + << *Root << ": irreducable GEP chain\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; +} + +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(); +} 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 @@ -48,6 +48,7 @@ initializeBPFCheckAndAdjustIRPass(PR); initializeBPFMIPeepholePass(PR); initializeBPFMIPeepholeTruncElimPass(PR); + initializeBPFRewriteContextAccessLegacyPassPass(PR); } // DataLayout: little or big endian @@ -103,11 +104,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 +132,7 @@ FunctionPassManager FPM; FPM.addPass(BPFAbstractMemberAccessPass(this)); FPM.addPass(BPFPreserveDITypePass()); + FPM.addPass(BPFRewriteContextAccessPass()); FPM.addPass(BPFIRPeepholePass()); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); }); 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