diff --git a/include/opcode/aarch64.h b/include/opcode/aarch64.h
index fe54de7cffe..dc9c98353d4 100644
--- a/include/opcode/aarch64.h
+++ b/include/opcode/aarch64.h
@@ -1261,7 +1261,9 @@ extern const aarch64_opcode aarch64_opcode_table[];
    allow.  This impacts the constraintts on assembly but yelds no
    impact on disassembly.  */
 #define F_OPD_NARROW (1ULL << 33)
-/* Next bit is 34.  */
+/* RCPC3 instruction has the field of 'size'.  */
+#define F_RCPC3_SIZE (1ULL << 34)
+/* Next bit is 35.  */
 
 /* Instruction constraints.  */
 /* This instruction has a predication constraint on the instruction at PC+4.  */
@@ -1328,7 +1330,7 @@ static inline bool
 opcode_has_special_coder (const aarch64_opcode *opcode)
 {
   return (opcode->flags & (F_SF | F_LSE_SZ | F_SIZEQ | F_FPTYPE | F_SSIZE | F_T
-	  | F_GPRSIZE_IN_Q | F_LDS_SIZE | F_MISC | F_N | F_COND)) != 0;
+	  | F_GPRSIZE_IN_Q | F_LDS_SIZE | F_MISC | F_N | F_COND | F_RCPC3_SIZE)) != 0;
 }
 
 struct aarch64_name_value_pair
diff --git a/opcodes/aarch64-asm.c b/opcodes/aarch64-asm.c
index 1db290eea7e..4be60964102 100644
--- a/opcodes/aarch64-asm.c
+++ b/opcodes/aarch64-asm.c
@@ -702,6 +702,24 @@ aarch64_ins_addr_offset (const aarch64_operand *self ATTRIBUTE_UNUSED,
   return true;
 }
 
