/* tc-mmix.c -- Assembler for Don Knuth's MMIX.
- Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation.
+ Copyright (C) 2001-2020 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
+ the Free Software Foundation; either version 3, or (at your option)
any later version.
GAS is distributed in the hope that it will be useful,
compatible syntax, but the main purpose is to serve GCC. */
-#include <stdio.h>
-#include <limits.h>
#include "as.h"
+#include <limits.h>
#include "subsegs.h"
-#include "bfd.h"
#include "elf/mmix.h"
#include "opcode/mmix.h"
#include "safe-ctype.h"
for example assert something of what it became or make a relocation. */
enum mmix_fixup_action
- {
- mmix_fixup_byte,
- mmix_fixup_register,
- mmix_fixup_register_or_adjust_for_byte
- };
+{
+ mmix_fixup_byte,
+ mmix_fixup_register,
+ mmix_fixup_register_or_adjust_for_byte
+};
static int get_spec_regno (char *);
static int get_operands (int, char *, expressionS *);
expressionS exp;
} mmix_raw_gregs[MAX_GREGS];
+static struct loc_assert_s
+ {
+ segT old_seg;
+ symbolS *loc_sym;
+ fragS *frag;
+ struct loc_assert_s *next;
+ } *loc_asserts = NULL;
+
/* Fixups for all unique GREG registers. We store the fixups here in
md_convert_frag, then we use the array to convert
BFD_RELOC_MMIX_BASE_PLUS_OFFSET fixups in tc_gen_reloc. The index is
/* Do we have a BSPEC in progress? */
static int doing_bspec = 0;
-static char *bspec_file;
+static const char *bspec_file;
static unsigned int bspec_line;
struct option md_longopts[] =
const char line_separator_chars[] = ";";
-const char mmix_exp_chars[] = "eE";
+const char EXP_CHARS[] = "eE";
-const char mmix_flt_chars[] = "rf";
+const char FLT_CHARS[] = "rf";
/* Fill in the offset-related part of GETA or Bcc. */
p++;
sregp = p;
input_line_pointer = sregp;
- c = get_symbol_end ();
+ c = get_symbol_name (&sregp);
sregend = input_line_pointer;
+ if (c == '"')
+ ++ input_line_pointer;
}
}
else
expp_sreg = &exp[0];
expp_reg = &exp[1];
- sregp = p;
- c = get_symbol_end ();
- sregend = p = input_line_pointer;
- *p = c;
+ c = get_symbol_name (&sregp);
+ sregend = input_line_pointer;
+ restore_line_pointer (c);
+ p = input_line_pointer;
/* Skip whitespace */
while (*p == ' ' || *p == '\t')
/* Handle MMIX-specific option. */
int
-md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+md_parse_option (int c, const char *arg ATTRIBUTE_UNUSED)
{
switch (c)
{
pass expressions as symbols and use fix_new, not fix_new_exp. */
sym = make_expr_symbol (exp + 1);
+ /* Mark the symbol as being OK for a reloc. */
+ symbol_get_bfdsym (sym)->flags |= BSF_KEEP;
+
/* Now we know it can be a "base address plus offset". Add
proper fixup types so we can handle this later, when we've
parsed everything. */
&& ((exp[1].X_op == O_register
&& exp[1].X_add_number < 512)
|| (exp[1].X_op == O_constant
- && exp[1].X_add_number < 0
- && exp[1].X_add_number > 4)
+ && (exp[1].X_add_number < 0
+ || exp[1].X_add_number > 4))
|| (exp[1].X_op != O_register
&& exp[1].X_op != O_constant))))
{
break;
case mmix_operands_xyz_opt:
- /* SWYM, TRIP, TRAP: zero, one, two or three operands. */
+ /* SWYM, TRIP, TRAP: zero, one, two or three operands. It's
+ unspecified whether operands are registers or constants, but
+ when we find register syntax, we require operands to be literal and
+ within 0..255. */
if (n_operands == 0 && ! mmix_gnu_syntax)
/* Zeros are in place - nothing needs to be done for zero
operands. We don't allow this in GNU syntax mode, because it
{
if (exp[0].X_op == O_constant)
{
- if (exp[0].X_add_number > 255*255*255
+ if (exp[0].X_add_number > 255*256*256
|| exp[0].X_add_number < 0)
{
as_bad (_("invalid operands to opcode %s: `%s'"),
if (exp[1].X_op == O_constant)
{
- if (exp[1].X_add_number > 255*255
+ if (exp[1].X_add_number > 255*256
|| exp[1].X_add_number < 0)
{
as_bad (_("invalid operands to opcode %s: `%s'"),
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
1, exp + 2, 0, BFD_RELOC_8);
}
- else if (n_operands <= 3
- && (strcmp (instruction->name, "trip") == 0
- || strcmp (instruction->name, "trap") == 0))
+ else
{
- /* The meaning of operands to TRIP and TRAP are not defined, so
- we add combinations not handled above here as we find them. */
+ /* We can't get here for other cases. */
+ gas_assert (n_operands <= 3);
+
+ /* The meaning of operands to TRIP and TRAP is not defined (and
+ SWYM operands aren't enforced in mmixal, so let's avoid
+ that). We add combinations not handled above here as we find
+ them and as they're reported. */
if (n_operands == 3)
{
/* Don't require non-register operands. Always generate
maintenance problems. TRIP is supposed to be a rare
instruction, so the overhead should not matter. We
aren't allowed to fix_new_exp for an expression which is
- an O_register at this point, however. */
+ an O_register at this point, however.
+
+ Don't use BFD_RELOC_MMIX_REG_OR_BYTE as that modifies
+ the insn for a register in the Z field and we want
+ consistency. */
if (exp[0].X_op == O_register)
opcodep[1] = exp[0].X_add_number;
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
- 1, exp, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+ 1, exp, 0, BFD_RELOC_8);
if (exp[1].X_op == O_register)
opcodep[2] = exp[1].X_add_number;
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
- 1, exp + 1, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+ 1, exp + 1, 0, BFD_RELOC_8);
if (exp[2].X_op == O_register)
opcodep[3] = exp[2].X_add_number;
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
- 1, exp + 2, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+ 1, exp + 2, 0, BFD_RELOC_8);
}
else if (n_operands == 2)
{
if (exp[0].X_op == O_register)
- opcodep[2] = exp[0].X_add_number;
+ opcodep[1] = exp[0].X_add_number;
else
- fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
- 1, exp, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
+ 1, exp, 0, BFD_RELOC_8);
if (exp[1].X_op == O_register)
opcodep[3] = exp[1].X_add_number;
else
- fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
- 1, exp + 1, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
+ 2, exp + 1, 0, BFD_RELOC_16);
}
else
{
- as_bad (_("unsupported operands to %s: `%s'"),
- instruction->name, operands);
- return;
+ /* We can't get here for other cases. */
+ gas_assert (n_operands == 1 && exp[0].X_op == O_register);
+
+ opcodep[3] = exp[0].X_add_number;
}
}
- else
- {
- as_bad (_("invalid operands to opcode %s: `%s'"),
- instruction->name, operands);
- return;
- }
break;
case mmix_operands_resume:
SKIP_WHITESPACE ();
- p = input_line_pointer;
-
- c = get_symbol_end ();
-
- /* Reseting prefix? */
+ c = get_symbol_name (&p);
+
+ /* Resetting prefix? */
if (*p == ':' && p[1] == 0)
mmix_current_prefix = NULL;
else
mmix_current_prefix = p;
}
- *input_line_pointer = c;
+ (void) restore_line_pointer (c);
mmix_handle_rest_of_empty_line ();
}
mmix_greg_internal (char *label)
{
expressionS *expP = &mmix_raw_gregs[n_of_raw_gregs].exp;
+ segT section;
/* Don't set the section to register contents section before the
expression has been parsed; it may refer to the current position. */
- expression (expP);
+ section = expression (expP);
/* FIXME: Check that no expression refers to the register contents
section. May need to be done in elf64-mmix.c. */
expP->X_op_symbol = NULL;
}
+ if (section == undefined_section)
+ {
+ /* This is an error or a LOC with an expression involving
+ forward references. For the expression to be correctly
+ evaluated, we need to force a proper symbol; gas loses track
+ of the segment for "local symbols". */
+ if (expP->X_op == O_add)
+ {
+ symbol_get_value_expression (expP->X_op_symbol);
+ symbol_get_value_expression (expP->X_add_symbol);
+ }
+ else
+ {
+ gas_assert (expP->X_op == O_symbol);
+ symbol_get_value_expression (expP->X_add_symbol);
+ }
+ }
+
/* We must handle prefixes here, as we save the labels and expressions
to be output later. */
mmix_raw_gregs[n_of_raw_gregs].label
{
char *p;
char c;
- p = input_line_pointer;
/* This will skip over what can be a symbol and zero out the next
character, which we assume is a ',' or other meaningful delimiter.
What comes after that is the initializer expression for the
register. */
- c = get_symbol_end ();
+ c = get_symbol_name (&p);
+
+ if (c == '"')
+ c = * ++ input_line_pointer;
if (! is_end_of_line[(unsigned char) c])
input_line_pointer++;
if (sec == NULL)
as_fatal (_("can't create section %s"), newsecname);
- if (!bfd_set_section_flags (stdoutput, sec,
- bfd_get_section_flags (stdoutput, sec)
- | SEC_READONLY))
+ if (!bfd_set_section_flags (sec,
+ bfd_section_flags (sec) | SEC_READONLY))
as_fatal (_("can't set section flags for section %s"), newsecname);
}
subseg_set (sec, 0);
/* Save position for missing ESPEC. */
- as_where (&bspec_file, &bspec_line);
+ bspec_file = as_where (&bspec_line);
doing_bspec = 1;
}
emitted is stored in *sizeP . An error message is returned, or NULL on
OK. */
-char *
+const char *
md_atof (int type, char *litP, int *sizeP)
{
- int prec;
- LITTLENUM_TYPE words[4];
- char *t;
- int i;
-
- switch (type)
- {
- /* FIXME: Having 'f' in mmix_flt_chars (and here) makes it
- problematic to also have a forward reference in an expression.
- The testsuite wants it, and it's customary.
- We'll deal with the real problems when they come; we share the
- problem with most other ports. */
- case 'f':
- case 'r':
- 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 = 0; i < prec; i++)
- {
- md_number_to_chars (litP, (valueT) words[i], 2);
- litP += 2;
- }
- return NULL;
+ if (type == 'r')
+ type = 'f';
+ /* FIXME: Having 'f' in FLT_CHARS (and here) makes it
+ problematic to also have a forward reference in an expression.
+ The testsuite wants it, and it's customary.
+ We'll deal with the real problems when they come; we share the
+ problem with most other ports. */
+ return ieee_md_atof (type, litP, sizeP, TRUE);
}
/* Convert variable-sized frags into one or more fixups. */
than just helping the user around this limitation here; hopefully the
code using the local expression is around. Putting the LOCAL
semantics in a relocation still seems right; a section didn't do. */
- if (bfd_section_size (section->owner, section) == 0)
+ if (bfd_section_size (section) == 0)
as_bad_where
(fixP->fx_file, fixP->fx_line,
fixP->fx_r_type == BFD_RELOC_MMIX_LOCAL
resolve the relocation here. */
if (addsy != NULL
&& (bfd_is_und_section (addsec)
- || strcmp (bfd_get_section_name (addsec->owner, addsec),
+ || strcmp (bfd_section_name (addsec),
MMIX_REG_CONTENTS_SECTION_NAME) == 0))
{
code = fixP->fx_r_type;
case BFD_RELOC_MMIX_BASE_PLUS_OFFSET:
if (addsy != NULL
- && strcmp (bfd_get_section_name (addsec->owner, addsec),
+ && strcmp (bfd_section_name (addsec),
MMIX_REG_CONTENTS_SECTION_NAME) == 0)
{
/* This changed into a register; the relocation is for the
case BFD_RELOC_MMIX_REG:
if (addsy != NULL
&& (bfd_is_und_section (addsec)
- || strcmp (bfd_get_section_name (addsec->owner, addsec),
+ || strcmp (bfd_section_name (addsec),
MMIX_REG_CONTENTS_SECTION_NAME) == 0))
{
code = fixP->fx_r_type;
return NULL;
}
- relP = (arelent *) xmalloc (sizeof (arelent));
- assert (relP != 0);
- relP->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ relP = XNEW (arelent);
+ gas_assert (relP != 0);
+ relP->sym_ptr_ptr = XNEW (asymbol *);
*relP->sym_ptr_ptr = baddsy;
relP->address = fixP->fx_frag->fr_address + fixP->fx_where;
caller is about to bump the counters. Adjust the error messages. */
if (is_end_of_line[(unsigned int) *s])
{
- char *name;
unsigned int line;
- as_where (&name, &line);
+ const char * name = as_where (&line);
as_bad_where (name, line + 1,
_("[0-9]H labels may not appear alone on a line"));
current_fb_label = -1;
}
if (*s == '.')
{
- char *name;
unsigned int line;
- as_where (&name, &line);
+ const char * name = as_where (&line);
as_bad_where (name, line + 1,
_("[0-9]H labels do not mix with dot-pseudos"));
current_fb_label = -1;
if (*s == 0 || is_end_of_line[(unsigned int) *s])
/* We avoid handling empty lines here. */
return;
-
+
if (is_name_beginner (*s))
label = s;
it the same alignment and address as the associated instruction. */
/* Make room for the label including the ending nul. */
- int len_0 = s - label + 1;
+ size_t len_0 = s - label + 1;
/* Save this label on the MMIX symbol obstack. Saving it on an
obstack is needless for "IS"-pseudos, but it's harmless and we
{
if ((s[1] != 'B' && s[1] != 'F')
|| is_part_of_name (s[-1])
- || is_part_of_name (s[2]))
+ || is_part_of_name (s[2])
+ /* Don't treat e.g. #1F as a local-label reference. */
+ || (s != input_line_pointer && s[-1] == '#'))
s++;
else
{
fragP->fr_subtype = ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO);
}
/* FALLTHROUGH. */
-
+
/* See if this PUSHJ is redirectable to a stub. */
case ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO):
{
{
fragS *fragP;
symbolS *mainsym;
+ asection *regsec;
+ struct loc_assert_s *loc_assert;
int i;
/* The first frag of GREG:s going into the register contents section. */
S_SET_EXTERNAL (mainsym);
}
+ /* Check that we didn't LOC into the unknown, or rather that when it
+ was unknown, we actually change sections. */
+ for (loc_assert = loc_asserts;
+ loc_assert != NULL;
+ loc_assert = loc_assert->next)
+ {
+ segT actual_seg;
+
+ resolve_symbol_value (loc_assert->loc_sym);
+ actual_seg = S_GET_SEGMENT (loc_assert->loc_sym);
+ if (actual_seg != loc_assert->old_seg)
+ {
+ const char *fnam;
+ unsigned int line;
+ int e_valid = expr_symbol_where (loc_assert->loc_sym, &fnam, &line);
+
+ gas_assert (e_valid == 1);
+ as_bad_where (fnam, line,
+ _("LOC to section unknown or indeterminable "
+ "at first pass"));
+
+ /* Patch up the generic location data to avoid cascading
+ error messages from later passes. (See original in
+ write.c:relax_segment.) */
+ fragP = loc_assert->frag;
+ fragP->fr_type = rs_align;
+ fragP->fr_subtype = 0;
+ fragP->fr_offset = 0;
+ fragP->fr_fix = 0;
+ }
+ }
+
if (n_of_raw_gregs != 0)
{
/* Emit GREGs. They are collected in order of appearance, but must
and the same allocation order (within a file) as mmixal. */
segT this_segment = now_seg;
subsegT this_subsegment = now_subseg;
- asection *regsec
- = bfd_make_section_old_way (stdoutput,
- MMIX_REG_CONTENTS_SECTION_NAME);
+
+ regsec = bfd_make_section_old_way (stdoutput,
+ MMIX_REG_CONTENTS_SECTION_NAME);
subseg_set (regsec, 0);
/* Finally emit the initialization-value. Emit a variable frag, which
subseg_set (this_segment, this_subsegment);
}
+ regsec = bfd_get_section_by_name (stdoutput, MMIX_REG_CONTENTS_SECTION_NAME);
+ /* Mark the section symbol as being OK for a reloc. */
+ if (regsec != NULL)
+ regsec->symbol->flags |= BSF_KEEP;
+
/* Iterate over frags resulting from GREGs and move those that evidently
have the same value together and point one to another.
if (gregs == NULL)
{
- gregs = xmalloc (sizeof (*gregs));
+ gregs = XNEW (struct mmix_symbol_gregs);
gregs->n_gregs = 0;
symbol_set_tc (sym, &gregs);
all_greg_symbols[n_greg_symbols++] = gregs;
if (real_reg_section != NULL)
{
/* FIXME: Pass error state gracefully. */
- if (bfd_get_section_flags (stdoutput, real_reg_section) & SEC_HAS_CONTENTS)
+ if (bfd_section_flags (real_reg_section) & SEC_HAS_CONTENTS)
as_fatal (_("register section has contents\n"));
bfd_section_list_remove (stdoutput, real_reg_section);
mmix_parse_predefined_name (char *name, expressionS *expP)
{
char *canon_name;
- char *handler_charp;
+ const char *handler_charp;
const char handler_chars[] = "DVWIOUZX";
symbolS *symp;
if (doing_bspec)
as_bad (_("section change from within a BSPEC/ESPEC pair is not supported"));
- last_alignment = bfd_get_section_alignment (now_seg->owner, now_seg);
+ last_alignment = bfd_section_alignment (now_seg);
want_unaligned = 0;
}
if (exp.X_op == O_illegal
|| exp.X_op == O_absent
- || exp.X_op == O_big
- || section == undefined_section)
+ || exp.X_op == O_big)
{
as_bad (_("invalid LOC expression"));
return;
}
+ if (section == undefined_section)
+ {
+ /* This is an error or a LOC with an expression involving
+ forward references. For the expression to be correctly
+ evaluated, we need to force a proper symbol; gas loses track
+ of the segment for "local symbols". */
+ if (exp.X_op == O_add)
+ {
+ symbol_get_value_expression (exp.X_op_symbol);
+ symbol_get_value_expression (exp.X_add_symbol);
+ }
+ else
+ {
+ gas_assert (exp.X_op == O_symbol);
+ symbol_get_value_expression (exp.X_add_symbol);
+ }
+ }
+
if (section == absolute_section)
{
/* Translate a constant into a suitable section. */
if (exp.X_add_number < ((offsetT) 0x20 << 56))
{
- /* Lower than Data_Segment - assume it's .text. */
+ /* Lower than Data_Segment or in the reserved area (the
+ segment number is >= 0x80, appearing negative) - assume
+ it's .text. */
section = text_section;
/* Save the lowest seen location, so we can pass on this
this one), we org at (this - lower). There's an implicit
"LOC 0" before any entered code. FIXME: handled by spurious
settings of text_has_contents. */
- if (exp.X_add_number < 0
- || exp.X_add_number < (offsetT) lowest_text_loc)
+ if (lowest_text_loc != (bfd_vma) -1
+ && (bfd_vma) exp.X_add_number < lowest_text_loc)
{
as_bad (_("LOC expression stepping backwards is not supported"));
exp.X_op = O_absent;
}
else
{
- /* Do the same for the .data section. */
+ /* Do the same for the .data section, except we don't have
+ to worry about exp.X_add_number carrying a sign. */
section = data_section;
if (exp.X_add_number < (offsetT) lowest_data_loc)
}
}
- if (section != now_seg)
+ /* If we can't deduce the section, it must be the current one.
+ Below, we arrange to assert this. */
+ if (section != now_seg && section != undefined_section)
{
obj_elf_section_change_hook ();
subseg_set (section, 0);
if (exp.X_op != O_absent)
{
+ symbolS *esym = NULL;
+
if (exp.X_op != O_constant && exp.X_op != O_symbol)
{
/* Handle complex expressions. */
- sym = make_expr_symbol (&exp);
+ esym = sym = make_expr_symbol (&exp);
off = 0;
}
else
{
sym = exp.X_add_symbol;
off = exp.X_add_number;
+
+ if (section == undefined_section)
+ {
+ /* We need an expr_symbol when tracking sections. In
+ order to make this an expr_symbol with file and line
+ tracked, we have to make the exp non-trivial; not an
+ O_symbol with .X_add_number == 0. The constant part
+ is unused. */
+ exp.X_add_number = 1;
+ esym = make_expr_symbol (&exp);
+ }
+ }
+
+ /* Track the LOC's where we couldn't deduce the section: assert
+ that we weren't supposed to change section. */
+ if (section == undefined_section)
+ {
+ struct loc_assert_s *next = loc_asserts;
+ loc_asserts = XNEW (struct loc_assert_s);
+ loc_asserts->next = next;
+ loc_asserts->old_seg = now_seg;
+ loc_asserts->loc_sym = esym;
+ loc_asserts->frag = frag_now;
}
p = frag_var (rs_org, 1, 1, (relax_substateT) 0, sym, off, (char *) 0);
mmix_byte (void)
{
unsigned int c;
- char *start;
if (now_seg == text_section)
text_has_contents = 1;
{
case '\"':
++input_line_pointer;
- start = input_line_pointer;
while (is_a_char (c = next_char_of_string ()))
{
FRAG_APPEND_1_CHAR (c);
mmix_cons (int nbytes)
{
expressionS exp;
- char *start;
/* If we don't have any contents, then it's ok to have a specified start
address that is not a multiple of the max data size. We will then
bytes. */
case '\"':
++input_line_pointer;
- start = input_line_pointer;
while (is_a_char (c = next_char_of_string ()))
{
exp.X_op = O_constant;