arm: Use unaligned ldr/str for packed fields on Armv6 [PR55218]
Commit Message
On Armv6 in Arm state a byte-aligned 16- or 32-bit field of a packed
structure, or such a bit-field, is accessed a byte at a time although
unaligned_access is the default: extv/extzv are disabled without Thumb-2
and insv FAILs for a MEM. Add ext<z>vmisalignsi, used only when
extv/extzv are disabled, and let insv use its unaligned store for a MEM
less aligned than the field. Thumb-1, Armv6T2 and later, and
-mno-unaligned-access are unaffected.
.text on trunk: bcm2835_defconfig vmlinux (its -Os -march=armv6k
-mtune=arm1136j-s) -33832 bytes (0.37%), 601 functions smaller and 6
larger by 32 bytes in total (KCFLAGS=-O2: -41040 bytes, 611 smaller, 7
larger by 28 bytes); Pi1MHz firmware (-O2 -mcpu=arm1176jzf-s) -5872
bytes (0.87%), none larger; of 5450 gcc.c-torture, gcc.dg/torture and
gcc.target/arm files at -march=armv6zk, 34 change at -O2 (-1588 bytes)
and 41 at -Os (-5352), none grows.
Tested on arm-none-eabi, with arm.exp, simd.exp, mve.exp,
advsimd-intrinsics.exp, acle.exp and cmse.exp on the default board and
on a -mthumb -mfloat-abi=hard -mtune=cortex-a9 -march=armv7-a
-mfpu=neon-fp-armv8 board, and with arm.exp, execute.exp and
dg-torture.exp on an Armv6 qemu board with the MMU on: no regressions.
Assisted-by: Claude Opus 5 (Anthropic)
PR target/55218
gcc/ChangeLog:
* config/arm/arm.md (insv): Without Thumb-2, use the unaligned
store path for a MEM less aligned than the field.
(ext<z>vmisalignsi): New expander.
* config/arm/iterators.md (any_extract): New code iterator.
(su): Handle sign_extract and zero_extract.
(z): New code attribute.
gcc/testsuite/ChangeLog:
* gcc.target/arm/pr55218-1.c: New test.
* gcc.target/arm/pr55218-2.c: New test.
* gcc.target/arm/pr55218-3.c: New test.
* gcc.target/arm/pr55218-4.c: New test.
* gcc.target/arm/pr55218-5.c: New test.
* gcc.target/arm/pr55218-6.c: New test.
Signed-off-by: Dominic P <gcc@gcc.dp11.uk>
---
Measured on trunk (not on silicon): see the numbers in the message above.
They are code sizes only; I make no cycle claim.
Only insv checks MEM_ALIGN: an aligned load is expanded as a plain load
(simple_mem_bitfield_p) before ext<z>vmisalignsi is tried, but an aligned
store to a generic-vector element can reach insv, and it keeps its old
expansion (pr55218-6.c).
A probe reading a packed short as (unsigned char) now gets ldrsh+uxtb
instead of ldrb, as Armv6T2 already does.
OK for trunk?
gcc/config/arm/arm.md | 24 ++++-
gcc/config/arm/iterators.md | 9 +-
gcc/testsuite/gcc.target/arm/pr55218-1.c | 101 +++++++++++++++++++
gcc/testsuite/gcc.target/arm/pr55218-2.c | 17 ++++
gcc/testsuite/gcc.target/arm/pr55218-3.c | 11 +++
gcc/testsuite/gcc.target/arm/pr55218-4.c | 64 ++++++++++++
gcc/testsuite/gcc.target/arm/pr55218-5.c | 120 +++++++++++++++++++++++
gcc/testsuite/gcc.target/arm/pr55218-6.c | 16 +++
8 files changed, 360 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/arm/pr55218-1.c
create mode 100644 gcc/testsuite/gcc.target/arm/pr55218-2.c
create mode 100644 gcc/testsuite/gcc.target/arm/pr55218-3.c
create mode 100644 gcc/testsuite/gcc.target/arm/pr55218-4.c
create mode 100644 gcc/testsuite/gcc.target/arm/pr55218-5.c
create mode 100644 gcc/testsuite/gcc.target/arm/pr55218-6.c
Comments
On 02/10/2026 20:06, Dominic P wrote:
> On Armv6 in Arm state a byte-aligned 16- or 32-bit field of a packed
> structure, or such a bit-field, is accessed a byte at a time although
> unaligned_access is the default: extv/extzv are disabled without Thumb-2
> and insv FAILs for a MEM. Add ext<z>vmisalignsi, used only when
> extv/extzv are disabled, and let insv use its unaligned store for a MEM
> less aligned than the field. Thumb-1, Armv6T2 and later, and
> -mno-unaligned-access are unaffected.
>
> .text on trunk: bcm2835_defconfig vmlinux (its -Os -march=armv6k
> -mtune=arm1136j-s) -33832 bytes (0.37%), 601 functions smaller and 6
> larger by 32 bytes in total (KCFLAGS=-O2: -41040 bytes, 611 smaller, 7
> larger by 28 bytes); Pi1MHz firmware (-O2 -mcpu=arm1176jzf-s) -5872
> bytes (0.87%), none larger; of 5450 gcc.c-torture, gcc.dg/torture and
> gcc.target/arm files at -march=armv6zk, 34 change at -O2 (-1588 bytes)
> and 41 at -Os (-5352), none grows.
>
> Tested on arm-none-eabi, with arm.exp, simd.exp, mve.exp,
> advsimd-intrinsics.exp, acle.exp and cmse.exp on the default board and
> on a -mthumb -mfloat-abi=hard -mtune=cortex-a9 -march=armv7-a
> -mfpu=neon-fp-armv8 board, and with arm.exp, execute.exp and
> dg-torture.exp on an Armv6 qemu board with the MMU on: no regressions.
>
> Assisted-by: Claude Opus 5 (Anthropic)
>
> PR target/55218
>
> gcc/ChangeLog:
>
> * config/arm/arm.md (insv): Without Thumb-2, use the unaligned
> store path for a MEM less aligned than the field.
> (ext<z>vmisalignsi): New expander.
> * config/arm/iterators.md (any_extract): New code iterator.
> (su): Handle sign_extract and zero_extract.
> (z): New code attribute.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.target/arm/pr55218-1.c: New test.
> * gcc.target/arm/pr55218-2.c: New test.
> * gcc.target/arm/pr55218-3.c: New test.
> * gcc.target/arm/pr55218-4.c: New test.
> * gcc.target/arm/pr55218-5.c: New test.
> * gcc.target/arm/pr55218-6.c: New test.
>
> Signed-off-by: Dominic P <gcc@gcc.dp11.uk>
This should all be handled by generic code that knows how to expand
packed structs when we have movmisalign. It shouldn't need additional
patterns in the backend.
So the question that needs answering first is why this isn't happening.
R.
> ---
> Measured on trunk (not on silicon): see the numbers in the message above.
> They are code sizes only; I make no cycle claim.
> Only insv checks MEM_ALIGN: an aligned load is expanded as a plain load
> (simple_mem_bitfield_p) before ext<z>vmisalignsi is tried, but an aligned
> store to a generic-vector element can reach insv, and it keeps its old
> expansion (pr55218-6.c).
> A probe reading a packed short as (unsigned char) now gets ldrsh+uxtb
> instead of ldrb, as Armv6T2 already does.
> OK for trunk?
>
> gcc/config/arm/arm.md | 24 ++++-
> gcc/config/arm/iterators.md | 9 +-
> gcc/testsuite/gcc.target/arm/pr55218-1.c | 101 +++++++++++++++++++
> gcc/testsuite/gcc.target/arm/pr55218-2.c | 17 ++++
> gcc/testsuite/gcc.target/arm/pr55218-3.c | 11 +++
> gcc/testsuite/gcc.target/arm/pr55218-4.c | 64 ++++++++++++
> gcc/testsuite/gcc.target/arm/pr55218-5.c | 120 +++++++++++++++++++++++
> gcc/testsuite/gcc.target/arm/pr55218-6.c | 16 +++
> 8 files changed, 360 insertions(+), 2 deletions(-)
> create mode 100644 gcc/testsuite/gcc.target/arm/pr55218-1.c
> create mode 100644 gcc/testsuite/gcc.target/arm/pr55218-2.c
> create mode 100644 gcc/testsuite/gcc.target/arm/pr55218-3.c
> create mode 100644 gcc/testsuite/gcc.target/arm/pr55218-4.c
> create mode 100644 gcc/testsuite/gcc.target/arm/pr55218-5.c
> create mode 100644 gcc/testsuite/gcc.target/arm/pr55218-6.c
>
> diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
> index febff17df0b..fc156620f1e 100644
> --- a/gcc/config/arm/arm.md
> +++ b/gcc/config/arm/arm.md
> @@ -3290,7 +3290,8 @@
> HOST_WIDE_INT mask = (HOST_WIDE_INT_1 << width) - 1;
> rtx target, subtarget;
>
> - if (arm_arch_thumb2)
> + if (arm_arch_thumb2
> + || (MEM_P (operands[0]) && MEM_ALIGN (operands[0]) < (unsigned) width))
> {
> if (unaligned_access && MEM_P (operands[0])
> && s_register_operand (operands[3], GET_MODE (operands[3]))
> @@ -4965,6 +4966,27 @@
> FAIL;
> })
>
> +(define_expand "ext<z>vmisalignsi"
> + [(set (match_operand:SI 0 "s_register_operand")
> + (any_extract:SI (match_operand:BLK 1 "memory_operand")
> + (match_operand 2 "const_int_operand")
> + (match_operand 3 "const_int_operand")))]
> + "TARGET_32BIT && unaligned_access"
> +{
> + if (INTVAL (operands[3]) % BITS_PER_UNIT != 0)
> + FAIL;
> + if (INTVAL (operands[2]) == 32)
> + emit_insn (gen_unaligned_loadsi (operands[0],
> + adjust_address (operands[1], SImode, 0)));
> + else if (INTVAL (operands[2]) == 16)
> + emit_insn (gen_unaligned_loadhi<su> (operands[0],
> + adjust_address (operands[1],
> + HImode, 0)));
> + else
> + FAIL;
> + DONE;
> +})
> +
> ; Helper to expand register forms of extv with the proper modes.
>
> (define_expand "extv_regsi"
> diff --git a/gcc/config/arm/iterators.md b/gcc/config/arm/iterators.md
> index c8fd31b52c5..602e874b27f 100644
> --- a/gcc/config/arm/iterators.md
> +++ b/gcc/config/arm/iterators.md
> @@ -377,6 +377,9 @@
> ;; A list of widening operators
> (define_code_iterator SE [sign_extend zero_extend])
>
> +;; Bit-field extractions
> +(define_code_iterator any_extract [sign_extract zero_extract])
> +
> ;; Right shifts
> (define_code_iterator RSHIFTS [ashiftrt lshiftrt])
>
> @@ -2104,7 +2107,11 @@
> (define_code_attr su_optab [(fix "") (unsigned_fix "u")])
>
> ;; Sign prefix to use in instruction type suffixes, i.e. s32, u32.
> -(define_code_attr su [(fix "s") (unsigned_fix "u")])
> +(define_code_attr su [(fix "s") (unsigned_fix "u")
> + (sign_extract "s") (zero_extract "u")])
> +
> +;; Infix of the bit-field extraction optab names, i.e. extv, extzv.
> +(define_code_attr z [(sign_extract "") (zero_extract "z")])
>
> ;; Right shifts
> (define_code_attr shift [(ashiftrt "ashr") (lshiftrt "lshr")])
> diff --git a/gcc/testsuite/gcc.target/arm/pr55218-1.c b/gcc/testsuite/gcc.target/arm/pr55218-1.c
> new file mode 100644
> index 00000000000..384f7ec2fb0
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/arm/pr55218-1.c
> @@ -0,0 +1,101 @@
> +/* PR target/55218: on Armv6 in Arm state a byte-aligned 16- or 32-bit
> + field of a packed structure is accessed with one unaligned
> + ldr/ldrh/ldrsh/str/strh, as on Armv6T2 and later. */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -marm -munaligned-access" } */
> +/* { dg-require-effective-target arm_arch_v6_arm_ok } */
> +/* { dg-add-options arm_arch_v6 } */
> +/* { dg-add-options check_function_bodies } */
> +/* { dg-final { check-function-bodies "**" "" "" } } */
> +
> +struct __attribute__ ((packed)) s
> +{
> + char c;
> + short h;
> + unsigned short uh;
> + int w;
> +};
> +
> +/*
> +** load_w:
> +** ldr r0, \[r0, #5\] @ unaligned
> +** bx lr
> +*/
> +int
> +load_w (struct s *p)
> +{
> + return p->w;
> +}
> +
> +/*
> +** load_h:
> +** ldrsh r0, \[r0, #1\] @ unaligned
> +** bx lr
> +*/
> +int
> +load_h (struct s *p)
> +{
> + return p->h;
> +}
> +
> +/*
> +** load_uh:
> +** ldrh r0, \[r0, #3\] @ unaligned
> +** bx lr
> +*/
> +unsigned
> +load_uh (struct s *p)
> +{
> + return p->uh;
> +}
> +
> +/*
> +** store_w:
> +** str r1, \[r0, #5\] @ unaligned
> +** bx lr
> +*/
> +void
> +store_w (struct s *p, int v)
> +{
> + p->w = v;
> +}
> +
> +/*
> +** store_h:
> +** strh r1, \[r0, #1\] @ unaligned
> +** bx lr
> +*/
> +void
> +store_h (struct s *p, short v)
> +{
> + p->h = v;
> +}
> +
> +/* A byte-aligned 16-bit bit-field of an ordinary structure. */
> +struct b
> +{
> + char c;
> + unsigned f : 16;
> +};
> +
> +/*
> +** load_bitfield:
> +** ldrh r0, \[r0, #1\] @ unaligned
> +** bx lr
> +*/
> +unsigned
> +load_bitfield (struct b *p)
> +{
> + return p->f;
> +}
> +
> +/*
> +** store_bitfield:
> +** strh r1, \[r0, #1\] @ unaligned
> +** bx lr
> +*/
> +void
> +store_bitfield (struct b *p, unsigned v)
> +{
> + p->f = v;
> +}
> diff --git a/gcc/testsuite/gcc.target/arm/pr55218-2.c b/gcc/testsuite/gcc.target/arm/pr55218-2.c
> new file mode 100644
> index 00000000000..dba97e8118e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/arm/pr55218-2.c
> @@ -0,0 +1,17 @@
> +/* PR target/55218: with -mno-unaligned-access packed fields are still
> + accessed a byte at a time. */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -marm -mno-unaligned-access" } */
> +/* { dg-require-effective-target arm_arch_v6_arm_ok } */
> +/* { dg-add-options arm_arch_v6 } */
> +
> +struct __attribute__ ((packed)) s { char c; short h; int w; };
> +
> +int load_w (struct s *p) { return p->w; }
> +int load_h (struct s *p) { return p->h; }
> +void store_w (struct s *p, int v) { p->w = v; }
> +void store_h (struct s *p, short v) { p->h = v; }
> +
> +/* { dg-final { scan-assembler-not "@ unaligned" } } */
> +/* { dg-final { scan-assembler-not "\tldrh\t" } } */
> +/* { dg-final { scan-assembler-not "\tstrh\t" } } */
> diff --git a/gcc/testsuite/gcc.target/arm/pr55218-3.c b/gcc/testsuite/gcc.target/arm/pr55218-3.c
> new file mode 100644
> index 00000000000..d65e9ebbf15
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/arm/pr55218-3.c
> @@ -0,0 +1,11 @@
> +/* PR target/55218: unaligned_loadhis is 32-bit only, so Thumb-1 with
> + -munaligned-access must not use the unaligned expansion. */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -mthumb -munaligned-access" } */
> +/* { dg-require-effective-target arm_arch_v6_ok } */
> +/* { dg-add-options arm_arch_v6 } */
> +
> +struct __attribute__ ((packed)) s { char c; short h; int w; };
> +
> +int load_h (struct s *p) { return p->h; }
> +int load_w (struct s *p) { return p->w; }
> diff --git a/gcc/testsuite/gcc.target/arm/pr55218-4.c b/gcc/testsuite/gcc.target/arm/pr55218-4.c
> new file mode 100644
> index 00000000000..f7ca15c8522
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/arm/pr55218-4.c
> @@ -0,0 +1,64 @@
> +/* PR target/55218: big-endian variant of pr55218-1.c. */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -marm -mbig-endian -munaligned-access" } */
> +/* { dg-require-effective-target arm_arch_v6_arm_ok } */
> +/* { dg-add-options arm_arch_v6 } */
> +/* { dg-add-options check_function_bodies } */
> +/* { dg-final { check-function-bodies "**" "" "" } } */
> +
> +struct __attribute__ ((packed)) s { char c; short h; unsigned short uh; int w; };
> +
> +/*
> +** load_w:
> +** ldr r0, \[r0, #5\] @ unaligned
> +** bx lr
> +*/
> +int
> +load_w (struct s *p)
> +{
> + return p->w;
> +}
> +
> +/*
> +** load_h:
> +** ldrsh r0, \[r0, #1\] @ unaligned
> +** bx lr
> +*/
> +int
> +load_h (struct s *p)
> +{
> + return p->h;
> +}
> +
> +/*
> +** load_uh:
> +** ldrh r0, \[r0, #3\] @ unaligned
> +** bx lr
> +*/
> +unsigned
> +load_uh (struct s *p)
> +{
> + return p->uh;
> +}
> +
> +/*
> +** store_w:
> +** str r1, \[r0, #5\] @ unaligned
> +** bx lr
> +*/
> +void
> +store_w (struct s *p, int v)
> +{
> + p->w = v;
> +}
> +
> +/*
> +** store_h:
> +** strh r1, \[r0, #1\] @ unaligned
> +** bx lr
> +*/
> +void
> +store_h (struct s *p, short v)
> +{
> + p->h = v;
> +}
> diff --git a/gcc/testsuite/gcc.target/arm/pr55218-5.c b/gcc/testsuite/gcc.target/arm/pr55218-5.c
> new file mode 100644
> index 00000000000..3adb4e7fbac
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/arm/pr55218-5.c
> @@ -0,0 +1,120 @@
> +/* PR target/55218: values read and written through unaligned packed
> + fields on Armv6 in Arm state. */
> +/* { dg-do run } */
> +/* { dg-require-effective-target arm_arch_v6_arm_multilib } */
> +/* { dg-options "-O2 -munaligned-access" } */
> +/* { dg-add-options arm_arch_v6_arm } */
> +
> +struct __attribute__ ((packed)) s
> +{
> + char c;
> + short h;
> + unsigned short uh;
> + int w;
> + unsigned uw;
> + long long d;
> +};
> +
> +struct b
> +{
> + char c;
> + unsigned f : 16;
> + int g : 16;
> +};
> +
> +struct __attribute__ ((packed)) c
> +{
> + unsigned a : 4, f : 16, g : 12;
> +};
> +
> +union u
> +{
> + struct s s;
> + unsigned char bytes[sizeof (struct s)];
> +};
> +
> +union v
> +{
> + struct b b;
> + unsigned char bytes[sizeof (struct b)];
> +};
> +
> +union w
> +{
> + struct c c;
> + unsigned char bytes[sizeof (struct c)];
> +};
> +
> +#define NI __attribute__ ((noipa))
> +
> +NI int load_h (struct s *p) { return p->h; }
> +NI unsigned load_uh (struct s *p) { return p->uh; }
> +NI int load_w (struct s *p) { return p->w; }
> +NI unsigned load_uw (struct s *p) { return p->uw; }
> +NI long long load_d (struct s *p) { return p->d; }
> +NI void store_h (struct s *p, short x) { p->h = x; }
> +NI void store_uh (struct s *p, unsigned short x) { p->uh = x; }
> +NI void store_w (struct s *p, int x) { p->w = x; }
> +NI void store_d (struct s *p, long long x) { p->d = x; }
> +NI unsigned load_f (struct b *p) { return p->f; }
> +NI int load_g (struct b *p) { return p->g; }
> +NI void store_f (struct b *p, unsigned x) { p->f = x; }
> +NI void store_g (struct b *p, int x) { p->g = x; }
> +NI unsigned load_cf (struct c *p) { return p->f; }
> +NI int load_cfs (struct c *p) { return (short) p->f; }
> +
> +#define AT(T, buf, off) \
> + ({ T t_; __builtin_memcpy (&t_, (buf) + (off), sizeof t_); t_; })
> +
> +int
> +main (void)
> +{
> + union u x;
> + for (unsigned i = 0; i < sizeof x.bytes; i++)
> + x.bytes[i] = 0x81 + 3 * i;
> +
> + if (load_h (&x.s) != AT (short, x.bytes, 1)
> + || load_uh (&x.s) != AT (unsigned short, x.bytes, 3)
> + || load_w (&x.s) != AT (int, x.bytes, 5)
> + || load_uw (&x.s) != AT (unsigned, x.bytes, 9)
> + || load_d (&x.s) != AT (long long, x.bytes, 13))
> + __builtin_abort ();
> +
> + unsigned uw = AT (unsigned, x.bytes, 9);
> + store_h (&x.s, -2);
> + store_uh (&x.s, 0x1234);
> + store_w (&x.s, 0x56789abc);
> + store_d (&x.s, 0x0102030405060708LL);
> + if (x.bytes[0] != 0x81
> + || AT (short, x.bytes, 1) != -2
> + || AT (unsigned short, x.bytes, 3) != 0x1234
> + || AT (int, x.bytes, 5) != 0x56789abc
> + || AT (unsigned, x.bytes, 9) != uw
> + || AT (long long, x.bytes, 13) != 0x0102030405060708LL)
> + __builtin_abort ();
> +
> + union v y;
> + for (unsigned i = 0; i < sizeof y.bytes; i++)
> + y.bytes[i] = 0xf0 - i;
> + if (load_f (&y.b) != AT (unsigned short, y.bytes, 1))
> + __builtin_abort ();
> + store_f (&y.b, 0xa55a);
> + store_g (&y.b, -3);
> + if (y.bytes[0] != 0xf0
> + || AT (unsigned short, y.bytes, 1) != 0xa55a
> + || load_f (&y.b) != 0xa55a || load_g (&y.b) != -3)
> + __builtin_abort ();
> +
> + union w z;
> + for (unsigned i = 0; i < sizeof z.bytes; i++)
> + z.bytes[i] = 0x9c + 5 * i;
> + unsigned cf;
> + if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
> + cf = (z.bytes[0] | z.bytes[1] << 8 | z.bytes[2] << 16) >> 4;
> + else
> + cf = (z.bytes[0] << 16 | z.bytes[1] << 8 | z.bytes[2]) >> 4;
> + cf &= 0xffff;
> + if (load_cf (&z.c) != cf || load_cfs (&z.c) != (short) cf)
> + __builtin_abort ();
> + return 0;
> +}
> diff --git a/gcc/testsuite/gcc.target/arm/pr55218-6.c b/gcc/testsuite/gcc.target/arm/pr55218-6.c
> new file mode 100644
> index 00000000000..d8e7bb4e2fd
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/arm/pr55218-6.c
> @@ -0,0 +1,16 @@
> +/* PR target/55218: an aligned store to an element of a generic vector
> + is not an unaligned access. */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -marm -munaligned-access" } */
> +/* { dg-require-effective-target arm_arch_v6_arm_ok } */
> +/* { dg-add-options arm_arch_v6 } */
> +
> +typedef int V __attribute__ ((vector_size (32)));
> +
> +void
> +f (V *p, V *q)
> +{
> + *p = (*p ^ *q) == *q;
> +}
> +
> +/* { dg-final { scan-assembler-not "@ unaligned" } } */
@@ -3290,7 +3290,8 @@
HOST_WIDE_INT mask = (HOST_WIDE_INT_1 << width) - 1;
rtx target, subtarget;
- if (arm_arch_thumb2)
+ if (arm_arch_thumb2
+ || (MEM_P (operands[0]) && MEM_ALIGN (operands[0]) < (unsigned) width))
{
if (unaligned_access && MEM_P (operands[0])
&& s_register_operand (operands[3], GET_MODE (operands[3]))
@@ -4965,6 +4966,27 @@
FAIL;
})
+(define_expand "ext<z>vmisalignsi"
+ [(set (match_operand:SI 0 "s_register_operand")
+ (any_extract:SI (match_operand:BLK 1 "memory_operand")
+ (match_operand 2 "const_int_operand")
+ (match_operand 3 "const_int_operand")))]
+ "TARGET_32BIT && unaligned_access"
+{
+ if (INTVAL (operands[3]) % BITS_PER_UNIT != 0)
+ FAIL;
+ if (INTVAL (operands[2]) == 32)
+ emit_insn (gen_unaligned_loadsi (operands[0],
+ adjust_address (operands[1], SImode, 0)));
+ else if (INTVAL (operands[2]) == 16)
+ emit_insn (gen_unaligned_loadhi<su> (operands[0],
+ adjust_address (operands[1],
+ HImode, 0)));
+ else
+ FAIL;
+ DONE;
+})
+
; Helper to expand register forms of extv with the proper modes.
(define_expand "extv_regsi"
@@ -377,6 +377,9 @@
;; A list of widening operators
(define_code_iterator SE [sign_extend zero_extend])
+;; Bit-field extractions
+(define_code_iterator any_extract [sign_extract zero_extract])
+
;; Right shifts
(define_code_iterator RSHIFTS [ashiftrt lshiftrt])
@@ -2104,7 +2107,11 @@
(define_code_attr su_optab [(fix "") (unsigned_fix "u")])
;; Sign prefix to use in instruction type suffixes, i.e. s32, u32.
-(define_code_attr su [(fix "s") (unsigned_fix "u")])
+(define_code_attr su [(fix "s") (unsigned_fix "u")
+ (sign_extract "s") (zero_extract "u")])
+
+;; Infix of the bit-field extraction optab names, i.e. extv, extzv.
+(define_code_attr z [(sign_extract "") (zero_extract "z")])
;; Right shifts
(define_code_attr shift [(ashiftrt "ashr") (lshiftrt "lshr")])
new file mode 100644
@@ -0,0 +1,101 @@
+/* PR target/55218: on Armv6 in Arm state a byte-aligned 16- or 32-bit
+ field of a packed structure is accessed with one unaligned
+ ldr/ldrh/ldrsh/str/strh, as on Armv6T2 and later. */
+/* { dg-do compile } */
+/* { dg-options "-O2 -marm -munaligned-access" } */
+/* { dg-require-effective-target arm_arch_v6_arm_ok } */
+/* { dg-add-options arm_arch_v6 } */
+/* { dg-add-options check_function_bodies } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+struct __attribute__ ((packed)) s
+{
+ char c;
+ short h;
+ unsigned short uh;
+ int w;
+};
+
+/*
+** load_w:
+** ldr r0, \[r0, #5\] @ unaligned
+** bx lr
+*/
+int
+load_w (struct s *p)
+{
+ return p->w;
+}
+
+/*
+** load_h:
+** ldrsh r0, \[r0, #1\] @ unaligned
+** bx lr
+*/
+int
+load_h (struct s *p)
+{
+ return p->h;
+}
+
+/*
+** load_uh:
+** ldrh r0, \[r0, #3\] @ unaligned
+** bx lr
+*/
+unsigned
+load_uh (struct s *p)
+{
+ return p->uh;
+}
+
+/*
+** store_w:
+** str r1, \[r0, #5\] @ unaligned
+** bx lr
+*/
+void
+store_w (struct s *p, int v)
+{
+ p->w = v;
+}
+
+/*
+** store_h:
+** strh r1, \[r0, #1\] @ unaligned
+** bx lr
+*/
+void
+store_h (struct s *p, short v)
+{
+ p->h = v;
+}
+
+/* A byte-aligned 16-bit bit-field of an ordinary structure. */
+struct b
+{
+ char c;
+ unsigned f : 16;
+};
+
+/*
+** load_bitfield:
+** ldrh r0, \[r0, #1\] @ unaligned
+** bx lr
+*/
+unsigned
+load_bitfield (struct b *p)
+{
+ return p->f;
+}
+
+/*
+** store_bitfield:
+** strh r1, \[r0, #1\] @ unaligned
+** bx lr
+*/
+void
+store_bitfield (struct b *p, unsigned v)
+{
+ p->f = v;
+}
new file mode 100644
@@ -0,0 +1,17 @@
+/* PR target/55218: with -mno-unaligned-access packed fields are still
+ accessed a byte at a time. */
+/* { dg-do compile } */
+/* { dg-options "-O2 -marm -mno-unaligned-access" } */
+/* { dg-require-effective-target arm_arch_v6_arm_ok } */
+/* { dg-add-options arm_arch_v6 } */
+
+struct __attribute__ ((packed)) s { char c; short h; int w; };
+
+int load_w (struct s *p) { return p->w; }
+int load_h (struct s *p) { return p->h; }
+void store_w (struct s *p, int v) { p->w = v; }
+void store_h (struct s *p, short v) { p->h = v; }
+
+/* { dg-final { scan-assembler-not "@ unaligned" } } */
+/* { dg-final { scan-assembler-not "\tldrh\t" } } */
+/* { dg-final { scan-assembler-not "\tstrh\t" } } */
new file mode 100644
@@ -0,0 +1,11 @@
+/* PR target/55218: unaligned_loadhis is 32-bit only, so Thumb-1 with
+ -munaligned-access must not use the unaligned expansion. */
+/* { dg-do compile } */
+/* { dg-options "-O2 -mthumb -munaligned-access" } */
+/* { dg-require-effective-target arm_arch_v6_ok } */
+/* { dg-add-options arm_arch_v6 } */
+
+struct __attribute__ ((packed)) s { char c; short h; int w; };
+
+int load_h (struct s *p) { return p->h; }
+int load_w (struct s *p) { return p->w; }
new file mode 100644
@@ -0,0 +1,64 @@
+/* PR target/55218: big-endian variant of pr55218-1.c. */
+/* { dg-do compile } */
+/* { dg-options "-O2 -marm -mbig-endian -munaligned-access" } */
+/* { dg-require-effective-target arm_arch_v6_arm_ok } */
+/* { dg-add-options arm_arch_v6 } */
+/* { dg-add-options check_function_bodies } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+struct __attribute__ ((packed)) s { char c; short h; unsigned short uh; int w; };
+
+/*
+** load_w:
+** ldr r0, \[r0, #5\] @ unaligned
+** bx lr
+*/
+int
+load_w (struct s *p)
+{
+ return p->w;
+}
+
+/*
+** load_h:
+** ldrsh r0, \[r0, #1\] @ unaligned
+** bx lr
+*/
+int
+load_h (struct s *p)
+{
+ return p->h;
+}
+
+/*
+** load_uh:
+** ldrh r0, \[r0, #3\] @ unaligned
+** bx lr
+*/
+unsigned
+load_uh (struct s *p)
+{
+ return p->uh;
+}
+
+/*
+** store_w:
+** str r1, \[r0, #5\] @ unaligned
+** bx lr
+*/
+void
+store_w (struct s *p, int v)
+{
+ p->w = v;
+}
+
+/*
+** store_h:
+** strh r1, \[r0, #1\] @ unaligned
+** bx lr
+*/
+void
+store_h (struct s *p, short v)
+{
+ p->h = v;
+}
new file mode 100644
@@ -0,0 +1,120 @@
+/* PR target/55218: values read and written through unaligned packed
+ fields on Armv6 in Arm state. */
+/* { dg-do run } */
+/* { dg-require-effective-target arm_arch_v6_arm_multilib } */
+/* { dg-options "-O2 -munaligned-access" } */
+/* { dg-add-options arm_arch_v6_arm } */
+
+struct __attribute__ ((packed)) s
+{
+ char c;
+ short h;
+ unsigned short uh;
+ int w;
+ unsigned uw;
+ long long d;
+};
+
+struct b
+{
+ char c;
+ unsigned f : 16;
+ int g : 16;
+};
+
+struct __attribute__ ((packed)) c
+{
+ unsigned a : 4, f : 16, g : 12;
+};
+
+union u
+{
+ struct s s;
+ unsigned char bytes[sizeof (struct s)];
+};
+
+union v
+{
+ struct b b;
+ unsigned char bytes[sizeof (struct b)];
+};
+
+union w
+{
+ struct c c;
+ unsigned char bytes[sizeof (struct c)];
+};
+
+#define NI __attribute__ ((noipa))
+
+NI int load_h (struct s *p) { return p->h; }
+NI unsigned load_uh (struct s *p) { return p->uh; }
+NI int load_w (struct s *p) { return p->w; }
+NI unsigned load_uw (struct s *p) { return p->uw; }
+NI long long load_d (struct s *p) { return p->d; }
+NI void store_h (struct s *p, short x) { p->h = x; }
+NI void store_uh (struct s *p, unsigned short x) { p->uh = x; }
+NI void store_w (struct s *p, int x) { p->w = x; }
+NI void store_d (struct s *p, long long x) { p->d = x; }
+NI unsigned load_f (struct b *p) { return p->f; }
+NI int load_g (struct b *p) { return p->g; }
+NI void store_f (struct b *p, unsigned x) { p->f = x; }
+NI void store_g (struct b *p, int x) { p->g = x; }
+NI unsigned load_cf (struct c *p) { return p->f; }
+NI int load_cfs (struct c *p) { return (short) p->f; }
+
+#define AT(T, buf, off) \
+ ({ T t_; __builtin_memcpy (&t_, (buf) + (off), sizeof t_); t_; })
+
+int
+main (void)
+{
+ union u x;
+ for (unsigned i = 0; i < sizeof x.bytes; i++)
+ x.bytes[i] = 0x81 + 3 * i;
+
+ if (load_h (&x.s) != AT (short, x.bytes, 1)
+ || load_uh (&x.s) != AT (unsigned short, x.bytes, 3)
+ || load_w (&x.s) != AT (int, x.bytes, 5)
+ || load_uw (&x.s) != AT (unsigned, x.bytes, 9)
+ || load_d (&x.s) != AT (long long, x.bytes, 13))
+ __builtin_abort ();
+
+ unsigned uw = AT (unsigned, x.bytes, 9);
+ store_h (&x.s, -2);
+ store_uh (&x.s, 0x1234);
+ store_w (&x.s, 0x56789abc);
+ store_d (&x.s, 0x0102030405060708LL);
+ if (x.bytes[0] != 0x81
+ || AT (short, x.bytes, 1) != -2
+ || AT (unsigned short, x.bytes, 3) != 0x1234
+ || AT (int, x.bytes, 5) != 0x56789abc
+ || AT (unsigned, x.bytes, 9) != uw
+ || AT (long long, x.bytes, 13) != 0x0102030405060708LL)
+ __builtin_abort ();
+
+ union v y;
+ for (unsigned i = 0; i < sizeof y.bytes; i++)
+ y.bytes[i] = 0xf0 - i;
+ if (load_f (&y.b) != AT (unsigned short, y.bytes, 1))
+ __builtin_abort ();
+ store_f (&y.b, 0xa55a);
+ store_g (&y.b, -3);
+ if (y.bytes[0] != 0xf0
+ || AT (unsigned short, y.bytes, 1) != 0xa55a
+ || load_f (&y.b) != 0xa55a || load_g (&y.b) != -3)
+ __builtin_abort ();
+
+ union w z;
+ for (unsigned i = 0; i < sizeof z.bytes; i++)
+ z.bytes[i] = 0x9c + 5 * i;
+ unsigned cf;
+ if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+ cf = (z.bytes[0] | z.bytes[1] << 8 | z.bytes[2] << 16) >> 4;
+ else
+ cf = (z.bytes[0] << 16 | z.bytes[1] << 8 | z.bytes[2]) >> 4;
+ cf &= 0xffff;
+ if (load_cf (&z.c) != cf || load_cfs (&z.c) != (short) cf)
+ __builtin_abort ();
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,16 @@
+/* PR target/55218: an aligned store to an element of a generic vector
+ is not an unaligned access. */
+/* { dg-do compile } */
+/* { dg-options "-O2 -marm -munaligned-access" } */
+/* { dg-require-effective-target arm_arch_v6_arm_ok } */
+/* { dg-add-options arm_arch_v6 } */
+
+typedef int V __attribute__ ((vector_size (32)));
+
+void
+f (V *p, V *q)
+{
+ *p = (*p ^ *q) == *q;
+}
+
+/* { dg-final { scan-assembler-not "@ unaligned" } } */