=================================================================== Index: gas/testsuite/gas/arm/vfp-bad.l --- gas/testsuite/gas/arm/vfp-bad.l (revision 48) +++ gas/testsuite/gas/arm/vfp-bad.l (revision 49) @@ -1,9 +1,9 @@ [^:]*: Assembler messages: -[^:]*:4: Error: garbage following instruction -- `fstd d0,\[r0\],#8' -[^:]*:5: Error: garbage following instruction -- `fstd d0,\[r0,#-8\]!' -[^:]*:6: Error: garbage following instruction -- `fsts s0,\[r0\],#8' -[^:]*:7: Error: garbage following instruction -- `fsts s0,\[r0,#-8\]!' -[^:]*:8: Error: garbage following instruction -- `fldd d0,\[r0\],#8' -[^:]*:9: Error: garbage following instruction -- `fldd d0,\[r0,#-8\]!' -[^:]*:10: Error: garbage following instruction -- `flds s0,\[r0\],#8' -[^:]*:11: Error: garbage following instruction -- `flds s0,\[r0,#-8\]!' +[^:]*:4: Error: instruction does not support writeback -- `fstd d0,\[r0\],#8' +[^:]*:5: Error: instruction does not support writeback -- `fstd d0,\[r0,#-8\]!' +[^:]*:6: Error: instruction does not support writeback -- `fsts s0,\[r0\],#8' +[^:]*:7: Error: instruction does not support writeback -- `fsts s0,\[r0,#-8\]!' +[^:]*:8: Error: instruction does not support writeback -- `fldd d0,\[r0\],#8' +[^:]*:9: Error: instruction does not support writeback -- `fldd d0,\[r0,#-8\]!' +[^:]*:10: Error: instruction does not support writeback -- `flds s0,\[r0\],#8' +[^:]*:11: Error: instruction does not support writeback -- `flds s0,\[r0,#-8\]!' =================================================================== Index: gas/config/tc-arm.c --- gas/config/tc-arm.c (revision 48) +++ gas/config/tc-arm.c (revision 49) @@ -198,9 +198,11 @@ int present : 1; /* operand present */ int isreg : 1; /* operand was a register */ int immisreg : 1; /* .imm field is a second register */ - int writeback : 1; /* operand has trailing ! */ int hasreloc : 1; /* operand has relocation suffix */ - int postind : 1; /* operand is post-indexed */ + int writeback : 1; /* operand has trailing ! */ + int preind : 1; /* preindexed address */ + int postind : 1; /* postindexed address */ + int negative : 1; /* index register was negated */ } operands[6]; }; @@ -287,9 +289,6 @@ #define CP_T_X 0x00008000 #define CP_T_Y 0x00400000 -#define CP_T_Pre 0x01000000 -#define CP_T_UD 0x00800000 -#define CP_T_WB 0x00200000 #define CONDS_BIT 0x00100000 #define LOAD_BIT 0x00100000 @@ -2096,141 +2095,155 @@ return FAIL; } +/* Parse a coprocessor address expression: + + [Rl]{!} shorthand for [Rl,#0]{!} + [Rl,offset]{!} +-8 bits, *4 (usually) - preindex, opt. writeback + [Rl],offset +-8 bits, *4 (usually) - postindex + [Rl],\{number\} 8 bits - no relocation - "unindexed" (magic) + label translated to [PC, offset] + + Data is written to inst.operands[i] and/or inst.reloc.exp. + Returns SUCCESS or FAIL. Does not check for no-writeback / no-unindex. */ + static int -cp_address_required_here (char ** str, int wb_ok, int reloc) +parse_cp_address (char **str, int i) { - char * p = * str; - int pre_inc = 0; - int write_back = 0; + char *p = *str; - if (*p == '[') - { - int reg; + if (skip_past_char (&p, '[') == FAIL) + { /* bare address - translate to PC-relative offset */ + if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX)) + return FAIL; - p++; + inst.reloc.pc_rel = 1; + inst.reloc.exp.X_add_number -= 8; /* PC rel adjust. */ + inst.operands[i].reg = REG_PC; + inst.operands[i].preind = 1; + *str = p; + return SUCCESS; + } - if ((reg = reg_required_here (&p, 16, REG_TYPE_RN)) == FAIL) - return FAIL; + if ((inst.operands[i].reg = reg_required_here (&p, -1, REG_TYPE_RN)) == FAIL) + return FAIL; - if (*p == ']') + if (skip_past_char (&p, ']') == SUCCESS) + { + if (*p == '\0' || *p == '!') /* [Rn]{!} - shorthand for [Rn,#0]{!} */ { - p++; + inst.operands[i].preind = 1; + inst.operands[i].writeback = (*p == '!'); + inst.reloc.exp.X_op = O_constant; + inst.reloc.exp.X_add_number = 0; + *str = p + (*p == '!'); + return SUCCESS; + } - if (*p == '\0') - { - /* As an extension to the official ARM syntax we allow: - [Rn] - as a short hand for: - [Rn,#0] */ - inst.instruction |= PRE_INDEX | INDEX_UP; - *str = p; - return SUCCESS; - } + if (skip_past_comma (&p) == FAIL) + { + inst.error = _("comma expected after closing square bracket"); + return FAIL; + } - if (skip_past_comma (& p) == FAIL) - { - inst.error = _("comma expected after closing square bracket"); - return FAIL; - } + if (skip_past_char (&p, '{') == SUCCESS) + { + /* [Rn], {expr} - unindexed, with option */ + if (immediate_required_here (&p, &inst.operands[i].imm, + 0, 255, TRUE) == FAIL) + return FAIL; - if (*p == '#') + if (skip_past_char (&p, '}') == FAIL) { - if (wb_ok) - { - /* [Rn], #expr */ - write_back = WRITE_BACK; - - if (reg == REG_PC) - { - inst.error = _("pc may not be used in post-increment"); - return FAIL; - } - - if (my_get_expression (& inst.reloc.exp, &p, GE_IMM_PREFIX)) - return FAIL; - inst.reloc.type = reloc; - } - else - pre_inc = PRE_INDEX | INDEX_UP; - } - else if (*p == '{') - { - int option; - - /* [Rn], {} */ - p++; - if (immediate_required_here (&p, &option, 0, 255, TRUE) == FAIL) - return FAIL; - - if (*p != '}') - { - inst.error = _("'}' expected at end of 'option' field"); - return FAIL; - } - else - { - p++; - inst.instruction |= option; - inst.instruction |= INDEX_UP; - } - } - else - { - inst.error = _("# or { expected after comma"); + inst.error = _("'}' expected at end of 'option' field"); return FAIL; } } else { - /* '['Rn, #expr']'[!] */ - - if (skip_past_comma (& p) == FAIL) - { - inst.error = _("pre-indexed expression expected"); - return FAIL; - } - - if (my_get_expression (& inst.reloc.exp, &p, GE_IMM_PREFIX)) + if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX)) return FAIL; - inst.reloc.type = reloc; - pre_inc = PRE_INDEX; - if (*p++ != ']') - { - inst.error = _("missing ]"); - return FAIL; - } - - if (wb_ok && *p == '!') - { - if (reg == REG_PC) - { - inst.error = _("pc may not be used with write-back"); - return FAIL; - } - - p++; - write_back = WRITE_BACK; - } + inst.operands[i].postind = 1; + inst.operands[i].writeback = 1; } } else { - if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX)) + /* '['Rn, #expr']'[!] */ + if (skip_past_comma (&p) == FAIL) + { + inst.error = _("pre-indexed expression expected"); + return FAIL; + } + + if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX)) return FAIL; - inst.reloc.type = reloc; - inst.reloc.exp.X_add_number -= 8; /* PC rel adjust. */ - inst.reloc.pc_rel = 1; - inst.instruction |= (REG_PC << 16); - pre_inc = PRE_INDEX; - } + if (skip_past_char (&p, ']') == FAIL) + { + inst.error = _("missing ]"); + return FAIL; + } - inst.instruction |= write_back | pre_inc; + if (skip_past_char (&p, '!') == SUCCESS) + inst.operands[i].writeback = 1; + + inst.operands[i].preind = 1; + } *str = p; return SUCCESS; } +/* inst.operands[i] was set up by parse_cp_address. Encode it into an + ARM-format instruction. If wb_ok is false, reject use of writeback; + if unind_ok is false, reject use of unindexed addressing. If + reloc_override is not 0, use it instead of BFD_ARM_CP_OFF_IMM. */ + +static int +encode_cp_address_arm (int i, int wb_ok, int unind_ok, int reloc_override) +{ + inst.instruction |= inst.operands[i].reg << 16; + + assert (!(inst.operands[i].preind && inst.operands[i].postind)); + + if (!inst.operands[i].preind && !inst.operands[i].postind) /* unindexed */ + { + assert (!inst.operands[i].writeback); + if (!unind_ok) + { + inst.error = _("instruction does not support unindexed addressing"); + return FAIL; + } + inst.instruction |= inst.operands[i].imm; + inst.instruction |= INDEX_UP; + return SUCCESS; + } + + if (inst.operands[i].preind) + inst.instruction |= PRE_INDEX; + + if (inst.operands[i].writeback) + { + if (inst.operands[i].reg == REG_PC) + { + inst.error = _("pc may not be used with write-back"); + return FAIL; + } + if (!wb_ok) + { + inst.error = _("instruction does not support writeback"); + return FAIL; + } + inst.instruction |= WRITE_BACK; + } + + if (reloc_override) + inst.reloc.type = reloc_override; + else + inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM; + return SUCCESS; +} + /* Parse a Thumb address expression: [Rl] @@ -4120,7 +4133,6 @@ #define OP_EXPr 026 /* same, with optional relocation suffix */ #define OP_I0 027 /* immediate value 0 */ -#define OP_I4 030 /* 1 .. 4 */ #define OP_I7 031 /* 0 .. 7 */ #define OP_I15 032 /* 0 .. 15 */ #define OP_I16 033 /* 1 .. 16 */ @@ -4133,6 +4145,7 @@ #define OP_bI7 041 /* immediate, prefix optional, 0 .. 7 */ #define OP_bI15 042 /* 0 .. 15 */ #define OP_bI31 043 /* 0 .. 31 */ +#define OP_bI4 030 /* 1 .. 4 */ #define OP_I31w 050 /* 0 .. 31, optional trailing ! */ #define OP_RRw 051 /* ARM register, not the PC, optional trailing ! */ @@ -4148,6 +4161,7 @@ #define OP_VRDLST 065 /* VFP double-precision register list */ #define OP_TADDR 070 /* Thumb memory address expression */ +#define OP_CADDR 071 /* Co-processor memory address expression */ /* This-or-that operands. All have bit 7 set. */ #define OP_RR_EX 100 /* ARM register or expression */ @@ -4156,6 +4170,7 @@ #define OP_RR_EXr 103 /* ARM register or expression with opt. reloc suff. */ #define OP_RR_iEX 104 /* ARM register or expression with imm prefix */ #define OP_RF_IF 105 /* FPA register or immediate */ +#define OP_RIWR_RIWC 106 /* iWMMXt R or C reg */ /* Optional operands. All have the high bit set. */ #define OP_obI7 200 /* optional, prefix optional, immediate 0 .. 7 */ @@ -4283,7 +4298,6 @@ /* Immediates */ I0: case OP_(I0): po_imm_or_fail ( 0, 0, FALSE); break; - case OP_(I4): po_imm_or_fail ( 1, 4, FALSE); break; case OP_(I7): po_imm_or_fail ( 0, 7, FALSE); break; case OP_(I15): po_imm_or_fail ( 0, 15, FALSE); break; case OP_(I16): po_imm_or_fail ( 1, 16, FALSE); break; @@ -4293,6 +4307,7 @@ case OP_(I255): po_imm_or_fail ( 0, 255, FALSE); break; case OP_(Iffff): po_imm_or_fail ( 0, 0xffff, FALSE); break; + case OP_(bI4): po_imm_or_fail ( 1, 4, TRUE); break; case OP_(obI7): case OP_(bI7): po_imm_or_fail ( 0, 7, TRUE); break; case OP_(bI15): po_imm_or_fail ( 0, 15, TRUE); break; @@ -4386,6 +4401,20 @@ } break; + /* Two kinds of register */ + case OP_(RIWR_RIWC): + { + struct reg_entry *rege = arm_reg_parse_multi (&str); + if (rege->type != REG_TYPE_MMXWR && rege->type != REG_TYPE_MMXWC) + { + inst.error = _("iWMMXt data or control register expected"); + return FAIL; + } + inst.operands[i].reg = rege->number; + inst.operands[i].isreg = (rege->type == REG_TYPE_MMXWR); + } + break; + /* Misc */ case OP_(CPSF): val = parse_cps_flags (&str); break; case OP_(ENDI): val = parse_endian_specifier (&str); break; @@ -4416,6 +4445,11 @@ return FAIL; break; + case OP_(CADDR): + if (parse_cp_address (&str, i)) + return FAIL; + break; + default: as_fatal ("unhandled operand code %03o", *p); } @@ -5446,21 +5480,17 @@ end_of_line (str); } +/* Co-processor register load/store. + Format: {cond}[L] CP#,CRd,
*/ static void do_lstc (char * str) { - /* Co-processor register load/store. - Format: */ - - reg_or_fail (&str, 8, REG_TYPE_CP); - comma_or_fail (&str); - - reg_or_fail (&str, 12, REG_TYPE_CN); - comma_or_fail (&str); - - if (cp_address_required_here (&str, TRUE, BFD_RELOC_ARM_CP_OFF_IMM) == FAIL) + if (parse_operands (str, OPERANDS3(RCP,RCN,CADDR))) return; - end_of_line (str); + + inst.instruction |= inst.operands[0].reg << 8; + inst.instruction |= inst.operands[1].reg << 12; + encode_cp_address_arm (2, TRUE, TRUE, 0); } static void @@ -6764,25 +6794,21 @@ static void do_vfp_sp_ldst (char * str) { - reg_or_fail (&str, VFP_REG_Sd, REG_TYPE_VFS); - comma_or_fail (&str); - - if (cp_address_required_here (&str, FALSE, BFD_RELOC_ARM_CP_OFF_IMM) == FAIL) + if (parse_operands (str, OPERANDS2(RVS,CADDR))) return; - end_of_line (str); + vfp_sp_encode_reg (inst.operands[0].reg, VFP_REG_Sd); + encode_cp_address_arm (1, FALSE, TRUE, 0); } static void do_vfp_dp_ldst (char * str) { - reg_or_fail (&str, VFP_REG_Dd, REG_TYPE_VFD); - comma_or_fail (&str); - - if (cp_address_required_here (&str, FALSE, BFD_RELOC_ARM_CP_OFF_IMM) == FAIL) + if (parse_operands (str, OPERANDS2(RVD,CADDR))) return; - end_of_line (str); + inst.instruction |= inst.operands[0].reg << VFP_REG_Dd; + encode_cp_address_arm (1, FALSE, TRUE, 0); } @@ -6934,108 +6960,54 @@ static void do_fpa_ldst (char * str) { - reg_or_fail (&str, 12, REG_TYPE_FN); - comma_or_fail (&str); - - if (cp_address_required_here (&str, TRUE, BFD_RELOC_ARM_CP_OFF_IMM) == FAIL) + if (parse_operands (str, OPERANDS2(RF,CADDR))) return; - - end_of_line (str); + inst.instruction |= (inst.operands[0].reg << 12); + encode_cp_address_arm (1, TRUE, TRUE, 0); } static void do_fpa_ldmstm (char * str) { - int num_regs; - - reg_or_fail (&str, 12, REG_TYPE_FN); - comma_or_fail (&str); - - /* Get Number of registers to transfer. */ - if (immediate_required_here (&str, &num_regs, 1, 4, TRUE) == FAIL) + if (parse_operands (str, OPERANDS3(RF,bI4,CADDR))) return; - comma_or_fail (&str); - switch (num_regs) + inst.instruction |= (inst.operands[0].reg << 12); + switch (inst.operands[1].imm) { - case 1: - inst.instruction |= CP_T_X; - break; - case 2: - inst.instruction |= CP_T_Y; - break; - case 3: - inst.instruction |= CP_T_Y | CP_T_X; - break; - case 4: - break; - default: - abort (); + case 1: inst.instruction |= CP_T_X; break; + case 2: inst.instruction |= CP_T_Y; break; + case 3: inst.instruction |= CP_T_Y | CP_T_X; break; + case 4: break; + default: abort (); } - if (inst.instruction & (CP_T_Pre | CP_T_UD)) /* ea/fd format. */ + if (inst.instruction & (PRE_INDEX | INDEX_UP)) { - int reg; - int write_back; - int offset; - /* The instruction specified "ea" or "fd", so we can only accept [Rn]{!}. The instruction does not really support stacking or unstacking, so we have to emulate these by setting appropriate bits and offsets. */ - - char_or_fail (&str, '['); - note_reg_or_fail (reg, &str, 16, REG_TYPE_RN); - char_or_fail (&str, ']'); - - if (*str == '!') + if (inst.reloc.exp.X_op != O_constant + || inst.reloc.exp.X_add_number != 0) { - write_back = 1; - str++; - if (reg == REG_PC) - { - inst.error = - _("r15 not allowed as base register with write-back"); - return; - } + inst.error = _("this instruction does not support indexing"); + return; } + if (inst.operands[2].writeback) + inst.instruction |= WRITE_BACK; + if (inst.instruction & (PRE_INDEX | WRITE_BACK)) + inst.reloc.exp.X_add_number = 12 * inst.operands[1].imm; else - write_back = 0; + inst.instruction |= PRE_INDEX; /* prevent use of unindexed mode */ - if (inst.instruction & CP_T_Pre) - { - /* Pre-decrement. */ - offset = 3 * num_regs; - if (write_back) - inst.instruction |= CP_T_WB; - } - else - { - /* Post-increment. */ - if (write_back) - { - inst.instruction |= CP_T_WB; - offset = 3 * num_regs; - } - else - { - /* No write-back, so convert this into a standard pre-increment - instruction -- aesthetically more pleasing. */ - inst.instruction |= CP_T_Pre | CP_T_UD; - offset = 0; - } - } + if (!(inst.instruction & INDEX_UP)) + inst.reloc.exp.X_add_number = -inst.reloc.exp.X_add_number; - inst.instruction |= offset; + inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM; } else - { - if (cp_address_required_here (&str, TRUE, BFD_RELOC_ARM_CP_OFF_IMM) - == FAIL) - return; - } - - end_of_line (str); + encode_cp_address_arm (2, TRUE, TRUE, 0); } @@ -7188,33 +7160,21 @@ static void do_iwmmxt_wldst (char * str) { - reg_or_fail (&str, 12, REG_TYPE_MMXWR); - comma_or_fail (&str); - - if (cp_address_required_here (&str, TRUE, BFD_RELOC_ARM_CP_OFF_IMM_S2) == FAIL) + if (parse_operands (str, OPERANDS2(RIWR,CADDR))) return; - end_of_line (str); + inst.instruction |= (inst.operands[0].reg << 12); + encode_cp_address_arm (1, TRUE, FALSE, BFD_RELOC_ARM_CP_OFF_IMM_S2); } static void do_iwmmxt_wldstw (char * str) { - struct reg_entry *reg; - - if ((reg = arm_reg_parse_multi (&str)) == NULL - || (reg->type != REG_TYPE_MMXWR && reg->type != REG_TYPE_MMXWC)) - { - inst.error = _("iWMMXt data or control register expected"); - return; - } - comma_or_fail (&str); - - if (cp_address_required_here (&str, TRUE, BFD_RELOC_ARM_CP_OFF_IMM) == FAIL) + if (parse_operands (str, OPERANDS2(RIWR_RIWC,CADDR))) return; - end_of_line (str); - if (reg->type == REG_TYPE_MMXWC) + /* RIWR_RIWC clears .isreg for a control register. */ + if (!inst.operands[0].isreg) { if ((inst.instruction & COND_MASK) != COND_ALWAYS) { @@ -7225,7 +7185,8 @@ inst.instruction |= 0xf0000000; } - inst.instruction |= reg->number << 12; + inst.instruction |= inst.operands[0].reg << 12; + encode_cp_address_arm (1, TRUE, TRUE, 0); } static void @@ -7606,37 +7567,37 @@ CRd,[Rn],. */ static void -do_mav_ldst (char * str, enum arm_reg_type reg0) +do_mav_ldst (char *str, const char *operands) { - reg_or_fail (&str, 12, reg0); - comma_or_fail (&str); + if (parse_operands (str, operands)) + return; - cp_address_required_here (&str, TRUE, BFD_RELOC_ARM_CP_OFF_IMM); - end_of_line (str); + inst.instruction |= inst.operands[0].reg << 12; + encode_cp_address_arm (1, TRUE, TRUE, 0); } static void do_mav_ldst_1 (char * str) { - do_mav_ldst (str, REG_TYPE_MVF); + do_mav_ldst (str, OPERANDS2(RMF,CADDR)); } static void do_mav_ldst_2 (char * str) { - do_mav_ldst (str, REG_TYPE_MVD); + do_mav_ldst (str, OPERANDS2(RMD,CADDR)); } static void do_mav_ldst_3 (char * str) { - do_mav_ldst (str, REG_TYPE_MVFX); + do_mav_ldst (str, OPERANDS2(RMFX,CADDR)); } static void do_mav_ldst_4 (char * str) { - do_mav_ldst (str, REG_TYPE_MVDX); + do_mav_ldst (str, OPERANDS2(RMDX,CADDR)); } /* XScale instructions. Also sorted arithmetic before move. */ @@ -10522,28 +10483,25 @@ #endif case BFD_RELOC_ARM_CP_OFF_IMM: - sign = value >= 0; if (value < -1023 || value > 1023 || (value & 3)) as_bad_where (fixP->fx_file, fixP->fx_line, _("co-processor offset out of range")); + cp_off_common: + sign = value >= 0; if (value < 0) value = -value; newval = md_chars_to_number (buf, INSN_SIZE) & 0xff7fff00; newval |= (value >> 2) | (sign ? INDEX_UP : 0); + if (value == 0) + newval &= ~WRITE_BACK; md_number_to_chars (buf, newval, INSN_SIZE); break; case BFD_RELOC_ARM_CP_OFF_IMM_S2: - sign = value >= 0; if (value < -255 || value > 255) as_bad_where (fixP->fx_file, fixP->fx_line, _("co-processor offset out of range")); - if (value < 0) - value = -value; - newval = md_chars_to_number (buf, INSN_SIZE) & 0xff7fff00; - newval |= value | (sign ? INDEX_UP : 0); - md_number_to_chars (buf, newval , INSN_SIZE); - break; + goto cp_off_common; case BFD_RELOC_ARM_THUMB_OFFSET: newval = md_chars_to_number (buf, THUMB_SIZE);