From e07e6e58be1c5273ed79a25c80ba831e71ac86b1 Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Mon, 22 Jun 2009 14:40:28 +0000 Subject: [PATCH] * config/tc-arm.c (implicit_it_mode): New enum. (implicit_it_mode): New global. (it_instruction_type): New enum. (arm_parse_it_mode): New function. (arm_long_opts): New option added. (arm_it): New field. (it_state): New enum. (now_it): New macro. (check_it_blocks_finished): New function. (insns[]): Use the IT Thumb opcodes for ARM too. (arm_cleanup): Call check_it_blocks_finished. (now_it_compatible): New function. (conditional_insn): New function. (set_it_insn_type): New macro. (set_it_insn_type_last): New macro. (do_it): Call automatic IT machinery functions. (do_t_add_sub): Likewise (do_t_arit3): Likewise. (do_t_arit3c): Likewise. (do_t_blx): Likewise. (do_t_branch): Likewise. (do_t_bkpt): Likewise. (do_t_branch23): Likewise. (do_t_bx): Likewise. (do_t_bxj): Likewise. (do_t_cps): Likewise. (do_t_cpsi): Likewise. (do_t_cbz): Likewise. (do_t_it): Likewise. (encode_thumb2_ldmstm): Likewise. (do_t_ldst): Likewise. (do_t_mov_cmp): Likewise. (do_t_mvn_tst): Likewise. (do_t_mul): Likewise. (do_t_neg): Likewise. (do_t_setend): Likewise. (do_t_shift): Likewise. (do_t_tb): Likewise. (output_it_inst): New function. (new_automatic_it_block): New function. (close_automatic_it_block): New function. (now_it_add_mask): New function. (it_fsm_pre_encode): New function. (handle_it_state): New function. (it_fsm_post_encode): New function. (force_automatic_it_block_close): New function. (in_it_block): New function. (md_assemble): Call automatic IT block machinery functions. (arm_frob_label): Likewise. (arm_opts): New element. * config/tc-arm.h (it_state): New enum. (current_it): New struct. (arm_segment_info_type): New member added. * doc/c-arm.texi: New option -mimplicit-it documented. * gas/arm/arm-it-auto.d: New test. * gas/arm/arm-it-auto.s: New file. * gas/arm/arm-it-auto-2.d: New test case. * gas/arm/arm-it-auto-2.s: New file. * gas/arm/arm-it-auto-3.d: New test case. * gas/arm/arm-it-auto-3.s: New file. * gas/arm/arm-it-bad.d: New test case. * gas/arm/arm-it-bad.l: New file. * gas/arm/arm-it-bad.s: New file. * gas/arm/arm-it-bad-2.d: New test case. * gas/arm/arm-it-bad-2.l: New file. * gas/arm/arm-it-bad-2.s: New file. * gas/arm/arm-it-bad-3.d: New test case. * gas/arm/arm-it-bad-3.l: New file. * gas/arm/arm-it-bad-3.s: New file. * gas/arm/thumb2_it_auto.d: New test. * gas/arm/thumb2_it_bad.l: Error message updated. * gas/arm/thumb2_it_bad_auto.d: New test. * gas/arm/thumb2_it.d: Comment added. * gas/arm/thumb2_it_bad.d: Comment added. --- gas/ChangeLog | 58 +- gas/config/tc-arm.c | 938 ++++++++++++++++++++------ gas/config/tc-arm.h | 21 +- gas/doc/c-arm.texi | 5 + gas/testsuite/ChangeLog | 23 + gas/testsuite/gas/arm/thumb2_it.d | 58 +- gas/testsuite/gas/arm/thumb2_it_bad.d | 1 + gas/testsuite/gas/arm/thumb2_it_bad.l | 2 +- 8 files changed, 850 insertions(+), 256 deletions(-) diff --git a/gas/ChangeLog b/gas/ChangeLog index 0e6f874219d..074e239dd71 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,9 +1,65 @@ +2009-06-22 Daniel Gutson + + * config/tc-arm.c (implicit_it_mode): New enum. + (implicit_it_mode): New global. + (it_instruction_type): New enum. + (arm_parse_it_mode): New function. + (arm_long_opts): New option added. + (arm_it): New field. + (it_state): New enum. + (now_it): New macro. + (check_it_blocks_finished): New function. + (insns[]): Use the IT Thumb opcodes for ARM too. + (arm_cleanup): Call check_it_blocks_finished. + (now_it_compatible): New function. + (conditional_insn): New function. + (set_it_insn_type): New macro. + (set_it_insn_type_last): New macro. + (do_it): Call automatic IT machinery functions. + (do_t_add_sub): Likewise + (do_t_arit3): Likewise. + (do_t_arit3c): Likewise. + (do_t_blx): Likewise. + (do_t_branch): Likewise. + (do_t_bkpt): Likewise. + (do_t_branch23): Likewise. + (do_t_bx): Likewise. + (do_t_bxj): Likewise. + (do_t_cps): Likewise. + (do_t_cpsi): Likewise. + (do_t_cbz): Likewise. + (do_t_it): Likewise. + (encode_thumb2_ldmstm): Likewise. + (do_t_ldst): Likewise. + (do_t_mov_cmp): Likewise. + (do_t_mvn_tst): Likewise. + (do_t_mul): Likewise. + (do_t_neg): Likewise. + (do_t_setend): Likewise. + (do_t_shift): Likewise. + (do_t_tb): Likewise. + (output_it_inst): New function. + (new_automatic_it_block): New function. + (close_automatic_it_block): New function. + (now_it_add_mask): New function. + (it_fsm_pre_encode): New function. + (handle_it_state): New function. + (it_fsm_post_encode): New function. + (force_automatic_it_block_close): New function. + (in_it_block): New function. + (md_assemble): Call automatic IT block machinery functions. + (arm_frob_label): Likewise. + (arm_opts): New element. + * config/tc-arm.h (it_state): New enum. + (current_it): New struct. + (arm_segment_info_type): New member added. + * doc/c-arm.texi: New option -mimplicit-it documented. + 2009-06-20 Alan Modra PR 10302 * doc/as.texinfo (Section ): Clarify tail merge. -======= 2009-06-19 Martin Schwidefsky * config/tc-s390.c (md_gather_operands): Accept an instruction diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c index 1190696d573..c3f775c31bc 100644 --- a/gas/config/tc-arm.c +++ b/gas/config/tc-arm.c @@ -265,6 +265,16 @@ static int thumb_mode = 0; tc_frag_data field of a frag. */ #define MODE_RECORDED (1 << 4) +/* Specifies the intrinsic IT insn behavior mode. */ +enum implicit_it_mode +{ + IMPLICIT_IT_MODE_NEVER = 0x00, + IMPLICIT_IT_MODE_ARM = 0x01, + IMPLICIT_IT_MODE_THUMB = 0x02, + IMPLICIT_IT_MODE_ALWAYS = (IMPLICIT_IT_MODE_ARM | IMPLICIT_IT_MODE_THUMB) +}; +static int implicit_it_mode = IMPLICIT_IT_MODE_ARM; + /* If unified_syntax is true, we are processing the new unified ARM/Thumb syntax. Important differences from the old ARM mode: @@ -315,6 +325,18 @@ struct neon_type unsigned elems; }; +enum it_instruction_type +{ + OUTSIDE_IT_INSN, + INSIDE_IT_INSN, + INSIDE_IT_LAST_INSN, + IF_INSIDE_IT_LAST_INSN, /* Either outside or inside; + if inside, should be the last one. */ + NEUTRAL_IT_INSN, /* This could be either inside or outside, + i.e. BKPT and NOP. */ + IT_INSN /* The IT insn has been parsed. */ +}; + struct arm_it { const char * error; @@ -337,6 +359,8 @@ struct arm_it int pc_rel; } reloc; + enum it_instruction_type it_insn_type; + struct { unsigned reg; @@ -674,6 +698,9 @@ struct asm_opcode #define BAD_BRANCH _("branch must be last instruction in IT block") #define BAD_NOT_IT _("instruction not allowed in IT block") #define BAD_FPU _("selected FPU does not support instruction") +#define BAD_OUT_IT _("thumb conditional instruction should be in IT block") +#define BAD_IT_COND _("incorrect condition in IT block") +#define BAD_IT_IT _("IT falling in the range of a previous IT block") static struct hash_control *arm_ops_hsh; static struct hash_control *arm_cond_hsh; @@ -695,7 +722,7 @@ static struct hash_control *arm_barrier_opt_hsh; symbolS * last_label_seen; static int label_is_thumb_function_name = FALSE; - + /* Literal pool structure. Held on a per-section and per-sub-section basis. */ @@ -714,10 +741,49 @@ typedef struct literal_pool /* Pointer to a linked list of literal pools. */ literal_pool * list_of_pools = NULL; -/* State variables for IT block handling. */ -static bfd_boolean current_it_mask = 0; -static int current_cc; - +#ifdef OBJ_ELF +# define now_it seg_info (now_seg)->tc_segment_info_data.current_it +#else +static struct current_it now_it; +#endif + +static inline int +now_it_compatible (int cond) +{ + return (cond & ~1) == (now_it.cc & ~1); +} + +static inline int +conditional_insn (void) +{ + return inst.cond != COND_ALWAYS; +} + +static int in_it_block (void); + +static int handle_it_state (void); + +static void force_automatic_it_block_close (void); + +#define set_it_insn_type(type) \ + do \ + { \ + inst.it_insn_type = type; \ + if (handle_it_state () == FAIL) \ + return; \ + } \ + while (0) + +#define set_it_insn_type_last() \ + do \ + { \ + if (inst.cond == COND_ALWAYS) \ + set_it_insn_type (IF_INSIDE_IT_LAST_INSN); \ + else \ + set_it_insn_type (INSIDE_IT_LAST_INSN); \ + } \ + while (0) + /* Pure syntax. */ /* This array holds the chars that always start a comment. If the @@ -1431,6 +1497,7 @@ parse_scalar (char **ccp, int elsize, struct neon_type_el *type) } /* Parse an ARM register list. Returns the bitmask, or FAIL. */ + static long parse_reg_list (char ** strp) { @@ -5068,7 +5135,7 @@ parse_cond (char **str) n = 0; while (ISALPHA (*q) && n < 3) { - cond[n] = TOLOWER(*q); + cond[n] = TOLOWER (*q); q++; n++; } @@ -5516,69 +5583,90 @@ parse_operands (char *str, const unsigned char *pattern) enum arm_reg_type rtype; parse_operand_result result; -#define po_char_or_fail(chr) do { \ - if (skip_past_char (&str, chr) == FAIL) \ - goto bad_args; \ -} while (0) +#define po_char_or_fail(chr) \ + do \ + { \ + if (skip_past_char (&str, chr) == FAIL) \ + goto bad_args; \ + } \ + while (0) -#define po_reg_or_fail(regtype) do { \ - val = arm_typed_reg_parse (&str, regtype, &rtype, \ - &inst.operands[i].vectype); \ - if (val == FAIL) \ +#define po_reg_or_fail(regtype) \ + do \ { \ - first_error (_(reg_expected_msgs[regtype])); \ - goto failure; \ + val = arm_typed_reg_parse (& str, regtype, & rtype, \ + & inst.operands[i].vectype); \ + if (val == FAIL) \ + { \ + first_error (_(reg_expected_msgs[regtype])); \ + goto failure; \ + } \ + inst.operands[i].reg = val; \ + inst.operands[i].isreg = 1; \ + inst.operands[i].isquad = (rtype == REG_TYPE_NQ); \ + inst.operands[i].issingle = (rtype == REG_TYPE_VFS); \ + inst.operands[i].isvec = (rtype == REG_TYPE_VFS \ + || rtype == REG_TYPE_VFD \ + || rtype == REG_TYPE_NQ); \ } \ - inst.operands[i].reg = val; \ - inst.operands[i].isreg = 1; \ - inst.operands[i].isquad = (rtype == REG_TYPE_NQ); \ - inst.operands[i].issingle = (rtype == REG_TYPE_VFS); \ - inst.operands[i].isvec = (rtype == REG_TYPE_VFS \ - || rtype == REG_TYPE_VFD \ - || rtype == REG_TYPE_NQ); \ -} while (0) - -#define po_reg_or_goto(regtype, label) do { \ - val = arm_typed_reg_parse (&str, regtype, &rtype, \ - &inst.operands[i].vectype); \ - if (val == FAIL) \ - goto label; \ + while (0) + +#define po_reg_or_goto(regtype, label) \ + do \ + { \ + val = arm_typed_reg_parse (& str, regtype, & rtype, \ + & inst.operands[i].vectype); \ + if (val == FAIL) \ + goto label; \ \ - inst.operands[i].reg = val; \ - inst.operands[i].isreg = 1; \ - inst.operands[i].isquad = (rtype == REG_TYPE_NQ); \ - inst.operands[i].issingle = (rtype == REG_TYPE_VFS); \ - inst.operands[i].isvec = (rtype == REG_TYPE_VFS \ - || rtype == REG_TYPE_VFD \ - || rtype == REG_TYPE_NQ); \ -} while (0) - -#define po_imm_or_fail(min, max, popt) do { \ - if (parse_immediate (&str, &val, min, max, popt) == FAIL) \ - goto failure; \ - inst.operands[i].imm = val; \ -} while (0) - -#define po_scalar_or_goto(elsz, label) do { \ - val = parse_scalar (&str, elsz, &inst.operands[i].vectype); \ - if (val == FAIL) \ - goto label; \ - inst.operands[i].reg = val; \ - inst.operands[i].isscalar = 1; \ -} while (0) - -#define po_misc_or_fail(expr) do { \ - if (expr) \ - goto failure; \ -} while (0) - -#define po_misc_or_fail_no_backtrack(expr) do { \ - result = expr; \ - if (result == PARSE_OPERAND_FAIL_NO_BACKTRACK)\ - backtrack_pos = 0; \ - if (result != PARSE_OPERAND_SUCCESS) \ - goto failure; \ -} while (0) + inst.operands[i].reg = val; \ + inst.operands[i].isreg = 1; \ + inst.operands[i].isquad = (rtype == REG_TYPE_NQ); \ + inst.operands[i].issingle = (rtype == REG_TYPE_VFS); \ + inst.operands[i].isvec = (rtype == REG_TYPE_VFS \ + || rtype == REG_TYPE_VFD \ + || rtype == REG_TYPE_NQ); \ + } \ + while (0) + +#define po_imm_or_fail(min, max, popt) \ + do \ + { \ + if (parse_immediate (&str, &val, min, max, popt) == FAIL) \ + goto failure; \ + inst.operands[i].imm = val; \ + } \ + while (0) + +#define po_scalar_or_goto(elsz, label) \ + do \ + { \ + val = parse_scalar (& str, elsz, & inst.operands[i].vectype); \ + if (val == FAIL) \ + goto label; \ + inst.operands[i].reg = val; \ + inst.operands[i].isscalar = 1; \ + } \ + while (0) + +#define po_misc_or_fail(expr) \ + do \ + { \ + if (expr) \ + goto failure; \ + } \ + while (0) + +#define po_misc_or_fail_no_backtrack(expr) \ + do \ + { \ + result = expr; \ + if (result == PARSE_OPERAND_FAIL_NO_BACKTRACK) \ + backtrack_pos = 0; \ + if (result != PARSE_OPERAND_SUCCESS) \ + goto failure; \ + } \ + while (0) skip_whitespace (str); @@ -5844,11 +5932,11 @@ parse_operands (char *str, const unsigned char *pattern) po_misc_or_fail (parse_half (&str)); break; - /* Register or expression */ + /* Register or expression. */ case OP_RR_EXr: po_reg_or_goto (REG_TYPE_RN, EXPr); break; case OP_RR_EXi: po_reg_or_goto (REG_TYPE_RN, EXPi); break; - /* Register or immediate */ + /* Register or immediate. */ case OP_RRnpc_I0: po_reg_or_goto (REG_TYPE_RN, I0); break; I0: po_imm_or_fail (0, 0, FALSE); break; @@ -5869,7 +5957,7 @@ parse_operands (char *str, const unsigned char *pattern) case OP_RIWR_I32z: po_reg_or_goto (REG_TYPE_MMXWR, I32z); break; I32z: po_imm_or_fail (0, 32, FALSE); break; - /* Two kinds of register */ + /* Two kinds of register. */ case OP_RIWR_RIWC: { struct reg_entry *rege = arm_reg_parse_multi (&str); @@ -5948,7 +6036,7 @@ parse_operands (char *str, const unsigned char *pattern) po_misc_or_fail (parse_tb (&str)); break; - /* Register lists */ + /* Register lists. */ case OP_REGLST: val = parse_reg_list (&str); if (*str == '^') @@ -6120,15 +6208,18 @@ parse_operands (char *str, const unsigned char *pattern) #undef po_reg_or_goto #undef po_imm_or_fail #undef po_scalar_or_fail - + /* Shorthand macro for instruction encoding functions issuing errors. */ -#define constraint(expr, err) do { \ - if (expr) \ +#define constraint(expr, err) \ + do \ { \ - inst.error = err; \ - return; \ + if (expr) \ + { \ + inst.error = err; \ + return; \ + } \ } \ -} while (0) + while (0) /* Reject "bad registers" for Thumb-2 instructions. Many Thumb-2 instructions are unpredictable if these registers are used. This @@ -6960,8 +7051,17 @@ static void do_it (void) { /* There is no IT instruction in ARM mode. We - process it but do not generate code for it. */ + process it to do the validation as if in + thumb mode, just in case the code gets + assembled for thumb using the unified syntax. */ + inst.size = 0; + if (unified_syntax) + { + set_it_insn_type (IT_INSN); + now_it.mask = (inst.instruction & 0xf) | 0x10; + now_it.cc = inst.operands[0].imm; + } } static void @@ -8482,6 +8582,9 @@ do_t_add_sub (void) ? inst.operands[1].reg /* Rd, Rs, foo */ : inst.operands[0].reg); /* Rd, foo -> Rd, Rd, foo */ + if (Rd == REG_PC) + set_it_insn_type_last (); + if (unified_syntax) { bfd_boolean flags; @@ -8491,9 +8594,9 @@ do_t_add_sub (void) flags = (inst.instruction == T_MNEM_adds || inst.instruction == T_MNEM_subs); if (flags) - narrow = (current_it_mask == 0); + narrow = !in_it_block (); else - narrow = (current_it_mask != 0); + narrow = in_it_block (); if (!inst.operands[2].isreg) { int add; @@ -8745,9 +8848,9 @@ do_t_arit3 (void) /* See if we can do this with a 16-bit instruction. */ if (THUMB_SETS_FLAGS (inst.instruction)) - narrow = current_it_mask == 0; + narrow = !in_it_block (); else - narrow = current_it_mask != 0; + narrow = in_it_block (); if (Rd > 7 || Rn > 7 || Rs > 7) narrow = FALSE; @@ -8833,9 +8936,9 @@ do_t_arit3c (void) /* See if we can do this with a 16-bit instruction. */ if (THUMB_SETS_FLAGS (inst.instruction)) - narrow = current_it_mask == 0; + narrow = !in_it_block (); else - narrow = current_it_mask != 0; + narrow = in_it_block (); if (Rd > 7 || Rn > 7 || Rs > 7) narrow = FALSE; @@ -8988,7 +9091,8 @@ do_t_bfx (void) static void do_t_blx (void) { - constraint (current_it_mask && current_it_mask != 0x10, BAD_BRANCH); + set_it_insn_type_last (); + if (inst.operands[0].isreg) { constraint (inst.operands[0].reg == REG_PC, BAD_PC); @@ -9010,13 +9114,14 @@ do_t_branch (void) int opcode; int cond; - if (current_it_mask) + cond = inst.cond; + set_it_insn_type (IF_INSIDE_IT_LAST_INSN); + + if (in_it_block ()) { /* Conditional branches inside IT blocks are encoded as unconditional branches. */ cond = COND_ALWAYS; - /* A branch must be the last instruction in an IT block. */ - constraint (current_it_mask != 0x10, BAD_BRANCH); } else cond = inst.cond; @@ -9066,13 +9171,14 @@ do_t_bkpt (void) constraint (inst.operands[0].imm > 255, _("immediate value out of range")); inst.instruction |= inst.operands[0].imm; + set_it_insn_type (NEUTRAL_IT_INSN); } } static void do_t_branch23 (void) { - constraint (current_it_mask && current_it_mask != 0x10, BAD_BRANCH); + set_it_insn_type_last (); inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH23; inst.reloc.pc_rel = 1; @@ -9093,7 +9199,7 @@ do_t_branch23 (void) static void do_t_bx (void) { - constraint (current_it_mask && current_it_mask != 0x10, BAD_BRANCH); + set_it_insn_type_last (); inst.instruction |= inst.operands[0].reg << 3; /* ??? FIXME: Should add a hacky reloc here if reg is REG_PC. The reloc should cause the alignment to be checked once it is known. This is @@ -9105,7 +9211,7 @@ do_t_bxj (void) { int Rm; - constraint (current_it_mask && current_it_mask != 0x10, BAD_BRANCH); + set_it_insn_type_last (); Rm = inst.operands[0].reg; reject_bad_reg (Rm); inst.instruction |= Rm << 16; @@ -9131,14 +9237,14 @@ do_t_clz (void) static void do_t_cps (void) { - constraint (current_it_mask, BAD_NOT_IT); + set_it_insn_type (OUTSIDE_IT_INSN); inst.instruction |= inst.operands[0].imm; } static void do_t_cpsi (void) { - constraint (current_it_mask, BAD_NOT_IT); + set_it_insn_type (OUTSIDE_IT_INSN); if (unified_syntax && (inst.operands[1].present || inst.size_req == 4) && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6_notm)) @@ -9185,7 +9291,7 @@ do_t_cpy (void) static void do_t_cbz (void) { - constraint (current_it_mask, BAD_NOT_IT); + set_it_insn_type (OUTSIDE_IT_INSN); constraint (inst.operands[0].reg > 7, BAD_HIREG); inst.instruction |= inst.operands[0].reg; inst.reloc.pc_rel = 1; @@ -9231,9 +9337,9 @@ do_t_it (void) { unsigned int cond = inst.operands[0].imm; - constraint (current_it_mask, BAD_NOT_IT); - current_it_mask = (inst.instruction & 0xf) | 0x10; - current_cc = cond; + set_it_insn_type (IT_INSN); + now_it.mask = (inst.instruction & 0xf) | 0x10; + now_it.cc = cond; /* If the condition is a negative condition, invert the mask. */ if ((cond & 0x1) == 0x0) @@ -9268,9 +9374,13 @@ encode_thumb2_ldmstm (int base, unsigned mask, bfd_boolean writeback) inst.error = _("SP not allowed in register list"); if (load) { - if (mask & (1 << 14) - && mask & (1 << 15)) - inst.error = _("LR and PC should not both be in register list"); + if (mask & (1 << 15)) + { + if (mask & (1 << 14)) + inst.error = _("LR and PC should not both be in register list"); + else + set_it_insn_type_last (); + } if ((mask & (1 << base)) != 0 && writeback) @@ -9446,6 +9556,11 @@ do_t_ldst (void) unsigned long opcode; int Rn; + if (inst.operands[0].isreg + && !inst.operands[0].preind + && inst.operands[0].reg == REG_PC) + set_it_insn_type_last (); + opcode = inst.instruction; if (unified_syntax) { @@ -9666,6 +9781,9 @@ do_t_mov_cmp (void) Rn = inst.operands[0].reg; Rm = inst.operands[1].reg; + if (Rn == REG_PC) + set_it_insn_type_last (); + if (unified_syntax) { int r0off = (inst.instruction == T_MNEM_mov @@ -9676,7 +9794,7 @@ do_t_mov_cmp (void) low_regs = (Rn <= 7 && Rm <= 7); opcode = inst.instruction; - if (current_it_mask) + if (in_it_block ()) narrow = opcode != T_MNEM_movs; else narrow = opcode != T_MNEM_movs || low_regs; @@ -9731,7 +9849,7 @@ do_t_mov_cmp (void) if (!inst.operands[1].isreg) { /* Immediate operand. */ - if (current_it_mask == 0 && opcode == T_MNEM_mov) + if (!in_it_block () && opcode == T_MNEM_mov) narrow = 0; if (low_regs && narrow) { @@ -9757,7 +9875,7 @@ do_t_mov_cmp (void) /* Register shifts are encoded as separate shift instructions. */ bfd_boolean flags = (inst.instruction == T_MNEM_movs); - if (current_it_mask) + if (in_it_block ()) narrow = !flags; else narrow = flags; @@ -9813,7 +9931,7 @@ do_t_mov_cmp (void) && (inst.instruction == T_MNEM_mov || inst.instruction == T_MNEM_movs)) { - if (current_it_mask) + if (in_it_block ()) narrow = (inst.instruction == T_MNEM_mov); else narrow = (inst.instruction == T_MNEM_movs); @@ -9975,9 +10093,9 @@ do_t_mvn_tst (void) else if (inst.instruction == T_MNEM_cmn) narrow = TRUE; else if (THUMB_SETS_FLAGS (inst.instruction)) - narrow = (current_it_mask == 0); + narrow = !in_it_block (); else - narrow = (current_it_mask != 0); + narrow = in_it_block (); if (!inst.operands[1].isreg) { @@ -10116,9 +10234,9 @@ do_t_mul (void) || Rm > 7) narrow = FALSE; else if (inst.instruction == T_MNEM_muls) - narrow = (current_it_mask == 0); + narrow = !in_it_block (); else - narrow = (current_it_mask != 0); + narrow = in_it_block (); } else { @@ -10143,8 +10261,8 @@ do_t_mul (void) } else { - constraint(inst.instruction != T_MNEM_mul, - _("Thumb-2 MUL must not set flags")); + constraint (inst.instruction != T_MNEM_mul, + _("Thumb-2 MUL must not set flags")); /* 32-bit MUL. */ inst.instruction = THUMB_OP32 (inst.instruction); inst.instruction |= Rd << 8; @@ -10184,6 +10302,8 @@ do_t_mull (void) static void do_t_nop (void) { + set_it_insn_type (NEUTRAL_IT_INSN); + if (unified_syntax) { if (inst.size_req == 4 || inst.operands[0].imm > 15) @@ -10220,9 +10340,9 @@ do_t_neg (void) bfd_boolean narrow; if (THUMB_SETS_FLAGS (inst.instruction)) - narrow = (current_it_mask == 0); + narrow = !in_it_block (); else - narrow = (current_it_mask != 0); + narrow = in_it_block (); if (inst.operands[0].reg > 7 || inst.operands[1].reg > 7) narrow = FALSE; if (inst.size_req == 4) @@ -10446,9 +10566,9 @@ do_t_rsb (void) bfd_boolean narrow; if ((inst.instruction & 0x00100000) != 0) - narrow = (current_it_mask == 0); + narrow = !in_it_block (); else - narrow = (current_it_mask != 0); + narrow = in_it_block (); if (Rd > 7 || Rs > 7) narrow = FALSE; @@ -10482,7 +10602,7 @@ do_t_rsb (void) static void do_t_setend (void) { - constraint (current_it_mask, BAD_NOT_IT); + set_it_insn_type (OUTSIDE_IT_INSN); if (inst.operands[0].imm) inst.instruction |= 0x8; } @@ -10512,9 +10632,9 @@ do_t_shift (void) } if (THUMB_SETS_FLAGS (inst.instruction)) - narrow = (current_it_mask == 0); + narrow = !in_it_block (); else - narrow = (current_it_mask != 0); + narrow = in_it_block (); if (inst.operands[0].reg > 7 || inst.operands[1].reg > 7) narrow = FALSE; if (!inst.operands[2].isreg && shift_kind == SHIFT_ROR) @@ -10813,7 +10933,7 @@ do_t_tb (void) int half; half = (inst.instruction & 0x10) != 0; - constraint (current_it_mask && current_it_mask != 0x10, BAD_BRANCH); + set_it_insn_type_last (); constraint (inst.operands[0].immisreg, _("instruction requires register index")); @@ -10878,7 +10998,7 @@ struct neon_tab_entry X(vcge, 0x0000310, 0x1000e00, 0x1b10080), \ X(vcgt, 0x0000300, 0x1200e00, 0x1b10000), \ /* Register variants of the following two instructions are encoded as - vcge / vcgt with the operands reversed. */ \ + vcge / vcgt with the operands reversed. */ \ X(vclt, 0x0000300, 0x1200e00, 0x1b10200), \ X(vcle, 0x0000310, 0x1000e00, 0x1b10180), \ X(vmla, 0x0000900, 0x0000d10, 0x0800040), \ @@ -14425,6 +14545,28 @@ output_inst (const char * str) dwarf2_emit_insn (inst.size); } +static char * +output_it_inst (int cond, int mask, char * to) +{ + unsigned long instruction = 0xbf00; + + mask &= 0xf; + instruction |= mask; + instruction |= cond << 4; + + if (to == NULL) + { + to = frag_more (2); +#ifdef OBJ_ELF + dwarf2_emit_insn (2); +#endif + } + + md_number_to_chars (to, instruction, 2); + + return to; +} + /* Tag values used in struct asm_opcode's tag field. */ enum opcode_tag { @@ -14670,6 +14812,320 @@ opcode_lookup (char **str) return 0; } +/* This function generates an initial IT instruction, leaving its block + virtually open for the new instructions. Eventually, + the mask will be updated by now_it_add_mask () each time + a new instruction needs to be included in the IT block. + Finally, the block is closed with close_automatic_it_block (). + The block closure can be requested either from md_assemble (), + a tencode (), or due to a label hook. */ + +static void +new_automatic_it_block (int cond) +{ + now_it.state = AUTOMATIC_IT_BLOCK; + now_it.mask = 0x18; + now_it.cc = cond; + now_it.block_length = 1; + now_it.insn = output_it_inst (cond, now_it.mask, NULL); +} + +/* Close an automatic IT block. + See comments in new_automatic_it_block (). */ + +static void +close_automatic_it_block (void) +{ + now_it.mask = 0x10; + now_it.block_length = 0; +} + +/* Update the mask of the current automatically-generated IT + instruction. See comments in new_automatic_it_block (). */ + +static void +now_it_add_mask (int cond) +{ +#define CLEAR_BIT(value, nbit) ((value) & ~(1 << (nbit))) +#define SET_BIT_VALUE(value, bitvalue, nbit) (CLEAR_BIT (value, nbit) \ + | ((bitvalue) << (nbit))) + + const int resulting_bit = (cond & 1); + now_it.mask &= 0xf; + now_it.mask = SET_BIT_VALUE (now_it.mask, + resulting_bit, + (5 - now_it.block_length)); + now_it.mask = SET_BIT_VALUE (now_it.mask, + 1, + ((5 - now_it.block_length) - 1) ); + output_it_inst (now_it.cc, now_it.mask, now_it.insn); + +#undef CLEAR_BIT +#undef SET_BIT_VALUE + +} + +/* The IT blocks handling machinery is accessed through the these functions: + it_fsm_pre_encode () from md_assemble () + set_it_insn_type () optional, from the tencode functions + set_it_insn_type_last () ditto + in_it_block () ditto + it_fsm_post_encode () from md_assemble () + force_automatic_it_block_close () from label habdling functions + + Rationale: + 1) md_assemble () calls it_fsm_pre_encode () before calling tencode (), + initializing the IT insn type with a generic initial value depending + on the inst.condition. + 2) During the tencode function, two things may happen: + a) The tencode function overrides the IT insn type by + calling either set_it_insn_type (type) or set_it_insn_type_last (). + b) The tencode function queries the IT block state by + calling in_it_block () (i.e. to determine narrow/not narrow mode). + + Both set_it_insn_type and in_it_block run the internal FSM state + handling function (handle_it_state), because: a) setting the IT insn + type may incur in an invalid state (exiting the function), + and b) querying the state requires the FSM to be updated. + Specifically we want to avoid creating an IT block for conditional + branches, so it_fsm_pre_encode is actually a guess and we can't + determine whether an IT block is required until the tencode () routine + has decided what type of instruction this actually it. + Because of this, if set_it_insn_type and in_it_block have to be used, + set_it_insn_type has to be called first. + + set_it_insn_type_last () is a wrapper of set_it_insn_type (type), that + determines the insn IT type depending on the inst.cond code. + When a tencode () routine encodes an instruction that can be + either outside an IT block, or, in the case of being inside, has to be + the last one, set_it_insn_type_last () will determine the proper + IT instruction type based on the inst.cond code. Otherwise, + set_it_insn_type can be called for overriding that logic or + for covering other cases. + + Calling handle_it_state () may not transition the IT block state to + OUTSIDE_IT_BLOCK immediatelly, since the (current) state could be + still queried. Instead, if the FSM determines that the state should + be transitioned to OUTSIDE_IT_BLOCK, a flag is marked to be closed + after the tencode () function: that's what it_fsm_post_encode () does. + + Since in_it_block () calls the state handling function to get an + updated state, an error may occur (due to invalid insns combination). + In that case, inst.error is set. + Therefore, inst.error has to be checked after the execution of + the tencode () routine. + + 3) Back in md_assemble(), it_fsm_post_encode () is called to commit + any pending state change (if any) that didn't take place in + handle_it_state () as explained above. */ + +static void +it_fsm_pre_encode (void) +{ + if (inst.cond != COND_ALWAYS) + inst.it_insn_type = INSIDE_IT_INSN; + else + inst.it_insn_type = OUTSIDE_IT_INSN; + + now_it.state_handled = 0; +} + +/* IT state FSM handling function. */ + +static int +handle_it_state (void) +{ + now_it.state_handled = 1; + + switch (now_it.state) + { + case OUTSIDE_IT_BLOCK: + switch (inst.it_insn_type) + { + case OUTSIDE_IT_INSN: + break; + + case INSIDE_IT_INSN: + case INSIDE_IT_LAST_INSN: + if (thumb_mode == 0) + { + if (unified_syntax + && !(implicit_it_mode & IMPLICIT_IT_MODE_ARM)) + as_tsktsk (_("Warning: conditional outside an IT block"\ + " for Thumb.")); + } + else + { + if ((implicit_it_mode & IMPLICIT_IT_MODE_THUMB) + && ARM_CPU_HAS_FEATURE (cpu_variant, arm_arch_t2)) + { + /* Automatically generate the IT instruction. */ + new_automatic_it_block (inst.cond); + if (inst.it_insn_type == INSIDE_IT_LAST_INSN) + close_automatic_it_block (); + } + else + { + inst.error = BAD_OUT_IT; + return FAIL; + } + } + break; + + case IF_INSIDE_IT_LAST_INSN: + case NEUTRAL_IT_INSN: + break; + + case IT_INSN: + now_it.state = MANUAL_IT_BLOCK; + now_it.block_length = 0; + break; + } + break; + + case AUTOMATIC_IT_BLOCK: + /* Three things may happen now: + a) We should increment current it block size; + b) We should close current it block (closing insn or 4 insns); + c) We should close current it block and start a new one (due + to incompatible conditions or + 4 insns-length block reached). */ + + switch (inst.it_insn_type) + { + case OUTSIDE_IT_INSN: + /* The closure of the block shall happen immediatelly, + so any in_it_block () call reports the block as closed. */ + force_automatic_it_block_close (); + break; + + case INSIDE_IT_INSN: + case INSIDE_IT_LAST_INSN: + case IF_INSIDE_IT_LAST_INSN: + now_it.block_length++; + + if (now_it.block_length > 4 + || !now_it_compatible (inst.cond)) + { + force_automatic_it_block_close (); + if (inst.it_insn_type != IF_INSIDE_IT_LAST_INSN) + new_automatic_it_block (inst.cond); + } + else + { + now_it_add_mask (inst.cond); + } + + if (now_it.state == AUTOMATIC_IT_BLOCK + && (inst.it_insn_type == INSIDE_IT_LAST_INSN + || inst.it_insn_type == IF_INSIDE_IT_LAST_INSN)) + close_automatic_it_block (); + break; + + case NEUTRAL_IT_INSN: + now_it.block_length++; + + if (now_it.block_length > 4) + force_automatic_it_block_close (); + else + now_it_add_mask (now_it.cc & 1); + break; + + case IT_INSN: + close_automatic_it_block (); + now_it.state = MANUAL_IT_BLOCK; + break; + } + break; + + case MANUAL_IT_BLOCK: + { + /* Check conditional suffixes. */ + const int cond = now_it.cc ^ ((now_it.mask >> 4) & 1) ^ 1; + int is_last; + now_it.mask <<= 1; + now_it.mask &= 0x1f; + is_last = (now_it.mask == 0x10); + + switch (inst.it_insn_type) + { + case OUTSIDE_IT_INSN: + inst.error = BAD_NOT_IT; + return FAIL; + + case INSIDE_IT_INSN: + if (cond != inst.cond) + { + inst.error = BAD_IT_COND; + return FAIL; + } + break; + + case INSIDE_IT_LAST_INSN: + case IF_INSIDE_IT_LAST_INSN: + if (cond != inst.cond) + { + inst.error = BAD_IT_COND; + return FAIL; + } + if (!is_last) + { + inst.error = BAD_BRANCH; + return FAIL; + } + break; + + case NEUTRAL_IT_INSN: + /* The BKPT instruction is unconditional even in an IT block. */ + break; + + case IT_INSN: + inst.error = BAD_IT_IT; + return FAIL; + } + } + break; + } + + return SUCCESS; +} + +static void +it_fsm_post_encode (void) +{ + int is_last; + + if (!now_it.state_handled) + handle_it_state (); + + is_last = (now_it.mask == 0x10); + if (is_last) + { + now_it.state = OUTSIDE_IT_BLOCK; + now_it.mask = 0; + } +} + +static void +force_automatic_it_block_close (void) +{ + if (now_it.state == AUTOMATIC_IT_BLOCK) + { + close_automatic_it_block (); + now_it.state = OUTSIDE_IT_BLOCK; + now_it.mask = 0; + } +} + +static int +in_it_block (void) +{ + if (!now_it.state_handled) + handle_it_state (); + + return now_it.state != OUTSIDE_IT_BLOCK; +} + void md_assemble (char *str) { @@ -14734,41 +15190,24 @@ md_assemble (char *str) /* Implicit require narrow instructions on Thumb-1. This avoids relaxation accidentally introducing Thumb-2 instructions. */ if (opcode->tencode != do_t_blx && opcode->tencode != do_t_branch23 - && !(ARM_CPU_HAS_FEATURE(*opcode->tvariant, arm_ext_msr) - || ARM_CPU_HAS_FEATURE(*opcode->tvariant, arm_ext_barrier))) + && !(ARM_CPU_HAS_FEATURE (*opcode->tvariant, arm_ext_msr) + || ARM_CPU_HAS_FEATURE (*opcode->tvariant, arm_ext_barrier))) inst.size_req = 2; } - /* Check conditional suffixes. */ - if (current_it_mask) - { - int cond; - cond = current_cc ^ ((current_it_mask >> 4) & 1) ^ 1; - current_it_mask <<= 1; - current_it_mask &= 0x1f; - /* The BKPT instruction is unconditional even in an IT block. */ - if (!inst.error - && cond != inst.cond && opcode->tencode != do_t_bkpt) - { - as_bad (_("incorrect condition in IT block")); - return; - } - } - else if (inst.cond != COND_ALWAYS && opcode->tencode != do_t_branch) - { - as_bad (_("thumb conditional instruction not in IT block")); - return; - } - mapping_state (MAP_THUMB); inst.instruction = opcode->tvalue; if (!parse_operands (p, opcode->operands)) - opcode->tencode (); + { + /* Prepare the it_insn_type for those encodings that don't set + it. */ + it_fsm_pre_encode (); - /* Clear current_it_mask at the end of an IT block. */ - if (current_it_mask == 0x10) - current_it_mask = 0; + opcode->tencode (); + + it_fsm_post_encode (); + } if (!(inst.error || inst.relax)) { @@ -14793,8 +15232,8 @@ md_assemble (char *str) This is overly pessimistic for relaxable instructions. */ if (((inst.size == 4 && (inst.instruction & 0xf800e800) != 0xf000e800) || inst.relax) - && !(ARM_CPU_HAS_FEATURE(*opcode->tvariant, arm_ext_msr) - || ARM_CPU_HAS_FEATURE(*opcode->tvariant, arm_ext_barrier))) + && !(ARM_CPU_HAS_FEATURE (*opcode->tvariant, arm_ext_msr) + || ARM_CPU_HAS_FEATURE (*opcode->tvariant, arm_ext_barrier))) ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, arm_ext_v6t2); } @@ -14827,7 +15266,11 @@ md_assemble (char *str) inst.instruction |= inst.cond << 28; inst.size = INSN_SIZE; if (!parse_operands (p, opcode->operands)) - opcode->aencode (); + { + it_fsm_pre_encode (); + opcode->aencode (); + it_fsm_post_encode (); + } /* Arm mode bx is marked as both v4T and v5 because it's still required on a hypothetical non-thumb v5 core. */ if (is_bx) @@ -14845,6 +15288,25 @@ md_assemble (char *str) output_inst (str); } +static void +check_it_blocks_finished (void) +{ +#ifdef OBJ_ELF + asection *sect; + + for (sect = stdoutput->sections; sect != NULL; sect = sect->next) + if (seg_info (sect)->tc_segment_info_data.current_it.state + == MANUAL_IT_BLOCK) + { + as_warn (_("section '%s' finished with an open IT block."), + sect->name); + } +#else + if (now_it.state == MANUAL_IT_BLOCK) + as_warn (_("file finished with an open IT block.")); +#endif +} + /* Various frobbings of labels and their addresses. */ void @@ -14864,6 +15326,8 @@ arm_frob_label (symbolS * sym) ARM_SET_INTERWORK (sym, support_interwork); #endif + force_automatic_it_block_close (); + /* Note - do not allow local symbols (.Lxxx) to be labelled as Thumb functions. This is because these labels, whilst they exist inside Thumb code, are not the entry points for @@ -15229,9 +15693,9 @@ static struct asm_barrier_opt barrier_opt_names[] = /* Two variants of the above - TCE for a numeric Thumb opcode, tCE for a T_MNEM_xyz enumerator. */ #define TCE(mnem, aop, top, nops, ops, ae, te) \ - TxCE(mnem, aop, 0x##top, nops, ops, ae, te) + TxCE (mnem, aop, 0x##top, nops, ops, ae, te) #define tCE(mnem, aop, top, nops, ops, ae, te) \ - TxCE(mnem, aop, T_MNEM_##top, nops, ops, ae, te) + TxCE (mnem, aop, T_MNEM_##top, nops, ops, ae, te) /* Second most common sort of mnemonic: has a Thumb variant, takes a conditional infix after the third character. */ @@ -15242,45 +15706,45 @@ static struct asm_barrier_opt barrier_opt_names[] = { #mnem, OPS##nops ops, OT_cinfix3_deprecated, 0x##op, top, ARM_VARIANT, \ THUMB_VARIANT, do_##ae, do_##te } #define TC3(mnem, aop, top, nops, ops, ae, te) \ - TxC3(mnem, aop, 0x##top, nops, ops, ae, te) + TxC3 (mnem, aop, 0x##top, nops, ops, ae, te) #define TC3w(mnem, aop, top, nops, ops, ae, te) \ - TxC3w(mnem, aop, 0x##top, nops, ops, ae, te) + TxC3w (mnem, aop, 0x##top, nops, ops, ae, te) #define tC3(mnem, aop, top, nops, ops, ae, te) \ - TxC3(mnem, aop, T_MNEM_##top, nops, ops, ae, te) + TxC3 (mnem, aop, T_MNEM_##top, nops, ops, ae, te) #define tC3w(mnem, aop, top, nops, ops, ae, te) \ - TxC3w(mnem, aop, T_MNEM_##top, nops, ops, ae, te) + TxC3w (mnem, aop, T_MNEM_##top, nops, ops, ae, te) /* Mnemonic with a conditional infix in an unusual place. Each and every variant has to appear in the condition table. */ #define TxCM_(m1, m2, m3, op, top, nops, ops, ae, te) \ - { #m1 #m2 #m3, OPS##nops ops, sizeof(#m2) == 1 ? OT_odd_infix_unc : OT_odd_infix_0 + sizeof(#m1) - 1, \ + { #m1 #m2 #m3, OPS##nops ops, sizeof (#m2) == 1 ? OT_odd_infix_unc : OT_odd_infix_0 + sizeof (#m1) - 1, \ 0x##op, top, ARM_VARIANT, THUMB_VARIANT, do_##ae, do_##te } #define TxCM(m1, m2, op, top, nops, ops, ae, te) \ - TxCM_(m1, , m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, eq, m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, ne, m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, cs, m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, hs, m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, cc, m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, ul, m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, lo, m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, mi, m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, pl, m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, vs, m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, vc, m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, hi, m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, ls, m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, ge, m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, lt, m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, gt, m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, le, m2, op, top, nops, ops, ae, te), \ - TxCM_(m1, al, m2, op, top, nops, ops, ae, te) + TxCM_ (m1, , m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, eq, m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, ne, m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, cs, m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, hs, m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, cc, m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, ul, m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, lo, m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, mi, m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, pl, m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, vs, m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, vc, m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, hi, m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, ls, m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, ge, m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, lt, m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, gt, m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, le, m2, op, top, nops, ops, ae, te), \ + TxCM_ (m1, al, m2, op, top, nops, ops, ae, te) #define TCM(m1,m2, aop, top, nops, ops, ae, te) \ - TxCM(m1,m2, aop, 0x##top, nops, ops, ae, te) -#define tCM(m1,m2, aop, top, nops, ops, ae, te) \ - TxCM(m1,m2, aop, T_MNEM_##top, nops, ops, ae, te) + TxCM (m1,m2, aop, 0x##top, nops, ops, ae, te) +#define tCM(m1,m2, aop, top, nops, ops, ae, te) \ + TxCM (m1,m2, aop, T_MNEM_##top, nops, ops, ae, te) /* Mnemonic that cannot be conditionalized. The ARM condition-code field is still 0xE. Many of the Thumb variants can be executed @@ -15327,29 +15791,29 @@ static struct asm_barrier_opt barrier_opt_names[] = #define xCM_(m1, m2, m3, op, nops, ops, ae) \ { #m1 #m2 #m3, OPS##nops ops, \ - sizeof(#m2) == 1 ? OT_odd_infix_unc : OT_odd_infix_0 + sizeof(#m1) - 1, \ + sizeof (#m2) == 1 ? OT_odd_infix_unc : OT_odd_infix_0 + sizeof (#m1) - 1, \ 0x##op, 0x0, ARM_VARIANT, 0, do_##ae, NULL } #define CM(m1, m2, op, nops, ops, ae) \ - xCM_(m1, , m2, op, nops, ops, ae), \ - xCM_(m1, eq, m2, op, nops, ops, ae), \ - xCM_(m1, ne, m2, op, nops, ops, ae), \ - xCM_(m1, cs, m2, op, nops, ops, ae), \ - xCM_(m1, hs, m2, op, nops, ops, ae), \ - xCM_(m1, cc, m2, op, nops, ops, ae), \ - xCM_(m1, ul, m2, op, nops, ops, ae), \ - xCM_(m1, lo, m2, op, nops, ops, ae), \ - xCM_(m1, mi, m2, op, nops, ops, ae), \ - xCM_(m1, pl, m2, op, nops, ops, ae), \ - xCM_(m1, vs, m2, op, nops, ops, ae), \ - xCM_(m1, vc, m2, op, nops, ops, ae), \ - xCM_(m1, hi, m2, op, nops, ops, ae), \ - xCM_(m1, ls, m2, op, nops, ops, ae), \ - xCM_(m1, ge, m2, op, nops, ops, ae), \ - xCM_(m1, lt, m2, op, nops, ops, ae), \ - xCM_(m1, gt, m2, op, nops, ops, ae), \ - xCM_(m1, le, m2, op, nops, ops, ae), \ - xCM_(m1, al, m2, op, nops, ops, ae) + xCM_ (m1, , m2, op, nops, ops, ae), \ + xCM_ (m1, eq, m2, op, nops, ops, ae), \ + xCM_ (m1, ne, m2, op, nops, ops, ae), \ + xCM_ (m1, cs, m2, op, nops, ops, ae), \ + xCM_ (m1, hs, m2, op, nops, ops, ae), \ + xCM_ (m1, cc, m2, op, nops, ops, ae), \ + xCM_ (m1, ul, m2, op, nops, ops, ae), \ + xCM_ (m1, lo, m2, op, nops, ops, ae), \ + xCM_ (m1, mi, m2, op, nops, ops, ae), \ + xCM_ (m1, pl, m2, op, nops, ops, ae), \ + xCM_ (m1, vs, m2, op, nops, ops, ae), \ + xCM_ (m1, vc, m2, op, nops, ops, ae), \ + xCM_ (m1, hi, m2, op, nops, ops, ae), \ + xCM_ (m1, ls, m2, op, nops, ops, ae), \ + xCM_ (m1, ge, m2, op, nops, ops, ae), \ + xCM_ (m1, lt, m2, op, nops, ops, ae), \ + xCM_ (m1, gt, m2, op, nops, ops, ae), \ + xCM_ (m1, le, m2, op, nops, ops, ae), \ + xCM_ (m1, al, m2, op, nops, ops, ae) #define UE(mnem, op, nops, ops, ae) \ { #mnem, OPS##nops ops, OT_unconditional, 0x##op, 0, ARM_VARIANT, 0, do_##ae, NULL } @@ -15377,10 +15841,10 @@ static struct asm_barrier_opt barrier_opt_names[] = THUMB_VARIANT, do_##enc, do_##enc } #define NCE(mnem, op, nops, ops, enc) \ - NCE_tag(mnem, op, nops, ops, enc, OT_csuffix) + NCE_tag (mnem, op, nops, ops, enc, OT_csuffix) #define NCEF(mnem, op, nops, ops, enc) \ - NCE_tag(mnem, op, nops, ops, enc, OT_csuffixF) + NCE_tag (mnem, op, nops, ops, enc, OT_csuffixF) /* Neon insn with conditional suffix for the ARM version, overloaded types. */ #define nCE_tag(mnem, op, nops, ops, enc, tag) \ @@ -15388,15 +15852,15 @@ static struct asm_barrier_opt barrier_opt_names[] = ARM_VARIANT, THUMB_VARIANT, do_##enc, do_##enc } #define nCE(mnem, op, nops, ops, enc) \ - nCE_tag(mnem, op, nops, ops, enc, OT_csuffix) + nCE_tag (mnem, op, nops, ops, enc, OT_csuffix) #define nCEF(mnem, op, nops, ops, enc) \ - nCE_tag(mnem, op, nops, ops, enc, OT_csuffixF) + nCE_tag (mnem, op, nops, ops, enc, OT_csuffixF) #define do_0 0 /* Thumb-only, unconditional. */ -#define UT(mnem, op, nops, ops, te) TUE(mnem, 0, op, nops, ops, 0, te) +#define UT(mnem, op, nops, ops, te) TUE (mnem, 0, op, nops, ops, 0, te) static const struct asm_opcode insns[] = { @@ -15818,24 +16282,26 @@ static const struct asm_opcode insns[] = UT(cbnz, b900, 2, (RR, EXP), t_cbz), UT(cbz, b100, 2, (RR, EXP), t_cbz), - /* ARM does not really have an IT instruction, so always allow it. */ + /* ARM does not really have an IT instruction, so always allow it. The opcode + is copied from Thumb in order to allow warnings + in -mimplicit-it=[never | arm] modes. */ #undef ARM_VARIANT #define ARM_VARIANT &arm_ext_v1 - TUE(it, 0, bf08, 1, (COND), it, t_it), - TUE(itt, 0, bf0c, 1, (COND), it, t_it), - TUE(ite, 0, bf04, 1, (COND), it, t_it), - TUE(ittt, 0, bf0e, 1, (COND), it, t_it), - TUE(itet, 0, bf06, 1, (COND), it, t_it), - TUE(itte, 0, bf0a, 1, (COND), it, t_it), - TUE(itee, 0, bf02, 1, (COND), it, t_it), - TUE(itttt, 0, bf0f, 1, (COND), it, t_it), - TUE(itett, 0, bf07, 1, (COND), it, t_it), - TUE(ittet, 0, bf0b, 1, (COND), it, t_it), - TUE(iteet, 0, bf03, 1, (COND), it, t_it), - TUE(ittte, 0, bf0d, 1, (COND), it, t_it), - TUE(itete, 0, bf05, 1, (COND), it, t_it), - TUE(ittee, 0, bf09, 1, (COND), it, t_it), - TUE(iteee, 0, bf01, 1, (COND), it, t_it), + TUE(it, bf08, bf08, 1, (COND), it, t_it), + TUE(itt, bf0c, bf0c, 1, (COND), it, t_it), + TUE(ite, bf04, bf04, 1, (COND), it, t_it), + TUE(ittt, bf0e, bf0e, 1, (COND), it, t_it), + TUE(itet, bf06, bf06, 1, (COND), it, t_it), + TUE(itte, bf0a, bf0a, 1, (COND), it, t_it), + TUE(itee, bf02, bf02, 1, (COND), it, t_it), + TUE(itttt, bf0f, bf0f, 1, (COND), it, t_it), + TUE(itett, bf07, bf07, 1, (COND), it, t_it), + TUE(ittet, bf0b, bf0b, 1, (COND), it, t_it), + TUE(iteet, bf03, bf03, 1, (COND), it, t_it), + TUE(ittte, bf0d, bf0d, 1, (COND), it, t_it), + TUE(itete, bf05, bf05, 1, (COND), it, t_it), + TUE(ittee, bf09, bf09, 1, (COND), it, t_it), + TUE(iteee, bf01, bf01, 1, (COND), it, t_it), /* ARM/Thumb-2 instructions with no Thumb-1 equivalent. */ TC3(rrx, 01a00060, ea4f0030, 2, (RR, RR), rd_rm, t_rrx), TC3(rrxs, 01b00060, ea5f0030, 2, (RR, RR), rd_rm, t_rrx), @@ -20246,15 +20712,14 @@ armelf_frob_symbol (symbolS * symp, /* MD interface: Finalization. */ -/* A good place to do this, although this was probably not intended - for this kind of use. We need to dump the literal pool before - references are made to a null symbol pointer. */ - void arm_cleanup (void) { literal_pool * pool; + /* Ensure that all the IT blocks are properly closed. */ + check_it_blocks_finished (); + for (pool = list_of_pools; pool; pool = pool->next) { /* Put it at the end of the relevant section. */ @@ -20927,10 +21392,10 @@ static const struct arm_cpu_option_table arm_cpus[] = {"arm1156t2f-s", ARM_ARCH_V6T2, FPU_ARCH_VFP_V2, NULL}, {"arm1176jz-s", ARM_ARCH_V6ZK, FPU_NONE, NULL}, {"arm1176jzf-s", ARM_ARCH_V6ZK, FPU_ARCH_VFP_V2, NULL}, - {"cortex-a8", ARM_ARCH_V7A, ARM_FEATURE(0, FPU_VFP_V3 + {"cortex-a8", ARM_ARCH_V7A, ARM_FEATURE (0, FPU_VFP_V3 | FPU_NEON_EXT_V1), NULL}, - {"cortex-a9", ARM_ARCH_V7A, ARM_FEATURE(0, FPU_VFP_V3 + {"cortex-a9", ARM_ARCH_V7A, ARM_FEATURE (0, FPU_VFP_V3 | FPU_NEON_EXT_V1), NULL}, {"cortex-r4", ARM_ARCH_V7R, FPU_NONE, NULL}, @@ -20944,7 +21409,7 @@ static const struct arm_cpu_option_table arm_cpus[] = {"iwmmxt2", ARM_ARCH_IWMMXT2,FPU_ARCH_VFP_V2, NULL}, {"i80200", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL}, /* Maverick */ - {"ep9312", ARM_FEATURE(ARM_AEXT_V4T, ARM_CEXT_MAVERICK), FPU_ARCH_MAVERICK, "ARM920T"}, + {"ep9312", ARM_FEATURE (ARM_AEXT_V4T, ARM_CEXT_MAVERICK), FPU_ARCH_MAVERICK, "ARM920T"}, {NULL, ARM_ARCH_NONE, ARM_ARCH_NONE, NULL} }; @@ -21264,6 +21729,29 @@ arm_parse_eabi (char * str) } #endif +static int +arm_parse_it_mode (char * str) +{ + int ret = 1; + + if (streq ("arm", str)) + implicit_it_mode = IMPLICIT_IT_MODE_ARM; + else if (streq ("thumb", str)) + implicit_it_mode = IMPLICIT_IT_MODE_THUMB; + else if (streq ("always", str)) + implicit_it_mode = IMPLICIT_IT_MODE_ALWAYS; + else if (streq ("never", str)) + implicit_it_mode = IMPLICIT_IT_MODE_NEVER; + else + { + as_bad (_("unknown implicit IT mode `%s', should be "\ + "arm, thumb, always, or never."), str); + ret = 0; + } + + return ret; +} + struct arm_long_option_table arm_long_opts[] = { {"mcpu=", N_("\t assemble for CPU "), @@ -21278,6 +21766,8 @@ struct arm_long_option_table arm_long_opts[] = {"meabi=", N_("\t\t assemble for eabi version "), arm_parse_eabi, NULL}, #endif + {"mimplicit-it=", N_("\t controls implicit insertion of IT instructions"), + arm_parse_it_mode, NULL}, {NULL, NULL, 0, NULL} }; diff --git a/gas/config/tc-arm.h b/gas/config/tc-arm.h index 13bc86ab799..05841df9bca 100644 --- a/gas/config/tc-arm.h +++ b/gas/config/tc-arm.h @@ -82,7 +82,7 @@ struct fix; #define TC_FORCE_RELOCATION(FIX) arm_force_relocation (FIX) #define md_relax_frag(segment, fragp, stretch) \ - arm_relax_frag(segment, fragp, stretch) + arm_relax_frag (segment, fragp, stretch) extern int arm_relax_frag (asection *, struct frag *, long); #define md_optimize_expr(l,o,r) arm_optimize_expr (l, o, r) @@ -123,6 +123,7 @@ bfd_boolean arm_is_eabi (void); #define ARM_IS_THUMB(s) (ARM_GET_FLAG (s) & ARM_FLAG_THUMB) #define ARM_IS_INTERWORK(s) (ARM_GET_FLAG (s) & ARM_FLAG_INTERWORK) + #ifdef OBJ_ELF /* For ELF objects THUMB_IS_FUNC is inferred from @@ -141,6 +142,7 @@ bfd_boolean arm_is_eabi (void); #else + #define THUMB_IS_FUNC(s) (ARM_GET_FLAG (s) & THUMB_FLAG_FUNC) #define ARM_IS_FUNC(s) (!THUMB_IS_FUNC (s) \ && (symbol_get_bfdsym (s)->flags & BSF_FUNCTION)) @@ -151,6 +153,7 @@ bfd_boolean arm_is_eabi (void); #define THUMB_SET_FUNC(s,t) ((t) ? ARM_SET_FLAG (s, THUMB_FLAG_FUNC) : ARM_RESET_FLAG (s, THUMB_FLAG_FUNC)) void arm_copy_symbol_attributes (symbolS *, symbolS *); + #ifndef TC_COPY_SYMBOL_ATTRIBUTES #define TC_COPY_SYMBOL_ATTRIBUTES(DEST, SRC) \ (arm_copy_symbol_attributes (DEST, SRC)) @@ -212,6 +215,21 @@ void arm_copy_symbol_attributes (symbolS *, symbolS *); /* Registers are generally saved at negative offsets to the CFA. */ #define DWARF2_CIE_DATA_ALIGNMENT (-4) +/* State variables for IT block handling. */ +enum it_state +{ + OUTSIDE_IT_BLOCK, MANUAL_IT_BLOCK, AUTOMATIC_IT_BLOCK +}; +struct current_it +{ + int mask; + enum it_state state; + int cc; + int block_length; + char *insn; + int state_handled; +}; + #ifdef OBJ_ELF # define obj_frob_symbol(sym, punt) armelf_frob_symbol ((sym), & (punt)) # define md_elf_section_change_hook() arm_elf_change_section () @@ -239,6 +257,7 @@ struct arm_segment_info_type { enum mstate mapstate; unsigned int marked_pr_dependency; + struct current_it current_it; }; /* We want .cfi_* pseudo-ops for generating unwind info. */ diff --git a/gas/doc/c-arm.texi b/gas/doc/c-arm.texi index cc9770042c3..7accce818e2 100644 --- a/gas/doc/c-arm.texi +++ b/gas/doc/c-arm.texi @@ -227,6 +227,11 @@ instructions; that is, it should behave as though the file starts with a This option specifies that the output generated by the assembler should be marked as supporting interworking. +@cindex @code{-mauto-it} command line option, ARM +@item -mauto-it +This option enables the automatic generation of IT instructions for +conditional instructions not covered by an IT block. + @cindex @code{-mapcs} command line option, ARM @item -mapcs @code{[26|32]} This option specifies that the output generated by the assembler should diff --git a/gas/testsuite/ChangeLog b/gas/testsuite/ChangeLog index bb400279cae..9ab0ef38af5 100644 --- a/gas/testsuite/ChangeLog +++ b/gas/testsuite/ChangeLog @@ -1,3 +1,26 @@ +2009-06-22 Daniel Gutson + + * gas/arm/arm-it-auto.d: New test. + * gas/arm/arm-it-auto.s: New file. + * gas/arm/arm-it-auto-2.d: New test case. + * gas/arm/arm-it-auto-2.s: New file. + * gas/arm/arm-it-auto-3.d: New test case. + * gas/arm/arm-it-auto-3.s: New file. + * gas/arm/arm-it-bad.d: New test case. + * gas/arm/arm-it-bad.l: New file. + * gas/arm/arm-it-bad.s: New file. + * gas/arm/arm-it-bad-2.d: New test case. + * gas/arm/arm-it-bad-2.l: New file. + * gas/arm/arm-it-bad-2.s: New file. + * gas/arm/arm-it-bad-3.d: New test case. + * gas/arm/arm-it-bad-3.l: New file. + * gas/arm/arm-it-bad-3.s: New file. + * gas/arm/thumb2_it_auto.d: New test. + * gas/arm/thumb2_it_bad.l: Error message updated. + * gas/arm/thumb2_it_bad_auto.d: New test. + * gas/arm/thumb2_it.d: Comment added. + * gas/arm/thumb2_it_bad.d: Comment added. + 2009-06-19 Alan Modra * gas/cfi/cfi-common-7.s: Skip a multiple of four. diff --git a/gas/testsuite/gas/arm/thumb2_it.d b/gas/testsuite/gas/arm/thumb2_it.d index 30a390bbb0a..6e6bdb803c5 100644 --- a/gas/testsuite/gas/arm/thumb2_it.d +++ b/gas/testsuite/gas/arm/thumb2_it.d @@ -1,62 +1,62 @@ # name: Mixed 16 and 32-bit Thumb conditional instructions # as: -march=armv6kt2 +#skip: *-*-*aout* # objdump: -dr --prefix-addresses --show-raw-insn -# Many of these patterns use "(eq|s)". These should be changed to just "eq" -# once the disassembler is fixed. Likewise for "(eq)?" +# Modifications to this file shall be mirrored to thumb2_it_auto.d .*: +file format .*arm.* Disassembly of section .text: 0+000 <[^>]+> bf05 ittet eq -0+002 <[^>]+> 1880 add(eq|s) r0, r0, r2 -0+004 <[^>]+> 4440 add(eq)? r0, r8 -0+006 <[^>]+> 1888 add(ne|s) r0, r1, r2 -0+008 <[^>]+> eb11 0002 adds(eq)?.w r0, r1, r2 +0+002 <[^>]+> 1880 addeq r0, r0, r2 +0+004 <[^>]+> 4440 addeq r0, r8 +0+006 <[^>]+> 1888 addne r0, r1, r2 +0+008 <[^>]+> eb11 0002 addseq.w r0, r1, r2 0+00c <[^>]+> 4410 add r0, r2 0+00e <[^>]+> 4440 add r0, r8 0+010 <[^>]+> 1880 adds r0, r0, r2 0+012 <[^>]+> eb10 0008 adds.w r0, r0, r8 0+016 <[^>]+> 1888 adds r0, r1, r2 0+018 <[^>]+> bf0a itet eq -0+01a <[^>]+> 4310 orr(eq|s) r0, r2 -0+01c <[^>]+> ea40 0008 orr(ne)?.w r0, r0, r8 -0+020 <[^>]+> ea50 0002 orrs(eq)?.w r0, r0, r2 +0+01a <[^>]+> 4310 orreq r0, r2 +0+01c <[^>]+> ea40 0008 orrne.w r0, r0, r8 +0+020 <[^>]+> ea50 0002 orrseq.w r0, r0, r2 0+024 <[^>]+> ea40 0002 orr.w r0, r0, r2 0+028 <[^>]+> ea40 0008 orr.w r0, r0, r8 0+02c <[^>]+> 4310 orrs r0, r2 0+02e <[^>]+> bf01 itttt eq -0+030 <[^>]+> 4090 lsl(eq|s) r0, r2 -0+032 <[^>]+> fa00 f008 lsl(eq)?.w r0, r0, r8 -0+036 <[^>]+> fa01 f002 lsl(eq)?.w r0, r1, r2 -0+03a <[^>]+> fa10 f002 lsls(eq)?.w r0, r0, r2 +0+030 <[^>]+> 4090 lsleq r0, r2 +0+032 <[^>]+> fa00 f008 lsleq.w r0, r0, r8 +0+036 <[^>]+> fa01 f002 lsleq.w r0, r1, r2 +0+03a <[^>]+> fa10 f002 lslseq.w r0, r0, r2 0+03e <[^>]+> bf02 ittt eq -0+040 <[^>]+> 0048 lsl(eq|s) r0, r1, #1 -0+042 <[^>]+> ea4f 0048 mov(eq)?.w r0, r8, lsl #1 -0+046 <[^>]+> ea5f 0040 movs(eq)?.w r0, r0, lsl #1 +0+040 <[^>]+> 0048 lsleq r0, r1, #1 +0+042 <[^>]+> ea4f 0048 moveq.w r0, r8, lsl #1 +0+046 <[^>]+> ea5f 0040 movseq.w r0, r0, lsl #1 0+04a <[^>]+> fa00 f002 lsl.w r0, r0, r2 0+04e <[^>]+> 4090 lsls r0, r2 0+050 <[^>]+> ea4f 0041 mov.w r0, r1, lsl #1 0+054 <[^>]+> 0048 lsls r0, r1, #1 0+056 <[^>]+> bf01 itttt eq -0+058 <[^>]+> 4288 cmp(eq)? r0, r1 -0+05a <[^>]+> 4540 cmp(eq)? r0, r8 -0+05c <[^>]+> 4608 mov(eq)? r0, r1 -0+05e <[^>]+> ea5f 0001 movs(eq)?.w r0, r1 +0+058 <[^>]+> 4288 cmpeq r0, r1 +0+05a <[^>]+> 4540 cmpeq r0, r8 +0+05c <[^>]+> 4608 moveq r0, r1 +0+05e <[^>]+> ea5f 0001 movseq.w r0, r1 0+062 <[^>]+> bf08 it eq -0+064 <[^>]+> 4640 mov(eq)? r0, r8 -0+066 <[^>]+> 4608 mov(eq)? r0, r1 +0+064 <[^>]+> 4640 moveq r0, r8 +0+066 <[^>]+> 4608 mov r0, r1 0+068 <[^>]+> 1c08 adds r0, r1, #0 0+06a <[^>]+> ea5f 0008 movs.w r0, r8 0+06e <[^>]+> bf01 itttt eq -0+070 <[^>]+> 43c8 mvn(eq|s) r0, r1 -0+072 <[^>]+> ea6f 0008 mvn(eq)?.w r0, r8 -0+076 <[^>]+> ea7f 0001 mvns(eq)?.w r0, r1 -0+07a <[^>]+> 42c8 cmn(eq)? r0, r1 +0+070 <[^>]+> 43c8 mvneq r0, r1 +0+072 <[^>]+> ea6f 0008 mvneq.w r0, r8 +0+076 <[^>]+> ea7f 0001 mvnseq.w r0, r1 +0+07a <[^>]+> 42c8 cmneq r0, r1 0+07c <[^>]+> ea6f 0001 mvn.w r0, r1 0+080 <[^>]+> 43c8 mvns r0, r1 0+082 <[^>]+> bf02 ittt eq -0+084 <[^>]+> 4248 neg(eq|s) r0, r1 -0+086 <[^>]+> f1c8 0000 rsb(eq)? r0, r8, #0 ; 0x0 -0+08a <[^>]+> f1d1 0000 rsbs(eq)? r0, r1, #0 ; 0x0 +0+084 <[^>]+> 4248 negeq r0, r1 +0+086 <[^>]+> f1c8 0000 rsbeq r0, r8, #0 ; 0x0 +0+08a <[^>]+> f1d1 0000 rsbseq r0, r1, #0 ; 0x0 0+08e <[^>]+> f1c1 0000 rsb r0, r1, #0 ; 0x0 0+092 <[^>]+> 4248 negs r0, r1 diff --git a/gas/testsuite/gas/arm/thumb2_it_bad.d b/gas/testsuite/gas/arm/thumb2_it_bad.d index 1cca8b9650c..0b841ed1852 100644 --- a/gas/testsuite/gas/arm/thumb2_it_bad.d +++ b/gas/testsuite/gas/arm/thumb2_it_bad.d @@ -1,3 +1,4 @@ #name: Invalid IT instructions #as: #error-output: thumb2_it_bad.l +# Modifications to this test shall be mirrored to thumb2_it_bad_auto.d. diff --git a/gas/testsuite/gas/arm/thumb2_it_bad.l b/gas/testsuite/gas/arm/thumb2_it_bad.l index e2e96cddb43..aa1f65874b9 100644 --- a/gas/testsuite/gas/arm/thumb2_it_bad.l +++ b/gas/testsuite/gas/arm/thumb2_it_bad.l @@ -9,4 +9,4 @@ [^:]*:17: Error: instruction not allowed in IT block -- `cpseq #0x10' [^:]*:19: Error: instruction is always unconditional -- `bkpteq 0' [^:]*:20: Error: instruction not allowed in IT block -- `setendeq le' -[^:]*:22: Error: instruction not allowed in IT block -- `iteq eq' +[^:]*:22: Error: IT falling in the range of a previous IT block -- `iteq eq' -- 2.30.2