llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-codegen
Author: yonghong-song
<details>
<summary>Changes</summary>
A member access is relocated with CO-RE either inside
`__builtin_preserve_access_index()` or through the preserve_access_index
attribute on the record. For an array member with more than one subscript, the
builtin works, but the attribute does not. With
```
#define __pai __attribute__((preserve_access_index))
struct S { long x; int y; } __pai;
union V { long l; struct S m[2][3]; } __pai;
struct T {
struct S a[2][3];
union V g[3][2];
struct S (*q)[3];
struct S *p[3];
struct S v[2][3][4];
} __pai;
```
the relocations for `t->a[1][2].y` are:
-`__builtin_preserve_access_index(t->a[1][2].y)`: one relocation, `struct
T::a[1][2].y`.
- `t->a[1][2].y` with only the attribute: `struct T::a[1]` and `struct
S::y`. The offset of `[2]` is fixed at 32 bytes at compile time, which is wrong
if the size of `struct S` changes.
This patch closes this gap.
With only the attribute:
- `emitArraySubscriptGEP()` emits a constant subscript as
`llvm.preserve.array.access.index`, instead of a plain GEP, only if
`IsPreserveAIArrayBase()` finds that the base of the subscript comes from a
record with the attribute.
- `IsPreserveAIArrayBase()` stripped implicit casts from the base and
accepted only a member of such a record, e.g., `t->a in t->a[1]`, or a
pointer to such a record.
- In `t->a[1][2]`, the base of `[2]` is the subscript `t->a[1]`, so
`[2]` became a plain GEP.
- The BPF backend follows only GEPs with all-zero indices, so that GEP ended
the CO-RE access chain. The rest of the access started a new chain, and the
offset of `[2]` was not relocated.
- Parentheses had the same effect, e.g., in `(t->a)[2]`, stripping stopped
at the `ParenExpr`.
Now `IsPreserveAIArrayBase()`:
- For the second and later subscripts, the base is another
`ArraySubscriptExpr`, e.g., `t->a[1]` for `[2]` in `t->a[1][2]`. It
replaces the `ArraySubscriptExpr` with its base, repeatedly, until the base is
no longer a subscript, e.g., the `MemberExpr t->a` for `[2]`, and checks
that as before.
- Uses `IgnoreParenImpCasts()` instead of `IgnoreImpCasts()`, so a
`ParenExpr` is skipped like an `ImplicitCastExpr`.
- Still stops at an explicit cast or any other operator.
The relocations with only the attribute are:
```
- t->a[1][2].y before: struct T::a[1], struct S::y, [2] fixed at 32 bytes
after: struct T::a[1][2].y
- t->g[2][1].m[1][2].y, struct -> 2-D array -> union -> 2-D array
-> struct before: struct T::g[2], union V::m[1], struct S::y, g's [1] and
m's [2] fixed after: struct T::g[2][1].m[1][2].y
- t->q[1][2].y, a pointer to an array before: struct T::q, struct S::[3],
struct S::y, [2] fixed at 32 bytes after: struct T::q, struct S::[5].y
- t->p[1][2].y, an array of pointers before: struct T::p[1], struct S::y,
[2] fixed at 32 bytes after: struct T::p[1], struct S::[2].y
- (t->a[1])[2].y before: struct T::a[1], struct S::y, [2] fixed at 32
bytes after: struct T::a[1][2].y, the same as without parentheses
- t->v[n][2][3].y, a 3-D array with a variable n before: struct T::v,
struct S::y, [2][3] fixed at 176 bytes after: struct T::v, struct S::[11].y
[n] is not relocated in either case, since a variable subscript cannot be
preserved, but [2] and [3] after it now are, folded into one index, 2 * 4 + 3 =
11.
```
In each case, "after" is the same as with
`__builtin_preserve_access_index()`. A pointer load still ends the chain, as in
the `t->q` and `t->p` examples, but the access after the load is now
relocated as a whole.
---
Patch is 41.99 KiB, truncated to 20.00 KiB below, full version:
https://github.com/llvm/llvm-project/pull/237589.diff
3 Files Affected:
- (modified) clang/lib/CodeGen/CGExpr.cpp (+5-1)
- (added) clang/test/CodeGen/bpf-attr-preserve-access-index-9.c (+195)
- (added) llvm/test/CodeGen/BPF/CORE/offset-reloc-subarray-record.ll (+469)
``````````diff
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 98133ecf1b48fc..ae5f4e723f5129 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -4902,7 +4902,11 @@ static bool IsPreserveAIArrayBase(CodeGenFunction &CGF,
const Expr *ArrayBase) {
// p[1].a
// p[1] will generate a DeclRefExpr and p[1].a is a MemberExpr.
// p->b[5] is a MemberExpr example.
- const Expr *E = ArrayBase->IgnoreImpCasts();
+ const Expr *E = ArrayBase->IgnoreParenImpCasts();
+ // In p->b[1][2], the base of [2] is the subscript p->b[1]. Step through
+ // subscripts to the member p->b.
+ while (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E))
+ E = ASE->getBase()->IgnoreParenImpCasts();
if (const auto *ME = dyn_cast<MemberExpr>(E))
return ME->getMemberDecl()->hasAttr<BPFPreserveAccessIndexAttr>();
diff --git a/clang/test/CodeGen/bpf-attr-preserve-access-index-9.c
b/clang/test/CodeGen/bpf-attr-preserve-access-index-9.c
new file mode 100644
index 00000000000000..dd9b1ea7911022
--- /dev/null
+++ b/clang/test/CodeGen/bpf-attr-preserve-access-index-9.c
@@ -0,0 +1,195 @@
+// REQUIRES: bpf-registered-target
+// RUN: %clang_cc1 -triple bpf -emit-llvm -debug-info-kind=limited
-disable-llvm-passes %s -o - | FileCheck %s
+
+#define __reloc__ __attribute__((preserve_access_index))
+
+// Every subscript of a multi-dimensional array member is preserved, so the
+// record behind the array stays in the same access chain.
+struct s1 {
+ long a;
+ int b;
+} __reloc__;
+typedef const struct s1 __s1;
+typedef struct s1 arr2_t[2][3];
+typedef struct s1 row_t[3];
+
+union u1 {
+ int c;
+ __s1 d;
+} __reloc__;
+
+struct s2 {
+ int e;
+ struct s1 f[2][3];
+ volatile union u1 g[2][3];
+ struct s1 (*h)[3];
+ struct s1 *i[3];
+ const arr2_t j;
+ row_t k[2];
+ struct s1 l[2][3][4];
+} __reloc__;
+typedef struct s2 __s2;
+
+union u2 {
+ int l;
+ __s1 m[2][3];
+} __reloc__;
+typedef union u2 u2row_t[2];
+
+struct s3 {
+ int n;
+ const u2row_t o[3];
+} __reloc__;
+
+int test1(__s2 *arg) { return arg->f[1][2].b; }
+
+// CHECK-LABEL: @test1
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s2) %{{[0-9a-z]+}}, i32 1, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([2 x [3 x %struct.s1]]) %{{[0-9a-z]+}}, i32 1, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([3 x %struct.s1]) %{{[0-9a-z]+}}, i32 1, i32 2)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s1) %{{[0-9a-z]+}}, i32 1, i32 1)
+
+int test2(__s2 *arg) { return arg->g[1][2].d.b; }
+
+// CHECK-LABEL: @test2
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s2) %{{[0-9a-z]+}}, i32 2, i32 2)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([2 x [3 x %union.u1]]) %{{[0-9a-z]+}}, i32 1, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([3 x %union.u1]) %{{[0-9a-z]+}}, i32 1, i32 2)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.union.access.index.p0.p0(ptr
%{{[0-9a-z]+}}, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s1) %{{[0-9a-z]+}}, i32 1, i32 1)
+
+int test3(union u2 *arg) { return arg->m[1][2].b; }
+
+// CHECK-LABEL: @test3
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.union.access.index.p0.p0(ptr
%{{[0-9a-z]+}}, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([2 x [3 x %struct.s1]]) %{{[0-9a-z]+}}, i32 1, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([3 x %struct.s1]) %{{[0-9a-z]+}}, i32 1, i32 2)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s1) %{{[0-9a-z]+}}, i32 1, i32 1)
+
+int test4(__s2 *arg) { return arg->h[1][2].b; }
+
+// CHECK-LABEL: @test4
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s2) %{{[0-9a-z]+}}, i32 3, i32 3)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([3 x %struct.s1]) %{{[0-9a-z]+}}, i32 0, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([3 x %struct.s1]) %{{[0-9a-z]+}}, i32 1, i32 2)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s1) %{{[0-9a-z]+}}, i32 1, i32 1)
+
+int test5(__s2 *arg) { return arg->i[1][2].b; }
+
+// CHECK-LABEL: @test5
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s2) %{{[0-9a-z]+}}, i32 4, i32 4)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([3 x ptr]) %{{[0-9a-z]+}}, i32 1, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype(%struct.s1) %{{[0-9a-z]+}}, i32 0, i32 2)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s1) %{{[0-9a-z]+}}, i32 1, i32 1)
+
+int test6(__s2 *arg) { return arg->j[1][2].b; }
+
+// CHECK-LABEL: @test6
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s2) %{{[0-9a-z]+}}, i32 5, i32 5)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([2 x [3 x %struct.s1]]) %{{[0-9a-z]+}}, i32 1, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([3 x %struct.s1]) %{{[0-9a-z]+}}, i32 1, i32 2)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s1) %{{[0-9a-z]+}}, i32 1, i32 1)
+
+int test7(__s2 *arg) { return arg->k[1][2].b; }
+
+// CHECK-LABEL: @test7
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s2) %{{[0-9a-z]+}}, i32 6, i32 6)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([2 x [3 x %struct.s1]]) %{{[0-9a-z]+}}, i32 1, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([3 x %struct.s1]) %{{[0-9a-z]+}}, i32 1, i32 2)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s1) %{{[0-9a-z]+}}, i32 1, i32 1)
+
+// struct -> array -> union -> array -> struct
+int test8(struct s3 *arg) { return arg->o[2][1].m[1][2].b; }
+
+// CHECK-LABEL: @test8
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s3) %{{[0-9a-z]+}}, i32 1, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([3 x [2 x %union.u2]]) %{{[0-9a-z]+}}, i32 1, i32 2)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([2 x %union.u2]) %{{[0-9a-z]+}}, i32 1, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.union.access.index.p0.p0(ptr
%{{[0-9a-z]+}}, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([2 x [3 x %struct.s1]]) %{{[0-9a-z]+}}, i32 1, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([3 x %struct.s1]) %{{[0-9a-z]+}}, i32 1, i32 2)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s1) %{{[0-9a-z]+}}, i32 1, i32 1)
+
+// A variable index cannot be preserved, but later constant subscripts can.
+int test9(__s2 *arg, int n) { return arg->f[n][2].b; }
+
+// CHECK-LABEL: @test9
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s2) %{{[0-9a-z]+}}, i32 1, i32 1)
+// CHECK: getelementptr inbounds [2 x [3 x %struct.s1]], ptr
%{{[0-9a-z]+}}, i64 0, i64 %{{[0-9a-z]+}}
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([3 x %struct.s1]) %{{[0-9a-z]+}}, i32 1, i32 2)
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s1) %{{[0-9a-z]+}}, i32 1, i32 1)
+
+// Parentheses do not change the access chain.
+int test10(__s2 *arg) { return (arg->f[1])[2].b; }
+
+// CHECK-LABEL: @test10
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s2) %{{[0-9a-z]+}}, i32 1, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([2 x [3 x %struct.s1]]) %{{[0-9a-z]+}}, i32 1, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([3 x %struct.s1]) %{{[0-9a-z]+}}, i32 1, i32 2)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s1) %{{[0-9a-z]+}}, i32 1, i32 1)
+
+int test11(__s2 *arg) { return (arg->f)[1][2].b; }
+
+// CHECK-LABEL: @test11
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s2) %{{[0-9a-z]+}}, i32 1, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([2 x [3 x %struct.s1]]) %{{[0-9a-z]+}}, i32 1, i32 1)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([3 x %struct.s1]) %{{[0-9a-z]+}}, i32 1, i32 2)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s1) %{{[0-9a-z]+}}, i32 1, i32 1)
+
+// A variable index in a 3-D array member: the constant subscripts after it are
+// still preserved.
+int test12(__s2 *arg, int n) { return arg->l[n][2][3].b; }
+
+// CHECK-LABEL: @test12
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s2) %{{[0-9a-z]+}}, i32 7, i32 7)
+// CHECK: getelementptr inbounds [2 x [3 x [4 x %struct.s1]]], ptr
%{{[0-9a-z]+}}, i64 0, i64 %{{[0-9a-z]+}}
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([3 x [4 x %struct.s1]]) %{{[0-9a-z]+}}, i32 1, i32 2)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.array.access.index.p0.p0(ptr
elementtype([4 x %struct.s1]) %{{[0-9a-z]+}}, i32 1, i32 3)
+// CHECK-NOT: getelementptr
+// CHECK: call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.s1) %{{[0-9a-z]+}}, i32 1, i32 1)
diff --git a/llvm/test/CodeGen/BPF/CORE/offset-reloc-subarray-record.ll
b/llvm/test/CodeGen/BPF/CORE/offset-reloc-subarray-record.ll
new file mode 100644
index 00000000000000..2e25097ff79199
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/CORE/offset-reloc-subarray-record.ll
@@ -0,0 +1,469 @@
+; RUN: opt -O2 %s | llc -filetype=obj -o - | \
+; RUN: llvm-objdump --no-addresses --no-show-raw-insn --no-print-imm-hex -dr
- | \
+; RUN: FileCheck %s --implicit-check-not=CO-RE
+
+; A record inside a multi-dimensional array member stays in the access chain,
+; so each access needs one relocation rooted at the outer record. This holds
+; through qualifiers, typedefs, array element typedefs and nested arrays.
+;
+; Source:
+; #define __pai __attribute__((preserve_access_index))
+; struct S { long x; int y; } __pai;
+; typedef const volatile struct S cvS;
+; typedef struct { long x; int y; } __pai Anon;
+; typedef struct S Arr2[2][3];
+; typedef struct S Row[3];
+; union U { int i; const struct S s; } __pai;
+; union V { long l; const struct S m[2][3]; } __pai;
+; typedef union V Vrow[2];
+; struct T { int pad; struct S a[2][3]; volatile union U b[2][3]; cvS
c[2][3]; Anon d[2][3]; const Arr2 e; Row f[2]; const Vrow g[3]; struct S
h[2][3][4]; } __pai;
+; typedef const struct T cT;
+; int rec(cT *t) { return t->a[1][2].y; }
+; int un(cT *t) { return t->b[1][2].s.y; }
+; int cv(cT *t) { return t->c[1][2].y; }
+; int anon(cT *t) { return t->d[1][2].y; }
+; int arr2(cT *t) { return t->e[1][2].y; }
+; int row(cT *t) { return t->f[1][2].y; }
+; int nest(cT *t) { return t->g[2][1].m[1][2].y; }
+; unsigned nest_sz(cT *t) { return
__builtin_preserve_field_info(t->g[2][1].m[1], 1); }
+; int var(cT *t, int n) { return t->a[n][2].y; }
+; int var3(cT *t, int n) { return t->h[n][2][3].y; }
+; Compilation flag:
+; clang -target bpf -O2 -g -S -emit-llvm -Xclang -disable-llvm-passes test.c
+
+; CHECK-LABEL: <rec>:
+; CHECK: w0 = *(u32 *)(r1 + 96)
+; CHECK-NEXT: CO-RE <byte_off> [{{[0-9]+}}] typedef cT::a[1][2].y (0:1:1:2:1)
+; CHECK-LABEL: <un>:
+; CHECK: w0 = *(u32 *)(r1 + 192)
+; CHECK-NEXT: CO-RE <byte_off> [{{[0-9]+}}] typedef cT::b[1][2].s.y
(0:2:1:2:1:1)
+; CHECK-LABEL: <cv>:
+; CHECK: w0 = *(u32 *)(r1 + 288)
+; CHECK-NEXT: CO-RE <byte_off> [{{[0-9]+}}] typedef cT::c[1][2].y (0:3:1:2:1)
+; CHECK-LABEL: <anon>:
+; CHECK: w0 = *(u32 *)(r1 + 384)
+; CHECK-NEXT: CO-RE <byte_off> [{{[0-9]+}}] typedef cT::d[1][2].y (0:4:1:2:1)
+; CHECK-LABEL: <arr2>:
+; CHECK: w0 = *(u32 *)(r1 + 480)
+; CHECK-NEXT: CO-RE <byte_off> [{{[0-9]+}}] typedef cT::e[1][2].y (0:5:1:2:1)
+; CHECK-LABEL: <row>:
+; CHECK: w0 = *(u32 *)(r1 + 576)
+; CHECK-NEXT: CO-RE <byte_off> [{{[0-9]+}}] typedef cT::f[1][2].y (0:6:1:2:1)
+; CHECK-LABEL: <nest>:
+; CHECK: w0 = *(u32 *)(r1 + 1152)
+; CHECK-NEXT: CO-RE <byte_off> [{{[0-9]+}}] typedef cT::g[2][1].m[1][2].y
(0:7:2:1:1:1:2:1)
+; CHECK-LABEL: <nest_sz>:
+; CHECK: r0 = 48
+; CHECK-NEXT: CO-RE <byte_sz> [{{[0-9]+}}] typedef cT::g[2][1].m[1]
(0:7:2:1:1:1)
+; The variable subscript [n] is not relocated, but the constant [2] after it
+; is part of the relocation rooted at struct S.
+; CHECK-LABEL: <var>:
+; CHECK: r{{[0-9]+}} = 8
+; CHECK-NEXT: CO-RE <byte_off> [{{[0-9]+}}] typedef cT::a (0:1)
+; CHECK: w0 = *(u32 *)(r1 + 40)
+; CHECK-NEXT: CO-RE <byte_off> [{{[0-9]+}}] struct S::[2].y (2:1)
+; In a 3-D array, both constant subscripts after [n] are folded into one
+; index, 2 * 4 + 3 = 11.
+; CHECK-LABEL: <var3>:
+; CHECK: r{{[0-9]+}} = 1160
+; CHECK-NEXT: CO-RE <byte_off> [{{[0-9]+}}] typedef cT::h (0:8)
+; CHECK: w0 = *(u32 *)(r1 + 184)
+; CHECK-NEXT: CO-RE <byte_off> [{{[0-9]+}}] struct S::[11].y (11:1)
+
+source_filename = "test.c"
+target triple = "bpf"
+
+%struct.T = type { i32, [2 x [3 x %struct.S]], [2 x [3 x %union.U]], [2 x [3 x
%struct.S]], [2 x [3 x %struct.Anon]], [2 x [3 x %struct.S]], [2 x [3 x
%struct.S]], [3 x [2 x %union.V]], [2 x [3 x [4 x %struct.S]]] }
+%union.U = type { %struct.S }
+%struct.S = type { i64, i32 }
+%struct.Anon = type { i64, i32 }
+%union.V = type { [2 x [3 x %struct.S]] }
+
+; Function Attrs: nounwind
+define dso_local i32 @rec(ptr noundef %0) #0 !dbg !93 {
+ %2 = alloca ptr, align 8
+ store ptr %0, ptr %2, align 8, !tbaa !99
+ #dbg_declare(ptr %2, !98, !DIExpression(), !102)
+ %3 = load ptr, ptr %2, align 8, !dbg !103, !tbaa !99
+ %4 = call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.T) %3, i32 1, i32 1), !dbg !104,
!llvm.preserve.access.index !3
+ %5 = call ptr @llvm.preserve.array.access.index.p0.p0(ptr elementtype([2 x
[3 x %struct.S]]) %4, i32 1, i32 1), !dbg !103, !llvm.preserve.access.index !62
+ %6 = call ptr @llvm.preserve.array.access.index.p0.p0(ptr elementtype([3 x
%struct.S]) %5, i32 1, i32 2), !dbg !103, !llvm.preserve.access.index !63
+ %7 = call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.S) %6, i32 1, i32 1), !dbg !105,
!llvm.preserve.access.index !26
+ %8 = load i32, ptr %7, align 8, !dbg !106, !tbaa !107
+ ret i32 %8, !dbg !110
+}
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(none)
+declare ptr @llvm.preserve.struct.access.index.p0.p0(ptr, i32 immarg, i32
immarg) #1
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(none)
+declare ptr @llvm.preserve.array.access.index.p0.p0(ptr, i32 immarg, i32
immarg) #1
+
+; Function Attrs: nounwind
+define dso_local i32 @un(ptr noundef %0) #0 !dbg !111 {
+ %2 = alloca ptr, align 8
+ store ptr %0, ptr %2, align 8, !tbaa !99
+ #dbg_declare(ptr %2, !113, !DIExpression(), !114)
+ %3 = load ptr, ptr %2, align 8, !dbg !115, !tbaa !99
+ %4 = call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.T) %3, i32 2, i32 2), !dbg !116,
!llvm.preserve.access.index !3
+ %5 = call ptr @llvm.preserve.array.access.index.p0.p0(ptr elementtype([2 x
[3 x %union.U]]) %4, i32 1, i32 1), !dbg !115, !llvm.preserve.access.index !64
+ %6 = call ptr @llvm.preserve.array.access.index.p0.p0(ptr elementtype([3 x
%union.U]) %5, i32 1, i32 2), !dbg !115, !llvm.preserve.access.index !66
+ %7 = call ptr @llvm.preserve.union.access.index.p0.p0(ptr %6, i32 1), !dbg
!117, !llvm.preserve.access.index !118
+ %8 = call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.S) %7, i32 1, i32 1), !dbg !119,
!llvm.preserve.access.index !26
+ %9 = load volatile i32, ptr %8, align 8, !dbg !120, !tbaa !121
+ ret i32 %9, !dbg !122
+}
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(none)
+declare ptr @llvm.preserve.union.access.index.p0.p0(ptr, i32 immarg) #1
+
+; Function Attrs: nounwind
+define dso_local i32 @cv(ptr noundef %0) #0 !dbg !123 {
+ %2 = alloca ptr, align 8
+ store ptr %0, ptr %2, align 8, !tbaa !99
+ #dbg_declare(ptr %2, !125, !DIExpression(), !126)
+ %3 = load ptr, ptr %2, align 8, !dbg !127, !tbaa !99
+ %4 = call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.T) %3, i32 3, i32 3), !dbg !128,
!llvm.preserve.access.index !3
+ %5 = call ptr @llvm.preserve.array.access.index.p0.p0(ptr elementtype([2 x
[3 x %struct.S]]) %4, i32 1, i32 1), !dbg !127, !llvm.preserve.access.index !28
+ %6 = call ptr @llvm.preserve.array.access.index.p0.p0(ptr elementtype([3 x
%struct.S]) %5, i32 1, i32 2), !dbg !127, !llvm.preserve.access.index !69
+ %7 = call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.S) %6, i32 1, i32 1), !dbg !129,
!llvm.preserve.access.index !29
+ %8 = load volatile i32, ptr %7, align 8, !dbg !130, !tbaa !107
+ ret i32 %8, !dbg !131
+}
+
+; Function Attrs: nounwind
+define dso_local i32 @anon(ptr noundef %0) #0 !dbg !132 {
+ %2 = alloca ptr, align 8
+ store ptr %0, ptr %2, align 8, !tbaa !99
+ #dbg_declare(ptr %2, !134, !DIExpression(), !135)
+ %3 = load ptr, ptr %2, align 8, !dbg !136, !tbaa !99
+ %4 = call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.T) %3, i32 4, i32 4), !dbg !137,
!llvm.preserve.access.index !3
+ %5 = call ptr @llvm.preserve.array.access.index.p0.p0(ptr elementtype([2 x
[3 x %struct.Anon]]) %4, i32 1, i32 1), !dbg !136, !llvm.preserve.access.index
!70
+ %6 = call ptr @llvm.preserve.array.access.index.p0.p0(ptr elementtype([3 x
%struct.Anon]) %5, i32 1, i32 2), !dbg !136, !llvm.preserve.access.index !71
+ %7 = call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.Anon) %6, i32 1, i32 1), !dbg !138,
!llvm.preserve.access.index !73
+ %8 = load i32, ptr %7, align 8, !dbg !139, !tbaa !140
+ ret i32 %8, !dbg !142
+}
+
+; Function Attrs: nounwind
+define dso_local i32 @arr2(ptr noundef %0) #0 !dbg !143 {
+ %2 = alloca ptr, align 8
+ store ptr %0, ptr %2, align 8, !tbaa !99
+ #dbg_declare(ptr %2, !145, !DIExpression(), !146)
+ %3 = load ptr, ptr %2, align 8, !dbg !147, !tbaa !99
+ %4 = call ptr @llvm.preserve.struct.access.index.p0.p0(ptr
elementtype(%struct.T) %3, i32 5, i32 5), !dbg !148,
!llvm.preserve.access.index !3
+ %5...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/237589
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits