REG_TYPE_MMXWCG,
   REG_TYPE_XSCALE,
   REG_TYPE_RNB,
-  REG_TYPE_ZR
+  REG_TYPE_ZR,
+  REG_TYPE_PSEUDO
 };
 
 /* Structure for a hash table entry for a register.
   [REG_TYPE_MQ]            = N_("MVE vector register expected"),
   [REG_TYPE_RNB]    = "",
   [REG_TYPE_ZR]     = N_("ZR register expected"),
+  [REG_TYPE_PSEUDO] = N_("Pseudo register expected"),
 };
 
 /* Some well known registers that we refer to directly elsewhere.  */
 enum reg_list_els
 {
   REGLIST_RN,
+  REGLIST_PSEUDO,
   REGLIST_CLRM,
   REGLIST_VFP_S,
   REGLIST_VFP_S_VPR,
   long range = 0;
   int another_range;
 
-  gas_assert (etype == REGLIST_RN || etype == REGLIST_CLRM);
+  gas_assert (etype == REGLIST_RN || etype == REGLIST_CLRM
+             || etype == REGLIST_PSEUDO);
 
   /* We come back here if we get ranges concatenated by '+' or '|'.  */
   do
              int reg;
              const char apsr_str[] = "apsr";
              int apsr_str_len = strlen (apsr_str);
+             enum arm_reg_type rt;
 
-             reg = arm_reg_parse (&str, REG_TYPE_RN);
+             if (etype == REGLIST_RN || etype == REGLIST_CLRM)
+               rt = REG_TYPE_RN;
+             else
+               rt = REG_TYPE_PSEUDO;
+
+             reg = arm_reg_parse (&str, rt);
              if (etype == REGLIST_CLRM)
                {
                  if (reg == REG_SP || reg == REG_PC)
                      return FAIL;
                    }
                }
+             else if (etype == REGLIST_PSEUDO)
+               {
+                 if (reg == FAIL)
+                   {
+                     first_error (_(reg_expected_msgs[REG_TYPE_PSEUDO]));
+                     return FAIL;
+                   }
+               }
              else /* etype == REGLIST_RN.  */
                {
                  if (reg == FAIL)
   demand_empty_rest_of_line ();
 }
 
+/* Parse a directive saving pseudo registers.  */
+
+static void
+s_arm_unwind_save_pseudo (void)
+{
+  valueT op;
+  long range;
+
+  range = parse_reg_list (&input_line_pointer, REGLIST_PSEUDO);
+  if (range == FAIL)
+    {
+      as_bad (_("expected pseudo register list"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  demand_empty_rest_of_line ();
+
+  if (range & (1 << 9))
+    {
+      /* Opcode for restoring RA_AUTH_CODE.  */
+      op = 0xb4;
+      add_unwind_opcode (op, 1);
+    }
+}
+
 
 /* Parse a directive saving core registers.  */
 
       s_arm_unwind_save_core ();
       return;
 
+    case REG_TYPE_PSEUDO:
+      s_arm_unwind_save_pseudo ();
+      return;
+
     case REG_TYPE_VFD:
       if (arch_v6)
        s_arm_unwind_save_vfp_armv6 ();
   /* XScale accumulator registers.  */
   REGNUM(acc,0,XSCALE), REGNUM(ACC,0,XSCALE),
 
-  /* Alias 'ra_auth_code' to r12 for pacbti.  */
-  REGDEF(ra_auth_code,12,RN),
+  /* DWARF ABI defines RA_AUTH_CODE to 143. It also reserves 134-142 for future
+     expansion.  RA_AUTH_CODE here is given the value 143 % 134 to make it easy
+     for tc_arm_regname_to_dw2regnum to translate to DWARF reg number using
+     134 + reg_number should the range 134 to 142 be used for more pseudo regs
+     in the future.  This also helps fit RA_AUTH_CODE into a bitmask.  */
+  REGDEF(ra_auth_code,9,PSEUDO),
 };
 #undef REGDEF
 #undef REGNUM
 
--- /dev/null
+#objdump: -sr
+#name: Unwind information for Armv8.1-M.Mainline PACBTI extension
+# This test is only valid on ELF based ports.
+#notarget: *-*-pe *-*-wince
+# VxWorks needs a special variant of this file.
+#skip: *-*-vxworks*
+
+.*:     file format.*
+
+RELOCATION RECORDS FOR \[.ARM.exidx\]:
+OFFSET   TYPE              VALUE 
+00000000 R_ARM_PREL31      .text
+00000000 R_ARM_NONE        __aeabi_unwind_cpp_pr0
+
+
+Contents of section .text:
+ 0000 10b54df8 04cd5df8 04cb10bd  .*
+Contents of section .ARM.exidx:
+ 0000 00000000 b0a8b480  .*
+Contents of section .ARM.attributes:
+ 0000 41290000 00616561 62690001 1f000000  .*
+ 0010 05382e31 2d4d2e4d 41494e00 0615074d  .*
+ 0020 09033202 34024a01 4c01               .*