+/* Load a half-word or byte as a signed value. */
+
+static void
+emit_ldX (const expressionS *tok,
+ int ntok,
+ const void * vlgsize)
+{
+ emit_ldXu (tok, ntok, vlgsize);
+ assemble_tokens (sextX_op[(long) vlgsize], tok, 1, 1);
+}
+
+/* Load an integral value from an unaligned address as an unsigned
+ value. */
+
+static void
+emit_uldXu (const expressionS *tok,
+ int ntok,
+ const void * vlgsize)
+{
+ long lgsize = (long) vlgsize;
+ expressionS newtok[3];
+
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+
+ /* Emit "lda $at, exp". */
+ memcpy (newtok, tok, sizeof (expressionS) * ntok);
+ newtok[0].X_add_number = AXP_REG_AT;
+ assemble_tokens ("lda", newtok, ntok, 1);
+
+ /* Emit "ldq_u $t9, 0($at)". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_const (newtok[1], 0);
+ set_tok_preg (newtok[2], AXP_REG_AT);
+ assemble_tokens ("ldq_u", newtok, 3, 1);
+
+ /* Emit "ldq_u $t10, size-1($at)". */
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_const (newtok[1], (1 << lgsize) - 1);
+ assemble_tokens ("ldq_u", newtok, 3, 1);
+
+ /* Emit "extXl $t9, $at, $t9". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_reg (newtok[1], AXP_REG_AT);
+ set_tok_reg (newtok[2], AXP_REG_T9);
+ assemble_tokens (extXl_op[lgsize], newtok, 3, 1);
+
+ /* Emit "extXh $t10, $at, $t10". */
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_reg (newtok[2], AXP_REG_T10);
+ assemble_tokens (extXh_op[lgsize], newtok, 3, 1);
+
+ /* Emit "or $t9, $t10, targ". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_reg (newtok[1], AXP_REG_T10);
+ newtok[2] = tok[0];
+ assemble_tokens ("or", newtok, 3, 1);
+}
+
+/* Load an integral value from an unaligned address as a signed value.
+ Note that quads should get funneled to the unsigned load since we
+ don't have to do the sign extension. */
+
+static void
+emit_uldX (const expressionS *tok,
+ int ntok,
+ const void * vlgsize)
+{
+ emit_uldXu (tok, ntok, vlgsize);
+ assemble_tokens (sextX_op[(long) vlgsize], tok, 1, 1);
+}
+
+/* Implement the ldil macro. */
+
+static void
+emit_ldil (const expressionS *tok,
+ int ntok,
+ const void * unused ATTRIBUTE_UNUSED)
+{
+ expressionS newtok[2];
+
+ memcpy (newtok, tok, sizeof (newtok));
+ newtok[1].X_add_number = sign_extend_32 (tok[1].X_add_number);
+
+ assemble_tokens ("lda", newtok, ntok, 1);
+}
+
+/* Store a half-word or byte. */
+
+static void
+emit_stX (const expressionS *tok,
+ int ntok,
+ const void * vlgsize)
+{
+ int lgsize = (int) (long) vlgsize;
+
+ if (alpha_target & AXP_OPCODE_BWX)
+ emit_loadstore (tok, ntok, stX_op[lgsize]);
+ else
+ {
+ expressionS newtok[3];
+ struct alpha_insn insn;
+ int basereg;
+ long lituse;
+
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+
+ if (ntok == 2)
+ basereg = (tok[1].X_op == O_constant
+ ? AXP_REG_ZERO : alpha_gp_register);
+ else
+ basereg = tok[2].X_add_number;
+
+ /* Emit "lda $at, exp". */
+ lituse = load_expression (AXP_REG_AT, &tok[1], &basereg, NULL, "lda");
+
+ /* Emit "ldq_u $t9, 0($at)". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_const (newtok[1], 0);
+ set_tok_preg (newtok[2], basereg);
+ assemble_tokens_to_insn ("ldq_u", newtok, 3, &insn);
+
+ if (lituse)
+ {
+ gas_assert (insn.nfixups < MAX_INSN_FIXUPS);
+ insn.fixups[insn.nfixups].reloc = DUMMY_RELOC_LITUSE_BASE;
+ insn.fixups[insn.nfixups].exp.X_op = O_absent;
+ insn.nfixups++;
+ insn.sequence = lituse;
+ }
+
+ emit_insn (&insn);
+
+ /* Emit "insXl src, $at, $t10". */
+ newtok[0] = tok[0];
+ set_tok_reg (newtok[1], basereg);
+ set_tok_reg (newtok[2], AXP_REG_T10);
+ assemble_tokens_to_insn (insXl_op[lgsize], newtok, 3, &insn);
+
+ if (lituse)
+ {
+ gas_assert (insn.nfixups < MAX_INSN_FIXUPS);
+ insn.fixups[insn.nfixups].reloc = DUMMY_RELOC_LITUSE_BYTOFF;
+ insn.fixups[insn.nfixups].exp.X_op = O_absent;
+ insn.nfixups++;
+ insn.sequence = lituse;
+ }
+
+ emit_insn (&insn);
+
+ /* Emit "mskXl $t9, $at, $t9". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ newtok[2] = newtok[0];
+ assemble_tokens_to_insn (mskXl_op[lgsize], newtok, 3, &insn);
+
+ if (lituse)
+ {
+ gas_assert (insn.nfixups < MAX_INSN_FIXUPS);
+ insn.fixups[insn.nfixups].reloc = DUMMY_RELOC_LITUSE_BYTOFF;
+ insn.fixups[insn.nfixups].exp.X_op = O_absent;
+ insn.nfixups++;
+ insn.sequence = lituse;
+ }
+
+ emit_insn (&insn);
+
+ /* Emit "or $t9, $t10, $t9". */
+ set_tok_reg (newtok[1], AXP_REG_T10);
+ assemble_tokens ("or", newtok, 3, 1);
+
+ /* Emit "stq_u $t9, 0($at). */
+ set_tok_const(newtok[1], 0);
+ set_tok_preg (newtok[2], AXP_REG_AT);
+ assemble_tokens_to_insn ("stq_u", newtok, 3, &insn);
+
+ if (lituse)
+ {
+ gas_assert (insn.nfixups < MAX_INSN_FIXUPS);
+ insn.fixups[insn.nfixups].reloc = DUMMY_RELOC_LITUSE_BASE;
+ insn.fixups[insn.nfixups].exp.X_op = O_absent;
+ insn.nfixups++;
+ insn.sequence = lituse;
+ }
+
+ emit_insn (&insn);
+ }
+}
+
+/* Store an integer to an unaligned address. */
+
+static void
+emit_ustX (const expressionS *tok,
+ int ntok,
+ const void * vlgsize)
+{
+ int lgsize = (int) (long) vlgsize;
+ expressionS newtok[3];
+
+ /* Emit "lda $at, exp". */
+ memcpy (newtok, tok, sizeof (expressionS) * ntok);
+ newtok[0].X_add_number = AXP_REG_AT;
+ assemble_tokens ("lda", newtok, ntok, 1);
+
+ /* Emit "ldq_u $9, 0($at)". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_const (newtok[1], 0);
+ set_tok_preg (newtok[2], AXP_REG_AT);
+ assemble_tokens ("ldq_u", newtok, 3, 1);
+
+ /* Emit "ldq_u $10, size-1($at)". */
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_const (newtok[1], (1 << lgsize) - 1);
+ assemble_tokens ("ldq_u", newtok, 3, 1);
+
+ /* Emit "insXl src, $at, $t11". */
+ newtok[0] = tok[0];
+ set_tok_reg (newtok[1], AXP_REG_AT);
+ set_tok_reg (newtok[2], AXP_REG_T11);
+ assemble_tokens (insXl_op[lgsize], newtok, 3, 1);
+
+ /* Emit "insXh src, $at, $t12". */
+ set_tok_reg (newtok[2], AXP_REG_T12);
+ assemble_tokens (insXh_op[lgsize], newtok, 3, 1);
+
+ /* Emit "mskXl $t9, $at, $t9". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ newtok[2] = newtok[0];
+ assemble_tokens (mskXl_op[lgsize], newtok, 3, 1);
+
+ /* Emit "mskXh $t10, $at, $t10". */
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ newtok[2] = newtok[0];
+ assemble_tokens (mskXh_op[lgsize], newtok, 3, 1);
+
+ /* Emit "or $t9, $t11, $t9". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_reg (newtok[1], AXP_REG_T11);
+ newtok[2] = newtok[0];
+ assemble_tokens ("or", newtok, 3, 1);
+
+ /* Emit "or $t10, $t12, $t10". */
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_reg (newtok[1], AXP_REG_T12);
+ newtok[2] = newtok[0];
+ assemble_tokens ("or", newtok, 3, 1);
+
+ /* Emit "stq_u $t10, size-1($at)". */
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_const (newtok[1], (1 << lgsize) - 1);
+ set_tok_preg (newtok[2], AXP_REG_AT);
+ assemble_tokens ("stq_u", newtok, 3, 1);
+
+ /* Emit "stq_u $t9, 0($at)". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_const (newtok[1], 0);
+ assemble_tokens ("stq_u", newtok, 3, 1);
+}
+
+/* Sign extend a half-word or byte. The 32-bit sign extend is
+ implemented as "addl $31, $r, $t" in the opcode table. */
+
+static void
+emit_sextX (const expressionS *tok,
+ int ntok,
+ const void * vlgsize)
+{
+ long lgsize = (long) vlgsize;
+
+ if (alpha_target & AXP_OPCODE_BWX)
+ assemble_tokens (sextX_op[lgsize], tok, ntok, 0);
+ else
+ {
+ int bitshift = 64 - 8 * (1 << lgsize);
+ expressionS newtok[3];
+
+ /* Emit "sll src,bits,dst". */
+ newtok[0] = tok[0];
+ set_tok_const (newtok[1], bitshift);
+ newtok[2] = tok[ntok - 1];
+ assemble_tokens ("sll", newtok, 3, 1);
+
+ /* Emit "sra dst,bits,dst". */
+ newtok[0] = newtok[2];
+ assemble_tokens ("sra", newtok, 3, 1);
+ }
+}
+
+/* Implement the division and modulus macros. */
+
+#ifdef OBJ_EVAX
+
+/* Make register usage like in normal procedure call.
+ Don't clobber PV and RA. */
+
+static void
+emit_division (const expressionS *tok,
+ int ntok,
+ const void * symname)
+{
+ /* DIVISION and MODULUS. Yech.
+
+ Convert
+ OP x,y,result
+ to
+ mov x,R16 # if x != R16
+ mov y,R17 # if y != R17
+ lda AT,__OP
+ jsr AT,(AT),0
+ mov R0,result
+
+ with appropriate optimizations if R0,R16,R17 are the registers
+ specified by the compiler. */
+
+ int xr, yr, rr;
+ symbolS *sym;
+ expressionS newtok[3];
+
+ xr = regno (tok[0].X_add_number);
+ yr = regno (tok[1].X_add_number);
+
+ if (ntok < 3)
+ rr = xr;
+ else
+ rr = regno (tok[2].X_add_number);
+
+ /* Move the operands into the right place. */
+ if (yr == AXP_REG_R16 && xr == AXP_REG_R17)
+ {
+ /* They are in exactly the wrong order -- swap through AT. */
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+
+ set_tok_reg (newtok[0], AXP_REG_R16);
+ set_tok_reg (newtok[1], AXP_REG_AT);
+ assemble_tokens ("mov", newtok, 2, 1);
+
+ set_tok_reg (newtok[0], AXP_REG_R17);
+ set_tok_reg (newtok[1], AXP_REG_R16);
+ assemble_tokens ("mov", newtok, 2, 1);
+
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ set_tok_reg (newtok[1], AXP_REG_R17);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+ else
+ {
+ if (yr == AXP_REG_R16)
+ {
+ set_tok_reg (newtok[0], AXP_REG_R16);
+ set_tok_reg (newtok[1], AXP_REG_R17);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+
+ if (xr != AXP_REG_R16)
+ {
+ set_tok_reg (newtok[0], xr);
+ set_tok_reg (newtok[1], AXP_REG_R16);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+
+ if (yr != AXP_REG_R16 && yr != AXP_REG_R17)
+ {
+ set_tok_reg (newtok[0], yr);
+ set_tok_reg (newtok[1], AXP_REG_R17);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+ }
+
+ sym = symbol_find_or_make ((const char *) symname);
+
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ set_tok_sym (newtok[1], sym, 0);
+ assemble_tokens ("lda", newtok, 2, 1);
+
+ /* Call the division routine. */
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ set_tok_cpreg (newtok[1], AXP_REG_AT);
+ set_tok_const (newtok[2], 0);
+ assemble_tokens ("jsr", newtok, 3, 1);
+
+ /* Move the result to the right place. */
+ if (rr != AXP_REG_R0)
+ {
+ set_tok_reg (newtok[0], AXP_REG_R0);
+ set_tok_reg (newtok[1], rr);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+}
+
+#else /* !OBJ_EVAX */
+
+static void
+emit_division (const expressionS *tok,
+ int ntok,
+ const void * symname)
+{
+ /* DIVISION and MODULUS. Yech.
+ Convert
+ OP x,y,result
+ to
+ lda pv,__OP
+ mov x,t10
+ mov y,t11
+ jsr t9,(pv),__OP
+ mov t12,result
+
+ with appropriate optimizations if t10,t11,t12 are the registers
+ specified by the compiler. */
+
+ int xr, yr, rr;
+ symbolS *sym;
+ expressionS newtok[3];
+
+ xr = regno (tok[0].X_add_number);
+ yr = regno (tok[1].X_add_number);
+
+ if (ntok < 3)
+ rr = xr;
+ else
+ rr = regno (tok[2].X_add_number);
+
+ sym = symbol_find_or_make ((const char *) symname);
+
+ /* Move the operands into the right place. */
+ if (yr == AXP_REG_T10 && xr == AXP_REG_T11)
+ {
+ /* They are in exactly the wrong order -- swap through AT. */
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_reg (newtok[1], AXP_REG_AT);
+ assemble_tokens ("mov", newtok, 2, 1);
+
+ set_tok_reg (newtok[0], AXP_REG_T11);
+ set_tok_reg (newtok[1], AXP_REG_T10);
+ assemble_tokens ("mov", newtok, 2, 1);
+
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ set_tok_reg (newtok[1], AXP_REG_T11);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+ else
+ {
+ if (yr == AXP_REG_T10)
+ {
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_reg (newtok[1], AXP_REG_T11);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+
+ if (xr != AXP_REG_T10)
+ {
+ set_tok_reg (newtok[0], xr);
+ set_tok_reg (newtok[1], AXP_REG_T10);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+
+ if (yr != AXP_REG_T10 && yr != AXP_REG_T11)
+ {
+ set_tok_reg (newtok[0], yr);
+ set_tok_reg (newtok[1], AXP_REG_T11);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+ }
+
+ /* Call the division routine. */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_sym (newtok[1], sym, 0);
+ assemble_tokens ("jsr", newtok, 2, 1);
+
+ /* Reload the GP register. */
+#ifdef OBJ_AOUT
+FIXME
+#endif
+#if defined(OBJ_ECOFF) || defined(OBJ_ELF)
+ set_tok_reg (newtok[0], alpha_gp_register);
+ set_tok_const (newtok[1], 0);
+ set_tok_preg (newtok[2], AXP_REG_T9);
+ assemble_tokens ("ldgp", newtok, 3, 1);
+#endif
+
+ /* Move the result to the right place. */
+ if (rr != AXP_REG_T12)
+ {
+ set_tok_reg (newtok[0], AXP_REG_T12);
+ set_tok_reg (newtok[1], rr);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+}
+
+#endif /* !OBJ_EVAX */
+
+/* The jsr and jmp macros differ from their instruction counterparts
+ in that they can load the target address and default most
+ everything. */
+
+static void
+emit_jsrjmp (const expressionS *tok,
+ int ntok,
+ const void * vopname)
+{
+ const char *opname = (const char *) vopname;
+ struct alpha_insn insn;
+ expressionS newtok[3];
+ int r, tokidx = 0;
+ long lituse = 0;
+
+ if (tokidx < ntok && tok[tokidx].X_op == O_register)
+ r = regno (tok[tokidx++].X_add_number);
+ else
+ r = strcmp (opname, "jmp") == 0 ? AXP_REG_ZERO : AXP_REG_RA;
+
+ set_tok_reg (newtok[0], r);
+
+ if (tokidx < ntok &&
+ (tok[tokidx].X_op == O_pregister || tok[tokidx].X_op == O_cpregister))
+ r = regno (tok[tokidx++].X_add_number);
+#ifdef OBJ_EVAX
+ /* Keep register if jsr $n.<sym>. */
+#else
+ else
+ {
+ int basereg = alpha_gp_register;
+ lituse = load_expression (r = AXP_REG_PV, &tok[tokidx],
+ &basereg, NULL, opname);
+ }
+#endif
+
+ set_tok_cpreg (newtok[1], r);
+
+#ifndef OBJ_EVAX
+ if (tokidx < ntok)
+ newtok[2] = tok[tokidx];
+ else
+#endif
+ set_tok_const (newtok[2], 0);
+
+ assemble_tokens_to_insn (opname, newtok, 3, &insn);
+
+ if (lituse)
+ {
+ gas_assert (insn.nfixups < MAX_INSN_FIXUPS);
+ insn.fixups[insn.nfixups].reloc = DUMMY_RELOC_LITUSE_JSR;
+ insn.fixups[insn.nfixups].exp.X_op = O_absent;
+ insn.nfixups++;
+ insn.sequence = lituse;
+ }
+
+#ifdef OBJ_EVAX
+ if (alpha_flag_replace
+ && r == AXP_REG_RA
+ && tok[tokidx].X_add_symbol
+ && alpha_linkage_symbol)
+ {
+ /* Create a BOH reloc for 'jsr $27,NAME'. */
+ const char *symname = S_GET_NAME (tok[tokidx].X_add_symbol);
+ int symlen = strlen (symname);
+ char *ensymname;
+
+ /* Build the entry name as 'NAME..en'. */
+ ensymname = (char *) xmalloc (symlen + 5);
+ memcpy (ensymname, symname, symlen);
+ memcpy (ensymname + symlen, "..en", 5);
+
+ gas_assert (insn.nfixups < MAX_INSN_FIXUPS);
+ if (insn.nfixups > 0)
+ {
+ memmove (&insn.fixups[1], &insn.fixups[0],
+ sizeof(struct alpha_fixup) * insn.nfixups);
+ }
+
+ /* The fixup must be the same as the BFD_RELOC_ALPHA_NOP
+ case in load_expression. See B.4.5.2 of the OpenVMS
+ Linker Utility Manual. */
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_BOH;
+ insn.fixups[0].exp.X_op = O_symbol;
+ insn.fixups[0].exp.X_add_symbol = symbol_find_or_make (ensymname);
+ insn.fixups[0].exp.X_add_number = 0;
+ insn.fixups[0].xtrasym = alpha_linkage_symbol;
+ insn.fixups[0].procsym = alpha_evax_proc->symbol;
+ insn.nfixups++;
+ alpha_linkage_symbol = 0;
+ free (ensymname);
+ }
+#endif
+
+ emit_insn (&insn);
+}
+
+/* The ret and jcr instructions differ from their instruction
+ counterparts in that everything can be defaulted. */
+
+static void
+emit_retjcr (const expressionS *tok,
+ int ntok,
+ const void * vopname)
+{
+ const char *opname = (const char *) vopname;
+ expressionS newtok[3];
+ int r, tokidx = 0;
+
+ if (tokidx < ntok && tok[tokidx].X_op == O_register)
+ r = regno (tok[tokidx++].X_add_number);
+ else
+ r = AXP_REG_ZERO;
+
+ set_tok_reg (newtok[0], r);
+
+ if (tokidx < ntok &&
+ (tok[tokidx].X_op == O_pregister || tok[tokidx].X_op == O_cpregister))
+ r = regno (tok[tokidx++].X_add_number);
+ else
+ r = AXP_REG_RA;
+
+ set_tok_cpreg (newtok[1], r);
+
+ if (tokidx < ntok)
+ newtok[2] = tok[tokidx];
+ else
+ set_tok_const (newtok[2], strcmp (opname, "ret") == 0);
+
+ assemble_tokens (opname, newtok, 3, 0);
+}
+
+/* Implement the ldgp macro. */
+
+static void
+emit_ldgp (const expressionS *tok ATTRIBUTE_UNUSED,
+ int ntok ATTRIBUTE_UNUSED,
+ const void * unused ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_AOUT
+FIXME
+#endif
+#if defined(OBJ_ECOFF) || defined(OBJ_ELF)
+ /* from "ldgp r1,n(r2)", generate "ldah r1,X(R2); lda r1,Y(r1)"
+ with appropriate constants and relocations. */
+ struct alpha_insn insn;
+ expressionS newtok[3];
+ expressionS addend;
+
+#ifdef OBJ_ECOFF
+ if (regno (tok[2].X_add_number) == AXP_REG_PV)
+ ecoff_set_gp_prolog_size (0);
+#endif
+
+ newtok[0] = tok[0];
+ set_tok_const (newtok[1], 0);
+ newtok[2] = tok[2];
+
+ assemble_tokens_to_insn ("ldah", newtok, 3, &insn);
+
+ addend = tok[1];
+
+#ifdef OBJ_ECOFF
+ if (addend.X_op != O_constant)
+ as_bad (_("can not resolve expression"));
+ addend.X_op = O_symbol;
+ addend.X_add_symbol = alpha_gp_symbol;
+#endif
+
+ insn.nfixups = 1;
+ insn.fixups[0].exp = addend;
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_GPDISP_HI16;
+ insn.sequence = next_sequence_num;
+
+ emit_insn (&insn);
+
+ set_tok_preg (newtok[2], tok[0].X_add_number);
+
+ assemble_tokens_to_insn ("lda", newtok, 3, &insn);
+
+#ifdef OBJ_ECOFF
+ addend.X_add_number += 4;
+#endif
+
+ insn.nfixups = 1;
+ insn.fixups[0].exp = addend;
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_GPDISP_LO16;
+ insn.sequence = next_sequence_num--;
+
+ emit_insn (&insn);
+#endif /* OBJ_ECOFF || OBJ_ELF */
+}
+
+/* The macro table. */
+
+static const struct alpha_macro alpha_macros[] =
+{
+/* Load/Store macros. */
+ { "lda", emit_lda, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldah", emit_ldah, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_EOA } },
+
+ { "ldl", emit_ir_load, "ldl",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldl_l", emit_ir_load, "ldl_l",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldq", emit_ir_load, "ldq",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldq_l", emit_ir_load, "ldq_l",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldq_u", emit_ir_load, "ldq_u",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldf", emit_loadstore, "ldf",
+ { MACRO_FPR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldg", emit_loadstore, "ldg",
+ { MACRO_FPR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "lds", emit_loadstore, "lds",
+ { MACRO_FPR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldt", emit_loadstore, "ldt",
+ { MACRO_FPR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+
+ { "ldb", emit_ldX, (void *) 0,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldbu", emit_ldXu, (void *) 0,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldw", emit_ldX, (void *) 1,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldwu", emit_ldXu, (void *) 1,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+
+ { "uldw", emit_uldX, (void *) 1,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "uldwu", emit_uldXu, (void *) 1,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "uldl", emit_uldX, (void *) 2,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "uldlu", emit_uldXu, (void *) 2,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "uldq", emit_uldXu, (void *) 3,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+
+ { "ldgp", emit_ldgp, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA } },
+
+ { "ldi", emit_lda, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ldil", emit_ldil, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ldiq", emit_lda, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_EOA } },
+
+ { "stl", emit_loadstore, "stl",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "stl_c", emit_loadstore, "stl_c",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "stq", emit_loadstore, "stq",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "stq_c", emit_loadstore, "stq_c",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "stq_u", emit_loadstore, "stq_u",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "stf", emit_loadstore, "stf",
+ { MACRO_FPR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "stg", emit_loadstore, "stg",
+ { MACRO_FPR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "sts", emit_loadstore, "sts",
+ { MACRO_FPR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "stt", emit_loadstore, "stt",
+ { MACRO_FPR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+
+ { "stb", emit_stX, (void *) 0,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "stw", emit_stX, (void *) 1,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ustw", emit_ustX, (void *) 1,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ustl", emit_ustX, (void *) 2,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ustq", emit_ustX, (void *) 3,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+
+/* Arithmetic macros. */
+
+ { "sextb", emit_sextX, (void *) 0,
+ { MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EOA,
+ /* MACRO_EXP, MACRO_IR, MACRO_EOA */ } },
+ { "sextw", emit_sextX, (void *) 1,
+ { MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EOA,
+ /* MACRO_EXP, MACRO_IR, MACRO_EOA */ } },
+
+ { "divl", emit_division, "__divl",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "divlu", emit_division, "__divlu",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "divq", emit_division, "__divq",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "divqu", emit_division, "__divqu",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "reml", emit_division, "__reml",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "remlu", emit_division, "__remlu",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "remq", emit_division, "__remq",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "remqu", emit_division, "__remqu",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+
+ { "jsr", emit_jsrjmp, "jsr",
+ { MACRO_PIR, MACRO_EXP, MACRO_EOA,
+ MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA,
+ MACRO_EXP, MACRO_EOA } },
+ { "jmp", emit_jsrjmp, "jmp",
+ { MACRO_PIR, MACRO_EXP, MACRO_EOA,
+ MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA,
+ MACRO_EXP, MACRO_EOA } },
+ { "ret", emit_retjcr, "ret",
+ { MACRO_IR, MACRO_EXP, MACRO_EOA,
+ MACRO_IR, MACRO_EOA,
+ MACRO_PIR, MACRO_EXP, MACRO_EOA,
+ MACRO_PIR, MACRO_EOA,
+ MACRO_EXP, MACRO_EOA,
+ MACRO_EOA } },
+ { "jcr", emit_retjcr, "jcr",
+ { MACRO_IR, MACRO_EXP, MACRO_EOA,
+ MACRO_IR, MACRO_EOA,
+ MACRO_PIR, MACRO_EXP, MACRO_EOA,
+ MACRO_PIR, MACRO_EOA,
+ MACRO_EXP, MACRO_EOA,
+ MACRO_EOA } },
+ { "jsr_coroutine", emit_retjcr, "jcr",
+ { MACRO_IR, MACRO_EXP, MACRO_EOA,
+ MACRO_IR, MACRO_EOA,
+ MACRO_PIR, MACRO_EXP, MACRO_EOA,
+ MACRO_PIR, MACRO_EOA,
+ MACRO_EXP, MACRO_EOA,
+ MACRO_EOA } },
+};
+
+static const unsigned int alpha_num_macros
+ = sizeof (alpha_macros) / sizeof (*alpha_macros);
+
+/* Search forward through all variants of a macro looking for a syntax
+ match. */
+
+static const struct alpha_macro *
+find_macro_match (const struct alpha_macro *first_macro,
+ const expressionS *tok,
+ int *pntok)
+
+{
+ const struct alpha_macro *macro = first_macro;
+ int ntok = *pntok;
+
+ do
+ {
+ const enum alpha_macro_arg *arg = macro->argsets;
+ int tokidx = 0;
+
+ while (*arg)
+ {
+ switch (*arg)
+ {
+ case MACRO_EOA:
+ if (tokidx == ntok)
+ return macro;
+ else
+ tokidx = 0;
+ break;
+
+ /* Index register. */
+ case MACRO_IR:
+ if (tokidx >= ntok || tok[tokidx].X_op != O_register
+ || !is_ir_num (tok[tokidx].X_add_number))
+ goto match_failed;
+ ++tokidx;
+ break;
+
+ /* Parenthesized index register. */
+ case MACRO_PIR:
+ if (tokidx >= ntok || tok[tokidx].X_op != O_pregister
+ || !is_ir_num (tok[tokidx].X_add_number))
+ goto match_failed;
+ ++tokidx;
+ break;
+
+ /* Optional parenthesized index register. */
+ case MACRO_OPIR:
+ if (tokidx < ntok && tok[tokidx].X_op == O_pregister
+ && is_ir_num (tok[tokidx].X_add_number))
+ ++tokidx;
+ break;
+
+ /* Leading comma with a parenthesized index register. */
+ case MACRO_CPIR:
+ if (tokidx >= ntok || tok[tokidx].X_op != O_cpregister
+ || !is_ir_num (tok[tokidx].X_add_number))
+ goto match_failed;
+ ++tokidx;
+ break;
+
+ /* Floating point register. */
+ case MACRO_FPR:
+ if (tokidx >= ntok || tok[tokidx].X_op != O_register
+ || !is_fpr_num (tok[tokidx].X_add_number))
+ goto match_failed;
+ ++tokidx;
+ break;