diff --git a/gcc/config/rs6000/mma.md b/gcc/config/rs6000/mma.md
index 87569f1c31d..fab849a4f12 100644
--- a/gcc/config/rs6000/mma.md
+++ b/gcc/config/rs6000/mma.md
@@ -272,7 +272,7 @@
 
 (define_insn_and_split "*movoo"
   [(set (match_operand:OO 0 "nonimmediate_operand" "=wa,m,wa")
-	(match_operand:OO 1 "input_operand" "m,wa,wa"))]
+	(match_operand:OO 1 "input_operand" "m,wa,waO"))]
   "TARGET_MMA
    && (gpc_reg_operand (operands[0], OOmode)
        || gpc_reg_operand (operands[1], OOmode))"
@@ -473,9 +473,7 @@
 	(const_int 0))]
   "TARGET_MMA"
 {
-  rtx xo0 = gen_rtx_UNSPEC (XOmode, gen_rtvec (1, const0_rtx),
-			    UNSPEC_MMA_XXSETACCZ);
-  emit_insn (gen_rtx_SET (operands[0], xo0));
+  emit_insn (gen_rtx_SET (operands[0], CONST0_RTX (XOmode)));
   DONE;
 })
 
diff --git a/gcc/config/rs6000/predicates.md b/gcc/config/rs6000/predicates.md
index 5d1952e59d3..30805ab0619 100644
--- a/gcc/config/rs6000/predicates.md
+++ b/gcc/config/rs6000/predicates.md
@@ -1081,6 +1081,10 @@
       && easy_vector_constant (op, mode))
     return 1;
 
+  /* For OOmode, zero is an easy constant.  */
+  if (mode == OOmode && op == CONST0_RTX (mode))
+    return 1;
+
   /* For floating-point or multi-word mode, the only remaining valid type
      is a register.  */
   if (SCALAR_FLOAT_MODE_P (mode)
diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index f5565a1a253..c726aa09d26 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -1752,6 +1752,10 @@ static const struct attribute_spec rs6000_attribute_table[] =
 
 #undef TARGET_INVALID_CONVERSION
 #define TARGET_INVALID_CONVERSION rs6000_invalid_conversion
+
+#undef TARGET_OPAQUE_CONST0_RTX
+#define TARGET_OPAQUE_CONST0_RTX rs6000_opaque_const0_rtx
+
 
 
 /* Processor table.  */
@@ -16624,6 +16628,19 @@ rs6000_split_multireg_move (rtx dst, rtx src)
 	  return;
 	}
 
+      /* Split the clearing of an OOmode register pair into clearing
+	 of its two constituent registers.  */
+      if (REG_P (dst) && mode == OOmode && src == CONST0_RTX (mode))
+	{
+	  int regno = REGNO (dst);
+	  gcc_assert (VSX_REGNO_P (regno));
+	  emit_insn (gen_rtx_SET (gen_rtx_REG (reg_mode, regno),
+				  CONST0_RTX (reg_mode)));
+	  emit_insn (gen_rtx_SET (gen_rtx_REG (reg_mode, regno + 1),
+				  CONST0_RTX (reg_mode)));
+	  return;
+	}
+
       /* Register -> register moves can use common code.  */
     }
 
@@ -27477,6 +27494,25 @@ rs6000_output_addr_vec_elt (FILE *file, int value)
   fprintf (file, "\n");
 }
 
+/* Implement TARGET_OPAQUE_CONST0_RTX.  */
+
+rtx
+rs6000_opaque_const0_rtx (machine_mode mode)
+{
+  gcc_assert (OPAQUE_MODE_P (mode));
+
+  switch (mode)
+    {
+    case E_OOmode:
+      return const0_rtx;
+    case E_XOmode:
+      return gen_rtx_UNSPEC (XOmode, gen_rtvec (1, const0_rtx),
+			     UNSPEC_MMA_XXSETACCZ);
+    default:
+      gcc_unreachable ();
+    }
+}
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-rs6000.h"
diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c
index b23284e5e56..4ac26491481 100644
--- a/gcc/emit-rtl.c
+++ b/gcc/emit-rtl.c
@@ -6408,6 +6408,11 @@ init_emit_once (void)
     if (GET_MODE_CLASS ((machine_mode) i) == MODE_CC)
       const_tiny_rtx[0][i] = const0_rtx;
 
