diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c index ca143a9c7d1ffd0c73dafc972f2c50aed631665c..98b5cfd5845e9c5ecb16493a5e20662288676959 100644 --- a/gas/config/tc-arm.c +++ b/gas/config/tc-arm.c @@ -1688,14 +1688,27 @@ parse_scalar (char **ccp, int elsize, struct neon_type_el *type) 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 @@ -1713,11 +1726,35 @@ parse_reg_list (char ** strp) 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) @@ -1761,7 +1798,7 @@ parse_reg_list (char ** strp) return FAIL; } } - else + else if (etype == REGLIST_RN) { expressionS exp; @@ -1816,15 +1853,6 @@ parse_reg_list (char ** strp) 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. @@ -1873,6 +1901,9 @@ parse_vfp_reg_list (char **ccp, unsigned int *pbase, enum reg_list_els etype) case REGLIST_NEON_D: regtype = REG_TYPE_NDQ; break; + + default: + gas_assert (0); } if (etype != REGLIST_VFP_S) @@ -3988,7 +4019,7 @@ s_arm_unwind_save_core (void) 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")); @@ -6548,6 +6579,7 @@ enum operand_parse_code 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) */ @@ -7173,7 +7205,7 @@ parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb) /* Register lists. */ case OP_REGLST: - val = parse_reg_list (&str); + val = parse_reg_list (&str, REGLIST_RN); if (*str == '^') { inst.operands[i].writeback = 1; @@ -7181,6 +7213,10 @@ parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb) } 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; @@ -7304,6 +7340,7 @@ parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb) case OP_COND: case OP_oBARRIER_I15: case OP_REGLST: + case OP_CLRMLST: case OP_VRSLST: case OP_VRDLST: case OP_VRSDLST: @@ -11489,16 +11526,19 @@ do_t_it (void) /* 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"); @@ -11513,13 +11553,13 @@ encode_thumb2_ldmstm (int base, unsigned mask, bfd_boolean writeback) 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) @@ -11548,7 +11588,8 @@ encode_thumb2_ldmstm (int base, unsigned mask, bfd_boolean writeback) inst.instruction |= WRITE_BACK; inst.instruction |= mask; - inst.instruction |= base << 16; + if (do_io) + inst.instruction |= base << 16; } static void @@ -11643,8 +11684,9 @@ do_t_ldmstm (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 @@ -12751,8 +12793,20 @@ do_t_push_pop (void) 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"); @@ -21834,6 +21888,8 @@ static const struct asm_opcode insns[] = 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 diff --git a/gas/testsuite/gas/arm/archv8m_1m-cmse-main-bad.d b/gas/testsuite/gas/arm/archv8m_1m-cmse-main-bad.d new file mode 100644 index 0000000000000000000000000000000000000000..a5c755804dd9bd4cd50b4a5cd9d48d7bf417016f --- /dev/null +++ b/gas/testsuite/gas/arm/archv8m_1m-cmse-main-bad.d @@ -0,0 +1,4 @@ +#name: Invalid Armv8.1-M Mainline Security Extensions instructions +#source: archv8m_1m-cmse-main-bad.s +#as: -march=armv8.1-m.main +#error_output: archv8m_1m-cmse-main-bad.l diff --git a/gas/testsuite/gas/arm/archv8m_1m-cmse-main-bad.l b/gas/testsuite/gas/arm/archv8m_1m-cmse-main-bad.l new file mode 100644 index 0000000000000000000000000000000000000000..ed440bf2baa36e0f60cccff417d8148a17749f44 --- /dev/null +++ b/gas/testsuite/gas/arm/archv8m_1m-cmse-main-bad.l @@ -0,0 +1,4 @@ +[^:]*: Assembler messages: +[^:]*:6: Error: r0-r12, lr or APSR expected -- `clrm {}' +[^:]*:7: Error: r0-r12, lr or APSR expected -- `clrm {sp}' +[^:]*:8: Error: r0-r12, lr or APSR expected -- `clrm {pc}' diff --git a/gas/testsuite/gas/arm/archv8m_1m-cmse-main-bad.s b/gas/testsuite/gas/arm/archv8m_1m-cmse-main-bad.s new file mode 100644 index 0000000000000000000000000000000000000000..c991a559093fd5da79f8eb54c02fde13208a8071 --- /dev/null +++ b/gas/testsuite/gas/arm/archv8m_1m-cmse-main-bad.s @@ -0,0 +1,8 @@ +.thumb +.syntax unified + +T: + +clrm {} @ Rejects empty list +clrm {sp} @ Rejects SP in list +clrm {pc} @ Reject PC in list diff --git a/gas/testsuite/gas/arm/archv8m_1m-cmse-main.d b/gas/testsuite/gas/arm/archv8m_1m-cmse-main.d new file mode 100644 index 0000000000000000000000000000000000000000..b082ff7e6451186763dd55cf96f9205d2f5ce4d8 --- /dev/null +++ b/gas/testsuite/gas/arm/archv8m_1m-cmse-main.d @@ -0,0 +1,14 @@ +#name: Armv8.1-M Mainline Security Extensions instructions +#source: archv8m_1m-cmse-main.s +#as: -march=armv8.1-m.main -mimplicit-it=always +#objdump: -dr --prefix-addresses --show-raw-insn -marmv8.1-m.main + +.*: +file format .*arm.* + +Disassembly of section .text: +0+.* <[^>]*> e89f 0005 clrm {r0, r2} +0+.* <[^>]*> e89f 8000 clrm {APSR} +0+.* <[^>]*> e89f 8008 clrm {r3, APSR} +0+.* <[^>]*> bf08 it eq +0+.* <[^>]*> e89f 0010 clrmeq {r4} +#... diff --git a/gas/testsuite/gas/arm/archv8m_1m-cmse-main.s b/gas/testsuite/gas/arm/archv8m_1m-cmse-main.s new file mode 100644 index 0000000000000000000000000000000000000000..084d83b88b63bb4f55fd9ab2388b80b91696f763 --- /dev/null +++ b/gas/testsuite/gas/arm/archv8m_1m-cmse-main.s @@ -0,0 +1,9 @@ +.thumb +.syntax unified + +T: + +clrm {r0, r2} @ Accepts list without APSR +clrm {APSR} @ Accepts APSR alone +clrm {r3, APSR} @ Accepts core register and APSR together +clrmeq {r4} @ Accepts conditional execution diff --git a/opcodes/arm-dis.c b/opcodes/arm-dis.c index 2cf9507fbb5fe6505df66d2cd0c5abda2d53bdf9..d7a64671dc132bbb974dda91d5760180079a02f6 100644 --- a/opcodes/arm-dis.c +++ b/opcodes/arm-dis.c @@ -2711,6 +2711,7 @@ static const struct opcode16 thumb_opcodes[] = %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 @@ -2752,7 +2753,8 @@ static const struct opcode16 thumb_opcodes[] = 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), @@ -2773,6 +2775,8 @@ static const struct opcode32 thumb32_opcodes[] = {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"}, @@ -5557,6 +5561,7 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given) 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; @@ -5852,6 +5857,9 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given) } break; + case 'n': + is_clrm = TRUE; + /* Fall through. */ case 'm': { int started = 0; @@ -5864,7 +5872,12 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given) 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, "}"); }