diff --git a/gcc/config/aarch64/aarch64-simd.md b/gcc/config/aarch64/aarch64-simd.md
index 9f4cb72f5c321136cfcc263bcb7c956365dbc512..292848984081e1fb517b85fc10816e50f5d0c841 100644
--- a/gcc/config/aarch64/aarch64-simd.md
+++ b/gcc/config/aarch64/aarch64-simd.md
@@ -808,6 +808,25 @@ (define_insn "aarch64_simd_vec_set<mode>"
   [(set_attr "type" "neon_ins<q>, neon_from_gp<q>, neon_load1_one_lane<q>")]
 )
 
+
+;; Helper for aarch64_expand_vector_init.
+
+(define_insn "aarch64_simd_vec_mov<mode>"
+  [(set (match_operand:VALL_F16 0 "register_operand" "=w,w,w")
+	(vec_merge:VALL_F16
+	    (vec_duplicate:VALL_F16
+		(match_operand:<VEL> 1 "general_operand" "w,?r,m"))
+	    (match_operand:VALL_F16 2 "aarch64_simd_imm_zero" )
+	    (match_operand:SI 3 "immediate_operand")))]
+  "TARGET_SIMD
+   && ENDIAN_LANE_N (<nunits>, exact_log2 (INTVAL (operands[3]))) == 0"
+  "@
+   fmov\\t%d0, %d1
+   fmov\\t%d0, %1
+   ldr\\t%<Vetype>0, %1"
+  [(set_attr "type" "fmov,f_mcr,neon_load1_1reg<q>")]
+)
+
 (define_insn "*aarch64_simd_vec_copy_lane<mode>"
   [(set (match_operand:VALL_F16 0 "register_operand" "=w")
 	(vec_merge:VALL_F16
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index b9759c638b010567f5047ff5129912986a30b49a..fdd8d2f587087180c71ce71003ec074dceeb6242 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -13879,6 +13879,44 @@ aarch64_simd_make_constant (rtx vals)
     return NULL_RTX;
 }
 
+/* Return the insn_code to use for MODE from the aarch64_simd_vec_mov
+   family of patterns.  */
+
+insn_code
+aarch64_get_aarch64_simd_vec_mov_code (machine_mode mode)
+{
+  switch (mode)
+    {
+      case E_V8QImode:
+	return CODE_FOR_aarch64_simd_vec_movv8qi;
+      case E_V16QImode:
+	return CODE_FOR_aarch64_simd_vec_movv16qi;
+      case E_V4HImode:
+	return CODE_FOR_aarch64_simd_vec_movv4hi;
+      case E_V8HImode:
+	return CODE_FOR_aarch64_simd_vec_movv8hi;
+      case E_V2SImode:
+	return CODE_FOR_aarch64_simd_vec_movv2si;
+      case E_V4SImode:
+	return CODE_FOR_aarch64_simd_vec_movv4si;
+      case E_V2DImode:
+	return CODE_FOR_aarch64_simd_vec_movv2di;
+      case E_V4HFmode:
+	return CODE_FOR_aarch64_simd_vec_movv4hf;
+      case E_V8HFmode:
+	return CODE_FOR_aarch64_simd_vec_movv8hf;
+      case E_V2SFmode:
+	return CODE_FOR_aarch64_simd_vec_movv2sf;
+      case E_V4SFmode:
+	return CODE_FOR_aarch64_simd_vec_movv4sf;
+      case E_V2DFmode:
+	return CODE_FOR_aarch64_simd_vec_movv2df;
+      default:
+	gcc_unreachable ();
+    }
+  return CODE_FOR_nothing;
+}
+
 /* Expand a vector initialisation sequence, such that TARGET is
    initialised to contain VALS.  */
 
@@ -13967,7 +14005,7 @@ aarch64_expand_vector_init (rtx target, rtx vals)
 	 are equally useless to us, in which case just immediately set the
 	 vector register using the first element.  */
 
-      if (maxv == 1)
+      if (maxv == 1 && GET_MODE_NUNITS (mode).is_constant ())
 	{
 	  /* For vectors of two 64-bit elements, we can do even better.  */
 	  if (n_elts == 2
@@ -13999,12 +14037,24 @@ aarch64_expand_vector_init (rtx target, rtx vals)
 		  return;
 		}
 	    }
-	  /* The subreg-move sequence below will move into lane zero of the
+	  /* The move sequence below will move into lane zero of the
 	     vector register.  For big-endian we want that position to hold
 	     the last element of VALS.  */
 	  maxelement = BYTES_BIG_ENDIAN ? n_elts - 1 : 0;
 	  rtx x = copy_to_mode_reg (inner_mode, XVECEXP (vals, 0, maxelement));
-	  aarch64_emit_move (target, lowpart_subreg (mode, x, inner_mode));
+
+	  /* We will be writing all the lanes in the vector but the init-regs
+	     machinery doesn't recognise the lane-by-lane initialisation
+	     sequence of the whole vector and will emit an explicit zeroing
+	     of TARGET.  We want to avoid that.
+	     We describe the first lane insert as a scalar value concatenated
+	     with a vector of zeroes.  */
+	  insn_code fmov_code = aarch64_get_aarch64_simd_vec_mov_code (mode);
+	  unsigned HOST_WIDE_INT merge_imm
+	    = HOST_WIDE_INT_1U
+	        << ENDIAN_LANE_N (GET_MODE_NUNITS (mode).to_constant (), 0);
+	  emit_insn (GEN_FCN (fmov_code) (target, x, CONST0_RTX (mode),
+		     gen_int_mode (merge_imm, SImode)));
 	}
       else
 	{
diff --git a/gcc/testsuite/g++.dg/torture/aarch64-vect-init-1.c b/gcc/testsuite/g++.dg/torture/aarch64-vect-init-1.c
new file mode 100644
index 0000000000000000000000000000000000000000..07892018a7331a819d14ae72e9ca549533092483
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/aarch64-vect-init-1.c
@@ -0,0 +1,28 @@
+class A {
+public:
+  unsigned char *fn1();
+  int fn2();
+};
+
+class B {
+  A fld1;
+  int fld2;
+  void fn3();
+  unsigned char fld3;
+};
+
+int a;
+
+void
+B::fn3() {
+  int b = fld1.fn2() / 8;
+  unsigned char *c = fld1.fn1(), *d = &fld3, *e = c;
+  for (; a < fld2;)
+    for (int j = 0; j < b; j++)
+      *d++ = e[j];
+  for (; 0 < fld2;)
+    for (int j = 0; j < b; j++)
+      e[j] = *d++;
+  for (; fld2;)
+    ;
+}
