+2019-04-15  Thomas Preud'homme  <thomas.preudhomme@arm.com>
+
+       * config/tc-arm.c (enum reg_list_els): Define earlier and add
+       REGLIST_RN and REGLIST_CLRM enumerators.
+       (parse_reg_list): Add etype parameter to distinguish between regular
+       core register list and CLRM register list.  Add logic to
+       recognize CLRM register list.
+       (parse_vfp_reg_list): Assert type is not for core register list.
+       (s_arm_unwind_save_core): Update call to parse_reg_list to new
+       prototype.
+       (enum operand_parse_code): Declare OP_CLRMLST enumerator.
+       (parse_operands): Update call to parse_reg_list to new prototype.  Add
+       logic for OP_CLRMLST.
+       (encode_thumb2_ldmstm): Rename into ...
+       (encode_thumb2_multi): This.  Add do_io parameter.  Add logic to
+       encode CLRM and guard LDM/STM only code by do_io.
+       (do_t_ldmstm): Adapt to use encode_thumb2_multi.
+       (do_t_push_pop): Likewise.
+       (do_t_clrm): New function.
+       (insns): Define CLRM.
+       * testsuite/gas/arm/archv8m_1m-cmse-main-bad.d: New file.
+       * testsuite/gas/arm/archv8m_1m-cmse-main-bad.l: Likewise.
+       * testsuite/gas/arm/archv8m_1m-cmse-main-bad.s: Likewise.
+       * testsuite/gas/arm/archv8m_1m-cmse-main.d: Likewise.
+       * testsuite/gas/arm/archv8m_1m-cmse-main.s: Likewise.
+
 2019-04-15  Sudakshina Das  <sudi.das@arm.com>
             Andre Vieira  <andre.simoesdiasvieira@arm.com>
 
 
   return reg * 16 + atype.index;
 }
 
+/* Types of registers in a list.  */
+
+enum reg_list_els
+{
+  REGLIST_RN,
+  REGLIST_CLRM,
+  REGLIST_VFP_S,
+  REGLIST_VFP_D,
+  REGLIST_NEON_D
+};
+
 /* Parse an ARM register list.  Returns the bitmask, or FAIL.  */
 
 static long
-parse_reg_list (char ** strp)
+parse_reg_list (char ** strp, enum reg_list_els etype)
 {
-  char * str = * strp;
-  long  range = 0;
-  int   another_range;
+  char *str = *strp;
+  long range = 0;
+  int another_range;
+
+  gas_assert (etype == REGLIST_RN || etype == REGLIST_CLRM);
 
   /* We come back here if we get ranges concatenated by '+' or '|'.  */
   do
          do
            {
              int reg;
+             const char apsr_str[] = "apsr";
+             int apsr_str_len = strlen (apsr_str);
 
-             if ((reg = arm_reg_parse (&str, REG_TYPE_RN)) == FAIL)
+             reg = arm_reg_parse (&str, REGLIST_RN);
+             if (etype == REGLIST_CLRM)
                {
-                 first_error (_(reg_expected_msgs[REG_TYPE_RN]));
-                 return FAIL;
+                 if (reg == REG_SP || reg == REG_PC)
+                   reg = FAIL;
+                 else if (reg == FAIL
+                          && !strncasecmp (str, apsr_str, apsr_str_len)
+                          && !ISALPHA (*(str + apsr_str_len)))
+                   {
+                     reg = 15;
+                     str += apsr_str_len;
+                   }
+
+                 if (reg == FAIL)
+                   {
+                     first_error (_("r0-r12, lr or APSR expected"));
+                     return FAIL;
+                   }
+               }
+             else /* etype == REGLIST_RN.  */
+               {
+                 if (reg == FAIL)
+                   {
+                     first_error (_(reg_expected_msgs[REGLIST_RN]));
+                     return FAIL;
+                   }
                }
 
              if (in_range)
              return FAIL;
            }
        }
-      else
+      else if (etype == REGLIST_RN)
        {
          expressionS exp;
 
   return range;
 }
 
-/* Types of registers in a list.  */
-
-enum reg_list_els
-{
-  REGLIST_VFP_S,
-  REGLIST_VFP_D,
-  REGLIST_NEON_D
-};
-
 /* Parse a VFP register list.  If the string is invalid return FAIL.
    Otherwise return the number of registers, and set PBASE to the first
    register.  Parses registers of type ETYPE.
     case REGLIST_NEON_D:
       regtype = REG_TYPE_NDQ;
       break;
+
+    default:
+      gas_assert (0);
     }
 
   if (etype != REGLIST_VFP_S)
   long range;
   int n;
 
-  range = parse_reg_list (&input_line_pointer);
+  range = parse_reg_list (&input_line_pointer, REGLIST_RN);
   if (range == FAIL)
     {
       as_bad (_("expected register list"));
   OP_RRnpcsp_I32, /* ARM register (no BadReg) or literal 1 .. 32 */
 
   OP_REGLST,   /* ARM register list */
+  OP_CLRMLST,  /* CLRM register list */
   OP_VRSLST,   /* VFP single-precision register list */
   OP_VRDLST,   /* VFP double-precision register list */
   OP_VRSDLST,   /* VFP single or double-precision register list (& quad) */
 
          /* Register lists.  */
        case OP_REGLST:
-         val = parse_reg_list (&str);
+         val = parse_reg_list (&str, REGLIST_RN);
          if (*str == '^')
            {
              inst.operands[i].writeback = 1;
            }
          break;
 
