__  https://reviews.llvm.org/D78466
 
+bpf_verif_scale/loop6.o test failure with Clang 12
+==================================================
+
+With Clang 12, the following bpf_verif_scale test failed:
+  * ``bpf_verif_scale/loop6.o``
+
+The verifier output looks like
+
+.. code-block:: c
+
+  R1 type=ctx expected=fp
+  The sequence of 8193 jumps is too complex.
+
+The reason is compiler generating the following code
+
+.. code-block:: c
+
+  ;       for (i = 0; (i < VIRTIO_MAX_SGS) && (i < num); i++) {
+      14:       16 05 40 00 00 00 00 00 if w5 == 0 goto +64 <LBB0_6>
+      15:       bc 51 00 00 00 00 00 00 w1 = w5
+      16:       04 01 00 00 ff ff ff ff w1 += -1
+      17:       67 05 00 00 20 00 00 00 r5 <<= 32
+      18:       77 05 00 00 20 00 00 00 r5 >>= 32
+      19:       a6 01 01 00 05 00 00 00 if w1 < 5 goto +1 <LBB0_4>
+      20:       b7 05 00 00 06 00 00 00 r5 = 6
+  00000000000000a8 <LBB0_4>:
+      21:       b7 02 00 00 00 00 00 00 r2 = 0
+      22:       b7 01 00 00 00 00 00 00 r1 = 0
+  ;       for (i = 0; (i < VIRTIO_MAX_SGS) && (i < num); i++) {
+      23:       7b 1a e0 ff 00 00 00 00 *(u64 *)(r10 - 32) = r1
+      24:       7b 5a c0 ff 00 00 00 00 *(u64 *)(r10 - 64) = r5
+
+Note that insn #15 has w1 = w5 and w1 is refined later but
+r5(w5) is eventually saved on stack at insn #24 for later use.
+This cause later verifier failure. The bug has been `fixed`__ in
+Clang 13.
+
+__  https://reviews.llvm.org/D97479
+
 BPF CO-RE-based tests and Clang version
 =======================================
 
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/ptrace.h>
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+char _license[] SEC("license") = "GPL";
+
+/* typically virtio scsi has max SGs of 6 */
+#define VIRTIO_MAX_SGS 6
+
+/* Verifier will fail with SG_MAX = 128. The failure can be
+ * workarounded with a smaller SG_MAX, e.g. 10.
+ */
+#define WORKAROUND
+#ifdef WORKAROUND
+#define SG_MAX         10
+#else
+/* typically virtio blk has max SEG of 128 */
+#define SG_MAX         128
+#endif
+
+#define SG_CHAIN       0x01UL
+#define SG_END         0x02UL
+
+struct scatterlist {
+       unsigned long   page_link;
+       unsigned int    offset;
+       unsigned int    length;
+};
+
+#define sg_is_chain(sg)                ((sg)->page_link & SG_CHAIN)
+#define sg_is_last(sg)         ((sg)->page_link & SG_END)
+#define sg_chain_ptr(sg)       \
+       ((struct scatterlist *) ((sg)->page_link & ~(SG_CHAIN | SG_END)))
+
+static inline struct scatterlist *__sg_next(struct scatterlist *sgp)
+{
+       struct scatterlist sg;
+
+       bpf_probe_read_kernel(&sg, sizeof(sg), sgp);
+       if (sg_is_last(&sg))
+               return NULL;
+
+       sgp++;
+
+       bpf_probe_read_kernel(&sg, sizeof(sg), sgp);
+       if (sg_is_chain(&sg))
+               sgp = sg_chain_ptr(&sg);
+
+       return sgp;
+}
+
+static inline struct scatterlist *get_sgp(struct scatterlist **sgs, int i)
+{
+       struct scatterlist *sgp;
+
+       bpf_probe_read_kernel(&sgp, sizeof(sgp), sgs + i);
+       return sgp;
+}
+
+int config = 0;
+int result = 0;
+
+SEC("kprobe/virtqueue_add_sgs")
+BPF_KPROBE(trace_virtqueue_add_sgs, void *unused, struct scatterlist **sgs,
+          unsigned int out_sgs, unsigned int in_sgs)
+{
+       struct scatterlist *sgp = NULL;
+       __u64 length1 = 0, length2 = 0;
+       unsigned int i, n, len;
+
+       if (config != 0)
+               return 0;
+
+       for (i = 0; (i < VIRTIO_MAX_SGS) && (i < out_sgs); i++) {
+               for (n = 0, sgp = get_sgp(sgs, i); sgp && (n < SG_MAX);
+                    sgp = __sg_next(sgp)) {
+                       bpf_probe_read_kernel(&len, sizeof(len), &sgp->length);
+                       length1 += len;
+                       n++;
+               }
+       }
+
+       for (i = 0; (i < VIRTIO_MAX_SGS) && (i < in_sgs); i++) {
+               for (n = 0, sgp = get_sgp(sgs, i); sgp && (n < SG_MAX);
+                    sgp = __sg_next(sgp)) {
+                       bpf_probe_read_kernel(&len, sizeof(len), &sgp->length);
+                       length2 += len;
+                       n++;
+               }
+       }
+
+       config = 1;
+       result = length2 - length1;
+       return 0;
+}