diff --git a/gcc/config/aarch64/aarch64-simd.md b/gcc/config/aarch64/aarch64-simd.md
index 23c03a96371..00286b8b020 100644
--- a/gcc/config/aarch64/aarch64-simd.md
+++ b/gcc/config/aarch64/aarch64-simd.md
@@ -3608,6 +3608,61 @@
   }
 )
 
+;; Emit a sequence for bitwise logical reductions over vectors for V8QI, V16QI,
+;; V4HI, and V8HI modes.  The reduction is achieved by iteratively operating
+;; on the two halves of the input.
+;; If the input has 128 bits, the first operation is performed in vector
+;; registers.  From 64 bits down, the reduction steps are performed in general
+;; purpose registers.
+;; For example, for V8HI and operation AND, the intended sequence is:
+;; EXT      v1.16b, v0.16b, v0.16b, #8
+;; AND      v0.8b, v1.8b, v0.8b
+;; FMOV     x0, d0
+;; AND      x0, x0, x0, 32
+;; AND      w0, w0, w0, 16
+;;
+;; For V8QI and operation AND, the sequence is:
+;; AND      x0, x0, x0, lsr 32
+;; AND      w0, w0, w0, lsr, 16
+;; AND      w0, w0, w0, lsr, 8
+
+(define_expand "reduc_<optab>_scal_<mode>"
+ [(match_operand:<VEL> 0 "register_operand")
+  (LOGICAL:VDQV_E (match_operand:VDQV_E 1 "register_operand"))]
+  "TARGET_SIMD"
+  {
+    rtx dst = operands[1];
+    rtx tdi = gen_reg_rtx (DImode);
+    rtx tsi = lowpart_subreg (SImode, tdi, DImode);
+    rtx op1_lo;
+    if (known_eq (GET_MODE_SIZE (<MODE>mode), 16))
+      {
+	rtx t0 = gen_reg_rtx (<MODE>mode);
+	rtx t1 = gen_reg_rtx (DImode);
+	rtx t2 = gen_reg_rtx (DImode);
+	rtx idx = GEN_INT (8 / GET_MODE_UNIT_SIZE (<MODE>mode));
+	emit_insn (gen_aarch64_ext<mode> (t0, dst, dst, idx));
+	op1_lo = lowpart_subreg (V2DImode, dst, <MODE>mode);
+	rtx t0_lo = lowpart_subreg (V2DImode, t0, <MODE>mode);
+	emit_insn (gen_aarch64_get_lanev2di (t1, op1_lo, GEN_INT (0)));
+	emit_insn (gen_aarch64_get_lanev2di (t2, t0_lo, GEN_INT (0)));
+	emit_insn (gen_<optab>di3 (t1, t1, t2));
+	emit_move_insn (tdi, t1);
+      }
+    else
+      {
+	op1_lo = lowpart_subreg (DImode, dst, <MODE>mode);
+	emit_move_insn (tdi, op1_lo);
+      }
+    emit_insn (gen_<optab>_lshrdi3 (tdi, tdi, GEN_INT (32), tdi));
+    emit_insn (gen_<optab>_lshrsi3 (tsi, tsi, GEN_INT (16), tsi));
+    if (known_eq (GET_MODE_UNIT_BITSIZE (<MODE>mode), 8))
+      emit_insn (gen_<optab>_lshrsi3 (tsi, tsi, GEN_INT (8), tsi));
+    emit_move_insn (operands[0], lowpart_subreg (<VEL>mode, tsi, SImode));
+    DONE;
+  }
+)
+
 (define_insn "aarch64_reduc_<optab>_internal<mode>"
  [(set (match_operand:VDQV_S 0 "register_operand" "=w")
        (unspec:VDQV_S [(match_operand:VDQV_S 1 "register_operand" "w")]
diff --git a/gcc/testsuite/gcc.target/aarch64/simd/logical_reduc.c b/gcc/testsuite/gcc.target/aarch64/simd/logical_reduc.c
new file mode 100644
index 00000000000..9508288b218
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/simd/logical_reduc.c
@@ -0,0 +1,208 @@
+/* { dg-options "-O2 -ftree-vectorize" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include <stdint.h>
+
+/*
+** fv16qi_and:
+**	ldr	q([0-9]+), \[x0\]
+**	ext	v([0-9]+)\.16b, v\1\.16b, v\1\.16b, #8
+**	and	v\1\.8b, v\2\.8b, v\1\.8b
+**	fmov	x0, d\1
+**	and	x0, x0, x0, lsr 32
+**	and	w0, w0, w0, lsr 16
+**	and	w0, w0, w0, lsr 8
+**	ret
+*/
+int8_t fv16qi_and (int8_t *a)
+{
+  int8_t b = -1;
+  for (int i = 0; i < 16; ++i)
+    b &= a[i];
+  return b;
+}
+
+/*
+** fv8hi_and:
+**	ldr	q([0-9]+), \[x0\]
+**	ext	v([0-9]+)\.16b, v\1\.16b, v\1\.16b, #8
+**	and	v\1\.8b, v\2\.8b, v\1\.8b
+**	fmov	x0, d\1
+**	and	x0, x0, x0, lsr 32
+**	and	w0, w0, w0, lsr 16
+**	ret
+*/
+int16_t fv8hi_and (int16_t *a)
+{
+  int16_t b = -1;
+  for (int i = 0; i < 8; ++i)
+    b &= a[i];
+  return b;
+}
+
+/*
+** fv16qi_or:
+**	ldr	q([0-9]+), \[x0\]
+**	ext	v([0-9]+)\.16b, v\1\.16b, v\1\.16b, #8
+**	orr	v\1\.8b, v\2\.8b, v\1\.8b
+**	fmov	x0, d\1
+**	orr	x0, x0, x0, lsr 32
+**	orr	w0, w0, w0, lsr 16
+**	orr	w0, w0, w0, lsr 8
+**	ret
+*/
+int8_t fv16qi_or (int8_t *a)
+{
+  int8_t b = 0;
+  for (int i = 0; i < 16; ++i)
+    b |= a[i];
+  return b;
+}
+
+/*
+** fv8hi_or:
+**	ldr	q([0-9]+), \[x0\]
+**	ext	v([0-9]+)\.16b, v\1\.16b, v\1\.16b, #8
+**	orr	v\1\.8b, v\2\.8b, v\1\.8b
+**	fmov	x0, d\1
+**	orr	x0, x0, x0, lsr 32
+**	orr	w0, w0, w0, lsr 16
+**	ret
+*/
+int16_t fv8hi_or (int16_t *a)
+{
+  int16_t b = 0;
+  for (int i = 0; i < 8; ++i)
+    b |= a[i];
+  return b;
+}
+
+/*
+** fv16qi_xor:
+**	ldr	q([0-9]+), \[x0\]
+**	ext	v([0-9]+)\.16b, v\1\.16b, v\1\.16b, #8
+**	eor	v\1\.8b, v\2\.8b, v\1\.8b
+**	fmov	x0, d\1
+**	eor	x0, x0, x0, lsr 32
+**	eor	w0, w0, w0, lsr 16
+**	eor	w0, w0, w0, lsr 8
+**	ret
+*/
+int8_t fv16qi_xor (int8_t *a)
+{
+  int8_t b = 0;
+  for (int i = 0; i < 16; ++i)
+    b ^= a[i];
+  return b;
+}
+
+/*
+** fv8hi_xor:
+**	ldr	q([0-9]+), \[x0\]
+**	ext	v([0-9]+)\.16b, v\1\.16b, v\1\.16b, #8
+**	eor	v\1\.8b, v\2\.8b, v\1\.8b
+**	fmov	x0, d\1
+**	eor	x0, x0, x0, lsr 32
+**	eor	w0, w0, w0, lsr 16
+**	ret
+*/
+int16_t fv8hi_xor (int16_t *a)
+{
+  int16_t b = 0;
+  for (int i = 0; i < 8; ++i)
+    b ^= a[i];
+  return b;
+}
+
+/*
+** fv8qi_and:
+**	ldr	x0, \[x0\]
+**	and	x0, x0, x0, lsr 32
+**	and	w0, w0, w0, lsr 16
+**	and	w0, w0, w0, lsr 8
+**	ret
+*/
+int8_t fv8qi_and (int8_t *a)
+{
+  int8_t b = -1;
+  for (int i = 0; i < 8; ++i)
+    b &= a[i];
+  return b;
+}
+
+/*
+** fv4hi_and:
+**	ldr	x0, \[x0\]
+**	and	x0, x0, x0, lsr 32
+**	and	w0, w0, w0, lsr 16
+**	ret
+*/
+int16_t fv4hi_and (int16_t *a)
+{
+  int16_t b = -1;
+  for (int i = 0; i < 4; ++i)
+    b &= a[i];
+  return b;
+}
+
+/*
+** fv8qi_or:
+**	ldr	x0, \[x0\]
+**	orr	x0, x0, x0, lsr 32
+**	orr	w0, w0, w0, lsr 16
+**	orr	w0, w0, w0, lsr 8
+**	ret
+*/
+int8_t fv8qi_or (int8_t *a)
+{
+  int8_t b = 0;
+  for (int i = 0; i < 8; ++i)
+    b |= a[i];
+  return b;
+}
+
+/*
+** fv4hi_or:
+**	ldr	x0, \[x0\]
+**	orr	x0, x0, x0, lsr 32
+**	orr	w0, w0, w0, lsr 16
+**	ret
+*/
+int16_t fv4hi_or (int16_t *a)
+{
+  int16_t b = 0;
+  for (int i = 0; i < 4; ++i)
+    b |= a[i];
+  return b;
+}
+
+/*
+** fv8qi_xor:
+**	ldr	x0, \[x0\]
+**	eor	x0, x0, x0, lsr 32
+**	eor	w0, w0, w0, lsr 16
+**	eor	w0, w0, w0, lsr 8
+**	ret
+*/
+int8_t fv8qi_xor (int8_t *a)
+{
+  int8_t b = 0;
+  for (int i = 0; i < 8; ++i)
+    b ^= a[i];
+  return b;
+}
+
+/*
+** fv4hi_xor:
+**	ldr	x0, \[x0\]
+**	eor	x0, x0, x0, lsr 32
+**	eor	w0, w0, w0, lsr 16
+**	ret
+*/
+int16_t fv4hi_xor (int16_t *a)
+{
+  int16_t b = 0;
+  for (int i = 0; i < 4; ++i)
+    b ^= a[i];
+  return b;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/vect-reduc-or_1.c b/gcc/testsuite/gcc.target/aarch64/vect-reduc-or_1.c
index 918822a7d00..70c4ca18094 100644
--- a/gcc/testsuite/gcc.target/aarch64/vect-reduc-or_1.c
+++ b/gcc/testsuite/gcc.target/aarch64/vect-reduc-or_1.c
@@ -32,4 +32,4 @@ main (unsigned char argc, char **argv)
   return 0;
 }
 
-/* { dg-final { scan-tree-dump "Reduce using vector shifts" "vect" } } */
+/* { dg-final { scan-tree-dump "Reduce using direct vector reduction" "vect" } } */
diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index 8f2afe866c7..44f737f15d0 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -9564,7 +9564,9 @@ proc check_effective_target_vect_logical_reduc { } {
 		   || [istarget amdgcn-*-*]
 		   || [check_effective_target_riscv_v]
 		   || [check_effective_target_loongarch_sx]
-		   || [istarget i?86-*-*] || [istarget x86_64-*-*]}]
+		   || [istarget i?86-*-*]
+		   || [istarget x86_64-*-*]
+		   || [istarget aarch64*-*-*]}]
 }
 
 # Return 1 if the target supports the fold_extract_last optab.
