+ allow_m32rx (enable_m32rx);
+
+ gas_cgen_initialize_saved_fixups_array ();
+}
+
+#define OPERAND_IS_COND_BIT(operand, indices, index) \
+ ((operand)->hw_type == HW_H_COND \
+ || ((operand)->hw_type == HW_H_PSW) \
+ || ((operand)->hw_type == HW_H_CR \
+ && (indices [index] == 0 || indices [index] == 1)))
+
+/* Returns true if an output of instruction 'a' is referenced by an operand
+ of instruction 'b'. If 'check_outputs' is true then b's outputs are
+ checked, otherwise its inputs are examined. */
+
+static int
+first_writes_to_seconds_operands (m32r_insn *a,
+ m32r_insn *b,
+ const int check_outputs)
+{
+ const CGEN_OPINST *a_operands = CGEN_INSN_OPERANDS (a->insn);
+ const CGEN_OPINST *b_ops = CGEN_INSN_OPERANDS (b->insn);
+ int a_index;
+
+ if (ignore_parallel_conflicts)
+ return 0;
+
+ /* If at least one of the instructions takes no operands, then there is
+ nothing to check. There really are instructions without operands,
+ eg 'nop'. */
+ if (a_operands == NULL || b_ops == NULL)
+ return 0;
+
+ /* Scan the operand list of 'a' looking for an output operand. */
+ for (a_index = 0;
+ a_operands->type != CGEN_OPINST_END;
+ a_index ++, a_operands ++)
+ {
+ if (a_operands->type == CGEN_OPINST_OUTPUT)
+ {
+ int b_index;
+ const CGEN_OPINST *b_operands = b_ops;
+
+ /* Special Case:
+ The Condition bit 'C' is a shadow of the CBR register (control
+ register 1) and also a shadow of bit 31 of the program status
+ word (control register 0). For now this is handled here, rather
+ than by cgen.... */
+
+ if (OPERAND_IS_COND_BIT (a_operands, a->indices, a_index))
+ {
+ /* Scan operand list of 'b' looking for another reference to the
+ condition bit, which goes in the right direction. */
+ for (b_index = 0;
+ b_operands->type != CGEN_OPINST_END;
+ b_index++, b_operands++)
+ {
+ if ((b_operands->type
+ == (check_outputs
+ ? CGEN_OPINST_OUTPUT
+ : CGEN_OPINST_INPUT))
+ && OPERAND_IS_COND_BIT (b_operands, b->indices, b_index))
+ return 1;
+ }
+ }
+ else
+ {
+ /* Scan operand list of 'b' looking for an operand that
+ references the same hardware element, and which goes in the
+ right direction. */
+ for (b_index = 0;
+ b_operands->type != CGEN_OPINST_END;
+ b_index++, b_operands++)
+ {
+ if ((b_operands->type
+ == (check_outputs
+ ? CGEN_OPINST_OUTPUT
+ : CGEN_OPINST_INPUT))
+ && (b_operands->hw_type == a_operands->hw_type)
+ && (a->indices[a_index] == b->indices[b_index]))
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Returns true if the insn can (potentially) alter the program counter. */
+
+static int
+writes_to_pc (m32r_insn *a)
+{
+ if (CGEN_INSN_ATTR_VALUE (a->insn, CGEN_INSN_UNCOND_CTI)
+ || CGEN_INSN_ATTR_VALUE (a->insn, CGEN_INSN_COND_CTI))
+ return 1;
+ return 0;
+}
+
+/* Return NULL if the two 16 bit insns can be executed in parallel.
+ Otherwise return a pointer to an error message explaining why not. */
+
+static const char *
+can_make_parallel (m32r_insn *a, m32r_insn *b)
+{
+ PIPE_ATTR a_pipe;
+ PIPE_ATTR b_pipe;
+
+ /* Make sure the instructions are the right length. */
+ if (CGEN_FIELDS_BITSIZE (&a->fields) != 16
+ || CGEN_FIELDS_BITSIZE (&b->fields) != 16)
+ abort ();
+
+ if (first_writes_to_seconds_operands (a, b, TRUE))
+ return _("instructions write to the same destination register.");
+
+ a_pipe = CGEN_INSN_ATTR_VALUE (a->insn, CGEN_INSN_PIPE);
+ b_pipe = CGEN_INSN_ATTR_VALUE (b->insn, CGEN_INSN_PIPE);
+
+ /* Make sure that the instructions use the correct execution pipelines. */
+ if (a_pipe == PIPE_NONE
+ || b_pipe == PIPE_NONE)
+ return _("Instructions do not use parallel execution pipelines.");
+
+ /* Leave this test for last, since it is the only test that can
+ go away if the instructions are swapped, and we want to make
+ sure that any other errors are detected before this happens. */
+ if (a_pipe == PIPE_S
+ || b_pipe == PIPE_O
+ || (b_pipe == PIPE_O_OS && (enable_m32rx != 2)))
+ return _("Instructions share the same execution pipeline");
+
+ return NULL;
+}
+
+/* Force the top bit of the second 16-bit insn to be set. */
+
+static void
+make_parallel (CGEN_INSN_BYTES_PTR buffer)
+{
+#if CGEN_INT_INSN_P
+ *buffer |= 0x8000;
+#else
+ buffer[CGEN_CPU_ENDIAN (gas_cgen_cpu_desc) == CGEN_ENDIAN_BIG ? 0 : 1]
+ |= 0x80;
+#endif
+}
+
+/* Same as make_parallel except buffer contains the bytes in target order. */
+
+static void
+target_make_parallel (char *buffer)
+{
+ buffer[CGEN_CPU_ENDIAN (gas_cgen_cpu_desc) == CGEN_ENDIAN_BIG ? 0 : 1]
+ |= 0x80;
+}
+
+/* Assemble two instructions with an explicit parallel operation (||) or
+ sequential operation (->). */
+
+static void
+assemble_two_insns (char *str1, char *str2, int parallel_p)
+{
+ char *str3;
+ m32r_insn first;
+ m32r_insn second;
+ char *errmsg;
+ char save_str2 = *str2;
+
+ /* Separate the two instructions. */
+ *str2 = 0;
+
+ /* Make sure the two insns begin on a 32 bit boundary.
+ This is also done for the serial case (foo -> bar), relaxing doesn't
+ affect insns written like this.
+ Note that we must always do this as we can't assume anything about
+ whether we're currently on a 32 bit boundary or not. Relaxing may
+ change this. */
+ fill_insn (0);
+
+ first.debug_sym_link = debug_sym_link;
+ debug_sym_link = (sym_linkS *) 0;
+
+ /* Parse the first instruction. */
+ if (! (first.insn = m32r_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str1, & first.fields, first.buffer, & errmsg)))
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+
+ /* Check it. */
+ if (CGEN_FIELDS_BITSIZE (&first.fields) != 16)
+ {
+ /* xgettext:c-format */
+ as_bad (_("not a 16 bit instruction '%s'"), str1);
+ return;
+ }
+#ifdef E_M32R2_ARCH
+ else if ((enable_m32rx == 1)
+ /* FIXME: Need standard macro to perform this test. */
+ && ((CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_MACH)
+ & (1 << MACH_M32R2))
+ && !((CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_MACH)
+ & (1 << MACH_M32RX)))))
+ {
+ /* xgettext:c-format */
+ as_bad (_("instruction '%s' is for the M32R2 only"), str1);
+ return;
+ }
+ else if ((! enable_special
+ && CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_SPECIAL))
+ || (! enable_special_m32r
+ && CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_SPECIAL_M32R)))
+#else
+ else if (! enable_special
+ && CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_SPECIAL))
+#endif
+ {
+ /* xgettext:c-format */
+ as_bad (_("unknown instruction '%s'"), str1);
+ return;
+ }
+ else if (! enable_m32rx
+ /* FIXME: Need standard macro to perform this test. */
+ && (CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_MACH)
+ == (1 << MACH_M32RX)))
+ {
+ /* xgettext:c-format */
+ as_bad (_("instruction '%s' is for the M32RX only"), str1);
+ return;
+ }
+
+ /* Check to see if this is an allowable parallel insn. */
+ if (parallel_p
+ && CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_PIPE) == PIPE_NONE)
+ {
+ /* xgettext:c-format */
+ as_bad (_("instruction '%s' cannot be executed in parallel."), str1);
+ return;
+ }
+
+ /* Restore the original assembly text, just in case it is needed. */
+ *str2 = save_str2;
+
+ /* Save the original string pointer. */
+ str3 = str1;
+
+ /* Advanced past the parsed string. */
+ str1 = str2 + 2;
+
+ /* Remember the entire string in case it is needed for error
+ messages. */
+ str2 = str3;
+
+ /* Convert the opcode to lower case. */
+ {
+ char *s2 = str1;
+
+ while (ISSPACE (*s2++))
+ continue;
+
+ --s2;
+
+ while (ISALNUM (*s2))
+ {
+ *s2 = TOLOWER (*s2);
+ s2++;
+ }
+ }
+
+ /* Preserve any fixups that have been generated and reset the list
+ to empty. */
+ gas_cgen_save_fixups (0);
+
+ /* Get the indices of the operands of the instruction. */
+ /* FIXME: CGEN_FIELDS is already recorded, but relying on that fact
+ doesn't seem right. Perhaps allow passing fields like we do insn. */
+ /* FIXME: ALIAS insns do not have operands, so we use this function
+ to find the equivalent insn and overwrite the value stored in our
+ structure. We still need the original insn, however, since this
+ may have certain attributes that are not present in the unaliased
+ version (eg relaxability). When aliases behave differently this
+ may have to change. */
+ first.orig_insn = first.insn;
+ {
+ CGEN_FIELDS tmp_fields;
+ first.insn = cgen_lookup_get_insn_operands
+ (gas_cgen_cpu_desc, NULL, INSN_VALUE (first.buffer), NULL, 16,
+ first.indices, &tmp_fields);
+ }
+
+ if (first.insn == NULL)
+ as_fatal (_("internal error: lookup/get operands failed"));
+
+ second.debug_sym_link = NULL;
+
+ /* Parse the second instruction. */
+ if (! (second.insn = m32r_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str1, & second.fields, second.buffer, & errmsg)))
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+
+ /* Check it. */
+ if (CGEN_FIELDS_BITSIZE (&second.fields) != 16)
+ {
+ /* xgettext:c-format */
+ as_bad (_("not a 16 bit instruction '%s'"), str1);
+ return;
+ }
+#ifdef E_M32R2_ARCH
+ else if ((enable_m32rx == 1)
+ /* FIXME: Need standard macro to perform this test. */
+ && ((CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_MACH)
+ & (1 << MACH_M32R2))
+ && !((CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_MACH)
+ & (1 << MACH_M32RX)))))
+ {
+ /* xgettext:c-format */
+ as_bad (_("instruction '%s' is for the M32R2 only"), str1);
+ return;
+ }
+ else if ((! enable_special
+ && CGEN_INSN_ATTR_VALUE (second.insn, CGEN_INSN_SPECIAL))
+ || (! enable_special_m32r
+ && CGEN_INSN_ATTR_VALUE (second.insn, CGEN_INSN_SPECIAL_M32R)))
+#else
+ else if (! enable_special
+ && CGEN_INSN_ATTR_VALUE (second.insn, CGEN_INSN_SPECIAL))
+#endif
+ {
+ /* xgettext:c-format */
+ as_bad (_("unknown instruction '%s'"), str1);
+ return;
+ }
+ else if (! enable_m32rx
+ && CGEN_INSN_ATTR_VALUE (second.insn, CGEN_INSN_MACH) == (1 << MACH_M32RX))
+ {
+ /* xgettext:c-format */
+ as_bad (_("instruction '%s' is for the M32RX only"), str1);
+ return;
+ }
+
+ /* Check to see if this is an allowable parallel insn. */
+ if (parallel_p
+ && CGEN_INSN_ATTR_VALUE (second.insn, CGEN_INSN_PIPE) == PIPE_NONE)
+ {
+ /* xgettext:c-format */
+ as_bad (_("instruction '%s' cannot be executed in parallel."), str1);
+ return;
+ }
+
+ if (parallel_p && ! enable_m32rx)
+ {
+ if (CGEN_INSN_NUM (first.insn) != M32R_INSN_NOP
+ && CGEN_INSN_NUM (second.insn) != M32R_INSN_NOP)
+ {
+ /* xgettext:c-format */
+ as_bad (_("'%s': only the NOP instruction can be issued in parallel on the m32r"), str2);
+ return;
+ }
+ }
+
+ /* Get the indices of the operands of the instruction. */
+ second.orig_insn = second.insn;
+ {
+ CGEN_FIELDS tmp_fields;
+ second.insn = cgen_lookup_get_insn_operands
+ (gas_cgen_cpu_desc, NULL, INSN_VALUE (second.buffer), NULL, 16,
+ second.indices, &tmp_fields);
+ }
+
+ if (second.insn == NULL)
+ as_fatal (_("internal error: lookup/get operands failed"));
+
+ /* We assume that if the first instruction writes to a register that is
+ read by the second instruction it is because the programmer intended
+ this to happen, (after all they have explicitly requested that these
+ two instructions be executed in parallel). Although if the global
+ variable warn_explicit_parallel_conflicts is true then we do generate
+ a warning message. Similarly we assume that parallel branch and jump
+ instructions are deliberate and should not produce errors. */
+
+ if (parallel_p && warn_explicit_parallel_conflicts)
+ {
+ if (first_writes_to_seconds_operands (&first, &second, FALSE))
+ /* xgettext:c-format */
+ as_warn (_("%s: output of 1st instruction is the same as an input to 2nd instruction - is this intentional ?"), str2);
+
+ if (first_writes_to_seconds_operands (&second, &first, FALSE))
+ /* xgettext:c-format */
+ as_warn (_("%s: output of 2nd instruction is the same as an input to 1st instruction - is this intentional ?"), str2);
+ }
+
+ if (!parallel_p
+ || (errmsg = (char *) can_make_parallel (&first, &second)) == NULL)
+ {
+ /* Get the fixups for the first instruction. */
+ gas_cgen_swap_fixups (0);
+
+ /* Write it out. */
+ expand_debug_syms (first.debug_sym_link, 1);
+ gas_cgen_finish_insn (first.orig_insn, first.buffer,
+ CGEN_FIELDS_BITSIZE (&first.fields), 0, NULL);
+
+ /* Force the top bit of the second insn to be set. */
+ if (parallel_p)
+ make_parallel (second.buffer);
+
+ /* Get its fixups. */
+ gas_cgen_restore_fixups (0);
+
+ /* Write it out. */
+ expand_debug_syms (second.debug_sym_link, 1);
+ gas_cgen_finish_insn (second.orig_insn, second.buffer,
+ CGEN_FIELDS_BITSIZE (&second.fields), 0, NULL);
+ }
+ /* Try swapping the instructions to see if they work that way. */
+ else if (can_make_parallel (&second, &first) == NULL)
+ {
+ /* Write out the second instruction first. */
+ expand_debug_syms (second.debug_sym_link, 1);
+ gas_cgen_finish_insn (second.orig_insn, second.buffer,
+ CGEN_FIELDS_BITSIZE (&second.fields), 0, NULL);
+
+ /* Force the top bit of the first instruction to be set. */
+ make_parallel (first.buffer);