+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg)
+{
+ char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
+ valueT val = 0;
+
+ /* Subtracted symbols are only allowed for a few relocation types, and
+ unless linkrelax is enabled, they should not make it to this point. */
+ if (fixP->fx_subsy && !(linkrelax && (fixP->fx_r_type == BFD_RELOC_32
+ || fixP->fx_r_type == BFD_RELOC_16
+ || fixP->fx_r_type == BFD_RELOC_8)))
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_32:
+ case BFD_RELOC_16:
+ case BFD_RELOC_8:
+ if (fixP->fx_subsy)
+ {
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF8;
+ break;
+ case BFD_RELOC_16:
+ fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF16;
+ break;
+ case BFD_RELOC_32:
+ fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF32;
+ break;
+ default:
+ break;
+ }
+
+ /* An offset is only allowed when it results from adjusting a
+ local symbol into a section-relative offset. If the offset
+ came from the original expression, tc_fix_adjustable will have
+ prevented the fix from being converted to a section-relative
+ form so that we can flag the error here. */
+ if (fixP->fx_offset != 0 && !symbol_section_p (fixP->fx_addsy))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("cannot represent subtraction with an offset"));
+
+ val = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
+ - S_GET_VALUE (fixP->fx_subsy));
+
+ /* The difference value gets written out, and the DIFF reloc
+ identifies the address of the subtracted symbol (i.e., the one
+ with the lowest address). */
+ *valP = val;
+ fixP->fx_offset -= val;
+ fixP->fx_subsy = NULL;
+ }
+ else if (! fixP->fx_addsy)
+ {
+ val = *valP;
+ fixP->fx_done = 1;
+ }
+ /* fall through */
+
+ case BFD_RELOC_XTENSA_PLT:
+ md_number_to_chars (fixpos, val, fixP->fx_size);
+ fixP->fx_no_overflow = 0; /* Use the standard overflow check. */
+ break;
+
+ case BFD_RELOC_XTENSA_SLOT0_OP:
+ case BFD_RELOC_XTENSA_SLOT1_OP:
+ case BFD_RELOC_XTENSA_SLOT2_OP:
+ case BFD_RELOC_XTENSA_SLOT3_OP:
+ case BFD_RELOC_XTENSA_SLOT4_OP:
+ case BFD_RELOC_XTENSA_SLOT5_OP:
+ case BFD_RELOC_XTENSA_SLOT6_OP:
+ case BFD_RELOC_XTENSA_SLOT7_OP:
+ case BFD_RELOC_XTENSA_SLOT8_OP:
+ case BFD_RELOC_XTENSA_SLOT9_OP:
+ case BFD_RELOC_XTENSA_SLOT10_OP:
+ case BFD_RELOC_XTENSA_SLOT11_OP:
+ case BFD_RELOC_XTENSA_SLOT12_OP:
+ case BFD_RELOC_XTENSA_SLOT13_OP:
+ case BFD_RELOC_XTENSA_SLOT14_OP:
+ if (linkrelax)
+ {
+ /* Write the tentative value of a PC-relative relocation to a
+ local symbol into the instruction. The value will be ignored
+ by the linker, and it makes the object file disassembly
+ readable when all branch targets are encoded in relocations. */
+
+ assert (fixP->fx_addsy);
+ if (S_GET_SEGMENT (fixP->fx_addsy) == seg
+ && !S_FORCE_RELOC (fixP->fx_addsy, 1))
+ {
+ val = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
+ - md_pcrel_from (fixP));
+ (void) xg_apply_fix_value (fixP, val);
+ }
+ }
+ else if (! fixP->fx_addsy)
+ {
+ val = *valP;
+ if (xg_apply_fix_value (fixP, val))
+ fixP->fx_done = 1;
+ }
+ break;
+
+ case BFD_RELOC_XTENSA_ASM_EXPAND:
+ case BFD_RELOC_XTENSA_SLOT0_ALT:
+ case BFD_RELOC_XTENSA_SLOT1_ALT:
+ case BFD_RELOC_XTENSA_SLOT2_ALT:
+ case BFD_RELOC_XTENSA_SLOT3_ALT:
+ case BFD_RELOC_XTENSA_SLOT4_ALT:
+ case BFD_RELOC_XTENSA_SLOT5_ALT:
+ case BFD_RELOC_XTENSA_SLOT6_ALT:
+ case BFD_RELOC_XTENSA_SLOT7_ALT:
+ case BFD_RELOC_XTENSA_SLOT8_ALT:
+ case BFD_RELOC_XTENSA_SLOT9_ALT:
+ case BFD_RELOC_XTENSA_SLOT10_ALT:
+ case BFD_RELOC_XTENSA_SLOT11_ALT:
+ case BFD_RELOC_XTENSA_SLOT12_ALT:
+ case BFD_RELOC_XTENSA_SLOT13_ALT:
+ case BFD_RELOC_XTENSA_SLOT14_ALT:
+ /* These all need to be resolved at link-time. Do nothing now. */
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ break;
+
+ default:
+ as_bad (_("unhandled local relocation fix %s"),
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+ }
+}
+
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ int prec;
+ LITTLENUM_TYPE words[4];
+ char *t;
+ int i;
+
+ switch (type)
+ {
+ case 'f':
+ prec = 2;
+ break;
+
+ case 'd':
+ prec = 4;
+ break;
+
+ default:
+ *sizeP = 0;
+ return "bad call to md_atof";
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizeP = prec * 2;
+
+ for (i = prec - 1; i >= 0; i--)
+ {
+ int idx = i;
+ if (target_big_endian)
+ idx = (prec - 1 - i);
+
+ md_number_to_chars (litP, (valueT) words[idx], 2);
+ litP += 2;
+ }
+
+ return NULL;
+}
+
+
+int
+md_estimate_size_before_relax (fragS *fragP, segT seg ATTRIBUTE_UNUSED)
+{
+ return total_frag_text_expansion (fragP);
+}
+
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ /* Make sure none of our internal relocations make it this far.
+ They'd better have been fully resolved by this point. */
+ assert ((int) fixp->fx_r_type > 0);
+
+ reloc->addend = fixp->fx_offset;
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent `%s' relocation in object file"),
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+ free (reloc->sym_ptr_ptr);
+ free (reloc);
+ return NULL;
+ }
+
+ if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
+ as_fatal (_("internal error? cannot generate `%s' relocation"),
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+
+ return reloc;
+}
+
+\f
+/* Checks for resource conflicts between instructions. */
+
+/* The func unit stuff could be implemented as bit-vectors rather
+ than the iterative approach here. If it ends up being too
+ slow, we will switch it. */
+
+resource_table *
+new_resource_table (void *data,
+ int cycles,
+ int nu,
+ unit_num_copies_func uncf,
+ opcode_num_units_func onuf,
+ opcode_funcUnit_use_unit_func ouuf,
+ opcode_funcUnit_use_stage_func ousf)
+{
+ int i;
+ resource_table *rt = (resource_table *) xmalloc (sizeof (resource_table));
+ rt->data = data;
+ rt->cycles = cycles;
+ rt->allocated_cycles = cycles;
+ rt->num_units = nu;
+ rt->unit_num_copies = uncf;
+ rt->opcode_num_units = onuf;
+ rt->opcode_unit_use = ouuf;
+ rt->opcode_unit_stage = ousf;
+
+ rt->units = (unsigned char **) xcalloc (cycles, sizeof (unsigned char *));
+ for (i = 0; i < cycles; i++)
+ rt->units[i] = (unsigned char *) xcalloc (nu, sizeof (unsigned char));
+
+ return rt;
+}
+
+
+void
+clear_resource_table (resource_table *rt)
+{
+ int i, j;
+ for (i = 0; i < rt->allocated_cycles; i++)
+ for (j = 0; j < rt->num_units; j++)
+ rt->units[i][j] = 0;
+}
+
+
+/* We never shrink it, just fake it into thinking so. */
+
+void
+resize_resource_table (resource_table *rt, int cycles)
+{
+ int i, old_cycles;
+
+ rt->cycles = cycles;
+ if (cycles <= rt->allocated_cycles)
+ return;
+
+ old_cycles = rt->allocated_cycles;
+ rt->allocated_cycles = cycles;
+
+ rt->units = xrealloc (rt->units,
+ rt->allocated_cycles * sizeof (unsigned char *));
+ for (i = 0; i < old_cycles; i++)
+ rt->units[i] = xrealloc (rt->units[i],
+ rt->num_units * sizeof (unsigned char));
+ for (i = old_cycles; i < cycles; i++)
+ rt->units[i] = xcalloc (rt->num_units, sizeof (unsigned char));
+}
+
+
+bfd_boolean
+resources_available (resource_table *rt, xtensa_opcode opcode, int cycle)
+{
+ int i;
+ int uses = (rt->opcode_num_units) (rt->data, opcode);
+
+ for (i = 0; i < uses; i++)
+ {
+ xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
+ int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
+ int copies_in_use = rt->units[stage + cycle][unit];
+ int copies = (rt->unit_num_copies) (rt->data, unit);
+ if (copies_in_use >= copies)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+void
+reserve_resources (resource_table *rt, xtensa_opcode opcode, int cycle)
+{
+ int i;
+ int uses = (rt->opcode_num_units) (rt->data, opcode);
+
+ for (i = 0; i < uses; i++)
+ {
+ xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
+ int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
+ /* Note that this allows resources to be oversubscribed. That's
+ essential to the way the optional scheduler works.
+ resources_available reports when a resource is over-subscribed,
+ so it's easy to tell. */
+ rt->units[stage + cycle][unit]++;
+ }
+}
+
+
+void
+release_resources (resource_table *rt, xtensa_opcode opcode, int cycle)
+{
+ int i;
+ int uses = (rt->opcode_num_units) (rt->data, opcode);
+
+ for (i = 0; i < uses; i++)
+ {
+ xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
+ int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
+ assert (rt->units[stage + cycle][unit] > 0);
+ rt->units[stage + cycle][unit]--;
+ }
+}
+
+
+/* Wrapper functions make parameterized resource reservation
+ more convenient. */
+
+int
+opcode_funcUnit_use_unit (void *data, xtensa_opcode opcode, int idx)
+{
+ xtensa_funcUnit_use *use = xtensa_opcode_funcUnit_use (data, opcode, idx);
+ return use->unit;
+}
+
+
+int
+opcode_funcUnit_use_stage (void *data, xtensa_opcode opcode, int idx)
+{
+ xtensa_funcUnit_use *use = xtensa_opcode_funcUnit_use (data, opcode, idx);
+ return use->stage;
+}
+
+
+/* Note that this function does not check issue constraints, but
+ solely whether the hardware is available to execute the given
+ instructions together. It also doesn't check if the tinsns
+ write the same state, or access the same tieports. That is
+ checked by check_t1_t2_reads_and_writes. */
+
+static bfd_boolean
+resources_conflict (vliw_insn *vinsn)
+{
+ int i;
+ static resource_table *rt = NULL;
+
+ /* This is the most common case by far. Optimize it. */
+ if (vinsn->num_slots == 1)
+ return FALSE;
+
+ if (rt == NULL)
+ {
+ xtensa_isa isa = xtensa_default_isa;
+ rt = new_resource_table
+ (isa, xtensa_isa_num_pipe_stages (isa),
+ xtensa_isa_num_funcUnits (isa),
+ (unit_num_copies_func) xtensa_funcUnit_num_copies,
+ (opcode_num_units_func) xtensa_opcode_num_funcUnit_uses,
+ opcode_funcUnit_use_unit,
+ opcode_funcUnit_use_stage);
+ }
+
+ clear_resource_table (rt);
+
+ for (i = 0; i < vinsn->num_slots; i++)
+ {
+ if (!resources_available (rt, vinsn->slots[i].opcode, 0))
+ return TRUE;
+ reserve_resources (rt, vinsn->slots[i].opcode, 0);
+ }
+
+ return FALSE;
+}
+
+\f
+/* finish_vinsn, emit_single_op and helper functions. */
+
+static bfd_boolean find_vinsn_conflicts (vliw_insn *);
+static xtensa_format xg_find_narrowest_format (vliw_insn *);
+static void xg_assemble_vliw_tokens (vliw_insn *);
+
+
+/* We have reached the end of a bundle; emit into the frag. */
+
+static void
+finish_vinsn (vliw_insn *vinsn)
+{
+ IStack slotstack;
+ int i;
+ char *file_name;
+ unsigned line;
+
+ if (find_vinsn_conflicts (vinsn))
+ {
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ /* First, find a format that works. */
+ if (vinsn->format == XTENSA_UNDEFINED)
+ vinsn->format = xg_find_narrowest_format (vinsn);
+
+ if (vinsn->format == XTENSA_UNDEFINED)
+ {
+ as_where (&file_name, &line);
+ as_bad_where (file_name, line,
+ _("couldn't find a valid instruction format"));
+ fprintf (stderr, _(" ops were: "));
+ for (i = 0; i < vinsn->num_slots; i++)
+ fprintf (stderr, _(" %s;"),
+ xtensa_opcode_name (xtensa_default_isa,
+ vinsn->slots[i].opcode));
+ fprintf (stderr, _("\n"));
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ if (vinsn->num_slots
+ != xtensa_format_num_slots (xtensa_default_isa, vinsn->format))
+ {
+ as_bad (_("format '%s' allows %d slots, but there are %d opcodes"),
+ xtensa_format_name (xtensa_default_isa, vinsn->format),
+ xtensa_format_num_slots (xtensa_default_isa, vinsn->format),
+ vinsn->num_slots);
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ if (resources_conflict (vinsn))
+ {
+ as_where (&file_name, &line);
+ as_bad_where (file_name, line, _("illegal resource usage in bundle"));
+ fprintf (stderr, " ops were: ");
+ for (i = 0; i < vinsn->num_slots; i++)
+ fprintf (stderr, " %s;",
+ xtensa_opcode_name (xtensa_default_isa,
+ vinsn->slots[i].opcode));
+ fprintf (stderr, "\n");
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ for (i = 0; i < vinsn->num_slots; i++)
+ {
+ if (vinsn->slots[i].opcode != XTENSA_UNDEFINED)
+ {
+ symbolS *lit_sym = NULL;
+ int j;
+ bfd_boolean e = FALSE;
+ bfd_boolean saved_density = density_supported;
+
+ /* We don't want to narrow ops inside multi-slot bundles. */
+ if (vinsn->num_slots > 1)
+ density_supported = FALSE;
+
+ istack_init (&slotstack);
+ if (vinsn->slots[i].opcode == xtensa_nop_opcode)
+ {
+ vinsn->slots[i].opcode =
+ xtensa_format_slot_nop_opcode (xtensa_default_isa,
+ vinsn->format, i);
+ vinsn->slots[i].ntok = 0;
+ }
+
+ if (xg_expand_assembly_insn (&slotstack, &vinsn->slots[i]))
+ {
+ e = TRUE;
+ continue;
+ }
+
+ density_supported = saved_density;
+
+ if (e)
+ {
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ for (j = 0; j < slotstack.ninsn; j++)
+ {
+ TInsn *insn = &slotstack.insn[j];
+ if (insn->insn_type == ITYPE_LITERAL)
+ {
+ assert (lit_sym == NULL);
+ lit_sym = xg_assemble_literal (insn);
+ }
+ else
+ {
+ assert (insn->insn_type == ITYPE_INSN);
+ if (lit_sym)
+ xg_resolve_literals (insn, lit_sym);
+ if (j != slotstack.ninsn - 1)
+ emit_single_op (insn);
+ }
+ }
+
+ if (vinsn->num_slots > 1)
+ {
+ if (opcode_fits_format_slot
+ (slotstack.insn[slotstack.ninsn - 1].opcode,
+ vinsn->format, i))
+ {
+ vinsn->slots[i] = slotstack.insn[slotstack.ninsn - 1];
+ }
+ else
+ {
+ emit_single_op (&slotstack.insn[slotstack.ninsn - 1]);
+ if (vinsn->format == XTENSA_UNDEFINED)
+ vinsn->slots[i].opcode = xtensa_nop_opcode;
+ else
+ vinsn->slots[i].opcode
+ = xtensa_format_slot_nop_opcode (xtensa_default_isa,
+ vinsn->format, i);
+
+ vinsn->slots[i].ntok = 0;
+ }
+ }
+ else
+ {
+ vinsn->slots[0] = slotstack.insn[slotstack.ninsn - 1];
+ vinsn->format = XTENSA_UNDEFINED;
+ }
+ }
+ }
+
+ /* Now check resource conflicts on the modified bundle. */
+ if (resources_conflict (vinsn))
+ {
+ as_where (&file_name, &line);
+ as_bad_where (file_name, line, _("illegal resource usage in bundle"));
+ fprintf (stderr, " ops were: ");
+ for (i = 0; i < vinsn->num_slots; i++)
+ fprintf (stderr, " %s;",
+ xtensa_opcode_name (xtensa_default_isa,
+ vinsn->slots[i].opcode));
+ fprintf (stderr, "\n");
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ /* First, find a format that works. */
+ if (vinsn->format == XTENSA_UNDEFINED)
+ vinsn->format = xg_find_narrowest_format (vinsn);
+
+ xg_assemble_vliw_tokens (vinsn);
+
+ xg_clear_vinsn (vinsn);
+}
+
+
+/* Given an vliw instruction, what conflicts are there in register
+ usage and in writes to states and queues?
+
+ This function does two things:
+ 1. Reports an error when a vinsn contains illegal combinations
+ of writes to registers states or queues.
+ 2. Marks individual tinsns as not relaxable if the combination
+ contains antidependencies.
+
+ Job 2 handles things like swap semantics in instructions that need
+ to be relaxed. For example,
+
+ addi a0, a1, 100000
+
+ normally would be relaxed to
+
+ l32r a0, some_label
+ add a0, a1, a0
+
+ _but_, if the above instruction is bundled with an a0 reader, e.g.,
+
+ { addi a0, a1, 10000 ; add a2, a0, a4 ; }
+
+ then we can't relax it into
+
+ l32r a0, some_label
+ { add a0, a1, a0 ; add a2, a0, a4 ; }
+
+ because the value of a0 is trashed before the second add can read it. */
+
+static char check_t1_t2_reads_and_writes (TInsn *, TInsn *);
+
+static bfd_boolean
+find_vinsn_conflicts (vliw_insn *vinsn)
+{
+ int i, j;
+ int branches = 0;
+ xtensa_isa isa = xtensa_default_isa;
+
+ assert (!past_xtensa_end);
+
+ for (i = 0 ; i < vinsn->num_slots; i++)
+ {
+ TInsn *op1 = &vinsn->slots[i];
+ if (op1->is_specific_opcode)
+ op1->keep_wide = TRUE;
+ else
+ op1->keep_wide = FALSE;
+ }
+
+ for (i = 0 ; i < vinsn->num_slots; i++)
+ {
+ TInsn *op1 = &vinsn->slots[i];
+
+ if (xtensa_opcode_is_branch (isa, op1->opcode) == 1)
+ branches++;
+
+ for (j = 0; j < vinsn->num_slots; j++)
+ {
+ if (i != j)
+ {
+ TInsn *op2 = &vinsn->slots[j];
+ char conflict_type = check_t1_t2_reads_and_writes (op1, op2);
+ switch (conflict_type)
+ {
+ case 'c':
+ as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same register"),
+ xtensa_opcode_name (isa, op1->opcode), i,
+ xtensa_opcode_name (isa, op2->opcode), j);
+ return TRUE;
+ case 'd':
+ as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same state"),
+ xtensa_opcode_name (isa, op1->opcode), i,
+ xtensa_opcode_name (isa, op2->opcode), j);
+ return TRUE;
+ case 'e':
+ as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same port"),
+ xtensa_opcode_name (isa, op1->opcode), i,
+ xtensa_opcode_name (isa, op2->opcode), j);
+ return TRUE;
+ case 'f':
+ as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) both have volatile port accesses"),
+ xtensa_opcode_name (isa, op1->opcode), i,
+ xtensa_opcode_name (isa, op2->opcode), j);
+ return TRUE;
+ default:
+ /* Everything is OK. */
+ break;
+ }
+ op2->is_specific_opcode = (op2->is_specific_opcode
+ || conflict_type == 'a');
+ }
+ }
+ }
+
+ if (branches > 1)
+ {
+ as_bad (_("multiple branches or jumps in the same bundle"));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/* Check how the state used by t1 and t2 relate.
+ Cases found are:
+
+ case A: t1 reads a register t2 writes (an antidependency within a bundle)
+ case B: no relationship between what is read and written (both could
+ read the same reg though)
+ case C: t1 writes a register t2 writes (a register conflict within a
+ bundle)
+ case D: t1 writes a state that t2 also writes
+ case E: t1 writes a tie queue that t2 also writes
+ case F: two volatile queue accesses
+*/
+
+static char
+check_t1_t2_reads_and_writes (TInsn *t1, TInsn *t2)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_regfile t1_regfile, t2_regfile;
+ int t1_reg, t2_reg;
+ int t1_base_reg, t1_last_reg;
+ int t2_base_reg, t2_last_reg;
+ char t1_inout, t2_inout;
+ int i, j;
+ char conflict = 'b';
+ int t1_states;
+ int t2_states;
+ int t1_interfaces;
+ int t2_interfaces;
+ bfd_boolean t1_volatile = FALSE;
+ bfd_boolean t2_volatile = FALSE;
+
+ /* Check registers. */
+ for (j = 0; j < t2->ntok; j++)
+ {
+ if (xtensa_operand_is_register (isa, t2->opcode, j) != 1)
+ continue;
+
+ t2_regfile = xtensa_operand_regfile (isa, t2->opcode, j);
+ t2_base_reg = t2->tok[j].X_add_number;
+ t2_last_reg = t2_base_reg + xtensa_operand_num_regs (isa, t2->opcode, j);
+
+ for (i = 0; i < t1->ntok; i++)
+ {
+ if (xtensa_operand_is_register (isa, t1->opcode, i) != 1)
+ continue;
+
+ t1_regfile = xtensa_operand_regfile (isa, t1->opcode, i);
+
+ if (t1_regfile != t2_regfile)
+ continue;
+
+ t1_inout = xtensa_operand_inout (isa, t1->opcode, i);
+ t2_inout = xtensa_operand_inout (isa, t2->opcode, j);
+
+ if (xtensa_operand_is_known_reg (isa, t1->opcode, i) == 0
+ || xtensa_operand_is_known_reg (isa, t2->opcode, j) == 0)
+ {
+ if (t1_inout == 'm' || t1_inout == 'o'
+ || t2_inout == 'm' || t2_inout == 'o')
+ {
+ conflict = 'a';
+ continue;
+ }
+ }
+
+ t1_base_reg = t1->tok[i].X_add_number;
+ t1_last_reg = (t1_base_reg
+ + xtensa_operand_num_regs (isa, t1->opcode, i));
+
+ for (t1_reg = t1_base_reg; t1_reg < t1_last_reg; t1_reg++)
+ {
+ for (t2_reg = t2_base_reg; t2_reg < t2_last_reg; t2_reg++)
+ {
+ if (t1_reg != t2_reg)
+ continue;
+
+ if (t2_inout == 'i' && (t1_inout == 'm' || t1_inout == 'o'))
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout == 'i' && (t2_inout == 'm' || t2_inout == 'o'))
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout != 'i' && t2_inout != 'i')
+ return 'c';
+ }
+ }
+ }
+ }
+
+ /* Check states. */
+ t1_states = xtensa_opcode_num_stateOperands (isa, t1->opcode);
+ t2_states = xtensa_opcode_num_stateOperands (isa, t2->opcode);
+ for (j = 0; j < t2_states; j++)
+ {
+ xtensa_state t2_so = xtensa_stateOperand_state (isa, t2->opcode, j);
+ t2_inout = xtensa_stateOperand_inout (isa, t2->opcode, j);
+ for (i = 0; i < t1_states; i++)
+ {
+ xtensa_state t1_so = xtensa_stateOperand_state (isa, t1->opcode, i);
+ t1_inout = xtensa_stateOperand_inout (isa, t1->opcode, i);
+ if (t1_so != t2_so)
+ continue;
+
+ if (t2_inout == 'i' && (t1_inout == 'm' || t1_inout == 'o'))
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout == 'i' && (t2_inout == 'm' || t2_inout == 'o'))
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout != 'i' && t2_inout != 'i')
+ return 'd';
+ }
+ }
+
+ /* Check tieports. */
+ t1_interfaces = xtensa_opcode_num_interfaceOperands (isa, t1->opcode);
+ t2_interfaces = xtensa_opcode_num_interfaceOperands (isa, t2->opcode);
+ for (j = 0; j < t2_interfaces; j++)
+ {
+ xtensa_interface t2_int
+ = xtensa_interfaceOperand_interface (isa, t2->opcode, j);
+ int t2_class = xtensa_interface_class_id (isa, t2_int);
+
+ t2_inout = xtensa_interface_inout (isa, t2_int);
+ if (xtensa_interface_has_side_effect (isa, t2_int) == 1)
+ t2_volatile = TRUE;
+
+ for (i = 0; i < t1_interfaces; i++)
+ {
+ xtensa_interface t1_int
+ = xtensa_interfaceOperand_interface (isa, t1->opcode, j);
+ int t1_class = xtensa_interface_class_id (isa, t1_int);
+
+ t1_inout = xtensa_interface_inout (isa, t1_int);
+ if (xtensa_interface_has_side_effect (isa, t1_int) == 1)
+ t1_volatile = TRUE;
+
+ if (t1_volatile && t2_volatile && (t1_class == t2_class))
+ return 'f';
+
+ if (t1_int != t2_int)
+ continue;
+
+ if (t2_inout == 'i' && t1_inout == 'o')
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout == 'i' && t2_inout == 'o')
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout != 'i' && t2_inout != 'i')
+ return 'e';
+ }
+ }
+
+ return conflict;
+}
+
+
+static xtensa_format
+xg_find_narrowest_format (vliw_insn *vinsn)
+{
+ /* Right now we assume that the ops within the vinsn are properly
+ ordered for the slots that the programmer wanted them in. In
+ other words, we don't rearrange the ops in hopes of finding a
+ better format. The scheduler handles that. */
+
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_format format;
+ vliw_insn v_copy = *vinsn;
+ xtensa_opcode nop_opcode = xtensa_nop_opcode;
+
+ if (vinsn->num_slots == 1)
+ return xg_get_single_format (vinsn->slots[0].opcode);
+
+ for (format = 0; format < xtensa_isa_num_formats (isa); format++)
+ {
+ v_copy = *vinsn;
+ if (xtensa_format_num_slots (isa, format) == v_copy.num_slots)
+ {
+ int slot;
+ int fit = 0;
+ for (slot = 0; slot < v_copy.num_slots; slot++)
+ {
+ if (v_copy.slots[slot].opcode == nop_opcode)
+ {
+ v_copy.slots[slot].opcode =
+ xtensa_format_slot_nop_opcode (isa, format, slot);
+ v_copy.slots[slot].ntok = 0;
+ }
+
+ if (opcode_fits_format_slot (v_copy.slots[slot].opcode,
+ format, slot))
+ fit++;
+ else if (v_copy.num_slots > 1)
+ {
+ TInsn widened;
+ /* Try the widened version. */
+ if (!v_copy.slots[slot].keep_wide
+ && !v_copy.slots[slot].is_specific_opcode
+ && xg_is_single_relaxable_insn (&v_copy.slots[slot],
+ &widened, TRUE)
+ && opcode_fits_format_slot (widened.opcode,
+ format, slot))
+ {
+ v_copy.slots[slot] = widened;
+ fit++;
+ }
+ }
+ }
+ if (fit == v_copy.num_slots)
+ {
+ *vinsn = v_copy;
+ xtensa_format_encode (isa, format, vinsn->insnbuf);
+ vinsn->format = format;
+ break;
+ }
+ }
+ }
+
+ if (format == xtensa_isa_num_formats (isa))
+ return XTENSA_UNDEFINED;
+
+ return format;
+}
+
+
+/* Return the additional space needed in a frag
+ for possible relaxations of any ops in a VLIW insn.
+ Also fill out the relaxations that might be required of
+ each tinsn in the vinsn. */
+
+static int
+relaxation_requirements (vliw_insn *vinsn, bfd_boolean *pfinish_frag)
+{
+ bfd_boolean finish_frag = FALSE;
+ int extra_space = 0;
+ int slot;
+
+ for (slot = 0; slot < vinsn->num_slots; slot++)
+ {
+ TInsn *tinsn = &vinsn->slots[slot];
+ if (!tinsn_has_symbolic_operands (tinsn))
+ {
+ /* A narrow instruction could be widened later to help
+ alignment issues. */
+ if (xg_is_single_relaxable_insn (tinsn, 0, TRUE)
+ && !tinsn->is_specific_opcode
+ && vinsn->num_slots == 1)
+ {
+ /* Difference in bytes between narrow and wide insns... */
+ extra_space += 1;
+ tinsn->subtype = RELAX_NARROW;
+ }
+ }
+ else
+ {
+ if (workaround_b_j_loop_end
+ && tinsn->opcode == xtensa_jx_opcode
+ && use_transform ())
+ {
+ /* Add 2 of these. */
+ extra_space += 3; /* for the nop size */
+ tinsn->subtype = RELAX_ADD_NOP_IF_PRE_LOOP_END;
+ }
+
+ /* Need to assemble it with space for the relocation. */
+ if (xg_is_relaxable_insn (tinsn, 0)
+ && !tinsn->is_specific_opcode)
+ {
+ int max_size = xg_get_max_insn_widen_size (tinsn->opcode);
+ int max_literal_size =
+ xg_get_max_insn_widen_literal_size (tinsn->opcode);
+
+ tinsn->literal_space = max_literal_size;
+
+ tinsn->subtype = RELAX_IMMED;
+ extra_space += max_size;
+ }
+ else
+ {
+ /* A fix record will be added for this instruction prior
+ to relaxation, so make it end the frag. */
+ finish_frag = TRUE;
+ }
+ }
+ }
+ *pfinish_frag = finish_frag;
+ return extra_space;
+}
+
+
+static void
+bundle_tinsn (TInsn *tinsn, vliw_insn *vinsn)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ int slot, chosen_slot;
+
+ vinsn->format = xg_get_single_format (tinsn->opcode);
+ assert (vinsn->format != XTENSA_UNDEFINED);
+ vinsn->num_slots = xtensa_format_num_slots (isa, vinsn->format);
+
+ chosen_slot = xg_get_single_slot (tinsn->opcode);
+ for (slot = 0; slot < vinsn->num_slots; slot++)
+ {
+ if (slot == chosen_slot)
+ vinsn->slots[slot] = *tinsn;
+ else
+ {
+ vinsn->slots[slot].opcode =
+ xtensa_format_slot_nop_opcode (isa, vinsn->format, slot);
+ vinsn->slots[slot].ntok = 0;
+ vinsn->slots[slot].insn_type = ITYPE_INSN;
+ }
+ }
+}
+
+
+static bfd_boolean
+emit_single_op (TInsn *orig_insn)
+{
+ int i;
+ IStack istack; /* put instructions into here */
+ symbolS *lit_sym = NULL;
+ symbolS *label_sym = NULL;
+
+ istack_init (&istack);
+
+ /* Special-case for "movi aX, foo" which is guaranteed to need relaxing.
+ Because the scheduling and bundling characteristics of movi and
+ l32r or const16 are so different, we can do much better if we relax
+ it prior to scheduling and bundling, rather than after. */
+ if ((orig_insn->opcode == xtensa_movi_opcode
+ || orig_insn->opcode == xtensa_movi_n_opcode)
+ && !cur_vinsn.inside_bundle
+ && (orig_insn->tok[1].X_op == O_symbol
+ || orig_insn->tok[1].X_op == O_pltrel)
+ && !orig_insn->is_specific_opcode && use_transform ())
+ xg_assembly_relax (&istack, orig_insn, now_seg, frag_now, 0, 1, 0);
+ else
+ if (xg_expand_assembly_insn (&istack, orig_insn))
+ return TRUE;
+
+ for (i = 0; i < istack.ninsn; i++)
+ {
+ TInsn *insn = &istack.insn[i];
+ switch (insn->insn_type)
+ {
+ case ITYPE_LITERAL:
+ assert (lit_sym == NULL);
+ lit_sym = xg_assemble_literal (insn);
+ break;
+ case ITYPE_LABEL:
+ {
+ static int relaxed_sym_idx = 0;
+ char *label = xmalloc (strlen (FAKE_LABEL_NAME) + 12);
+ sprintf (label, "%s_rl_%x", FAKE_LABEL_NAME, relaxed_sym_idx++);
+ colon (label);
+ assert (label_sym == NULL);
+ label_sym = symbol_find_or_make (label);
+ assert (label_sym);
+ free (label);
+ }
+ break;
+ case ITYPE_INSN:
+ {
+ vliw_insn v;
+ if (lit_sym)
+ xg_resolve_literals (insn, lit_sym);
+ if (label_sym)
+ xg_resolve_labels (insn, label_sym);
+ xg_init_vinsn (&v);
+ bundle_tinsn (insn, &v);
+ finish_vinsn (&v);
+ xg_free_vinsn (&v);
+ }
+ break;
+ default:
+ assert (0);
+ break;
+ }
+ }
+ return FALSE;
+}
+
+
+static int
+total_frag_text_expansion (fragS *fragP)
+{
+ int slot;
+ int total_expansion = 0;
+
+ for (slot = 0; slot < MAX_SLOTS; slot++)
+ total_expansion += fragP->tc_frag_data.text_expansion[slot];
+
+ return total_expansion;
+}
+
+
+/* Emit a vliw instruction to the current fragment. */
+
+static void
+xg_assemble_vliw_tokens (vliw_insn *vinsn)
+{
+ bfd_boolean finish_frag;
+ bfd_boolean is_jump = FALSE;
+ bfd_boolean is_branch = FALSE;
+ xtensa_isa isa = xtensa_default_isa;
+ int i;
+ int insn_size;
+ int extra_space;
+ char *f = NULL;
+ int slot;
+ unsigned current_line, best_linenum;
+ char *current_file;
+
+ best_linenum = UINT_MAX;
+
+ if (generating_literals)
+ {
+ static int reported = 0;
+ if (reported < 4)
+ as_bad_where (frag_now->fr_file, frag_now->fr_line,
+ _("cannot assemble into a literal fragment"));
+ if (reported == 3)
+ as_bad (_("..."));
+ reported++;
+ return;
+ }
+
+ if (frag_now_fix () != 0
+ && (! frag_now->tc_frag_data.is_insn
+ || (vinsn_has_specific_opcodes (vinsn) && use_transform ())
+ || !use_transform () != frag_now->tc_frag_data.is_no_transform
+ || (directive_state[directive_longcalls]
+ != frag_now->tc_frag_data.use_longcalls)
+ || (directive_state[directive_absolute_literals]
+ != frag_now->tc_frag_data.use_absolute_literals)))
+ {
+ frag_wane (frag_now);
+ frag_new (0);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+
+ if (workaround_a0_b_retw
+ && vinsn->num_slots == 1
+ && (get_last_insn_flags (now_seg, now_subseg) & FLAG_IS_A0_WRITER) != 0
+ && xtensa_opcode_is_branch (isa, vinsn->slots[0].opcode) == 1
+ && use_transform ())
+ {
+ has_a0_b_retw = TRUE;
+
+ /* Mark this fragment with the special RELAX_ADD_NOP_IF_A0_B_RETW.
+ After the first assembly pass we will check all of them and
+ add a nop if needed. */
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 4, 4,
+ RELAX_ADD_NOP_IF_A0_B_RETW,
+ frag_now->fr_symbol,
+ frag_now->fr_offset,
+ NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 4, 4,
+ RELAX_ADD_NOP_IF_A0_B_RETW,
+ frag_now->fr_symbol,
+ frag_now->fr_offset,
+ NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+
+ for (i = 0; i < vinsn->num_slots; i++)