diff --git a/gcc/config/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc
index a85d7630b41..e14cecba0ef 100644
--- a/gcc/config/rs6000/rs6000.cc
+++ b/gcc/config/rs6000/rs6000.cc
@@ -1758,6 +1758,9 @@ static const struct attribute_spec rs6000_attribute_table[] =
 #undef TARGET_NEED_IPA_FN_TARGET_INFO
 #define TARGET_NEED_IPA_FN_TARGET_INFO rs6000_need_ipa_fn_target_info
 
+#undef TARGET_BLOCK_MOVE_FOR_STRUCT
+#define TARGET_BLOCK_MOVE_FOR_STRUCT rs600_block_move_for_struct
+
 #undef TARGET_UPDATE_IPA_FN_TARGET_INFO
 #define TARGET_UPDATE_IPA_FN_TARGET_INFO rs6000_update_ipa_fn_target_info
 
@@ -23672,6 +23675,47 @@ rs6000_function_value (const_tree valtype,
   return gen_rtx_REG (mode, regno);
 }
 
+/* Subroutine of rs600_block_move_for_struct, to get the internal mode which
+   would be used to move the struct.  */
+static machine_mode
+submode_for_struct_block_move (tree type)
+{
+  gcc_assert (TREE_CODE (type) == RECORD_TYPE);
+
+  /* The sub mode may not be the field's type of the struct.
+     It would be fine to use the mode as if the type is used as a function
+     parameter or return value.  For example: DF for "{double a[4];}", and
+     DI for "{doubel a[3]; long l;}".
+     Here, using the mode as if it is function return type.  */
+  rtx val = rs6000_function_value (type, NULL, 0);
+  return (GET_CODE (val) == PARALLEL) ? GET_MODE (XEXP (XVECEXP (val, 0, 0), 0))
+				      : word_mode;
+}
+
+/* Implement the TARGET_BLOCK_MOVE_FOR_STRUCT hook.  */
+static void
+rs600_block_move_for_struct (rtx x, rtx y, tree exp, HOST_WIDE_INT method)
+{
+  machine_mode mode = submode_for_struct_block_move (TREE_TYPE (exp));
+  int mode_size = GET_MODE_SIZE (mode);
+  int size = UINTVAL (expr_size (exp));
+  if (size < mode_size || (size % mode_size) != 0 || size > 64)
+    {
+      default_block_move_for_struct (x, y, exp, method);
+      return;
+    }
+
+  int len = size / mode_size;
+  for (int i = 0; i < len; i++)
+    {
+      rtx temp = gen_reg_rtx (mode);
+      rtx src = adjust_address (y, mode, mode_size * i);
+      rtx dest = adjust_address (x, mode, mode_size * i);
+      emit_move_insn (temp, src);
+      emit_move_insn (dest, temp);
+    }
+}
+
 /* Define how to find the value returned by a library function
    assuming the value has mode MODE.  */
 rtx
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 8572313b308..c8a1c1f30cf 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -1380,6 +1380,12 @@ retain the field's mode.
 Normally, this is not needed.
 @end deftypefn
 
+@deftypefn {Target Hook} void TARGET_BLOCK_MOVE_FOR_STRUCT (rtx @var{x}, rtx @var{y}, tree @var{exp}, HOST_WIDE_INT @var{method})
+Move from @var{y} to @var{x}, where @var{y} is as @var{exp} in structure
+type. @var{method} is the method if this function invokes block_move.
+The default definition invokes block_move.
+@end deftypefn
+
 @defmac ROUND_TYPE_ALIGN (@var{type}, @var{computed}, @var{specified})
 Define this macro as an expression for the alignment of a type (given
 by @var{type} as a tree node) if the alignment computed in the usual
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 986e8f0da09..f0f525a2008 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -1226,6 +1226,8 @@ to aligning a bit-field within the structure.
 
 @hook TARGET_MEMBER_TYPE_FORCES_BLK
 
+@hook TARGET_BLOCK_MOVE_FOR_STRUCT
+
 @defmac ROUND_TYPE_ALIGN (@var{type}, @var{computed}, @var{specified})
 Define this macro as an expression for the alignment of a type (given
 by @var{type} as a tree node) if the alignment computed in the usual
diff --git a/gcc/expr.cc b/gcc/expr.cc
index c6917fbf7bd..734dc07a76b 100644
--- a/gcc/expr.cc
+++ b/gcc/expr.cc
@@ -6504,9 +6504,17 @@ store_expr (tree exp, rtx target, int call_param_p,
 	emit_group_store (target, temp, TREE_TYPE (exp),
 			  int_size_in_bytes (TREE_TYPE (exp)));
       else if (GET_MODE (temp) == BLKmode)
-	emit_block_move (target, temp, expr_size (exp),
-			 (call_param_p
-			  ? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
+	{
+	  if (TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE)
+	    targetm.block_move_for_struct (target, temp, exp,
+					   call_param_p ? BLOCK_OP_CALL_PARM
+							: BLOCK_OP_NORMAL);
+	  else
+	    emit_block_move (target, temp, expr_size (exp),
+			     (call_param_p ? BLOCK_OP_CALL_PARM
+					   : BLOCK_OP_NORMAL));
+	}
+
       /* If we emit a nontemporal store, there is nothing else to do.  */
       else if (nontemporal && emit_storent_insn (target, temp))
 	;
diff --git a/gcc/target.def b/gcc/target.def
index 25f94c19fa7..e141f72a8a3 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -5584,6 +5584,16 @@ Normally, this is not needed.",
  bool, (const_tree field, machine_mode mode),
  default_member_type_forces_blk)
 
+
+/* Move block for structure type.  */
+DEFHOOK
+(block_move_for_struct,
+ "Move from @var{y} to @var{x}, where @var{y} is as @var{exp} in structure\n\
+type. @var{method} is the method if this function invokes block_move.\n\
+The default definition invokes block_move.",
+ void, (rtx x, rtx y, tree exp, HOST_WIDE_INT method),
+ default_block_move_for_struct)
+
 /* See tree-ssa-math-opts.cc:divmod_candidate_p for conditions
    that gate the divod transform.  */
 DEFHOOK
diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc
index 12a58456b39..2b96c348419 100644
--- a/gcc/targhooks.cc
+++ b/gcc/targhooks.cc
@@ -2330,6 +2330,13 @@ default_member_type_forces_blk (const_tree, machine_mode)
   return false;
 }
 
+/* Default version of block_move_for_struct.  */
+void
+default_block_move_for_struct (rtx x, rtx y, tree exp, HOST_WIDE_INT method)
+{
+  emit_block_move (x, y, expr_size (exp), (enum block_op_methods)method);
+}
+
 /* Default version of canonicalize_comparison.  */
 
 void
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index a6a423c1abb..c284a35ee28 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -264,6 +264,7 @@ extern void default_asm_output_ident_directive (const char*);
 
 extern scalar_int_mode default_cstore_mode (enum insn_code);
 extern bool default_member_type_forces_blk (const_tree, machine_mode);
+extern void default_block_move_for_struct (rtx, rtx, tree, HOST_WIDE_INT);
 extern void default_atomic_assign_expand_fenv (tree *, tree *, tree *);
 extern tree build_va_arg_indirect_ref (tree);
 extern tree std_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *);
