BPF: fix FIELD_EXISTS relocation with array subscripts

Lorenz Bauer reported an issue in bpf mailing list ([1]) where
for FIELD_EXISTS relocation, if the object is an array subscript,
the patched immediate is the object offset from the base address,
instead of 1.

Currently in BPF AbstractMemberAccess pass, the final offset
from the base address is the patched offset except FIELD_EXISTS
which is 1 unconditionally. In this particular case, the last
data structure access is not a field (struct/union offset)
so it didn't hit the place to set patched immediate to be 1.

This patch fixed the issue by checking the relocation type.
If the type is FIELD_EXISTS, just set to 1.
Tested by modifying some bpf selftests, libbpf is okay with
such types with FIELD_EXISTS relocation.

 [1] https://lore.kernel.org/bpf/CACAyw99n-cMEtVst7aK-3BfHb99GMEChmRLCvhrjsRpHhPrtvA@mail.gmail.com/

Differential Revision: https://reviews.llvm.org/D102036
diff --git a/llvm/lib/Target/BPF/BPFAbstractMemberAccess.cpp b/llvm/lib/Target/BPF/BPFAbstractMemberAccess.cpp
index cd994a9..a12ef39 100644
--- a/llvm/lib/Target/BPF/BPFAbstractMemberAccess.cpp
+++ b/llvm/lib/Target/BPF/BPFAbstractMemberAccess.cpp
@@ -872,6 +872,8 @@
 
     if (CInfo.Kind == BPFPreserveFieldInfoAI) {
       InfoKind = CInfo.AccessIndex;
+      if (InfoKind == BPFCoreSharedInfo::FIELD_EXISTENCE)
+        PatchImm = 1;
       break;
     }
 
diff --git a/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-existence-4.ll b/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-existence-4.ll
new file mode 100644
index 0000000..eb2b649
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-existence-4.ll
@@ -0,0 +1,109 @@
+; RUN: opt -O2 -mtriple=bpf-pc-linux %s | llvm-dis > %t1
+; RUN: llc -filetype=asm -o - %t1 | FileCheck %s
+; RUN: llc -mattr=+alu32 -filetype=asm -o - %t1 | FileCheck %s
+; Source code:
+;   #define BPF_FIELD_EXISTS 2
+;   unsigned test1() {
+;     struct t {
+;       int val;
+;     } bar;
+;     return __builtin_preserve_field_info((&bar)[1], BPF_FIELD_EXISTS);
+;   }
+; Compilation flag:
+;   clang -target bpf -O2 -g -S -emit-llvm -Xclang -disable-llvm-passes test.c
+
+%struct.t = type { i32 }
+
+; Function Attrs: nounwind
+define dso_local i32 @test1() #0 !dbg !6 {
+entry:
+  %bar = alloca %struct.t, align 4
+  %0 = bitcast %struct.t* %bar to i8*, !dbg !20
+  call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #5, !dbg !20
+  call void @llvm.dbg.declare(metadata %struct.t* %bar, metadata !11, metadata !DIExpression()), !dbg !21
+  %1 = call %struct.t* @llvm.preserve.array.access.index.p0s_struct.ts.p0s_struct.ts(%struct.t* %bar, i32 0, i32 1), !dbg !22, !llvm.preserve.access.index !4
+  %2 = call i32 @llvm.bpf.preserve.field.info.p0s_struct.ts(%struct.t* %1, i64 2), !dbg !23
+  %3 = bitcast %struct.t* %bar to i8*, !dbg !24
+  call void @llvm.lifetime.end.p0i8(i64 4, i8* %3) #5, !dbg !24
+  ret i32 %2, !dbg !25
+}
+
+; CHECK:             r0 = 1
+; CHECK-NEXT:        exit
+
+; CHECK:             .long   26                              # BTF_KIND_STRUCT(id = 4)
+; CHECK-NEXT:        .long   67108865                        # 0x4000001
+; CHECK-NEXT:        .long   4
+; CHECK-NEXT:        .long   28
+; CHECK-NEXT:        .long   5
+; CHECK-NEXT:        .long   0                               # 0x0
+; CHECK-NEXT:        .long   32                              # BTF_KIND_INT(id = 5)
+; CHECK-NEXT:        .long   16777216                        # 0x1000000
+; CHECK-NEXT:        .long   4
+; CHECK-NEXT:        .long   16777248                        # 0x1000020
+
+; CHECK:             .byte   116                             # string offset=26
+; CHECK:             .ascii  "val"                           # string offset=28
+; CHECK:             .ascii  "int"                           # string offset=32
+; CHECK:             .byte   49                              # string offset=36
+
+; CHECK:             .long   16                              # FieldReloc
+; CHECK-NEXT:        .long   {{[0-9]+}}                      # Field reloc section string
+; CHECK-NEXT:        .long   1
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   4
+; CHECK-NEXT:        .long   36
+; CHECK-NEXT:        .long   2
+
+; Function Attrs: argmemonly nofree nosync nounwind willreturn
+declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
+
+; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #2
+
+; Function Attrs: nofree nosync nounwind readnone willreturn
+declare %struct.t* @llvm.preserve.array.access.index.p0s_struct.ts.p0s_struct.ts(%struct.t*, i32 immarg, i32 immarg) #3
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0s_struct.ts(%struct.t*, i64 immarg) #4
+
+; Function Attrs: argmemonly nofree nosync nounwind willreturn
+declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
+
+attributes #0 = { nounwind "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #1 = { argmemonly nofree nosync nounwind willreturn }
+attributes #2 = { nofree nosync nounwind readnone speculatable willreturn }
+attributes #3 = { nofree nosync nounwind readnone willreturn }
+attributes #4 = { nounwind readnone }
+attributes #5 = { nounwind }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!15, !16, !17, !18}
+!llvm.ident = !{!19}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 13.0.0 (https://github.com/llvm/llvm-project.git 2e0ee68dc85c0a2b7e65e489a60ab363393b06a8)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{}
+!3 = !{!4}
+!4 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !5, size: 64)
+!5 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t", scope: !6, file: !1, line: 3, size: 32, elements: !12)
+!6 = distinct !DISubprogram(name: "test1", scope: !1, file: !1, line: 2, type: !7, scopeLine: 2, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10)
+!7 = !DISubroutineType(types: !8)
+!8 = !{!9}
+!9 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!10 = !{!11}
+!11 = !DILocalVariable(name: "bar", scope: !6, file: !1, line: 5, type: !5)
+!12 = !{!13}
+!13 = !DIDerivedType(tag: DW_TAG_member, name: "val", scope: !5, file: !1, line: 4, baseType: !14, size: 32)
+!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!15 = !{i32 7, !"Dwarf Version", i32 4}
+!16 = !{i32 2, !"Debug Info Version", i32 3}
+!17 = !{i32 1, !"wchar_size", i32 4}
+!18 = !{i32 7, !"frame-pointer", i32 2}
+!19 = !{!"clang version 13.0.0 (https://github.com/llvm/llvm-project.git 2e0ee68dc85c0a2b7e65e489a60ab363393b06a8)"}
+!20 = !DILocation(line: 3, column: 3, scope: !6)
+!21 = !DILocation(line: 5, column: 5, scope: !6)
+!22 = !DILocation(line: 6, column: 40, scope: !6)
+!23 = !DILocation(line: 6, column: 10, scope: !6)
+!24 = !DILocation(line: 7, column: 1, scope: !6)
+!25 = !DILocation(line: 6, column: 3, scope: !6)