+/* Encode the address operand for e.g.
+     stlur <Xt>, [<Xn|SP>{, <amount>}].  */
+bool
+aarch64_ins_rcpc3_addr_offset (const aarch64_operand *self ATTRIBUTE_UNUSED,
+			       const aarch64_opnd_info *info, aarch64_insn *code,
+			       const aarch64_inst *inst ATTRIBUTE_UNUSED,
+			       aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+  /* Rn */
+  insert_field (self->fields[0], code, info->addr.base_regno, 0);
+
+  /* simm9 */
+  int imm = info->addr.offset.imm;
+  insert_field (self->fields[1], code, imm, 0);
+
+  return true;
+}
+
 /* Encode the address operand for e.g. LDRSW <Xt>, [<Xn|SP>, #<simm>]!.  */
 bool
 aarch64_ins_addr_simm (const aarch64_operand *self,
@@ -736,6 +754,28 @@ aarch64_ins_addr_simm (const aarch64_operand *self,
   return true;
 }
 
+/* Encode the address operand, potentially offset by the load/store ammount,
+   e.g. LDIAPP <Xt>, <Xt2> [<Xn|SP>, #<simm>]
+   and  STILP  <Xt>, <Xt2> [<Xn|SP>], #<simm>.*/
+bool
+aarch64_ins_rcpc3_addr_opt_offset (const aarch64_operand *self ATTRIBUTE_UNUSED,
+				   const aarch64_opnd_info *info,
+				   aarch64_insn *code,
+				   const aarch64_inst *inst ATTRIBUTE_UNUSED,
+				   aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+  int imm;
+
+  /* Rn */
+  insert_field (FLD_Rn, code, info->addr.base_regno, 0);
+  /* simm */
+  imm = info->addr.offset.imm;
+  if (!imm)
+    insert_field (FLD_opc2, code, 1, 0);
+
+  return true;
+}
+
 /* Encode the address operand for e.g. LDRAA <Xt>, [<Xn|SP>{, #<simm>}].  */
 bool
 aarch64_ins_addr_simm10 (const aarch64_operand *self,
@@ -1858,6 +1898,22 @@ do_special_encoding (struct aarch64_inst *inst)
 	? 1 : 0;
       insert_field (FLD_lse_sz, &inst->value, value, 0);
     }
+  if (inst->opcode->flags & F_RCPC3_SIZE)
+    {
+      switch (inst->operands[0].qualifier)
+	{
+	case AARCH64_OPND_QLF_W: value = 2; break;
+	case AARCH64_OPND_QLF_X: value = 3; break;
+	case AARCH64_OPND_QLF_S_B: value = 0; break;
+	case AARCH64_OPND_QLF_S_H: value = 1; break;
+	case AARCH64_OPND_QLF_S_S: value = 2; break;
+	case AARCH64_OPND_QLF_S_D: value = 3; break;
+	case AARCH64_OPND_QLF_S_Q: value = 0; break;
+	default: return;
+	}
+      insert_field (FLD_rcpc3_size, &inst->value, value, 0);
+    }
+
   if (inst->opcode->flags & F_SIZEQ)
     encode_sizeq (inst);
   if (inst->opcode->flags & F_FPTYPE)
diff --git a/opcodes/aarch64-asm.h b/opcodes/aarch64-asm.h
index a3bf7bda013..0b4ae7625db 100644
--- a/opcodes/aarch64-asm.h
+++ b/opcodes/aarch64-asm.h
@@ -112,6 +112,8 @@ AARCH64_DECL_OPD_INSERTER (ins_imm_rotate2);
 AARCH64_DECL_OPD_INSERTER (ins_x0_to_x30);
 AARCH64_DECL_OPD_INSERTER (ins_simple_index);
 AARCH64_DECL_OPD_INSERTER (ins_plain_shrimm);
+AARCH64_DECL_OPD_INSERTER (ins_rcpc3_addr_opt_offset);
+AARCH64_DECL_OPD_INSERTER (ins_rcpc3_addr_offset);
 
 #undef AARCH64_DECL_OPD_INSERTER
 
diff --git a/opcodes/aarch64-dis.c b/opcodes/aarch64-dis.c
index 7e088a93c10..0734c448e32 100644
--- a/opcodes/aarch64-dis.c
+++ b/opcodes/aarch64-dis.c
@@ -1032,6 +1032,70 @@ aarch64_ext_addr_simple (const aarch64_operand *self ATTRIBUTE_UNUSED,
   return true;
 }
 
+/* Decode the address operand for rcpc3 instructions with optional load/store
+   datasize offset, e.g. STILPP <Xs>, <Xt>, [<Xn|SP>{,#-16}]! and
+   LIDAP <Xs>, <Xt>, [<Xn|SP>]{,#-16}.  */
+bool
+aarch64_ext_rcpc3_addr_opt_offset (const aarch64_operand *self ATTRIBUTE_UNUSED,
+				   aarch64_opnd_info *info,
+				   aarch64_insn code,
+				   const aarch64_inst *inst ATTRIBUTE_UNUSED,
+				   aarch64_operand_error *err ATTRIBUTE_UNUSED)
+{
+  info->addr.base_regno = extract_field (FLD_Rn, code, 0);
+  if (!extract_field (FLD_opc2, code, 0))
+    {
+      info->addr.writeback = 1;
+
+      enum aarch64_opnd type;
+      for (int i = 0; i < AARCH64_MAX_OPND_NUM; i++)
+	{
+	  aarch64_opnd_info opnd = info[i];
+	  type = opnd.type;
+	  if (aarch64_operands[type].op_class == AARCH64_OPND_CLASS_ADDRESS)
+	    break;
+	}
+
+      assert (aarch64_operands[type].op_class == AARCH64_OPND_CLASS_ADDRESS);
+      int offset = calc_ldst_datasize (inst->operands);
+
+      switch (type)
+	{
+	case AARCH64_OPND_RCPC3_ADDR_OPT_PREIND_WB:
+	case AARCH64_OPND_RCPC3_ADDR_PREIND_WB:
+	  info->addr.offset.imm = -offset;
+	  info->addr.preind = 1;
+	  break;
+	case AARCH64_OPND_RCPC3_ADDR_OPT_POSTIND:
+	case AARCH64_OPND_RCPC3_ADDR_POSTIND:
+	  info->addr.offset.imm = offset;
+	  info->addr.postind = 1;
+	  break;
+	default:
+	  return false;
+	}
+    }
+  return true;
+}
+
+bool
+aarch64_ext_rcpc3_addr_offset (const aarch64_operand *self ATTRIBUTE_UNUSED,
+			       aarch64_opnd_info *info,
+			       aarch64_insn code,
+			       const aarch64_inst *inst ATTRIBUTE_UNUSED,
+			       aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+  info->qualifier = get_expected_qualifier (inst, info->idx);
+
+  /* Rn */
+  info->addr.base_regno = extract_field (self->fields[0], code, 0);
+
+  /* simm9 */
+  aarch64_insn imm = extract_fields (code, 0, 1, self->fields[1]);
+  info->addr.offset.imm = sign_extend (imm, 8);
+  return true;
+}
+
 /* Decode the address operand for e.g.
      stlur <Xt>, [<Xn|SP>{, <amount>}].  */
 bool
@@ -2465,6 +2529,26 @@ do_special_decoding (aarch64_inst *inst)
       value = extract_field (FLD_lse_sz, inst->value, 0);
       inst->operands[idx].qualifier = get_greg_qualifier_from_value (value);
     }