+  FOR_EACH_MODE_IN_CLASS (mode, MODE_OPAQUE)
+    {
+      const_tiny_rtx[0][(int) mode] = targetm.opaque_const0_rtx (mode);
+    }
+
   pc_rtx = gen_rtx_fmt_ (PC, VOIDmode);
   ret_rtx = gen_rtx_fmt_ (RETURN, VOIDmode);
   simple_return_rtx = gen_rtx_fmt_ (SIMPLE_RETURN, VOIDmode);
diff --git a/gcc/hooks.c b/gcc/hooks.c
index 680271f76a4..8e71105265a 100644
--- a/gcc/hooks.c
+++ b/gcc/hooks.c
@@ -380,6 +380,12 @@ hook_bool_wint_wint_uint_bool_true (const widest_int &, const widest_int &,
   return true;
 }
 
+rtx
+hook_rtx_mode_unreachable (machine_mode mode ATTRIBUTE_UNUSED)
+{
+  gcc_unreachable ();
+}
+
 /* Generic hook that takes an rtx and returns it.  */
 rtx
 hook_rtx_rtx_identity (rtx x)
diff --git a/gcc/hooks.h b/gcc/hooks.h
index add9a742e41..ba9b6d9ff75 100644
--- a/gcc/hooks.h
+++ b/gcc/hooks.h
@@ -119,6 +119,7 @@ extern unsigned int hook_uint_mode_0 (machine_mode);
 extern bool default_can_output_mi_thunk_no_vcall (const_tree, HOST_WIDE_INT,
 						  HOST_WIDE_INT, const_tree);
 
+extern rtx hook_rtx_mode_unreachable (machine_mode);
 extern rtx hook_rtx_rtx_identity (rtx);
 extern rtx hook_rtx_rtx_null (rtx);
 extern rtx hook_rtx_tree_int_null (tree, int);
diff --git a/gcc/target.def b/gcc/target.def
index be7fcde961a..c026e584c78 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -3362,6 +3362,13 @@ constants can be done inline.  The function\n\
  HOST_WIDE_INT, (const_tree constant, HOST_WIDE_INT basic_align),
  default_constant_alignment)
 
+DEFHOOK
+(opaque_const0_rtx,
+ "Return an RTX representing the value @code{0} for opaque mode @var{mode}.\n\
+The default version of this hook always throws an error.",
+rtx, (machine_mode mode),
+hook_rtx_mode_unreachable)
+
 DEFHOOK
 (translate_mode_attribute,
  "Define this hook if during mode attribute processing, the port should\n\
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 3b19e6f4281..74f6626112b 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -3361,6 +3361,8 @@ stack.
 
 @hook TARGET_REF_MAY_ALIAS_ERRNO
 
+@hook TARGET_OPAQUE_CONST0_RTX
+
 @hook TARGET_TRANSLATE_MODE_ATTRIBUTE
 
 @hook TARGET_SCALAR_MODE_SUPPORTED_P
diff --git a/gcc/testsuite/gcc.target/powerpc/pr98872.c b/gcc/testsuite/gcc.target/powerpc/pr98872.c
new file mode 100644
index 00000000000..580b90de7dc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/pr98872.c
@@ -0,0 +1,20 @@
+/* PR target/98872 */
+/* { dg-do compile } */
+/* { dg-require-effective-target power10_ok } */
+/* { dg-options "-O2 -mdejagnu-cpu=power10" } */
+
+/* Verify we do not ICE on the tests below.  */
+
+void
+foo (__vector_quad *dst)
+{
+  __vector_quad acc;
+  *dst = acc;
+}
+
+void
+bar (__vector_pair *dst)
+{
+  __vector_pair pair;
+  *dst = pair;
+}
