diff options
Diffstat (limited to 'gas/config')
-rw-r--r-- | gas/config/tc-arm.c | 226 |
1 files changed, 167 insertions, 59 deletions
diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c index 28c4d24..ae9ba18 100644 --- a/gas/config/tc-arm.c +++ b/gas/config/tc-arm.c @@ -5347,39 +5347,79 @@ parse_half (char **str) /* Parse a PSR flag operand. The value returned is FAIL on syntax error, or a bitmask suitable to be or-ed into the ARM msr instruction. */ static int -parse_psr (char **str) +parse_psr (char **str, bfd_boolean lhs) { char *p; unsigned long psr_field; const struct asm_psr *psr; char *start; + bfd_boolean is_apsr = FALSE; + bfd_boolean m_profile = ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_m); /* CPSR's and SPSR's can now be lowercase. This is just a convenience feature for ease of use and backwards compatibility. */ p = *str; if (strncasecmp (p, "SPSR", 4) == 0) - psr_field = SPSR_BIT; - else if (strncasecmp (p, "CPSR", 4) == 0 - || (strncasecmp (p, "APSR", 4) == 0 - && !ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m))) - psr_field = 0; - else + { + if (m_profile) + goto unsupported_psr; + + psr_field = SPSR_BIT; + } + else if (strncasecmp (p, "CPSR", 4) == 0) + { + if (m_profile) + goto unsupported_psr; + + psr_field = 0; + } + else if (strncasecmp (p, "APSR", 4) == 0) + { + /* APSR[_<bits>] can be used as a synonym for CPSR[_<flags>] on ARMv7-A + and ARMv7-R architecture CPUs. */ + is_apsr = TRUE; + psr_field = 0; + } + else if (m_profile) { start = p; do p++; while (ISALNUM (*p) || *p == '_'); + if (strncasecmp (start, "iapsr", 5) == 0 + || strncasecmp (start, "eapsr", 5) == 0 + || strncasecmp (start, "xpsr", 4) == 0 + || strncasecmp (start, "psr", 3) == 0) + p = start + strcspn (start, "rR") + 1; + psr = (const struct asm_psr *) hash_find_n (arm_v7m_psr_hsh, start, p - start); + if (!psr) return FAIL; + /* If APSR is being written, a bitfield may be specified. Note that + APSR itself is handled above. */ + if (psr->field <= 3) + { + psr_field = psr->field; + is_apsr = TRUE; + goto check_suffix; + } + *str = p; - return psr->field; + /* M-profile MSR instructions have the mask field set to "10", except + *PSR variants which modify APSR, which may use a different mask (and + have been handled already). Do that by setting the PSR_f field + here. */ + return psr->field | (lhs ? PSR_f : 0); } + else + goto unsupported_psr; p += 4; +check_suffix: if (*p == '_') { /* A suffix follows. */ @@ -5390,23 +5430,106 @@ parse_psr (char **str) p++; while (ISALNUM (*p) || *p == '_'); - psr = (const struct asm_psr *) hash_find_n (arm_psr_hsh, start, - p - start); - if (!psr) - goto error; + if (is_apsr) + { + /* APSR uses a notation for bits, rather than fields. */ + unsigned int nzcvq_bits = 0; + unsigned int g_bit = 0; + char *bit; + + for (bit = start; bit != p; bit++) + { + switch (TOLOWER (*bit)) + { + case 'n': + nzcvq_bits |= (nzcvq_bits & 0x01) ? 0x20 : 0x01; + break; + + case 'z': + nzcvq_bits |= (nzcvq_bits & 0x02) ? 0x20 : 0x02; + break; + + case 'c': + nzcvq_bits |= (nzcvq_bits & 0x04) ? 0x20 : 0x04; + break; + + case 'v': + nzcvq_bits |= (nzcvq_bits & 0x08) ? 0x20 : 0x08; + break; + + case 'q': + nzcvq_bits |= (nzcvq_bits & 0x10) ? 0x20 : 0x10; + break; + + case 'g': + g_bit |= (g_bit & 0x1) ? 0x2 : 0x1; + break; + + default: + inst.error = _("unexpected bit specified after APSR"); + return FAIL; + } + } + + if (nzcvq_bits == 0x1f) + psr_field |= PSR_f; + + if (g_bit == 0x1) + { + if (!ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp)) + { + inst.error = _("selected processor does not " + "support DSP extension"); + return FAIL; + } + + psr_field |= PSR_s; + } + + if ((nzcvq_bits & 0x20) != 0 + || (nzcvq_bits != 0x1f && nzcvq_bits != 0) + || (g_bit & 0x2) != 0) + { + inst.error = _("bad bitmask specified after APSR"); + return FAIL; + } + } + else + { + psr = (const struct asm_psr *) hash_find_n (arm_psr_hsh, start, + p - start); + if (!psr) + goto error; - psr_field |= psr->field; + psr_field |= psr->field; + } } else { if (ISALNUM (*p)) goto error; /* Garbage after "[CS]PSR". */ - psr_field |= (PSR_c | PSR_f); + /* Unadorned APSR is equivalent to APSR_nzcvq/CPSR_f (for writes). This + is deprecated, but allow it anyway. */ + if (is_apsr && lhs) + { + psr_field |= PSR_f; + as_tsktsk (_("writing to APSR without specifying a bitmask is " + "deprecated")); + } + else if (!m_profile) + /* These bits are never right for M-profile devices: don't set them + (only code paths which read/write APSR reach here). */ + psr_field |= (PSR_c | PSR_f); } *str = p; return psr_field; + unsupported_psr: + inst.error = _("selected processor does not support requested special " + "purpose register"); + return FAIL; + error: inst.error = _("flag for {c}psr instruction expected"); return FAIL; @@ -5932,11 +6055,11 @@ enum operand_parse_code OP_CPSF, /* CPS flags */ OP_ENDI, /* Endianness specifier */ - OP_PSR, /* CPSR/SPSR mask for msr */ + OP_wPSR, /* CPSR/SPSR/APSR mask for msr (writing). */ + OP_rPSR, /* CPSR/SPSR/APSR mask for msr (reading). */ OP_COND, /* conditional code */ OP_TB, /* Table branch. */ - OP_RVC_PSR, /* CPSR/SPSR mask for msr, or VFP control register. */ OP_APSR_RR, /* ARM register or "APSR_nzcv". */ OP_RRnpc_I0, /* ARM register or literal 0 */ @@ -6402,7 +6525,6 @@ parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb) case OP_CPSF: val = parse_cps_flags (&str); break; case OP_ENDI: val = parse_endian_specifier (&str); break; case OP_oROR: val = parse_ror (&str); break; - case OP_PSR: val = parse_psr (&str); break; case OP_COND: val = parse_cond (&str); break; case OP_oBARRIER_I15: po_barrier_or_imm (str); break; @@ -6411,11 +6533,8 @@ parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb) goto failure; break; - case OP_RVC_PSR: - po_reg_or_goto (REG_TYPE_VFC, try_banked_reg); - inst.operands[i].isvec = 1; /* Mark VFP control reg as vector. */ - break; - try_banked_reg: + case OP_wPSR: + case OP_rPSR: po_reg_or_goto (REG_TYPE_RNB, try_psr); if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_virt)) { @@ -6424,9 +6543,9 @@ parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb) goto failure; } break; - try_psr: - val = parse_psr (&str); - break; + try_psr: + val = parse_psr (&str, op_parse_code == OP_wPSR); + break; case OP_APSR_RR: po_reg_or_goto (REG_TYPE_RN, try_apsr); @@ -6583,8 +6702,8 @@ parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb) case OP_CPSF: case OP_ENDI: case OP_oROR: - case OP_PSR: - case OP_RVC_PSR: + case OP_wPSR: + case OP_rPSR: case OP_COND: case OP_oBARRIER_I15: case OP_REGLST: @@ -7912,7 +8031,7 @@ do_mrs (void) /* mrs only accepts CPSR/SPSR/CPSR_all/SPSR_all. */ constraint ((inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f)) != (PSR_c|PSR_f), - _("'CPSR' or 'SPSR' expected")); + _("'APSR', 'CPSR' or 'SPSR' expected")); br = (15<<16) | (inst.operands[1].imm & SPSR_BIT); } @@ -10828,21 +10947,14 @@ do_t_mrs (void) { int flags = inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT); - if (flags == 0) - { - constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_m), - _("selected processor does not support " - "requested special purpose register")); - } + if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m)) + constraint (flags != 0, _("selected processor does not support " + "requested special purpose register")); else - { - constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1), - _("selected processor does not support " - "requested special purpose register")); - /* mrs only accepts CPSR/SPSR/CPSR_all/SPSR_all. */ - constraint ((flags & ~SPSR_BIT) != (PSR_c|PSR_f), - _("'CPSR' or 'SPSR' expected")); - } + /* mrs only accepts APSR/CPSR/SPSR/CPSR_all/SPSR_all (for non-M profile + devices). */ + constraint ((flags & ~SPSR_BIT) != (PSR_c|PSR_f), + _("'APSR', 'CPSR' or 'SPSR' expected")); inst.instruction |= (flags & SPSR_BIT) >> 2; inst.instruction |= inst.operands[1].imm & 0xff; @@ -10867,19 +10979,20 @@ do_t_msr (void) else flags = inst.operands[0].imm; - if (flags & ~0xff) + if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m)) { - constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1), - _("selected processor does not support " - "requested special purpose register")); + int bits = inst.operands[0].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT); + + constraint ((ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp) + && (bits & ~(PSR_s | PSR_f)) != 0) + || (!ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp) + && bits != PSR_f), + _("selected processor does not support requested special " + "purpose register")); } else - { - constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_m), - _("selected processor does not support " - "requested special purpose register")); - flags |= PSR_f; - } + constraint ((flags & 0xff) != 0, _("selected processor does not support " + "requested special purpose register")); Rn = inst.operands[1].reg; reject_bad_reg (Rn); @@ -16440,7 +16553,6 @@ static const struct asm_psr psrs[] = {"c", PSR_c}, {"x", PSR_x}, {"s", PSR_s}, - {"g", PSR_s}, /* Combinations of flags. */ {"fs", PSR_f | PSR_s}, @@ -16503,10 +16615,6 @@ static const struct asm_psr psrs[] = {"csxf", PSR_c | PSR_s | PSR_x | PSR_f}, {"cxfs", PSR_c | PSR_x | PSR_f | PSR_s}, {"cxsf", PSR_c | PSR_x | PSR_s | PSR_f}, - - /* APSR flags */ - {"nzcvq", PSR_f}, - {"nzcvqg", PSR_s | PSR_f} }; /* Table of V7M psr names. */ @@ -16955,8 +17063,8 @@ static const struct asm_opcode insns[] = #undef THUMB_VARIANT #define THUMB_VARIANT & arm_ext_msr - TCE("mrs", 1000000, f3e08000, 2, (APSR_RR, RVC_PSR), mrs, t_mrs), - TCE("msr", 120f000, f3808000, 2, (RVC_PSR, RR_EXi), msr, t_msr), + TCE("mrs", 1000000, f3e08000, 2, (RRnpc, rPSR), mrs, t_mrs), + TCE("msr", 120f000, f3808000, 2, (wPSR, RR_EXi), msr, t_msr), #undef ARM_VARIANT #define ARM_VARIANT & arm_ext_v3m /* ARM 7M long multiplies. */ |