+ for (j = 0; fm->operands[j]; j++)
+ if (myops[j].X_op == O_register
+ && (myops[j].X_add_number & 1)
+ && (d30v_operand_table[fm->operands[j]].flags & OPERAND_2REG))
+ as_warn (_("Odd numbered register used as target of multi-register instruction"));
+
+ return fm;
+ }
+ fm = (struct d30v_format *) &d30v_format_table[++k];
+ }
+ }
+ return NULL;
+}
+
+/* Assemble a single instruction and return an opcode.
+ Return -1 (an invalid opcode) on error. */
+
+#define NAME_BUF_LEN 20
+
+static long long
+do_assemble (char *str,
+ struct d30v_insn *opcode,
+ int shortp,
+ int is_parallel)
+{
+ char *op_start;
+ char *save;
+ char *op_end;
+ char name[NAME_BUF_LEN];
+ int cmp_hack;
+ int nlen = 0;
+ int fsize = (shortp ? FORCE_SHORT : 0);
+ expressionS myops[6];
+ long long insn;
+
+ /* Drop leading whitespace. */
+ while (*str == ' ')
+ str++;
+
+ /* Find the opcode end. */
+ for (op_start = op_end = str;
+ *op_end
+ && nlen < (NAME_BUF_LEN - 1)
+ && *op_end != '/'
+ && !is_end_of_line[(unsigned char) *op_end] && *op_end != ' ';
+ op_end++)
+ {
+ name[nlen] = TOLOWER (op_start[nlen]);
+ nlen++;
+ }
+
+ if (nlen == 0)
+ return -1;
+
+ name[nlen] = 0;
+
+ /* If there is an execution condition code, handle it. */
+ if (*op_end == '/')
+ {
+ int i = 0;
+ while ((i < ECC_MAX) && strncasecmp (d30v_ecc_names[i], op_end + 1, 2))
+ i++;
+
+ if (i == ECC_MAX)
+ {
+ char tmp[4];
+ strncpy (tmp, op_end + 1, 2);
+ tmp[2] = 0;
+ as_bad (_("unknown condition code: %s"), tmp);
+ return -1;
+ }
+ opcode->ecc = i;
+ op_end += 3;
+ }
+ else
+ opcode->ecc = ECC_AL;
+
+ /* CMP and CMPU change their name based on condition codes. */
+ if (!strncmp (name, "cmp", 3))
+ {
+ int p, i;
+ char **d30v_str = (char **) d30v_cc_names;
+
+ if (name[3] == 'u')
+ p = 4;
+ else
+ p = 3;
+
+ for (i = 1; *d30v_str && strncmp (*d30v_str, &name[p], 2); i++, d30v_str++)
+ ;
+
+ /* cmpu only supports some condition codes. */
+ if (p == 4)
+ {
+ if (i < 3 || i > 6)
+ {
+ name[p + 2] = 0;
+ as_bad (_("cmpu doesn't support condition code %s"), &name[p]);
+ }
+ }
+
+ if (!*d30v_str)
+ {
+ name[p + 2] = 0;
+ as_bad (_("unknown condition code: %s"), &name[p]);
+ }
+
+ cmp_hack = i;
+ name[p] = 0;
+ }
+ else
+ cmp_hack = 0;
+
+ /* Need to look for .s or .l. */
+ if (name[nlen - 2] == '.')
+ {
+ switch (name[nlen - 1])
+ {
+ case 's':
+ fsize = FORCE_SHORT;
+ break;
+ case 'l':
+ fsize = FORCE_LONG;
+ break;
+ }
+ name[nlen - 2] = 0;
+ }
+
+ /* Find the first opcode with the proper name. */
+ opcode->op = (struct d30v_opcode *) hash_find (d30v_hash, name);
+ if (opcode->op == NULL)
+ {
+ as_bad (_("unknown opcode: %s"), name);
+ return -1;
+ }
+
+ save = input_line_pointer;
+ input_line_pointer = op_end;
+ while (!(opcode->form = find_format (opcode->op, myops, fsize, cmp_hack)))
+ {
+ opcode->op++;
+ if (opcode->op->name == NULL || strcmp (opcode->op->name, name))
+ {
+ as_bad (_("operands for opcode `%s' do not match any valid format"),
+ name);
+ return -1;
+ }
+ }
+ input_line_pointer = save;
+
+ insn = build_insn (opcode, myops);
+
+ /* Propagate multiply status. */
+ if (insn != -1)
+ {
+ if (is_parallel && prev_mul32_p)
+ cur_mul32_p = 1;
+ else
+ {
+ prev_mul32_p = cur_mul32_p;
+ cur_mul32_p = (opcode->op->flags_used & FLAG_MUL32) != 0;
+ }
+ }
+
+ /* Propagate left_kills_right status. */
+ if (insn != -1)
+ {
+ prev_left_kills_right_p = cur_left_kills_right_p;
+
+ if (opcode->op->flags_set & FLAG_LKR)
+ {
+ cur_left_kills_right_p = 1;
+
+ if (strcmp (opcode->op->name, "mvtsys") == 0)
+ {
+ /* Left kills right for only mvtsys only for
+ PSW/PSWH/PSWL/flags target. */
+ if ((myops[0].X_op == O_register) &&
+ ((myops[0].X_add_number == OPERAND_CONTROL) || /* psw */
+ (myops[0].X_add_number == OPERAND_CONTROL+MAX_CONTROL_REG+2) || /* pswh */
+ (myops[0].X_add_number == OPERAND_CONTROL+MAX_CONTROL_REG+1) || /* pswl */
+ (myops[0].X_add_number == OPERAND_FLAG+0) || /* f0 */
+ (myops[0].X_add_number == OPERAND_FLAG+1) || /* f1 */
+ (myops[0].X_add_number == OPERAND_FLAG+2) || /* f2 */
+ (myops[0].X_add_number == OPERAND_FLAG+3) || /* f3 */
+ (myops[0].X_add_number == OPERAND_FLAG+4) || /* f4 */
+ (myops[0].X_add_number == OPERAND_FLAG+5) || /* f5 */
+ (myops[0].X_add_number == OPERAND_FLAG+6) || /* f6 */
+ (myops[0].X_add_number == OPERAND_FLAG+7))) /* f7 */
+ {
+ cur_left_kills_right_p = 1;
+ }
+ else
+ {
+ /* Other mvtsys target registers don't kill right
+ instruction. */
+ cur_left_kills_right_p = 0;
+ }
+ } /* mvtsys */
+ }
+ else
+ cur_left_kills_right_p = 0;
+ }
+
+ return insn;
+}
+
+/* Called internally to handle all alignment needs. This takes care
+ of eliding calls to frag_align if'n the cached current alignment
+ says we've already got it, as well as taking care of the auto-aligning
+ labels wrt code. */
+
+static void
+d30v_align (int n, char *pfill, symbolS *label)
+{
+ /* The front end is prone to changing segments out from under us
+ temporarily when -g is in effect. */
+ int switched_seg_p = (d30v_current_align_seg != now_seg);
+
+ /* Do not assume that if 'd30v_current_align >= n' and
+ '! switched_seg_p' that it is safe to avoid performing
+ this alignment request. The alignment of the current frag
+ can be changed under our feet, for example by a .ascii
+ directive in the source code. cf testsuite/gas/d30v/reloc.s */
+ d30v_cleanup (FALSE);
+
+ if (pfill == NULL)
+ {
+ if (n > 2
+ && (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
+ {
+ static char const nop[4] = { 0x00, 0xf0, 0x00, 0x00 };
+
+ /* First, make sure we're on a four-byte boundary, in case
+ someone has been putting .byte values the text section. */
+ if (d30v_current_align < 2 || switched_seg_p)
+ frag_align (2, 0, 0);
+ frag_align_pattern (n, nop, sizeof nop, 0);
+ }
+ else
+ frag_align (n, 0, 0);
+ }
+ else
+ frag_align (n, *pfill, 0);
+
+ if (!switched_seg_p)
+ d30v_current_align = n;
+
+ if (label != NULL)
+ {
+ symbolS *sym;
+ int label_seen = FALSE;
+ struct frag *old_frag;
+ valueT old_value;
+ valueT new_value;
+
+ gas_assert (S_GET_SEGMENT (label) == now_seg);
+
+ old_frag = symbol_get_frag (label);
+ old_value = S_GET_VALUE (label);
+ new_value = (valueT) frag_now_fix ();
+
+ /* It is possible to have more than one label at a particular
+ address, especially if debugging is enabled, so we must
+ take care to adjust all the labels at this address in this
+ fragment. To save time we search from the end of the symbol
+ list, backwards, since the symbols we are interested in are
+ almost certainly the ones that were most recently added.
+ Also to save time we stop searching once we have seen at least
+ one matching label, and we encounter a label that is no longer
+ in the target fragment. Note, this search is guaranteed to
+ find at least one match when sym == label, so no special case
+ code is necessary. */
+ for (sym = symbol_lastP; sym != NULL; sym = symbol_previous (sym))
+ {
+ if (symbol_get_frag (sym) == old_frag
+ && S_GET_VALUE (sym) == old_value)
+ {
+ label_seen = TRUE;
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, new_value);
+ }
+ else if (label_seen && symbol_get_frag (sym) != old_frag)
+ break;
+ }
+ }
+
+ record_alignment (now_seg, n);
+}
+
+/* This is the main entry point for the machine-dependent assembler.
+ STR points to a machine-dependent instruction. This function is
+ supposed to emit the frags/bytes it assembles to. For the D30V, it
+ mostly handles the special VLIW parsing and packing and leaves the
+ difficult stuff to do_assemble (). */
+
+static long long prev_insn = -1;
+static struct d30v_insn prev_opcode;
+static subsegT prev_subseg;
+static segT prev_seg = 0;
+
+void
+md_assemble (char *str)
+{
+ struct d30v_insn opcode;
+ long long insn;
+ /* Execution type; parallel, etc. */
+ exec_type_enum extype = EXEC_UNKNOWN;
+ /* Saved extype. Used for multiline instructions. */
+ static exec_type_enum etype = EXEC_UNKNOWN;
+ char *str2;
+
+ if ((prev_insn != -1) && prev_seg
+ && ((prev_seg != now_seg) || (prev_subseg != now_subseg)))
+ d30v_cleanup (FALSE);
+
+ if (d30v_current_align < 3)
+ d30v_align (3, NULL, d30v_last_label);
+ else if (d30v_current_align > 3)
+ d30v_current_align = 3;
+ d30v_last_label = NULL;
+
+ flag_explicitly_parallel = 0;
+ flag_xp_state = 0;
+ if (etype == EXEC_UNKNOWN)
+ {
+ /* Look for the special multiple instruction separators. */
+ str2 = strstr (str, "||");
+ if (str2)
+ {
+ extype = EXEC_PARALLEL;
+ flag_xp_state = 1;
+ }
+ else
+ {
+ str2 = strstr (str, "->");
+ if (str2)
+ extype = EXEC_SEQ;
+ else
+ {
+ str2 = strstr (str, "<-");
+ if (str2)
+ extype = EXEC_REVSEQ;
+ }
+ }
+
+ /* STR2 points to the separator, if one. */
+ if (str2)
+ {
+ *str2 = 0;
+
+ /* If two instructions are present and we already have one saved,
+ then first write it out. */
+ d30v_cleanup (FALSE);
+
+ /* Assemble first instruction and save it. */
+ prev_insn = do_assemble (str, &prev_opcode, 1, 0);
+ if (prev_insn == -1)
+ as_bad (_("Cannot assemble instruction"));
+ if (prev_opcode.form != NULL && prev_opcode.form->form >= LONG)
+ as_bad (_("First opcode is long. Unable to mix instructions as specified."));
+ fixups = fixups->next;
+ str = str2 + 2;
+ prev_seg = now_seg;
+ prev_subseg = now_subseg;
+ }
+ }
+
+ insn = do_assemble (str, &opcode,
+ (extype != EXEC_UNKNOWN || etype != EXEC_UNKNOWN),
+ extype == EXEC_PARALLEL);
+ if (insn == -1)
+ {
+ if (extype != EXEC_UNKNOWN)
+ etype = extype;
+ as_bad (_("Cannot assemble instruction"));
+ return;
+ }
+
+ if (etype != EXEC_UNKNOWN)
+ {
+ extype = etype;
+ etype = EXEC_UNKNOWN;
+ }
+
+ /* Word multiply instructions must not be followed by either a load or a
+ 16-bit multiply instruction in the next cycle. */
+ if ( (extype != EXEC_REVSEQ)
+ && prev_mul32_p
+ && (opcode.op->flags_used & (FLAG_MEM | FLAG_MUL16)))
+ {
+ /* However, load and multiply should able to be combined in a parallel
+ operation, so check for that first. */
+ if (prev_insn != -1
+ && (opcode.op->flags_used & FLAG_MEM)
+ && opcode.form->form < LONG
+ && (extype == EXEC_PARALLEL || (Optimizing && extype == EXEC_UNKNOWN))
+ && parallel_ok (&prev_opcode, (long) prev_insn,
+ &opcode, (long) insn, extype)
+ && write_2_short (&prev_opcode, (long) prev_insn,
+ &opcode, (long) insn, extype, fixups) == 0)
+ {
+ /* No instructions saved. */
+ prev_insn = -1;
+ return;
+ }
+ else
+ {
+ /* Can't parallelize, flush previous instruction and emit a
+ word of NOPS, unless the previous instruction is a NOP,
+ in which case just flush it, as this will generate a word
+ of NOPs for us. */
+
+ if (prev_insn != -1 && (strcmp (prev_opcode.op->name, "nop") == 0))
+ d30v_cleanup (FALSE);
+ else
+ {
+ char *f;
+
+ if (prev_insn != -1)
+ d30v_cleanup (TRUE);
+ else
+ {
+ f = frag_more (8);
+ dwarf2_emit_insn (8);
+ d30v_number_to_chars (f, NOP2, 8);
+
+ if (warn_nops == NOP_ALL || warn_nops == NOP_MULTIPLY)
+ {
+ if (opcode.op->flags_used & FLAG_MEM)
+ as_warn (_("word of NOPs added between word multiply and load"));
+ else
+ as_warn (_("word of NOPs added between word multiply and 16-bit multiply"));
+ }
+ }
+ }
+
+ extype = EXEC_UNKNOWN;
+ }
+ }
+ else if ( (extype == EXEC_REVSEQ)
+ && cur_mul32_p
+ && (prev_opcode.op->flags_used & (FLAG_MEM | FLAG_MUL16)))
+ {
+ /* Can't parallelize, flush current instruction and add a
+ sequential NOP. */
+ write_1_short (&opcode, (long) insn, fixups->next->next, TRUE);
+
+ /* Make the previous instruction the current one. */
+ extype = EXEC_UNKNOWN;
+ insn = prev_insn;
+ now_seg = prev_seg;
+ now_subseg = prev_subseg;
+ prev_insn = -1;
+ cur_mul32_p = prev_mul32_p;
+ prev_mul32_p = 0;
+ memcpy (&opcode, &prev_opcode, sizeof (prev_opcode));
+ }
+
+ /* If this is a long instruction, write it and any previous short
+ instruction. */
+ if (opcode.form->form >= LONG)
+ {
+ if (extype != EXEC_UNKNOWN)
+ as_bad (_("Instruction uses long version, so it cannot be mixed as specified"));
+ d30v_cleanup (FALSE);
+ write_long (&opcode, insn, fixups);
+ prev_insn = -1;
+ }
+ else if ((prev_insn != -1)
+ && (write_2_short
+ (&prev_opcode, (long) prev_insn, &opcode,
+ (long) insn, extype, fixups) == 0))
+ {
+ /* No instructions saved. */
+ prev_insn = -1;
+ }
+ else
+ {
+ if (extype != EXEC_UNKNOWN)
+ as_bad (_("Unable to mix instructions as specified"));
+
+ /* Save off last instruction so it may be packed on next pass. */
+ memcpy (&prev_opcode, &opcode, sizeof (prev_opcode));
+ prev_insn = insn;
+ prev_seg = now_seg;
+ prev_subseg = now_subseg;
+ fixups = fixups->next;
+ prev_mul32_p = cur_mul32_p;
+ }
+}
+
+/* If while processing a fixup, a reloc really needs to be created,
+ then it is done here. */
+
+arelent *
+tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+ reloc = xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("reloc %d not supported by object file format"),
+ (int) fixp->fx_r_type);
+ return NULL;
+ }
+
+ reloc->addend = 0;
+ return reloc;
+}
+
+int
+md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
+ asection *seg ATTRIBUTE_UNUSED)
+{
+ abort ();
+ return 0;
+}
+
+long
+md_pcrel_from_section (fixS *fixp, segT sec)
+{
+ if (fixp->fx_addsy != (symbolS *) NULL
+ && (!S_IS_DEFINED (fixp->fx_addsy)
+ || (S_GET_SEGMENT (fixp->fx_addsy) != sec)))
+ return 0;
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+}
+
+/* Called after the assembler has finished parsing the input file or
+ after a label is defined. Because the D30V assembler sometimes
+ saves short instructions to see if it can package them with the
+ next instruction, there may be a short instruction that still needs
+ written. */
+
+int
+d30v_cleanup (int use_sequential)
+{
+ segT seg;
+ subsegT subseg;
+
+ if (prev_insn != -1)
+ {
+ seg = now_seg;
+ subseg = now_subseg;
+ subseg_set (prev_seg, prev_subseg);
+ write_1_short (&prev_opcode, (long) prev_insn, fixups->next,
+ use_sequential);
+ subseg_set (seg, subseg);
+ prev_insn = -1;
+ if (use_sequential)
+ prev_mul32_p = FALSE;
+ }
+
+ return 1;
+}
+
+/* This function is called at the start of every line. It checks to
+ see if the first character is a '.', which indicates the start of a
+ pseudo-op. If it is, then write out any unwritten instructions. */
+
+void
+d30v_start_line (void)
+{
+ char *c = input_line_pointer;
+
+ while (ISSPACE (*c))
+ c++;
+
+ if (*c == '.')
+ d30v_cleanup (FALSE);