+ 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_section_flags (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;