+       case OP_CLRMLST:
+         val = parse_reg_list (&str, REGLIST_CLRM);
+         break;
+
        case OP_VRSLST:
          val = parse_vfp_reg_list (&str, &inst.operands[i].reg, REGLIST_VFP_S);
          break;
        case OP_COND:
        case OP_oBARRIER_I15:
        case OP_REGLST:
+       case OP_CLRMLST:
        case OP_VRSLST:
        case OP_VRDLST:
        case OP_VRSDLST:
 
 /* Helper function used for both push/pop and ldm/stm.  */
 static void
-encode_thumb2_ldmstm (int base, unsigned mask, bfd_boolean writeback)
+encode_thumb2_multi (bfd_boolean do_io, int base, unsigned mask,
+                    bfd_boolean writeback)
 {
-  bfd_boolean load;
+  bfd_boolean load, store;
 
-  load = (inst.instruction & (1 << 20)) != 0;
+  gas_assert (base != -1 || !do_io);
+  load = do_io && ((inst.instruction & (1 << 20)) != 0);
+  store = do_io && !load;
 
   if (mask & (1 << 13))
     inst.error =  _("SP not allowed in register list");
 
-  if ((mask & (1 << base)) != 0
+  if (do_io && (mask & (1 << base)) != 0
       && writeback)
     inst.error = _("having the base register in the register list when "
                   "using write back is UNPREDICTABLE");
            set_it_insn_type_last ();
        }
     }
-  else
+  else if (store)
     {
       if (mask & (1 << 15))
        inst.error = _("PC not allowed in register list");
     }
 
-  if ((mask & (mask - 1)) == 0)
+  if (do_io && ((mask & (mask - 1)) == 0))
     {
       /* Single register transfers implemented as str/ldr.  */
       if (writeback)
     inst.instruction |= WRITE_BACK;
 
   inst.instruction |= mask;
-  inst.instruction |= base << 16;
+  if (do_io)
+    inst.instruction |= base << 16;
 }
 
 static void
          if (inst.instruction < 0xffff)
            inst.instruction = THUMB_OP32 (inst.instruction);
 
-         encode_thumb2_ldmstm (inst.operands[0].reg, inst.operands[1].imm,
-                               inst.operands[0].writeback);
+         encode_thumb2_multi (TRUE /* do_io */, inst.operands[0].reg,
+                              inst.operands[1].imm,
+                              inst.operands[0].writeback);
        }
     }
   else
   else if (unified_syntax)
     {
       inst.instruction = THUMB_OP32 (inst.instruction);
-      encode_thumb2_ldmstm (13, mask, TRUE);
+      encode_thumb2_multi (TRUE /* do_io */, 13, mask, TRUE);
+    }
+  else
+    {
+      inst.error = _("invalid register list to push/pop instruction");
+      return;
     }
+}
+
+static void
+do_t_clrm (void)
+{
+  if (unified_syntax)
+    encode_thumb2_multi (FALSE /* do_io */, -1, inst.operands[0].imm, FALSE);
   else
     {
       inst.error = _("invalid register list to push/pop instruction");
  toU("dls", _dls, 2, (LR, RRnpcsp),     t_loloop),
  toU("wls", _wls, 3, (LR, RRnpcsp, EXP), t_loloop),
  toU("le",  _le,  2, (oLR, EXP),        t_loloop),
+
+ ToC("clrm",   e89f0000, 1, (CLRMLST),  t_clrm)
 };
 #undef ARM_VARIANT
 #undef THUMB_VARIANT
 
        %a              print the address of a plain load/store
        %w              print the width and signedness of a core load/store
        %m              print register mask for ldm/stm
+       %n              print register mask for clrm
 
        %E              print the lsb and width fields of a bfc/bfi instruction
        %F              print the lsb and width fields of a sbfx/ubfx instruction
    makes heavy use of special-case bit patterns.  */
 static const struct opcode32 thumb32_opcodes[] =
 {
-  /* Armv8.1-M Mainline instructions.  */
+  /* Armv8.1-M Mainline and Armv8.1-M Mainline Security Extensions
+     instructions.  */
   {ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8_1M_MAIN),
     0xf040c001, 0xfff0f001, "wls\tlr, %16-19S, %Q"},
   {ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8_1M_MAIN),
   {ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8_1M_MAIN),
     0xf000e001, 0xf840f001, "bfcsel\t%G, %Z, %18-21c"},
 
+  {ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8_1M_MAIN),
+    0xe89f0000, 0xffff2000, "clrm%c\t%n"},
 
   /* ARMv8-M and ARMv8-M Security Extensions instructions.  */
   {ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8M), 0xe97fe97f, 0xffffffff, "sg"},
   for (insn = thumb32_opcodes; insn->assembler; insn++)
     if ((given & insn->mask) == insn->value)
       {
+       bfd_boolean is_clrm = FALSE;
        bfd_boolean is_unpredictable = FALSE;
        signed long value_in_comment = 0;
        const char *c = insn->assembler;
                }
                break;
 
+             case 'n':
+               is_clrm = TRUE;
+               /* Fall through.  */
              case 'm':
                {
                  int started = 0;
                        if (started)
                          func (stream, ", ");
                        started = 1;
-                       func (stream, "%s", arm_regnames[reg]);
+                       if (is_clrm && reg == 13)
+                         func (stream, "(invalid: %s)", arm_regnames[reg]);
+                       else if (is_clrm && reg == 15)
+                         func (stream, "%s", "APSR");
+                       else
+                         func (stream, "%s", arm_regnames[reg]);
                      }
                  func (stream, "}");
                }