+  /* rcpc3 'size' field.  */
+  if (inst->opcode->flags & F_RCPC3_SIZE)
+    {
+      value = extract_field (FLD_rcpc3_size, inst->value, 0);
+      for (int i = 0;
+	   aarch64_operands[inst->operands[i].type].op_class != AARCH64_OPND_CLASS_ADDRESS;
+	   i++)
+	{
+	  if (aarch64_operands[inst->operands[i].type].op_class
+	      == AARCH64_OPND_CLASS_INT_REG)
+	    inst->operands[i].qualifier = get_greg_qualifier_from_value (value & 1);
+	  else if (aarch64_operands[inst->operands[i].type].op_class
+	      == AARCH64_OPND_CLASS_FP_REG)
+	    {
+	      value += (extract_field (FLD_opc1, inst->value, 0) << 2);
+	      inst->operands[i].qualifier = get_sreg_qualifier_from_value (value);
+	    }
+	}
+    }
+
   /* size:Q fields.  */
   if (inst->opcode->flags & F_SIZEQ)
     return decode_sizeq (inst);
diff --git a/opcodes/aarch64-dis.h b/opcodes/aarch64-dis.h
index 20387db7b39..2d3af5b3ee3 100644
--- a/opcodes/aarch64-dis.h
+++ b/opcodes/aarch64-dis.h
@@ -136,6 +136,9 @@ AARCH64_DECL_OPD_EXTRACTOR (ext_imm_rotate2);
 AARCH64_DECL_OPD_EXTRACTOR (ext_x0_to_x30);
 AARCH64_DECL_OPD_EXTRACTOR (ext_simple_index);
 AARCH64_DECL_OPD_EXTRACTOR (ext_plain_shrimm);
+AARCH64_DECL_OPD_EXTRACTOR (ext_rcpc3_addr_opt_offset);
+AARCH64_DECL_OPD_EXTRACTOR (ext_rcpc3_addr_offset);
+
 
 #undef AARCH64_DECL_OPD_EXTRACTOR
 
diff --git a/opcodes/aarch64-opc.c b/opcodes/aarch64-opc.c
index bf506546fd0..9d994c12f1a 100644
--- a/opcodes/aarch64-opc.c
+++ b/opcodes/aarch64-opc.c
@@ -400,6 +400,8 @@ const aarch64_field fields[] =
     { 22,  1 }, /* sz: 1-bit element size select.  */
     { 22,  2 },	/* type: floating point type field in fp data inst.  */
     { 10,  2 },	/* vldst_size: size field in the AdvSIMD load/store inst.  */
+    { 12,  4 },	/* opc2: in rcpc3 ld/st pair inst deciding the pre/post-index.  */
+    { 30,  2 },	/* rcpc3_size: in rcpc3 ld/st pair, field controls Rt/Rt2 width.  */
 };
 
 enum aarch64_operand_class
diff --git a/opcodes/aarch64-opc.h b/opcodes/aarch64-opc.h
index f193a90ecc5..b05aaadb5dc 100644
--- a/opcodes/aarch64-opc.h
+++ b/opcodes/aarch64-opc.h
@@ -210,6 +210,8 @@ enum aarch64_field_kind
   FLD_sz,
   FLD_type,
   FLD_vldst_size,
+  FLD_opc2,
+  FLD_rcpc3_size,
 };
 
 /* Field description.  */
