* Date: Jan 1993
*/
+#include <ctype.h>
+
#include "as.h"
#include "alpha-opcode.h"
#include "subsegs.h"
/* @@ Will a simple 0x8000 work here? If not, why not? */
#define GP_ADJUSTMENT (0x8000 - 0x10)
+/* Which machine type is this? Currently stores an integer for the
+ model, one of: 21064, 21066, 21164. */
+static unsigned long machine;
+
/* These are exported to relaxing code, even though we don't do any
relaxing on this processor currently. */
const relax_typeS md_relax_table[1];
/* We'll probably be using this relocation frequently, and we
will want to compare for it. */
-static reloc_howto_type *gpdisp_hi16_howto;
+static const reloc_howto_type *gpdisp_hi16_howto;
/* These are exported to ECOFF code. */
unsigned long alpha_gprmask, alpha_fprmask;
s_data (), float_cons ();
/* Static functions, needing forward declarations. */
-static void s_mask (), s_base (), s_proc (), s_alpha_set ();
+static void s_base (), s_proc (), s_alpha_set ();
static void s_gprel32 (), s_rdata (), s_sdata (), s_alpha_comm ();
static int alpha_ip ();
+static void emit_unaligned_io PARAMS ((char *, int, valueT, int));
+static void emit_load_unal PARAMS ((int, valueT, int));
+static void emit_store_unal PARAMS ((int, valueT, int));
+static void emit_byte_manip_r PARAMS ((char *, int, int, int, int, int));
+static void emit_extract_r PARAMS ((int, int, int, int, int));
+static void emit_insert_r PARAMS ((int, int, int, int, int));
+static void emit_mask_r PARAMS ((int, int, int, int, int));
+static void emit_sign_extend PARAMS ((int, int));
+static void emit_bis_r PARAMS ((int, int, int));
+static int build_mem PARAMS ((int, int, int, bfd_signed_vma));
+static int build_operate_n PARAMS ((int, int, int, int, int));
+static void emit_sll_n PARAMS ((int, int, int));
+static void emit_ldah_num PARAMS ((int, bfd_vma, int));
+static void emit_addq_r PARAMS ((int, int, int));
+static void emit_lda_n PARAMS ((int, bfd_vma, int));
+static void emit_add64 PARAMS ((int, int, bfd_vma));
+static int in_range PARAMS ((bfd_vma, int, int));
+
const pseudo_typeS md_pseudo_table[] =
{
{"common", s_comm, 0}, /* is this used? */
#define T9 23
#define T10 24
#define T11 25
-#define RA 26
+#define T12 26
+#define RA 26 /* note: same as T12 */
#define PV 27
#define AT 28
#define GP 29
/* Note that input_file.c hand checks for '#' at the beginning of the
first line of the input file. This is because the compiler outputs
#NO_APP at the beginning of its output. */
-/* Also note that '/*' will always start a comment */
+/* Also note that C style comments are always recognized. */
const char line_comment_chars[] = "#!";
/* Chars that can be used to separate mant from exp in floating point nums */
/* Chars that mean this number is a floating point constant, as in
"0f12.456" or "0d1.2345e12". */
+/* @@ Do all of these really get used on the alpha?? */
char FLT_CHARS[] = "rRsSfFdDxXpP";
/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
tc_get_register (frame)
int frame;
{
- int reg;
int framereg = SP;
SKIP_WHITESPACE ();
reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
- if (fixp->fx_r_type > BFD_RELOC_UNUSED || fixp->fx_r_type < 0)
+ if (fixp->fx_r_type > BFD_RELOC_UNUSED)
abort ();
if (fixp->fx_r_type == BFD_RELOC_ALPHA_GPDISP_HI16)
assert (reloc->howto != 0);
if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
{
- as_fatal ("bug in handling type-%d relocs", fixp->fx_r_type);
- abort ();
+ as_fatal ("internal error? cannot generate `%s' relocation",
+ bfd_get_reloc_code_name (fixp->fx_r_type));
}
assert (!fixp->fx_pcrel == !reloc->howto->pc_relative);
if (base_register < 0 || base_register > 31)
{
base_register = GP;
- as_warn ("Bad base register, using $r.", base_register);
+ as_warn ("Bad base register, using $%d.", base_register);
}
demand_empty_rest_of_line ();
}
+static int in_range (val, nbits, unsignedness)
+ bfd_vma val;
+ int nbits, unsignedness;
+{
+ /* Look at top bit of value that would be stored, figure out how it
+ would be extended by the hardware, and see if that matches the
+ original supplied value. */
+ bfd_vma mask;
+ bfd_vma one = 1;
+ bfd_vma top_bit, stored_value, missing_bits;
+
+ mask = (one << nbits) - 1;
+ stored_value = val & mask;
+ top_bit = stored_value & (one << nbits - 1);
+ missing_bits = val & ~mask;
+ if (unsignedness)
+ {
+ return missing_bits == 0;
+ }
+ else
+ {
+ /* will sign-extend */
+ if (top_bit)
+ {
+ /* all remaining bits beyond mask should be one */
+ missing_bits |= mask;
+ return missing_bits + 1 == 0;
+ }
+ else
+ {
+ /* all other bits should be zero */
+ return missing_bits == 0;
+ }
+ }
+}
+
static void
s_gprel32 ()
{
return retval;
}
-/* This function is called once, at assembler startup time. It should
- set up all the tables, etc. that the MD part of the assembler will need. */
-void
-md_begin ()
-{
- const char *retval;
- int lose = 0;
- unsigned int i = 0;
+#define load_insn(NAME, OP) (hash_insert (op_hash, (NAME), (PTR) (OP)))
- op_hash = hash_new ();
+static void
+load_insn_table (ops, size)
+ struct alpha_opcode *ops;
+ int size;
+{
+ struct alpha_opcode *end = ops + size;
+ struct alpha_opcode *op;
+ const char *name;
- for (i = 0; i < NUMOPCODES; )
+ for (op = ops; op < end; )
{
- const char *name = alpha_opcodes[i].name;
- retval = hash_insert (op_hash, name, (PTR) & alpha_opcodes[i]);
+ const char *retval;
+
+ name = op->name;
+
+ retval = load_insn (op->name, op);
if (retval)
- {
- as_bad ("internal error: can't hash opcode `%s': %s",
- alpha_opcodes[i].name, retval);
- lose = 1;
- }
+ as_fatal ("internal error: can't hash opcode `%s': %s",
+ op->name, retval);
+
do
- ++i;
- while (i < NUMOPCODES
- && (alpha_opcodes[i].name == name
- || !strcmp (alpha_opcodes[i].name, name)));
+ op++;
+ while (op < end
+ && (op->name == name
+ || !strcmp (op->name, name)));
}
/* Some opcodes include modifiers of various sorts with a "/mod"
syntax, like the architecture documentation suggests. However,
for use with gcc at least, we also need to access those same
opcodes without the "/". */
- for (i = 0; i < NUMOPCODES; )
+ for (op = ops; op < end; )
{
- const char *name = alpha_opcodes[i].name;
+ name = op->name;
+
if (strchr (name, '/'))
{
- char *p = xmalloc (strlen (name));
- const char *q = name;
- char *q2 = p;
-
- for (; *q; q++)
- if (*q != '/')
- *q2++ = *q;
-
- *q2++ = 0;
- retval = hash_insert (op_hash, p, (PTR) & alpha_opcodes[i]);
- if (retval)
- {
- /* Ignore failures -- the opcode table does duplicate
- some variants in different forms, like "hw_st/q" and
- "hw_stq". */
-#if 0
- as_bad ("internal error: can't hash opcode variant `%s': %s",
- p, retval);
- lose = 1;
-#endif
- }
+ char *name2, *p;
+ const char *q;
+
+ name2 = xmalloc (strlen (name));
+ p = name2;
+ q = name;
+
+ while (*q)
+ if (*q == '/')
+ q++;
+ else
+ *p++ = *q++;
+ *p = 0;
+ /* Ignore failures -- the opcode table does duplicate some
+ variants in different forms, like "hw_stq" and "hw_st/q".
+ Maybe the variants can be eliminated, and this error checking
+ restored. */
+ load_insn (name2, op);
}
+
do
- ++i;
- while (i < NUMOPCODES
- && (alpha_opcodes[i].name == name
- || !strcmp (alpha_opcodes[i].name, name)));
+ op++;
+ while (op < end
+ && (op->name == name
+ || !strcmp (op->name, name)));
}
+}
+
+static struct alpha_it clear_insn;
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc. that the MD part of the assembler will
+ need, that can be determined before arguments are parsed. */
+void
+md_begin ()
+{
+ int i;
+
+ op_hash = hash_new ();
+ load_insn_table (alpha_opcodes, NUMOPCODES);
+ /* Default to 21064 PAL instructions. */
+ if (machine == 0)
+ machine = 21064;
- if (lose)
- as_fatal ("Broken assembler. No assembly attempted.");
+ switch (machine)
+ {
+ case 21064:
+ case 21066:
+ load_insn_table (alpha_pal21064_opcodes, NUM21064OPCODES);
+ break;
+ case 21164:
+ load_insn_table (alpha_pal21164_opcodes, NUM21164OPCODES);
+ break;
+ default:
+ as_fatal ("palcode set unknown (internal error)");
+ }
lituse_basereg.X_op = O_constant;
lituse_basereg.X_add_number = 1;
create_lita_section ();
/* For handling the GP, create a symbol that won't be output in the
symbol table. We'll edit it out of relocs later. */
- gp = symbol_new ("<GP value>", lita_sec, 0x8000, &zero_address_frag);
- symbol_remove (gp, &symbol_rootP, &symbol_lastP);
+ gp = symbol_create ("<GP value>", lita_sec, 0x8000, &zero_address_frag);
+
+ memset (&clear_insn, 0, sizeof (clear_insn));
+ for (i = 0; i < MAX_RELOCS; i++)
+ clear_insn.reloc[i].code = BFD_RELOC_NONE;
}
int optnum = 1;
+static void
+emit_insn (insn)
+ struct alpha_it *insn;
+{
+ char *toP;
+ int j;
+
+ toP = frag_more (4);
+
+ /* put out the opcode */
+ md_number_to_chars (toP, insn->opcode, 4);
+
+ /* put out the symbol-dependent stuff */
+ for (j = 0; j < MAX_RELOCS; j++)
+ {
+ struct reloc_data *r = &insn->reloc[j];
+ fixS *f;
+
+ if (r->code != BFD_RELOC_NONE)
+ {
+ if (r->exp.X_op == O_constant)
+ {
+ r->exp.X_add_symbol = section_symbol (absolute_section);
+ r->exp.X_op = O_symbol;
+ }
+ f = fix_new_exp (frag_now, (toP - frag_now->fr_literal), 4,
+ &r->exp, r->pcrel, r->code);
+ }
+ if (r->code == BFD_RELOC_ALPHA_GPDISP_LO16)
+ {
+ static bit_fixS cookie;
+ /* @@ This'll make the range checking in write.c shut up. */
+ f->fx_bit_fixP = &cookie;
+ }
+ }
+}
+
void
md_assemble (str)
char *str;
{
- char *toP;
- int i, j, count;
+ int i, count;
#define MAX_INSNS 5
struct alpha_it insns[MAX_INSNS];
return;
for (i = 0; i < count; i++)
- {
- toP = frag_more (4);
-
- /* put out the opcode */
- md_number_to_chars (toP, insns[i].opcode, 4);
-
- /* put out the symbol-dependent stuff */
- for (j = 0; j < MAX_RELOCS; j++)
- {
- struct reloc_data *r = &insns[i].reloc[j];
- fixS *f;
-
- if (r->code != BFD_RELOC_NONE)
- {
- if (r->exp.X_op == O_constant)
- {
- r->exp.X_add_symbol = section_symbol (absolute_section);
- r->exp.X_op = O_symbol;
- }
- f = fix_new_exp (frag_now, (toP - frag_now->fr_literal), 4,
- &r->exp, r->pcrel, r->code);
- }
- if (r->code == BFD_RELOC_ALPHA_GPDISP_LO16)
- {
- static bit_fixS cookie;
- /* This'll make the range checking in write.c shut up. */
- f->fx_bit_fixP = &cookie;
- }
- }
- }
+ emit_insn (&insns[i]);
}
static inline void
case BFD_RELOC_8:
case BFD_RELOC_23_PCREL_S2:
case BFD_RELOC_14:
+ case BFD_RELOC_26:
return 0;
default:
abort ();
return 0;
case BFD_RELOC_GPREL32:
return 1;
+ default:
+ return !alpha_force_relocation (f);
}
- return !alpha_force_relocation (f);
+ /*NOTREACHED*/
}
valueT
}
/* Add this thing to the .lita section and produce a LITERAL reloc referring
- to it.
+ to it. */
- TODO:
- Remove duplicates.
- Set GP value properly, and have values in LITERAL references set
- accordingly.
- */
+/* Are we currently eligible to emit a LITUSE reloc for the literal
+ references just generated? */
+static int lituse_pending;
static void
load_symbol_address (reg, insn)
static symbolS *lita_sym;
int x;
- addressT reloc_addr;
valueT retval;
- char *p;
- symbolS *sym;
- valueT addend;
if (!lita_sym)
{
insn->reloc[0].exp.X_add_symbol = lita_sym;
insn->reloc[0].exp.X_add_number = retval;
insn->reloc[0].code = BFD_RELOC_ALPHA_LITERAL;
+ lituse_pending = 1;
if (retval == 0x8000)
/* Overflow? */
int reg;
struct alpha_it *insn;
{
- valueT addend;
+ valueT addend, addendhi, addendlo;
int num_insns = 1;
if (insn->reloc[0].exp.X_add_symbol->bsym->flags & BSF_SECTION_SYM)
load_symbol_address (reg, insn);
if (addend)
{
- num_insns++;
- {
- valueT x = addend;
- if ((x & ~0x7fff) != 0
- && (x & ~0x7fff) + 0x8000 != 0)
- {
- as_bad ("assembler not prepared to handle constants >16 bits yet");
- addend = 0;
- }
- }
- insn[1].opcode = (0x20000000 /* lda */
- | (reg << SA)
- | (reg << SB)
- | (addend & 0xffff));
- insn[1].reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
- insn[1].reloc[0].exp = lituse_basereg;
+ if ((addend & ~0x7fffffff) != 0
+ && (addend & ~0x7fffffff) + 0x80000000 != 0)
+ {
+ as_bad ("assembler not prepared to handle constants >32 bits yet");
+ addend = 0;
+ }
+ addendlo = addend & 0xffff;
+ addend -= addendlo;
+ addendhi = addend >> 16;
+ if (addendlo & 0x8000)
+ addendhi++;
+ /* It appears that the BASEREG LITUSE reloc should not be used on
+ an LDAH instruction. */
+ if (addendlo)
+ {
+ insn[1].opcode = (0x20000000 /* lda */
+ | (reg << SA)
+ | (reg << SB)
+ | (addendlo & 0xffff));
+ insn[1].reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
+ insn[1].reloc[0].exp = lituse_basereg;
+ num_insns++;
+ }
+ if (addendhi)
+ {
+ insn[num_insns].opcode = (0x24000000
+ | (reg << SA)
+ | (reg << SB)
+ | (addendhi & 0xffff));
+ num_insns++;
+ }
+ if (num_insns == 1)
+ abort ();
+ lituse_pending = 0;
}
return num_insns;
}
input_line_pointer = save_in;
}
+static void
+emit_unaligned_io (dir, addr_reg, addr_offset, reg)
+ char *dir;
+ int addr_reg, reg;
+ valueT addr_offset;
+{
+ char buf[90];
+ sprintf (buf, "%sq_u $%d,%ld($%d)", dir, reg, (long) addr_offset, addr_reg);
+ md_assemble (buf);
+}
+
+static void
+emit_load_unal (addr_reg, addr_offset, reg)
+ int addr_reg, reg;
+ valueT addr_offset;
+{
+ emit_unaligned_io ("ld", addr_reg, addr_offset, reg);
+}
+
+static void
+emit_store_unal (addr_reg, addr_offset, reg)
+ int addr_reg, reg;
+ valueT addr_offset;
+{
+ emit_unaligned_io ("st", addr_reg, addr_offset, reg);
+}
+
+static void
+emit_byte_manip_r (op, in, mask, out, mode, which)
+ char *op;
+ int in, mask, out, mode, which;
+{
+ char buf[90];
+ sprintf (buf, "%s%c%c $%d,$%d,$%d", op, mode, which, in, mask, out);
+ md_assemble (buf);
+}
+
+static void
+emit_extract_r (in, mask, out, mode, which)
+ int in, mask, out, mode, which;
+{
+ emit_byte_manip_r ("ext", in, mask, out, mode, which);
+}
+
+static void
+emit_insert_r (in, mask, out, mode, which)
+ int in, mask, out, mode, which;
+{
+ emit_byte_manip_r ("ins", in, mask, out, mode, which);
+}
+
+static void
+emit_mask_r (in, mask, out, mode, which)
+ int in, mask, out, mode, which;
+{
+ emit_byte_manip_r ("msk", in, mask, out, mode, which);
+}
+
+static void
+emit_sign_extend (reg, size)
+ int reg, size;
+{
+ char buf[90];
+ sprintf (buf, "sll $%d,0x%x,$%d", reg, 64 - size, reg);
+ md_assemble (buf);
+ sprintf (buf, "sra $%d,0x%x,$%d", reg, 64 - size, reg);
+ md_assemble (buf);
+}
+
+static void
+emit_bis_r (in1, in2, out)
+ int in1, in2, out;
+{
+ char buf[90];
+ sprintf (buf, "bis $%d,$%d,$%d", in1, in2, out);
+ md_assemble (buf);
+}
+
+static int
+build_mem (opc, ra, rb, disp)
+ int opc, ra, rb;
+ bfd_signed_vma disp;
+{
+ if ((disp >> 15) != 0
+ && (disp >> 15) + 1 != 0)
+ abort ();
+ return ((opc << 26) | (ra << SA) | (rb << SB) | (disp & 0xffff));
+}
+
+static int
+build_operate_n (opc, fn, ra, lit, rc)
+ int opc, fn, ra, rc;
+ int lit;
+{
+ if (lit & ~0xff)
+ abort ();
+ return ((opc << 26) | (fn << 5) | (ra << SA) | (lit << SN) | (1 << 12) | (rc << SC));
+}
+
+static void
+emit_sll_n (dest, disp, src)
+ int dest, disp, src;
+{
+ struct alpha_it insn = clear_insn;
+ insn.opcode = build_operate_n (0x12, 0x39, src, disp, dest);
+ emit_insn (&insn);
+}
+
+static void
+emit_ldah_num (dest, addend, src)
+ int dest, src;
+ bfd_vma addend;
+{
+ struct alpha_it insn = clear_insn;
+ insn.opcode = build_mem (0x09, dest, src, addend);
+ emit_insn (&insn);
+}
+
+static void
+emit_addq_r (in1, in2, out)
+ int in1, in2, out;
+{
+ struct alpha_it insn = clear_insn;
+ insn.opcode = 0x40000400 | (in1 << SA) | (in2 << SB) | (out << SC);
+ emit_insn (&insn);
+}
+
+static void
+emit_lda_n (dest, addend, src)
+ int dest, src;
+ bfd_vma addend;
+{
+ struct alpha_it insn = clear_insn;
+ insn.opcode = build_mem (0x08, dest, src, addend);
+ emit_insn (&insn);
+}
+
+static void
+emit_add64 (in, out, num)
+ int in, out;
+ bfd_vma num;
+{
+ bfd_signed_vma snum = num;
+
+ if (in_range (num, 16, 0))
+ {
+ emit_lda_n (out, num, in);
+ return;
+ }
+ if ((num & 0xffff) == 0
+ && in == ZERO
+ && in_range (snum >> 16, 16, 0))
+ {
+ emit_ldah_num (out, snum >> 16, in);
+ return;
+ }
+ /* I'm not sure this one is getting invoked when it could. */
+ if ((num & 1) == 0 && in == ZERO)
+ {
+ if (in_range (snum >> 1, 16, 0))
+ {
+ emit_lda_n (out, snum >> 1, in);
+ emit_addq_r (out, out, out);
+ return;
+ }
+ else if (num & 0x1fffe == 0
+ && in_range (snum >> 17, 16, 0))
+ {
+ emit_ldah_num (out, snum >> 17, in);
+ emit_addq_r (out, out, out);
+ return;
+ }
+ }
+ if (in_range (num, 32, 0))
+ {
+ bfd_vma lo = num & 0xffff;
+ if (lo & 0x8000)
+ lo -= 0x10000;
+ num -= lo;
+ emit_ldah_num (out, snum >> 16, in);
+ if (lo)
+ emit_lda_n (out, lo, out);
+ return;
+ }
+
+ if (in != ZERO && in != AT && out != AT && at_ok)
+ {
+ emit_add64 (ZERO, AT, num);
+ emit_addq_r (AT, in, out);
+ return;
+ }
+
+ if (in != ZERO)
+ as_bad ("load expression too complex to expand");
+
+ /* Could check also for loading 16- or 32-bit value and shifting by
+ arbitrary displacement. */
+
+ {
+ bfd_vma lo = snum & 0xffffffff;
+ if (lo & 0x80000000)
+ lo -= ((bfd_vma)0x10000000 << 4);
+ snum -= lo;
+ emit_add64 (ZERO, out, snum >> 32);
+ emit_sll_n (out, 32, out);
+ if (lo != 0)
+ emit_add64 (out, out, lo);
+ }
+}
+
/* Note that for now, this function is called recursively (by way of
calling md_assemble again). Some of the macros defined as part of
the assembly language are currently rewritten as sequences of
{
opcode = pattern->match;
num_gen = 1;
- memset (insns, 0, sizeof (*insns));
- for (i = 0; i < MAX_RELOCS; i++)
- insns[0].reloc[i].code = BFD_RELOC_NONE;
- for (i = 1; i < MAX_INSNS; i++)
- insns[i] = insns[0];
+ for (i = 0; i < MAX_INSNS; i++)
+ insns[i] = clear_insn;
/* Build the opcode, checking as we go to make sure that the
operands match. */
insns[0].reloc[0].code = BFD_RELOC_8;
goto immediate;
+ case 'I': /* 26 bit immediate, for PALcode */
+ insns[0].reloc[0].code = BFD_RELOC_26;
+ goto immediate;
+
#if 0
case 't': /* 12 bit 0...11 */
insns[0].reloc = RELOC_0_12;
#else
case 't':
case '8':
- case 'I':
abort ();
#endif
/*FALLTHROUGH*/
else if (at_ok && macro_ok)
{
/* Constant value supplied, but it's too large. */
- char expansion[64];
- sprintf (expansion, "lda $%d,%d($%d)", AT,
- insns[0].reloc[0].exp.X_add_number, ZERO);
- md_assemble (expansion);
- opcode |= 0x1000 /* use reg */ | (AT << SB);
+ emit_add64 (ZERO, AT,
+ insns[0].reloc[0].exp.X_add_number);
+ opcode &= ~ 0x1000;
+ opcode |= (AT << SB);
insns[0].reloc[0].code = BFD_RELOC_NONE;
}
else
as_bad ("overflow in 8-bit literal field in `operate' format insn");
}
+ else if (insns[0].reloc[0].code == BFD_RELOC_16
+ && insns[0].reloc[0].exp.X_op == O_constant
+ && !in_range (insns[0].reloc[0].exp.X_add_number,
+ 16, 0))
+ {
+ bfd_vma val = insns[0].reloc[0].exp.X_add_number;
+ if (OPCODE (opcode) == 0x08)
+ {
+ emit_add64 (ZERO, AT, val);
+ opcode &= ~0x1000;
+ opcode |= (AT << SB);
+ insns[0].reloc[0].code = BFD_RELOC_NONE;
+ }
+ else if (OPCODE (opcode) == 0x09
+ && in_range (val >> 16, 16, 0))
+ {
+ /* ldah with high operand - convert to low */
+ insns[0].reloc[0].exp.X_add_number >>= 16;
+ }
+ else
+ as_bad ("I don't know how to handle 32+ bit constants here yet, sorry.");
+ }
+ else if (insns[0].reloc[0].code == BFD_RELOC_32
+ && insns[0].reloc[0].exp.X_op == O_constant)
+ {
+ bfd_vma val = insns[0].reloc[0].exp.X_add_number;
+ bfd_signed_vma sval = val;
+ if (val >> 32 != 0
+ && sval >> 32 != 0
+ && sval >> 32 != -1)
+ as_bad ("I don't know how to handle 64 bit constants here yet, sorry.");
+ }
continue;
case 'F':
{
- int format, length, mode, i, size;
+ int format, length, mode, i;
char temp[20 /*MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT*/];
char *err;
static const char formats[4] = "FGfd";
insns[0].reloc[0].exp.X_op = O_symbol;
offset &= 0xffff;
num_gen = load_expression (AT, &insns[0]);
- insns[num_gen].reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
- insns[num_gen].reloc[0].exp = lituse_basereg;
+ if (lituse_pending)
+ {
+ insns[num_gen].reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
+ insns[num_gen].reloc[0].exp = lituse_basereg;
+ lituse_pending = 0;
+ }
insns[num_gen++].opcode = opcode | (AT << SB) | offset;
opcode = insns[0].opcode;
s = input_line_pointer;
/* fall through */
case 'G': /* Addressing macros: GET */
- get_macro:
/* All it is missing is the expression, which is what we
will get now */
}
if (insns[0].reloc[0].exp.X_op == O_constant)
{
- /* This only handles 32bit numbers */
- register int val = insns[0].reloc[0].exp.X_add_number;
- register short sval;
+ bfd_vma val = insns[0].reloc[0].exp.X_add_number;
+ bfd_vma top, low;
insns[0].reloc[0].code = BFD_RELOC_NONE;
insns[1].reloc[0].code = BFD_RELOC_NONE;
- sval = val;
- if ((sval != val) && (val & 0x8000))
+ low = val & 0xffff;
+ if (low & 0x8000)
+ low -= 0x10000;
+ top = val - low;
+ if (top)
{
- val += 0x10000;
- sval = val;
- }
-
- if (optnum && (sval == val))
- {
- /* optimize away the ldah */
- num_gen = 1;
- opcode |= (ZERO << SB) | (val & 0xffff);
+ emit_add64 (ZERO, AT, top);
+ opcode |= AT << SB;
}
else
- {
- num_gen = 2;
- insns[1].opcode = opcode | (mask << SB) | (val & 0xffff);
- opcode = 0x24000000 /*ldah*/ |
- mask << SA | (ZERO << SB) |
- ((val >> 16) & 0xffff);
- }
+ opcode |= ZERO << SB;
+ opcode &= ~0x1000;
+ opcode |= low & 0xffff;
}
else if (insns[0].reloc[0].exp.X_op == O_symbol)
{
tmp_reg = AT;
num_gen = load_expression (tmp_reg, insns);
opcode = insns[0].opcode;
- /* lda is opcode 8, 0x20000000 */
- if (OPCODE (old_opcode) != 0x08)
+ /* lda is opcode 8, 0x20000000, and the macros that use
+ this code have an opcode field of 0. The latter
+ require further processing, and we don't have the
+ true opcode here. */
+ if (OPCODE (old_opcode) != 0
+ && OPCODE (old_opcode) != 0x08)
{
struct alpha_it *i;
i = &insns[num_gen++];
i->opcode = old_opcode | (tmp_reg << SB);
- i->reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
- i->reloc[0].exp = lituse_basereg;
+ if (lituse_pending)
+ {
+ i->reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
+ i->reloc[0].exp = lituse_basereg;
+ lituse_pending = 0;
+ }
}
}
else
insns[1].opcode |= addend & 0xffff;
insns[0].opcode |= ((addend >> 16)
+ (addend & 0x8000 ? 1 : 0));
- ecoff_set_gp_prolog_size (0);
+ if (r2 == PV)
+ ecoff_set_gp_prolog_size (0);
}
break;
default:
| (mask << SA)
| (PV << SB)
| 0);
- if (num_gen == 2)
+ if (lituse_pending)
{
/* LITUSE wasn't emitted yet */
jsr->reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
jsr->reloc[0].exp = lituse_jsr;
r = &jsr->reloc[1];
+ lituse_pending = 0;
}
else
r = &jsr->reloc[0];
}
continue;
+ case 'd':
+ /* Sub-word loads and stores. We load the address into
+ $at, which might involve using the `P' parameter
+ processing too, then emit a sequence to get the job
+ done, using unaligned memory accesses and byte
+ manipulation, with t9 and t10 as temporaries. */
+ {
+ /* Characteristics of access. */
+ int is_load, is_unsigned = 0, is_unaligned = 0;
+ int mode_size, mode;
+ /* Register operand. */
+ int reg;
+ /* Addend for loads and stores. */
+ valueT addend;
+ /* Which register do we use for the address? */
+ int addr;
+
+ {
+ /* Pick apart name and set flags. */
+ const char *s = pattern->name;
+
+ if (*s == 'u')
+ {
+ is_unaligned = 1;
+ s++;
+ }
+
+ if (s[0] == 'l' && s[1] == 'd')
+ is_load = 1;
+ else if (s[0] == 's' && s[1] == 't')
+ is_load = 0;
+ else
+ as_fatal ("unrecognized sub-word access insn `%s'",
+ str);
+ s += 2;
+
+ mode = *s++;
+ if (mode == 'b') mode_size = 1;
+ else if (mode == 'w') mode_size = 2;
+ else if (mode == 'l') mode_size = 4;
+ else if (mode == 'q') mode_size = 8;
+ else abort ();
+
+ if (*s == 'u')
+ {
+ is_unsigned = 1;
+ s++;
+ }
+
+ assert (*s == 0);
+
+ /* Longwords are always kept sign-extended. */
+ if (mode == 'l' && is_unsigned)
+ abort ();
+ /* There's no special unaligned byte handling. */
+ if (mode == 'b' && is_unaligned)
+ abort ();
+ /* Stores don't care about signedness. */
+ if (!is_load && is_unsigned)
+ abort ();
+ }
+
+ if (args[-2] == 'P')
+ {
+ addr = AT;
+ addend = 0;
+ }
+ else
+ {
+ /* foo r1,num(r2)
+ r2 -> mask
+ r1 -> (opcode >> SA) & 31
+ num -> insns->reloc[0].*
+
+ We want to emit "lda at,num(r2)", since these
+ operations require the use of a single register
+ with the starting address of the memory operand
+ we want to access.
+
+ We could probably get away without doing this
+ (and use r2 below, with the addend for the
+ actual reads and writes) in cases where the
+ addend is known to be a multiple of 8. */
+ int r2 = mask;
+ int r1 = (opcode >> SA) & 31;
+
+ if (insns[0].reloc[0].code == BFD_RELOC_NONE)
+ addend = 0;
+ else if (insns[0].reloc[0].code == BFD_RELOC_16)
+ {
+ if (insns[0].reloc[0].exp.X_op != O_constant)
+ abort ();
+ addend = insns[0].reloc[0].exp.X_add_number;
+ }
+ else
+ abort ();
+
+ if (addend + mode_size - 1 < 0x7fff
+ && (addend % 8) == 0
+ && (r2 < T9 || r2 > T12))
+ {
+ addr = r2;
+ num_gen = 0;
+ }
+ else
+ {
+ /* Let later relocation processing deal
+ with the addend field. */
+ insns[num_gen-1].opcode = (0x20000000 /* lda */
+ | (AT << SA)
+ | (r2 << SB));
+ addr = AT;
+ addend = 0;
+ }
+ reg = r1;
+ }
+
+ /* Because the emit_* routines append directly to
+ the current frag, we now need to flush any
+ pending insns. */
+ {
+ int i;
+ for (i = 0; i < num_gen; i++)
+ emit_insn (&insns[i]);
+ num_gen = 0;
+ }
+
+ if (is_load)
+ {
+ int reg2, reg3;
+
+ if (is_unaligned)
+ reg2 = T9, reg3 = T10;
+ else
+ reg2 = reg;
+
+ emit_load_unal (addr, addend, T9);
+ if (is_unaligned)
+ emit_load_unal (addr, addend + mode_size - 1, T10);
+ emit_extract_r (T9, addr, reg2, mode, 'l');
+ if (is_unaligned)
+ {
+ emit_extract_r (T10, addr, reg3, mode, 'h');
+ emit_bis_r (T9, T10, reg);
+ }
+ if (!is_unsigned)
+ emit_sign_extend (reg, mode_size * 8);
+ }
+ else
+ {
+ /* The second word gets processed first
+ because if the address does turn out to be
+ aligned, the processing for the second word
+ will be pushing around all-zeros, and the
+ entire value will be handled as the `first'
+ word. So we want to store the `first' word
+ last. */
+ /* Pair these up so that the memory loads get
+ separated from each other, as well as being
+ well in advance of the uses of the values
+ loaded. */
+ if (is_unaligned)
+ {
+ emit_load_unal (addr, addend + mode_size - 1, T11);
+ emit_insert_r (reg, addr, T12, mode, 'h');
+ }
+ emit_load_unal (addr, addend, T9);
+ emit_insert_r (reg, addr, T10, mode, 'l');
+ if (is_unaligned)
+ emit_mask_r (T12, addr, T12, mode, 'h');
+ emit_mask_r (T10, addr, T10, mode, 'l');
+ if (is_unaligned)
+ emit_bis_r (T11, T12, T11);
+ emit_bis_r (T9, T10, T9);
+ if (is_unaligned)
+ emit_store_unal (addr, addend + mode_size - 1, T11);
+ emit_store_unal (addr, addend, T9);
+ }
+ }
+ return 0;
+
/* DIVISION and MODULUS. Yech.
Convert OP x,y,result
to mov x,t10
while (--nb);
}
}
+\f
+CONST char *md_shortopts = "Fm:";
+struct option md_longopts[] = {
+#define OPTION_32ADDR (OPTION_MD_BASE)
+ {"32addr", no_argument, NULL, OPTION_32ADDR},
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
int
-md_parse_option (argP, cntP, vecP)
- char **argP;
- int *cntP;
- char ***vecP;
+md_parse_option (c, arg)
+ int c;
+ char *arg;
{
- if (**argP == 'F')
+ switch (c)
{
+ case 'F':
nofloats = 1;
- return 1;
- }
-#if 0 /* I have no idea if this stuff would work any more. And it's
- probably not right for ECOFF anyways. */
- /* Use base-register addressing, e.g. PIC code */
- if (**argP == 'B')
- {
- if (first_32bit_quadrant)
- {
- first_32bit_quadrant = 0;
- base_register = GP;
- }
- else
- {
- first_32bit_quadrant = 1;
- base_register = ZERO;
- }
- if (argP[0][1] == 'k')
- no_mixed_code = 1;
- argP[0][1] = 0;
- return 1;
- }
-#endif
- if (!strcmp (*argP, "32addr"))
- {
+ break;
+
+ case OPTION_32ADDR:
addr32 = 1;
- *argP += 6;
- return 1;
- }
- if (!strcmp (*argP, "nocpp"))
- {
- *argP += 5;
- return 1;
+ break;
+
+ case 'm':
+ {
+ unsigned long mach;
+
+ if (!strcmp (arg, "21064"))
+ mach = 21064;
+ else if (!strcmp (arg, "21066"))
+ mach = 21066;
+ else if (!strcmp (arg, "21164"))
+ mach = 21164;
+ else
+ {
+ as_bad ("invalid architecture %s", arg);
+ return 0;
+ }
+
+ if (machine != 0 && machine != mach)
+ {
+ as_warn ("machine type %lu already chosen, overriding with %lu",
+ machine, mach);
+ }
+ machine = mach;
+ }
+ break;
+
+ default:
+ return 0;
}
- return 0;
+
+ return 1;
}
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ fprintf(stream, "\
+Alpha options:\n\
+-32addr treat addresses as 32-bit values\n\
+-F lack floating point instructions support\n\
+-m21064 | -m21066 | -m21164\n\
+ specify variant of Alpha architecture\n");
+}
+\f
static void
s_proc (is_static)
+ int is_static;
{
/* XXXX Align to cache linesize XXXXX */
char *name;
int
alpha_do_align (n, fill)
int n;
- char *fill;
+ const char *fill;
{
if (!fill
&& (now_seg == text_section
*p |= value;
value >>= 5;
fixP->fx_done = 1;
- check_zov:
if (value != 0)
as_bad_where (fixP->fx_file, fixP->fx_line,
"overflow in type-%d reloc", (int) fixP->fx_r_type);
}
break;
+ case BFD_RELOC_26:
+ if (fixP->fx_addsy != 0
+ && fixP->fx_addsy->bsym->section != absolute_section)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ "PALcode instructions require immediate constant function code");
+ else if (value >> 26 != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ "overflow in 26-bit PALcode function field");
+ *p++ = value & 0xff;
+ value >>= 8;
+ *p++ = value & 0xff;
+ value >>= 8;
+ *p++ = value & 0xff;
+ value >>= 8;
+ {
+ char x = *p;
+ x &= ~3;
+ x |= (value & 3);
+ *p++ = x;
+ }
+ goto done;
+
case BFD_RELOC_14:
if (fixP->fx_addsy != 0
&& fixP->fx_addsy->bsym->section != absolute_section)