/* tc-sh.c -- Assemble code for the Renesas / SuperH SH
- Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
- Free Software Foundation, Inc.
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003, 2004 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
#endif
#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
typedef struct
{
int sh_small;
+/* Flag to generate relocations against symbol values for local symbols. */
+
+static int dont_adjust_reloc_32;
+
/* preset architecture set, if given; zero otherwise. */
-static int preset_target_arch;
+static unsigned int preset_target_arch;
/* The bit mask of architectures that could
accommodate the insns seen so far. */
-static int valid_arch;
+static unsigned int valid_arch;
const char EXP_CHARS[] = "eE";
return;
}
+#ifdef md_cons_align
+ md_cons_align (nbytes);
+#endif
+
do
{
expression (&exp);
{
const sh_opcode_info *opcode;
char *prev_name = "";
- int target_arch;
+ unsigned int target_arch;
target_arch
- = preset_target_arch ? preset_target_arch : arch_sh1_up & ~arch_sh_dsp_up;
+ = preset_target_arch ? preset_target_arch : arch_sh1_up & ~arch_sh_has_dsp;
valid_arch = target_arch;
#ifdef HAVE_SH64
{
if (strcmp (prev_name, opcode->name) != 0)
{
- if (! (opcode->arch & target_arch))
+ if (!SH_MERGE_ARCH_SET_VALID (opcode->arch, target_arch))
continue;
prev_name = opcode->name;
hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
|| (l0 == 'i' && (l1 == 'x' || l1 == 's')))
{
src += 2;
- op->type = A_PMOD_N;
+ op->type = AX_PMOD_N;
}
else if ( (l0 == 'r' && l1 == '9')
|| (l0 == 'i' && l1 == 'y'))
{
src += 2;
- op->type = A_PMODY_N;
+ op->type = AY_PMOD_N;
}
else
op->type = A_INC_N;
case V_REG_N:
case FPUL_N:
case FPSCR_N:
- case A_PMOD_N:
- case A_PMODY_N:
case DSP_REG_N:
/* Opcode needs rn */
if (user->type != arg)
reg_m = user->reg;
break;
+ case AS_DEC_N:
+ if (user->type != A_DEC_N)
+ goto fail;
+ if (user->reg < 2 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AS_INC_N:
+ if (user->type != A_INC_N)
+ goto fail;
+ if (user->reg < 2 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AS_IND_N:
+ if (user->type != A_IND_N)
+ goto fail;
+ if (user->reg < 2 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AS_PMOD_N:
+ if (user->type != AX_PMOD_N)
+ goto fail;
+ if (user->reg < 2 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AX_INC_N:
+ if (user->type != A_INC_N)
+ goto fail;
+ if (user->reg < 4 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AX_IND_N:
+ if (user->type != A_IND_N)
+ goto fail;
+ if (user->reg < 4 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AX_PMOD_N:
+ if (user->type != AX_PMOD_N)
+ goto fail;
+ if (user->reg < 4 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AXY_INC_N:
+ if (user->type != A_INC_N)
+ goto fail;
+ if ((user->reg < 4 || user->reg > 5)
+ && (user->reg < 0 || user->reg > 1))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AXY_IND_N:
+ if (user->type != A_IND_N)
+ goto fail;
+ if ((user->reg < 4 || user->reg > 5)
+ && (user->reg < 0 || user->reg > 1))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AXY_PMOD_N:
+ if (user->type != AX_PMOD_N)
+ goto fail;
+ if ((user->reg < 4 || user->reg > 5)
+ && (user->reg < 0 || user->reg > 1))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AY_INC_N:
+ if (user->type != A_INC_N)
+ goto fail;
+ if (user->reg < 6 || user->reg > 7)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AY_IND_N:
+ if (user->type != A_IND_N)
+ goto fail;
+ if (user->reg < 6 || user->reg > 7)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AY_PMOD_N:
+ if (user->type != AY_PMOD_N)
+ goto fail;
+ if (user->reg < 6 || user->reg > 7)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AYX_INC_N:
+ if (user->type != A_INC_N)
+ goto fail;
+ if ((user->reg < 6 || user->reg > 7)
+ && (user->reg < 2 || user->reg > 3))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AYX_IND_N:
+ if (user->type != A_IND_N)
+ goto fail;
+ if ((user->reg < 6 || user->reg > 7)
+ && (user->reg < 2 || user->reg > 3))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AYX_PMOD_N:
+ if (user->type != AY_PMOD_N)
+ goto fail;
+ if ((user->reg < 6 || user->reg > 7)
+ && (user->reg < 2 || user->reg > 3))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case DSP_REG_A_M:
+ if (user->type != DSP_REG_N)
+ goto fail;
+ if (user->reg != A_A0_NUM
+ && user->reg != A_A1_NUM)
+ goto fail;
+ reg_m = user->reg;
+ break;
+
+ case DSP_REG_AX:
+ if (user->type != DSP_REG_N)
+ goto fail;
+ switch (user->reg)
+ {
+ case A_A0_NUM:
+ reg_x = 0;
+ break;
+ case A_A1_NUM:
+ reg_x = 2;
+ break;
+ case A_X0_NUM:
+ reg_x = 1;
+ break;
+ case A_X1_NUM:
+ reg_x = 3;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+
+ case DSP_REG_XY:
+ if (user->type != DSP_REG_N)
+ goto fail;
+ switch (user->reg)
+ {
+ case A_X0_NUM:
+ reg_x = 0;
+ break;
+ case A_X1_NUM:
+ reg_x = 2;
+ break;
+ case A_Y0_NUM:
+ reg_x = 1;
+ break;
+ case A_Y1_NUM:
+ reg_x = 3;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+
+ case DSP_REG_AY:
+ if (user->type != DSP_REG_N)
+ goto fail;
+ switch (user->reg)
+ {
+ case A_A0_NUM:
+ reg_y = 0;
+ break;
+ case A_A1_NUM:
+ reg_y = 1;
+ break;
+ case A_Y0_NUM:
+ reg_y = 2;
+ break;
+ case A_Y1_NUM:
+ reg_y = 3;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+
+ case DSP_REG_YX:
+ if (user->type != DSP_REG_N)
+ goto fail;
+ switch (user->reg)
+ {
+ case A_Y0_NUM:
+ reg_y = 0;
+ break;
+ case A_Y1_NUM:
+ reg_y = 1;
+ break;
+ case A_X0_NUM:
+ reg_y = 2;
+ break;
+ case A_X1_NUM:
+ reg_y = 3;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+
case DSP_REG_X:
if (user->type != DSP_REG_N)
goto fail;
goto fail;
}
}
- if ( !(valid_arch & this_try->arch))
+ if ( !SH_MERGE_ARCH_SET_VALID (valid_arch, this_try->arch))
goto fail;
- valid_arch &= this_try->arch;
+ valid_arch = SH_MERGE_ARCH_SET (valid_arch, this_try->arch);
return this_try;
fail:
;
switch (i)
{
case REG_N:
+ case REG_N_D:
nbuf[index] = reg_n;
break;
case REG_M:
case REG_B:
nbuf[index] = reg_b | 0x08;
break;
+ case REG_N_B01:
+ nbuf[index] = reg_n | 0x01;
+ break;
case IMM0_4BY4:
insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0, operand);
break;
if (opcode->arg[0] != A_END)
op_end = get_operands (opcode, op_end, operand);
+ try_another_opcode:
opcode = get_specific (opcode, operand);
if (opcode == 0)
{
movy = DDT_BASE;
break;
+ case MOVX_NOPY:
+ if (movx)
+ as_bad (_("multiple movx specifications"));
+ if ((reg_n < 4 || reg_n > 5)
+ && (reg_n < 0 || reg_n > 1))
+ as_bad (_("invalid movx address register"));
+ if (movy && movy != DDT_BASE)
+ as_bad (_("insn cannot be combined with non-nopy"));
+ movx = ((((reg_n & 1) != 0) << 9)
+ + (((reg_n & 4) == 0) << 8)
+ + (reg_x << 6)
+ + (opcode->nibbles[2] << 4)
+ + opcode->nibbles[3]
+ + DDT_BASE);
+ break;
+
+ case MOVY_NOPX:
+ if (movy)
+ as_bad (_("multiple movy specifications"));
+ if ((reg_n < 6 || reg_n > 7)
+ && (reg_n < 2 || reg_n > 3))
+ as_bad (_("invalid movy address register"));
+ if (movx && movx != DDT_BASE)
+ as_bad (_("insn cannot be combined with non-nopx"));
+ movy = ((((reg_n & 1) != 0) << 8)
+ + (((reg_n & 4) == 0) << 9)
+ + (reg_y << 6)
+ + (opcode->nibbles[2] << 4)
+ + opcode->nibbles[3]
+ + DDT_BASE);
+ break;
+
case MOVX:
if (movx)
as_bad (_("multiple movx specifications"));
+ if (movy & 0x2ac)
+ as_bad (_("previous movy requires nopx"));
if (reg_n < 4 || reg_n > 5)
as_bad (_("invalid movx address register"));
if (opcode->nibbles[2] & 8)
case MOVY:
if (movy)
as_bad (_("multiple movy specifications"));
+ if (movx & 0x153)
+ as_bad (_("previous movx requires nopy"));
if (opcode->nibbles[2] & 8)
{
/* Bit 3 in nibbles[2] is intended for bit 4 of the opcode,
| (operand[0].immediate.X_add_number & 127) << 4
| reg_n);
break;
+ case PPI3NC:
+ if (cond)
+ {
+ opcode++;
+ goto try_another_opcode;
+ }
+ /* Fall through. */
case PPI3:
if (field_b)
as_bad (_("multiple parallel processing specifications"));
field_b = ((opcode->nibbles[2] << 12) + (opcode->nibbles[3] << 8)
+ (reg_x << 6) + (reg_y << 4) + reg_n);
+ switch (opcode->nibbles[4])
+ {
+ case HEX_0:
+ case HEX_XX00:
+ case HEX_00YY:
+ break;
+ case HEX_1:
+ case HEX_4:
+ field_b += opcode->nibbles[4] << 4;
+ break;
+ default:
+ abort ();
+ }
break;
case PDC:
if (cond)
field_b = ((opcode->nibbles[2] << 12) + (opcode->nibbles[3] << 8)
+ cond + (reg_x << 6) + (reg_y << 4) + reg_n);
cond = 0;
+ switch (opcode->nibbles[4])
+ {
+ case HEX_0:
+ case HEX_XX00:
+ case HEX_00YY:
+ break;
+ case HEX_1:
+ case HEX_4:
+ field_b += opcode->nibbles[4] << 4;
+ break;
+ default:
+ abort ();
+ }
break;
case PMUL:
if (field_b)
{
- if ((field_b & 0xef00) != 0xa100)
+ if ((field_b & 0xef00) == 0xa100)
+ field_b -= 0x8100;
+ /* pclr Dz pmuls Se,Sf,Dg */
+ else if ((field_b & 0xff00) == 0x8d00
+ && (SH_MERGE_ARCH_SET_VALID (valid_arch, arch_sh4al_dsp_up)))
+ {
+ valid_arch = SH_MERGE_ARCH_SET (valid_arch, arch_sh4al_dsp_up);
+ field_b -= 0x8cf0;
+ }
+ else
as_bad (_("insn cannot be combined with pmuls"));
- field_b -= 0x8100;
switch (field_b & 0xf)
{
case A_X0_NUM:
field_b += 3 - A_A1_NUM;
break;
default:
- as_bad (_("bad padd / psub pmuls output operand"));
+ as_bad (_("bad combined pmuls output operand"));
}
/* Generate warning if the destination register for padd / psub
and pmuls is the same ( only for A0 or A1 ).
sh_operand_info operand[3];
sh_opcode_info *opcode;
unsigned int size = 0;
+ char *initial_str = str;
#ifdef HAVE_SH64
if (sh64_isa_mode == sh64_isa_shmedia)
if (opcode == NULL)
{
- as_bad (_("unknown opcode"));
+ /* The opcode is not in the hash table.
+ This means we definately have an assembly failure,
+ but the instruction may be valid in another CPU variant.
+ In this case emit something better than 'unknown opcode'.
+ Search the full table in sh-opc.h to check. */
+
+ char *name = initial_str;
+ int name_length = 0;
+ const sh_opcode_info *op;
+ int found = 0;
+
+ /* identify opcode in string */
+ while (isspace (*name))
+ {
+ name++;
+ }
+ while (!isspace (name[name_length]))
+ {
+ name_length++;
+ }
+
+ /* search for opcode in full list */
+ for (op = sh_table; op->name; op++)
+ {
+ if (strncasecmp (op->name, name, name_length) == 0
+ && op->name[name_length] == '\0')
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ if ( found )
+ {
+ as_bad (_("opcode not valid for this cpu variant"));
+ }
+ else
+ {
+ as_bad (_("unknown opcode"));
+ }
return;
}
{
/* Since we skip get_specific here, we have to check & update
valid_arch now. */
- if (valid_arch & opcode->arch)
- valid_arch &= opcode->arch;
+ if (SH_MERGE_ARCH_SET_VALID (valid_arch, opcode->arch))
+ valid_arch = SH_MERGE_ARCH_SET (valid_arch, opcode->arch);
else
as_bad (_("Delayed branches not available on SH1"));
parse_exp (op_end + 1, &operand[0]);
#define OPTION_SMALL (OPTION_LITTLE + 1)
#define OPTION_DSP (OPTION_SMALL + 1)
#define OPTION_ISA (OPTION_DSP + 1)
+#define OPTION_RENESAS (OPTION_ISA + 1)
{"relax", no_argument, NULL, OPTION_RELAX},
{"big", no_argument, NULL, OPTION_BIG},
{"small", no_argument, NULL, OPTION_SMALL},
{"dsp", no_argument, NULL, OPTION_DSP},
{"isa", required_argument, NULL, OPTION_ISA},
+ {"renesas", no_argument, NULL, OPTION_RENESAS},
+
#ifdef HAVE_SH64
-#define OPTION_ABI (OPTION_ISA + 1)
+#define OPTION_ABI (OPTION_RENESAS + 1)
#define OPTION_NO_MIX (OPTION_ABI + 1)
#define OPTION_SHCOMPACT_CONST_CRANGE (OPTION_NO_MIX + 1)
#define OPTION_NO_EXPAND (OPTION_SHCOMPACT_CONST_CRANGE + 1)
break;
case OPTION_DSP:
- preset_target_arch = arch_sh1_up & ~arch_sh3e_up;
+ preset_target_arch = arch_sh1_up & ~(arch_sh_sp_fpu|arch_sh_dp_fpu);
+ break;
+
+ case OPTION_RENESAS:
+ dont_adjust_reloc_32 = 1;
break;
case OPTION_ISA:
if (strcasecmp (arg, "sh4") == 0)
preset_target_arch = arch_sh4;
+ else if (strcasecmp (arg, "sh4-nofpu") == 0)
+ preset_target_arch = arch_sh4_nofpu;
+ else if (strcasecmp (arg, "sh4-nommu-nofpu") == 0)
+ preset_target_arch = arch_sh4_nommu_nofpu;
+ else if (strcasecmp (arg, "sh4a") == 0)
+ preset_target_arch = arch_sh4a;
+ else if (strcasecmp (arg, "dsp") == 0)
+ preset_target_arch = arch_sh1_up & ~(arch_sh_sp_fpu|arch_sh_dp_fpu);
+ else if (strcasecmp (arg, "fp") == 0)
+ preset_target_arch = arch_sh1_up & ~arch_sh_has_dsp;
else if (strcasecmp (arg, "any") == 0)
preset_target_arch = arch_sh1_up;
#ifdef HAVE_SH64
}
#endif /* HAVE_SH64 */
else
- as_bad ("Invalid argument to --isa option: %s", arg);
+ {
+ extern const bfd_arch_info_type bfd_sh_arch;
+ extern unsigned int sh_ef_archset_table[];
+ bfd_arch_info_type *bfd_arch = &bfd_sh_arch;
+ preset_target_arch = 0;
+ for (; bfd_arch; bfd_arch=bfd_arch->next)
+ {
+ int len = strlen(bfd_arch->printable_name);
+
+ if (bfd_arch->mach == bfd_mach_sh5)
+ continue;
+
+ if (strncasecmp (bfd_arch->printable_name, arg, len) != 0)
+ continue;
+
+ if (arg[len] == '\0')
+ preset_target_arch =
+ sh_get_arch_from_bfd_mach (bfd_arch->mach);
+ else if (strcasecmp(&arg[len], "-up") == 0)
+ preset_target_arch =
+ sh_get_arch_up_from_bfd_mach (bfd_arch->mach);
+ else
+ continue;
+ break;
+ }
+
+ if (!preset_target_arch)
+ as_bad ("Invalid argument to --isa option: %s", arg);
+ }
break;
#ifdef HAVE_SH64
-little generate little endian code\n\
-big generate big endian code\n\
-relax alter jump instructions for long displacements\n\
+-renesas disable optimization with section symbol for\n\
+ compatibility with Renesas assembler.\n\
-small align sections to 4 byte boundaries, not 16\n\
--dsp enable sh-dsp insns, and disable sh2e/sh3e/sh4 insns.\n"));
+-dsp enable sh-dsp insns, and disable floating-point ISAs.\n\
+-isa=[any use most appropriate isa\n\
+ | dsp same as '-dsp'\n\
+ | fp"));
+ {
+ extern const bfd_arch_info_type bfd_sh_arch;
+ bfd_arch_info_type *bfd_arch = &bfd_sh_arch;
+ for (; bfd_arch; bfd_arch=bfd_arch->next)
+ if (bfd_arch->mach != bfd_mach_sh5)
+ {
+ fprintf (stream, "\n | %s", bfd_arch->printable_name);
+ fprintf (stream, "\n | %s-up", bfd_arch->printable_name);
+ }
+ }
+ fprintf (stream, "]\n");
#ifdef HAVE_SH64
fprintf (stream, _("\
--isa=[shmedia set default instruction set for SH64\n\
- | SHmedia\n\
- | shcompact\n\
- | SHcompact]\n\
+-isa=[shmedia set as the default instruction set for SH64\n\
+ | SHmedia\n\
+ | shcompact\n\
+ | SHcompact]\n"));
+ fprintf (stream, _("\
-abi=[32|64] set size of expanded SHmedia operands and object\n\
file type\n\
-shcompact-const-crange emit code-range descriptors for constants in\n\
constants and SHcompact code\n\
-no-expand do not expand MOVI, PT, PTA or PTB instructions\n\
-expand-pt32 with -abi=64, expand PT, PTA and PTB instructions\n\
- to 32 bits only"));
+ to 32 bits only\n"));
#endif /* HAVE_SH64 */
}
\f
if (fixP->fx_r_type == BFD_RELOC_32_PLT_PCREL
|| fixP->fx_r_type == BFD_RELOC_32_GOT_PCREL
|| fixP->fx_r_type == BFD_RELOC_SH_GOTPC
+ || ((fixP->fx_r_type == BFD_RELOC_32) && dont_adjust_reloc_32)
|| fixP->fx_r_type == BFD_RELOC_RVA)
return 0;
int val;
/* Set file-specific flags to indicate if this code needs
- a processor with the sh-dsp / sh3e ISA to execute. */
+ a processor with the sh-dsp / sh2e ISA to execute. */
#ifdef HAVE_SH64
/* SH5 and above don't know about the valid_arch arch_sh* bits defined
in sh-opc.h, so check SH64 mode before checking valid_arch. */
val = EF_SH5;
else
#endif /* HAVE_SH64 */
- if (valid_arch & arch_sh1)
- val = EF_SH1;
- else if (valid_arch & arch_sh2)
- val = EF_SH2;
- else if (valid_arch & arch_sh2e)
- val = EF_SH2E;
- else if (valid_arch & arch_sh_dsp)
- val = EF_SH_DSP;
- else if (valid_arch & arch_sh3)
- val = EF_SH3;
- else if (valid_arch & arch_sh3_dsp)
- val = EF_SH_DSP;
- else if (valid_arch & arch_sh3e)
- val = EF_SH3E;
- else if (valid_arch & arch_sh4)
- val = EF_SH4;
- else
- abort ();
+ val = sh_find_elf_flags (valid_arch);
elf_elfheader (stdoutput)->e_flags &= ~EF_SH_MACH_MASK;
elf_elfheader (stdoutput)->e_flags |= val;
rel->addend = 0;
rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
-#ifdef OBJ_ELF
- if (rel->howto->type == R_SH_IND12W)
- rel->addend += fixp->fx_offset - 4;
-#endif
+
if (rel->howto == NULL)
{
as_bad_where (fixp->fx_file, fixp->fx_line,
rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
assert (rel->howto != NULL);
}
+#ifdef OBJ_ELF
+ else if (rel->howto->type == R_SH_IND12W)
+ rel->addend += fixp->fx_offset - 4;
+#endif
return rel;
}
return 1;
}
#endif
+
+void
+sh_cfi_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa (15, 0);
+}
+
+int
+sh_regname_to_dw2regnum (const char *regname)
+{
+ unsigned int regnum = -1;
+ unsigned int i;
+ const char *p;
+ char *q;
+ static struct { char *name; int dw2regnum; } regnames[] =
+ {
+ { "pr", 17 }, { "t", 18 }, { "gbr", 19 }, { "mach", 20 },
+ { "macl", 21 }, { "fpul", 23 }
+ };
+
+ for (i = 0; i < ARRAY_SIZE (regnames); ++i)
+ if (strcmp (regnames[i].name, regname) == 0)
+ return regnames[i].dw2regnum;
+
+ if (regname[0] == 'r')
+ {
+ p = regname + 1;
+ regnum = strtoul (p, &q, 10);
+ if (p == q || *q || regnum >= 16)
+ return -1;
+ }
+ else if (regname[0] == 'f' && regname[1] == 'r')
+ {
+ p = regname + 2;
+ regnum = strtoul (p, &q, 10);
+ if (p == q || *q || regnum >= 16)
+ return -1;
+ regnum += 25;
+ }
+ else if (regname[0] == 'x' && regname[1] == 'd')
+ {
+ p = regname + 2;
+ regnum = strtoul (p, &q, 10);
+ if (p == q || *q || regnum >= 8)
+ return -1;
+ regnum += 87;
+ }
+ return regnum;
+}
#endif /* BFD_ASSEMBLER */