-/* tc-m32r.c -- Assembler for the Mitsubishi M32R/X.
+/* tc-m32r.c -- Assembler for the Mitsubishi M32R.
Copyright (C) 1996, 1997, 1998 Free Software Foundation.
This file is part of GAS, the GNU Assembler.
#include <ctype.h>
#include "as.h"
#include "subsegs.h"
+#include "symcat.h"
#include "cgen-opc.h"
+#include "cgen.h"
+/* Linked list of symbols that are debugging symbols to be defined as the
+ beginning of the current instruction. */
+typedef struct sym_link
+{
+ struct sym_link *next;
+ symbolS *symbol;
+} sym_linkS;
+
+static sym_linkS *debug_sym_link = (sym_linkS *)0;
+
+/* Structure to hold all of the different components describing an individual instruction. */
typedef struct
{
const CGEN_INSN * insn;
+ const CGEN_INSN * orig_insn;
CGEN_FIELDS fields;
#ifdef CGEN_INT_INSN
cgen_insn_t buffer [CGEN_MAX_INSN_SIZE / sizeof (cgen_insn_t)];
#endif
char * addr;
fragS * frag;
+ int num_fixups;
+ fixS * fixups [CGEN_MAX_FIXUPS];
+ int indices [MAX_OPERAND_INSTANCES];
+ sym_linkS *debug_sym_link;
}
m32r_insn;
/* Non-zero if the programmer should be warned when an explicit parallel
instruction might have constraint violations. */
static int warn_explicit_parallel_conflicts = 1;
+
+/* Non-zero if insns can be made parallel. */
+static int optimize;
/* end-sanitize-m32rx */
/* stuff for .scomm symbols. */
\f
/* start-sanitize-m32rx */
static void
-allow_m32rx (int on)
+allow_m32rx (on)
+ int on;
{
enable_m32rx = on;
}
/* end-sanitize-m32rx */
\f
-const char * md_shortopts = "";
+#define M32R_SHORTOPTS ""
+/* start-sanitize-m32rx */
+#undef M32R_SHORTOPTS
+#define M32R_SHORTOPTS "O"
+/* end-sanitize-m32rx */
+const char * md_shortopts = M32R_SHORTOPTS;
struct option md_longopts[] =
{
{"m32rx", no_argument, NULL, OPTION_M32RX},
#define OPTION_WARN (OPTION_MD_BASE + 1)
{"warn-explicit-parallel-conflicts", no_argument, NULL, OPTION_WARN},
+ {"Wp", no_argument, NULL, OPTION_WARN},
#define OPTION_NO_WARN (OPTION_MD_BASE + 2)
{"no-warn-explicit-parallel-conflicts", no_argument, NULL, OPTION_NO_WARN},
+ {"Wnp", no_argument, NULL, OPTION_NO_WARN},
/* end-sanitize-m32rx */
#if 0 /* not supported yet */
switch (c)
{
/* start-sanitize-m32rx */
+ case 'O':
+ optimize = 1;
+ break;
+
case OPTION_M32RX:
allow_m32rx (1);
break;
md_show_usage (stream)
FILE * stream;
{
- fprintf (stream, "M32R/X options:\n");
/* start-sanitize-m32rx */
- fprintf (stream, "\
---m32rx support the extended m32rx instruction set\n");
-
- fprintf (stream, "\
---warn-explicit-parallel-conflicts Warn when parallel instrucitons violate contraints\
---no-warn-explicit-parallel-conflicts Do not warn when parallel instrucitons violate contraints\n");
+ fprintf (stream, _("M32R/X specific command line options:\n"));
+ fprintf (stream, _("\
+--m32rx support the extended m32rx instruction set\n"));
+
+ fprintf (stream, _("\
+-O try to combine instructions in parallel\n"));
+
+ fprintf (stream, _("\
+--warn-explicit-parallel-conflicts warn when parallel instrucitons violate contraints\n"));
+ fprintf (stream, _("\
+--no-warn-explicit-parallel-conflicts do not warn when parallel instrucitons violate contraints\n"));
+ fprintf (stream, _("\
+--Wp synonym for --warn-explicit-parallel-conflicts\n"));
+ fprintf (stream, _("\
+--Wnp synonym for --no-warn-explicit-parallel-conflicts\n"));
/* end-sanitize-m32rx */
#if 0
- fprintf (stream, "\
---relax create linker relaxable code\n");
- fprintf (stream, "\
---cpu-desc provide runtime cpu description file\n");
+ fprintf (stream, _("\
+--relax create linker relaxable code\n"));
+ fprintf (stream, _("\
+--cpu-desc provide runtime cpu description file\n"));
#endif
}
static void fill_insn PARAMS ((int));
static void m32r_scomm PARAMS ((int));
+static void debug_sym PARAMS ((int));
+static void expand_debug_syms PARAMS ((sym_linkS *, int));
/* Set by md_assemble for use by m32r_fill_insn. */
static subsegT prev_subseg;
/* The target specific pseudo-ops which we support. */
const pseudo_typeS md_pseudo_table[] =
{
- { "word", cons, 4 },
- { "fillinsn", fill_insn, 0 },
- { "scomm", m32r_scomm, 0 },
+ { "word", cons, 4 },
+ { "fillinsn", fill_insn, 0 },
+ { "scomm", m32r_scomm, 0 },
+ { "debugsym", debug_sym, 0 },
/* start-sanitize-m32rx */
- { "m32r", allow_m32rx, 0},
- { "m32rx", allow_m32rx, 1},
+ { "m32r", allow_m32rx, 0 },
+ { "m32rx", allow_m32rx, 1 },
/* end-sanitize-m32rx */
{ NULL, NULL, 0 }
};
seen_relaxable_p = 0;
}
+/* Record the symbol so that when we output the insn, we can create
+ a symbol that is at the start of the instruction. This is used
+ to emit the label for the start of a breakpoint without causing
+ the assembler to emit a NOP if the previous instruction was a
+ 16 bit instruction. */
+
+static void
+debug_sym (ignore)
+ int ignore;
+{
+ register char *name;
+ register char delim;
+ register char *end_name;
+ register symbolS *symbolP;
+ register sym_linkS *link;
+
+ name = input_line_pointer;
+ delim = get_symbol_end ();
+ end_name = input_line_pointer;
+
+ if ((symbolP = symbol_find (name)) == NULL
+ && (symbolP = md_undefined_symbol (name)) == NULL)
+ {
+ symbolP = symbol_new (name, undefined_section, 0, &zero_address_frag);
+ }
+
+ symbol_table_insert (symbolP);
+ if (S_IS_DEFINED (symbolP) && S_GET_SEGMENT (symbolP) != reg_section)
+ as_bad (_("symbol `%s' already defined"), S_GET_NAME (symbolP));
+
+ else
+ {
+ link = (sym_linkS *) xmalloc (sizeof (sym_linkS));
+ link->symbol = symbolP;
+ link->next = debug_sym_link;
+ debug_sym_link = link;
+ symbolP->local = 1;
+ }
+
+ *end_name = delim;
+ demand_empty_rest_of_line ();
+}
+
+/* Second pass to expanding the debug symbols, go through linked
+ list of symbols and reassign the address. */
+
+static void
+expand_debug_syms (syms, align)
+ sym_linkS *syms;
+ int align;
+{
+ char *save_input_line = input_line_pointer;
+ sym_linkS *next_syms;
+ expressionS exp;
+
+ if (!syms)
+ return;
+
+ (void) m32r_do_align (align, NULL, 0, 0);
+ for (; syms != (sym_linkS *)0; syms = next_syms)
+ {
+ symbolS *symbolP = syms->symbol;
+ next_syms = syms->next;
+ input_line_pointer = ".\n";
+ pseudo_set (symbolP);
+ free ((char *)syms);
+ }
+
+ input_line_pointer = save_input_line;
+}
+
/* Cover function to fill_insn called after a label and at end of assembly.
The result is always 1: we're called in a conditional to see if the
m32r_fill_insn (done)
int done;
{
- segT seg;
- subsegT subseg;
-
if (prev_seg != NULL)
{
- seg = now_seg;
- subseg = now_subseg;
-
+ segT seg = now_seg;
+ subsegT subseg = now_subseg;
+
subseg_set (prev_seg, prev_subseg);
fill_insn (0);
}
/* start-sanitize-m32rx */
-/* Returns non zero if the given instruction writes to a destination register. */
-static int
-writes_to_dest_reg (insn)
- const CGEN_INSN * insn;
-{
- unsigned char * syntax = CGEN_SYNTAX_STRING (CGEN_INSN_SYNTAX (insn));
- unsigned char c;
-
- /* Scan the syntax string looking for a destination register. */
- while ((c = (* syntax ++)) != 0)
- if (c == 128 + M32R_OPERAND_DR)
- break;
- return c;
-}
+#define OPERAND_IS_COND_BIT(operand, indices, index) \
+ (CGEN_OPERAND_INSTANCE_HW (operand)->type == HW_H_COND \
+ || (CGEN_OPERAND_INSTANCE_HW (operand)->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. */
-/* Returns non zero if the given instruction reads from a source register.
- Ignores the first 'num_ignore' macthes in the syntax string. */
static int
-reads_from_src_reg (insn, num_ignore)
- const CGEN_INSN * insn;
- int num_ignore;
+first_writes_to_seconds_operands (a, b, check_outputs)
+ m32r_insn * a;
+ m32r_insn * b;
+ const int check_outputs;
{
- unsigned char * syntax = CGEN_SYNTAX_STRING (CGEN_INSN_SYNTAX (insn));
- unsigned char c;
-
- /* Scan the syntax string looking for a source register. */
- while ((c = (* syntax ++)) != 0)
+ const CGEN_OPERAND_INSTANCE * a_operands = CGEN_INSN_OPERANDS (a->insn);
+ const CGEN_OPERAND_INSTANCE * b_ops = CGEN_INSN_OPERANDS (b->insn);
+ int a_index;
+
+ /* 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;
+ CGEN_OPERAND_INSTANCE_TYPE (a_operands) != CGEN_OPERAND_INSTANCE_END;
+ a_index ++, a_operands ++)
{
- if ( c == 128 + M32R_OPERAND_SR
- || c == 128 + M32R_OPERAND_SRC1
- || c == 128 + M32R_OPERAND_SRC2)
+ if (CGEN_OPERAND_INSTANCE_TYPE (a_operands) == CGEN_OPERAND_INSTANCE_OUTPUT)
{
- if (num_ignore -- > 0)
- continue;
+ int b_index;
+ const CGEN_OPERAND_INSTANCE * 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;
+ CGEN_OPERAND_INSTANCE_TYPE (b_operands) != CGEN_OPERAND_INSTANCE_END;
+ b_index ++, b_operands ++)
+ {
+ if ((CGEN_OPERAND_INSTANCE_TYPE (b_operands) ==
+ (check_outputs ? CGEN_OPERAND_INSTANCE_OUTPUT : CGEN_OPERAND_INSTANCE_INPUT))
+ && OPERAND_IS_COND_BIT (b_operands, b->indices, b_index))
+ return 1;
+ }
+ }
else
- break;
+ {
+ /* 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;
+ CGEN_OPERAND_INSTANCE_TYPE (b_operands) != CGEN_OPERAND_INSTANCE_END;
+ b_index ++, b_operands ++)
+ {
+ if ((CGEN_OPERAND_INSTANCE_TYPE (b_operands) ==
+ (check_outputs ? CGEN_OPERAND_INSTANCE_OUTPUT : CGEN_OPERAND_INSTANCE_INPUT))
+ && (CGEN_OPERAND_INSTANCE_HW (b_operands) == CGEN_OPERAND_INSTANCE_HW (a_operands))
+ && (a->indices [a_index] == b->indices [b_index]))
+ return 1;
+ }
+ }
}
}
- return c;
+ return 0;
}
-/* Returns the integer value of the destination register held in the fields. */
-#define get_dest_reg(fields) (fields).f_r1
+/* Returns true if the insn can (potentially) alter the program counter. */
-/* Returns an integer representing the source register of the given type. */
static int
-get_src_reg (syntax_field, fields)
- unsigned char syntax_field;
- CGEN_FIELDS * fields;
+writes_to_pc (a)
+ m32r_insn * a;
{
- switch (syntax_field)
- {
- case 128 + M32R_OPERAND_SR: return fields->f_r2;
- /* Relies upon the fact that no instruction with a $src1 operand
- also has a $dr operand. */
- case 128 + M32R_OPERAND_SRC1: return fields->f_r1;
- case 128 + M32R_OPERAND_SRC2: return fields->f_r2;
- default: abort(); return -1;
- }
-}
+#if 0 /* Once PC operands are working.... */
+ const CGEN_OPERAND_INSTANCE * a_operands == CGEN_INSN_OPERANDS (a->insn);
-/* Returns zero iff the output register of instruction 'a'
- is an input register to instruction 'b'. */
-static int
-check_parallel_io_clash (a, b)
- m32r_insn * a;
- m32r_insn * b;
-{
- if (writes_to_dest_reg (a->insn))
+ if (a_operands == NULL)
+ return 0;
+
+ while (CGEN_OPERAND_INSTANCE_TYPE (a_operands) != CGEN_OPERAND_INSTANCE_END)
{
- unsigned char syntax_field;
- int skip = 0;
+ if (CGEN_OPERAND_INSTANCE_OPERAND (a_operands) != NULL
+ && CGEN_OPERAND_INDEX (CGEN_OPERAND_INSTANCE_OPERAND (a_operands)) == M32R_OPERAND_PC)
+ return 1;
- while (syntax_field = reads_from_src_reg (b->insn, skip ++))
- {
- if (get_src_reg (syntax_field, & b->fields) == get_dest_reg (a->fields))
- return 0;
- }
+ a_operands ++;
}
-
- return 1;
+#else
+ if (CGEN_INSN_ATTR (a->insn, CGEN_INSN_UNCOND_CTI)
+ || CGEN_INSN_ATTR (a->insn, CGEN_INSN_COND_CTI))
+ return 1;
+#endif
+ return 0;
}
-
/* Returns NULL if the two 16 bit insns can be executed in parallel,
otherwise it returns a pointer to an error message explaining why not. */
+
static const char *
can_make_parallel (a, b)
m32r_insn * a;
m32r_insn * b;
{
-/* start-sanitize-m32rx */
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 (a->insn, CGEN_INSN_PIPE);
b_pipe = CGEN_INSN_ATTR (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.";
-
+ 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)
- return "Instructions share the same execution pipeline";
-
-/* end-sanitize-m32rx */
- if ( writes_to_dest_reg (a->insn)
- && writes_to_dest_reg (b->insn)
- && (get_dest_reg (a->fields) == get_dest_reg (b->fields)))
- return "Instructions write to the same destination register.";
-
+ return _("Instructions share the same execution pipeline");
+
return NULL;
}
#ifdef CGEN_INT_INSN
+
static void
make_parallel (buffer)
cgen_insn_t * buffer;
bfd_putl16 (value, (char *) buffer);
}
}
+
#else
+
static void
make_parallel (buffer)
char * buffer;
buffer [CGEN_CURRENT_ENDIAN == CGEN_ENDIAN_BIG ? 0 : 1] |= 0x80;
}
-#endif
+#endif /* ! CGEN_INT_INSN */
static void
assemble_parallel_insn (str, str2)
if (prev_insn.insn)
fill_insn (0);
+ first.debug_sym_link = debug_sym_link;
+ debug_sym_link = (sym_linkS *)0;
+
/* Parse the first instruction. */
if (! (first.insn = CGEN_SYM (assemble_insn)
(str, & first.fields, first.buffer, & errmsg)))
as_bad (errmsg);
return;
}
-
-/* start-sanitize-m32rx */
- /* Check to see if this is an allowable parallel insn. */
- if (CGEN_INSN_ATTR (first.insn, CGEN_INSN_PIPE) == PIPE_NONE)
+
+ if (! enable_m32rx
+ /* FIXME: Need standard macro to perform this test. */
+ && CGEN_INSN_ATTR (first.insn, CGEN_INSN_MACH) == (1 << MACH_M32RX))
{
- as_bad ("instruction '%s' cannot be executed in parallel.", str);
+ as_bad (_("instruction '%s' is for the M32RX only"), str);
return;
}
-
- if (! enable_m32rx
- && CGEN_INSN_ATTR (first.insn, CGEN_INSN_MACH) == (1 << MACH_M32RX))
+
+ /* Check to see if this is an allowable parallel insn. */
+ if (CGEN_INSN_ATTR (first.insn, CGEN_INSN_PIPE) == PIPE_NONE)
{
- as_bad ("instruction '%s' is for the M32RX only", str);
+ as_bad (_("instruction '%s' cannot be executed in parallel."), str);
return;
}
-/* end-sanitize-m32rx */
*str2 = '|'; /* Restore the original assembly text, just in case it is needed. */
str3 = str; /* Save the original string pointer. */
str = str2 + 2; /* Advanced past the parsed string. */
str2 = str3; /* Remember the entire string in case it is needed for error messages. */
-
+
/* Preserve any fixups that have been generated and reset the list to empty. */
cgen_save_fixups();
+ /* 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;
+ first.insn = m32r_cgen_lookup_get_insn_operands (NULL,
+ bfd_getb16 ((char *) first.buffer),
+ 16,
+ first.indices);
+ if (first.insn == NULL)
+ as_fatal (_("internal error: m32r_cgen_lookup_get_insn_operands failed for first insn"));
+
+ second.debug_sym_link = NULL;
+
/* Parse the second instruction. */
if (! (second.insn = CGEN_SYM (assemble_insn)
(str, & second.fields, second.buffer, & errmsg)))
return;
}
-/* start-sanitize-m32rx */
/* Check it. */
if (! enable_m32rx
&& CGEN_INSN_ATTR (second.insn, CGEN_INSN_MACH) == (1 << MACH_M32RX))
{
- as_bad ("instruction '%s' is for the M32RX only", str);
+ as_bad (_("instruction '%s' is for the M32RX only"), str);
+ return;
+ }
+
+ /* Check to see if this is an allowable parallel insn. */
+ if (CGEN_INSN_ATTR (second.insn, CGEN_INSN_PIPE) == PIPE_NONE)
+ {
+ as_bad (_("instruction '%s' cannot be executed in parallel."), str);
return;
}
if (! enable_m32rx)
{
- if ( strcmp (first.insn->name, "nop") != 0
- && strcmp (second.insn->name, "nop") != 0)
+ if (CGEN_INSN_NUM (first.insn) != M32R_INSN_NOP
+ && CGEN_INSN_NUM (second.insn) != M32R_INSN_NOP)
{
- as_bad ("'%s': only the NOP instruction can be issued in parallel on the m32r", str2);
+ as_bad (_("'%s': only the NOP instruction can be issued in parallel on the m32r"), str2);
return;
}
}
-/* end-sanitize-m32rx */
+
+ /* Get the indices of the operands of the instruction. */
+ second.orig_insn = second.insn;
+ second.insn = m32r_cgen_lookup_get_insn_operands (NULL,
+ bfd_getb16 ((char *) second.buffer),
+ 16,
+ second.indices);
+ if (second.insn == NULL)
+ as_fatal (_("internal error: m32r_cgen_lookup_get_insn_operands failed for second insn"));
/* We assume that if the first instruction writes to a register that is
read by the second instruction it is because the programmer intended
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. */
+ instructions are deliberate and should not produce errors. */
- if (can_make_parallel (& first, & second) == NULL)
+ if (warn_explicit_parallel_conflicts)
{
- if (warn_explicit_parallel_conflicts
- && (! check_parallel_io_clash (& first, & second)))
- as_warn ("%s: output of first instruction fails to overwrite input of second instruction.", str2);
+ if (first_writes_to_seconds_operands (& first, & second, false))
+ 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))
+ as_warn (_("%s: output of 2nd instruction is the same as an input to 1st instruction - is this intentional ?"), str2);
+ }
+ if ((errmsg = (char *) can_make_parallel (& first, & second)) == NULL)
+ {
/* Get the fixups for the first instruction. */
cgen_swap_fixups ();
/* Write it out. */
- (void) cgen_asm_finish_insn (first.insn, first.buffer,
- CGEN_FIELDS_BITSIZE (& first.fields));
+ expand_debug_syms (first.debug_sym_link, 1);
+ cgen_asm_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. */
make_parallel (second.buffer);
cgen_restore_fixups ();
/* Write it out. */
- (void) cgen_asm_finish_insn (second.insn, second.buffer,
- CGEN_FIELDS_BITSIZE (& second.fields));
+ expand_debug_syms (second.debug_sym_link, 1);
+ cgen_asm_finish_insn (second.orig_insn, second.buffer,
+ CGEN_FIELDS_BITSIZE (& second.fields), 0, NULL);
}
- else if ((errmsg = (char *) can_make_parallel (& second, & first,
- false, false)) == NULL)
+ /* Try swapping the instructions to see if they work that way. */
+ else if (can_make_parallel (& second, & first) == NULL)
{
- if (warn_explicit_parallel_conflicts
- && (! check_parallel_io_clash (& second, & first)))
- as_warn ("%s: output of second instruction fails to overwrite input of first instruction.", str2);
-
/* Write out the second instruction first. */
- (void) cgen_asm_finish_insn (second.insn, second.buffer,
- CGEN_FIELDS_BITSIZE (& second.fields));
+ expand_debug_syms (second.debug_sym_link, 1);
+ cgen_asm_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);
cgen_restore_fixups ();
/* Write out the first instruction. */
- (void) cgen_asm_finish_insn (first.insn, first.buffer,
- CGEN_FIELDS_BITSIZE (& first.fields));
+ expand_debug_syms (first.debug_sym_link, 1);
+ cgen_asm_finish_insn (first.orig_insn, first.buffer,
+ CGEN_FIELDS_BITSIZE (& first.fields), 0, NULL);
}
else
{
/* Set these so m32r_fill_insn can use them. */
prev_seg = now_seg;
prev_subseg = now_subseg;
-
- return;
}
+
/* end-sanitize-m32rx */
}
/* end-sanitize-m32rx */
+ insn.debug_sym_link = debug_sym_link;
+ debug_sym_link = (sym_linkS *)0;
+
insn.insn = CGEN_SYM (assemble_insn) (str, & insn.fields, insn.buffer, & errmsg);
if (!insn.insn)
{
/* start-sanitize-m32rx */
if (! enable_m32rx && CGEN_INSN_ATTR (insn.insn, CGEN_INSN_MACH) == (1 << MACH_M32RX))
{
- as_bad ("instruction '%s' is for the M32RX only", str);
+ as_bad (_("instruction '%s' is for the M32RX only"), str);
return;
}
/* end-sanitize-m32rx */
/* 32 bit insns must live on 32 bit boundaries. */
if (prev_insn.insn || seen_relaxable_p)
{
- /* FIXME: If calling fill_insn too many times turns us into a memory
+ /* ??? If calling fill_insn too many times turns us into a memory
pig, can we call assemble_nop instead of !seen_relaxable_p? */
fill_insn (0);
}
-
- (void) cgen_asm_finish_insn (insn.insn, insn.buffer,
- CGEN_FIELDS_BITSIZE (& insn.fields));
+
+ expand_debug_syms (insn.debug_sym_link, 2);
+
+ /* Doesn't really matter what we pass for RELAX_P here. */
+ cgen_asm_finish_insn (insn.insn, insn.buffer,
+ CGEN_FIELDS_BITSIZE (& insn.fields), 1, NULL);
}
else
{
+ int on_32bit_boundary_p;
/* start-sanitize-m32rx */
-/* start-sanitize-phase2-m32rx */
int swap = false;
-/* end-sanitize-phase2-m32rx */
/* end-sanitize-m32rx */
-
+
if (CGEN_INSN_BITSIZE (insn.insn) != 16)
abort();
-
- /* Keep track of whether we've seen a pair of 16 bit insns.
- prev_insn.insn is NULL when we're on a 32 bit boundary. */
- if (prev_insn.insn)
- {
+
+ insn.orig_insn = insn.insn;
/* start-sanitize-m32rx */
-/* start-sanitize-phase2-m32rx */
- /* Look to see if this instruction can be combined with the
- previous instruction to make one, parallel, 32 bit instruction.
- If the previous instruction (potentially) changed the flow of
- program control, then it cannot be combined with the current
- instruction. Also if the output of the previous instruction
- is used as an input to the current instruction then it cannot
- be combined. Otherwise call can_make_parallel() with both
- orderings of the instructions to see if they can be combined. */
- if ( ! CGEN_INSN_ATTR (prev_insn.insn, CGEN_INSN_COND_CTI)
- && ! CGEN_INSN_ATTR (prev_insn.insn, CGEN_INSN_UNCOND_CTI)
- && check_parallel_io_clash (& prev_insn, &insn)
- )
- {
- if (can_make_parallel (& prev_insn, & insn) == NULL)
- make_parallel (insn.buffer);
- else if (can_make_parallel (& insn, & prev_insn.insn) == NULL)
- swap = true;
- }
-/* end-sanitize-phase2-m32rx */
-/* end-sanitize-m32rx */
-
- prev_insn.insn = NULL;
+ if (enable_m32rx)
+ {
+ /* Get the indices of the operands of the instruction.
+ FIXME: See assemble_parallel for notes on orig_insn. */
+ insn.insn = m32r_cgen_lookup_get_insn_operands (NULL,
+ bfd_getb16 ((char *) insn.buffer),
+ 16,
+ insn.indices);
+ if (insn.insn == NULL)
+ as_fatal (_("internal error: m32r_cgen_get_insn_operands failed"));
}
- else
+/* end-sanitize-m32rx */
+
+ /* Compute whether we're on a 32 bit boundary or not.
+ prev_insn.insn is NULL when we're on a 32 bit boundary. */
+ on_32bit_boundary_p = prev_insn.insn == NULL;
+
+/* start-sanitize-m32rx */
+ /* Look to see if this instruction can be combined with the
+ previous instruction to make one, parallel, 32 bit instruction.
+ If the previous instruction (potentially) changed the flow of
+ program control, then it cannot be combined with the current
+ instruction. If the current instruction is relaxable, then it
+ might be replaced with a longer version, so we cannot combine it.
+ Also if the output of the previous instruction is used as an
+ input to the current instruction then it cannot be combined.
+ Otherwise call can_make_parallel() with both orderings of the
+ instructions to see if they can be combined. */
+ if ( ! on_32bit_boundary_p
+ && enable_m32rx
+ && optimize
+ && CGEN_INSN_ATTR (insn.orig_insn, CGEN_INSN_RELAXABLE) == 0
+ && ! writes_to_pc (& prev_insn)
+ && ! first_writes_to_seconds_operands (& prev_insn, &insn, false)
+ )
{
- prev_insn = insn;
+ if (can_make_parallel (& prev_insn, & insn) == NULL)
+ make_parallel (insn.buffer);
+ else if (can_make_parallel (& insn, & prev_insn) == NULL)
+ swap = true;
}
+/* end-sanitize-m32rx */
- /* Record the frag that might be used by this insn. */
- insn.frag = frag_now;
- insn.addr = cgen_asm_finish_insn (insn.insn, insn.buffer,
- CGEN_FIELDS_BITSIZE (& insn.fields));
+ expand_debug_syms (insn.debug_sym_link, 1);
+
+ {
+ int i;
+ finished_insnS fi;
+
+ /* Ensure each pair of 16 bit insns is in the same frag. */
+ frag_grow (4);
+
+ cgen_asm_finish_insn (insn.orig_insn, insn.buffer,
+ CGEN_FIELDS_BITSIZE (& insn.fields),
+ 1 /*relax_p*/, &fi);
+ insn.addr = fi.addr;
+ insn.frag = fi.frag;
+ insn.num_fixups = fi.num_fixups;
+ for (i = 0; i < fi.num_fixups; ++i)
+ insn.fixups[i] = fi.fixups[i];
+ }
/* start-sanitize-m32rx */
-/* start-sanitize-phase2-m32rx */
if (swap)
{
- int tmp;
-
+ int i,tmp;
+
#define SWAP_BYTES(a,b) tmp = a; a = b; b = tmp
/* Swap the two insns */
make_parallel (insn.addr);
/* Swap any relaxable frags recorded for the two insns. */
+ /* FIXME: Clarify. relaxation precludes parallel insns */
if (prev_insn.frag->fr_opcode == prev_insn.addr)
- {
- prev_insn.frag->fr_opcode = insn.addr;
- }
+ prev_insn.frag->fr_opcode = insn.addr;
else if (insn.frag->fr_opcode == insn.addr)
- {
- insn.frag->fr_opcode = prev_insn.addr;
- }
+ insn.frag->fr_opcode = prev_insn.addr;
+
+ /* Update the addresses in any fixups.
+ Note that we don't have to handle the case where each insn is in
+ a different frag as we ensure they're in the same frag above. */
+ for (i = 0; i < prev_insn.num_fixups; ++i)
+ prev_insn.fixups[i]->fx_where += 2;
+ for (i = 0; i < insn.num_fixups; ++i)
+ insn.fixups[i]->fx_where -= 2;
}
-/* end-sanitize-phase2-m32rx */
-
- /* Record where this instruction was assembled. */
- prev_insn.addr = insn.addr;
- prev_insn.frag = insn.frag;
/* end-sanitize-m32rx */
+
+ /* Keep track of whether we've seen a pair of 16 bit insns.
+ prev_insn.insn is NULL when we're on a 32 bit boundary. */
+ if (on_32bit_boundary_p)
+ prev_insn = insn;
+ else
+ prev_insn.insn = NULL;
/* If the insn needs the following one to be on a 32 bit boundary
(e.g. subroutine calls), fill this insn's slot. */
- if (prev_insn.insn
- && CGEN_INSN_ATTR (insn.insn, CGEN_INSN_FILL_SLOT) != 0)
+ if (on_32bit_boundary_p
+ && CGEN_INSN_ATTR (insn.orig_insn, CGEN_INSN_FILL_SLOT) != 0)
fill_insn (0);
/* If this is a relaxable insn (can be replaced with a larger version)
mark the fact so that we can emit an alignment directive for a
following 32 bit insn if we see one. */
- if (CGEN_INSN_ATTR (insn.insn, CGEN_INSN_RELAXABLE) != 0)
+ if (CGEN_INSN_ATTR (insn.orig_insn, CGEN_INSN_RELAXABLE) != 0)
seen_relaxable_p = 1;
}
SKIP_WHITESPACE ();
if (* input_line_pointer != ',')
{
- as_bad ("Expected comma after symbol-name: rest of line ignored.");
+ as_bad (_("Expected comma after symbol-name: rest of line ignored."));
ignore_rest_of_line ();
return;
}
input_line_pointer ++; /* skip ',' */
if ((size = get_absolute_expression ()) < 0)
{
- as_warn (".SCOMMon length (%ld.) <0! Ignored.", (long) size);
+ as_warn (_(".SCOMMon length (%ld.) <0! Ignored."), (long) size);
ignore_rest_of_line ();
return;
}
align = get_absolute_expression ();
if (align <= 0)
{
- as_warn ("ignoring bad alignment");
+ as_warn (_("ignoring bad alignment"));
align = 8;
}
}
continue;
if (align != 1)
{
- as_bad ("Common alignment not a power of 2");
+ as_bad (_("Common alignment not a power of 2"));
ignore_rest_of_line ();
return;
}
if (S_IS_DEFINED (symbolP))
{
- as_bad ("Ignoring attempt to re-define symbol `%s'.",
+ as_bad (_("Ignoring attempt to re-define symbol `%s'."),
S_GET_NAME (symbolP));
ignore_rest_of_line ();
return;
if (S_GET_VALUE (symbolP) && S_GET_VALUE (symbolP) != (valueT) size)
{
- as_bad ("Length of .scomm \"%s\" is already %ld. Not changed to %ld.",
+ as_bad (_("Length of .scomm \"%s\" is already %ld. Not changed to %ld."),
S_GET_NAME (symbolP),
(long) S_GET_VALUE (symbolP),
(long) size);
{
/* symbol must be resolved by linker */
if (fragP->fr_offset & 3)
- as_warn ("Addend to unresolved symbol not on word boundary.");
+ as_warn (_("Addend to unresolved symbol not on word boundary."));
addend = fragP->fr_offset >> 2;
}
else
if (pass == 1)
as_warn_where (l->fixp->fx_file, l->fixp->fx_line,
- "Unmatched high/shigh reloc");
+ _("Unmatched high/shigh reloc"));
}
}
}
default:
* sizeP = 0;
- return "Bad call to md_atof()";
+ return _("Bad call to md_atof()");
}
t = atof_ieee (input_line_pointer, type, words);
return 0;
}
+
+void
+m32r_elf_section_change_hook ()
+{
+ /* If we have reached the end of a section and we have just emitted a
+ 16 bit insn, then emit a nop to make sure that the section ends on
+ a 32 bit boundary. */
+
+ if (prev_insn.insn || seen_relaxable_p)
+ (void) m32r_fill_insn (0);
+}