https://gcc.gnu.org/g:09021961ea027196cafca0d0308fd7c5e997bff8
commit r17-2213-g09021961ea027196cafca0d0308fd7c5e997bff8 Author: Tamar Christina <[email protected]> Date: Wed Jul 8 08:35:45 2026 +0100 vect: unshare header computation for more CSE The example loop #define N 512 #define START 1 #define END 505 int x[N] __attribute__((aligned(32))); int __attribute__((noipa)) foo (void) { for (int *p = x + START; p < x + END; ++p) if (*p == 0) return START; return -1; } shows that when we vectorize we generate the following pre-header when doing peeling for alignment: _22 = (unsigned long) &MEM <int[512]> [(void *)&x + 4B]; _23 = _22 & POLY_INT_CST [15, 16]; _24 = _23 >> 2; _26 = 1 - _24; _27 = _26 * 4; vectp_x.6_25 = &x + _27; _36 = 504 + _24; max_mask_37 = .WHILE_ULT (0, _36, { 0, ... }); _38 = .WHILE_ULT (0, _24, { 0, ... }); _39 = ~_38; _40 = max_mask_37 & _39; notice how _22 and and the actual vector pointer vectp_x.6_25 start at the same offset but have a different base. This happens because in vect_create_addr_base_for_vector_ref we fold the offset into the base very early and so we can't form or share the address computation anymore with the scalar address which needs to be there for the alignment checks. The patch delays this and forms an explicit base + offset and keeping base as a separate value. This allows VN at the end of vect to share the computations and we get _22 = (unsigned long) &MEM <int[512]> [(void *)&x + 4B]; _23 = _22 & POLY_INT_CST [15, 16]; _24 = _23 >> 2; _27 = _24 * 4; _28 = -_27; vectp_x.6_26 = &MEM <int[512]> [(void *)&x + 4B] + _28; _37 = 504 + _24; max_mask_38 = .WHILE_ULT (0, _37, { 0, ... }); _39 = .WHILE_ULT (0, _24, { 0, ... }); _40 = ~_39; _41 = max_mask_38 & _40; Notice how they now share the same base address. This drops two instructions off the loop pre-header: from foo: cntb x0 sub x3, x0, #1 cmp x0, 4096 and x0, x0, x3 ccmp x0, 0, 0, ls bne .L7 adrp x4, .LANCHOR0 add x4, x4, :lo12:.LANCHOR0 add x1, x4, 4 mov w2, 1 and x1, x1, x3 mov w0, 0 cntw x3 lsr x1, x1, 2 whilelo p15.s, xzr, x1 sub x2, x2, x1 add x1, x1, 504 whilelo p7.s, xzr, x1 not p7.b, p7/z, p15.b add x2, x4, x2, lsl 2 b .L4 to foo: cntb x0 sub x1, x0, #1 cmp x0, 4096 and x0, x0, x1 adrp x2, .LANCHOR0 ccmp x0, 0, 0, ls add x2, x2, :lo12:.LANCHOR0 bne .L7 add x2, x2, 4 mov w0, 0 and x1, x2, x1 cntw x3 sub x2, x2, x1 lsr x1, x1, 2 whilelo p15.s, xzr, x1 add x1, x1, 504 whilelo p7.s, xzr, x1 not p7.b, p7/z, p15.b b .L4 gcc/ChangeLog: * tree-vect-data-refs.cc (vect_create_addr_base_for_vector_ref): Restructure computations to force separate base. gcc/testsuite/ChangeLog: * gcc.target/aarch64/sve/vect-early-break-cbranch_18.c: New test. Diff: --- .../aarch64/sve/vect-early-break-cbranch_18.c | 22 ++++++++++++++++ gcc/tree-vect-data-refs.cc | 30 ++++++++++++++++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/gcc/testsuite/gcc.target/aarch64/sve/vect-early-break-cbranch_18.c b/gcc/testsuite/gcc.target/aarch64/sve/vect-early-break-cbranch_18.c new file mode 100644 index 000000000000..9129f82cffe5 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/sve/vect-early-break-cbranch_18.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-Ofast -msve-vector-bits=256 -mautovec-preference=sve-only -fdump-tree-vect-details" } */ + +#define N 512 +#define START 1 +#define END 505 + +int x[N] __attribute__((aligned(32))); + +int __attribute__((noipa)) +foo (void) +{ + for (int *p = x + START; p < x + END; ++p) + if (*p == 0) + return START; + + return -1; +} + +/* { dg-final { scan-tree-dump "Alignment of access forced using peeling" "vect" } } */ +/* { dg-final { scan-tree-dump-times {Value numbering stmt = vectp_x\.[0-9]+_[0-9]+ = &x \+ 4;} 1 "vect" } } */ +/* { dg-final { scan-tree-dump-times {Value numbering stmt = vectp_x\.[0-9]+_[0-9]+ = vectp_x\.[0-9]+_[0-9]+ \+ 18446744073709551612;} 1 "vect" } } */ diff --git a/gcc/tree-vect-data-refs.cc b/gcc/tree-vect-data-refs.cc index 80f7539614c2..0e0754769ae4 100644 --- a/gcc/tree-vect-data-refs.cc +++ b/gcc/tree-vect-data-refs.cc @@ -5729,7 +5729,13 @@ vect_create_addr_base_for_vector_ref (vec_info *vinfo, stmt_vec_info stmt_info, innermost_loop_behavior *drb = vect_dr_behavior (vinfo, dr_info); tree data_ref_base = unshare_expr (drb->base_address); - tree base_offset = unshare_expr (get_dr_vinfo_offset (vinfo, dr_info, true)); + tree vector_offset = NULL_TREE; + if (loop_vinfo && dr_info->offset) + vector_offset = unshare_expr (dr_info->offset); + tree base_offset = unshare_expr (vector_offset + ? drb->offset + : get_dr_vinfo_offset (vinfo, dr_info, + true)); tree init = unshare_expr (drb->init); if (loop_vinfo) @@ -5767,7 +5773,27 @@ vect_create_addr_base_for_vector_ref (vec_info *vinfo, stmt_vec_info stmt_info, vect_ptr_type = build_pointer_type (TREE_TYPE (DR_REF (dr))); dest = vect_get_new_vect_var (vect_ptr_type, vect_pointer_var, base_name); - addr_base = force_gimple_operand (addr_base, &seq, true, dest); + + /* Keep vectorizer-added offsets separate from the original scalar access + address. Forming "base + scalar offset" first gives the target a better + chance of sharing it with other address calculations, such as the + misalignment check used for masked alignment peeling. */ + if (vector_offset) + { + tree scalar_dest = vect_get_new_vect_var (vect_ptr_type, + vect_pointer_var, base_name); + gimple_seq addr_seq = NULL; + addr_base = force_gimple_operand (addr_base, &addr_seq, true, + scalar_dest); + gimple_seq_add_seq (&seq, addr_seq); + addr_base = fold_build_pointer_plus (addr_base, + fold_convert (sizetype, + vector_offset)); + } + + gimple_seq addr_seq = NULL; + addr_base = force_gimple_operand (addr_base, &addr_seq, true, dest); + gimple_seq_add_seq (&seq, addr_seq); gimple_seq_add_seq (new_stmt_list, seq); if (TREE_CODE (addr_base) == SSA_NAME
