summaryrefslogtreecommitdiffstats
path: root/contrib/binutils/gas/config/tc-sh.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/binutils/gas/config/tc-sh.c')
-rw-r--r--contrib/binutils/gas/config/tc-sh.c233
1 files changed, 229 insertions, 4 deletions
diff --git a/contrib/binutils/gas/config/tc-sh.c b/contrib/binutils/gas/config/tc-sh.c
index 0c0bf4b..eaa6146 100644
--- a/contrib/binutils/gas/config/tc-sh.c
+++ b/contrib/binutils/gas/config/tc-sh.c
@@ -48,6 +48,7 @@ static void sh_frob_section PARAMS ((bfd *, segT, PTR));
void cons ();
void s_align_bytes ();
+static void s_uacons PARAMS ((int));
int shl = 0;
@@ -70,6 +71,8 @@ const pseudo_typeS md_pseudo_table[] =
{"page", listing_eject, 0},
{"program", s_ignore, 0},
{"uses", s_uses, 0},
+ {"uaword", s_uacons, 2},
+ {"ualong", s_uacons, 4},
{0, 0, 0}
};
@@ -77,6 +80,10 @@ const pseudo_typeS md_pseudo_table[] =
int sh_relax; /* set if -relax seen */
+/* Whether -small was seen. */
+
+int sh_small;
+
const char EXP_CHARS[] = "eE";
/* Chars that mean this number is a floating point constant */
@@ -259,6 +266,18 @@ parse_reg (src, mode, reg)
return 3;
}
+ if (src[0] == 's' && src[1] == 'g' && src[2] == 'r' && ! isalnum (src[3]))
+ {
+ *mode = A_SGR;
+ return 3;
+ }
+
+ if (src[0] == 'd' && src[1] == 'b' && src[2] == 'r' && ! isalnum (src[3]))
+ {
+ *mode = A_DBR;
+ return 3;
+ }
+
if (src[0] == 's' && src[1] == 'r' && ! isalnum (src[2]))
{
*mode = A_SR;
@@ -324,6 +343,61 @@ parse_reg (src, mode, reg)
return 3;
}
}
+ if (src[0] == 'd' && src[1] == 'r')
+ {
+ if (src[2] == '1')
+ {
+ if (src[3] >= '0' && src[3] <= '4' && ! ((src[3] - '0') & 1)
+ && ! isalnum (src[4]))
+ {
+ *mode = D_REG_N;
+ *reg = 10 + src[3] - '0';
+ return 4;
+ }
+ }
+ if (src[2] >= '0' && src[2] <= '8' && ! ((src[2] - '0') & 1)
+ && ! isalnum (src[3]))
+ {
+ *mode = D_REG_N;
+ *reg = (src[2] - '0');
+ return 3;
+ }
+ }
+ if (src[0] == 'x' && src[1] == 'd')
+ {
+ if (src[2] == '1')
+ {
+ if (src[3] >= '0' && src[3] <= '4' && ! ((src[3] - '0') & 1)
+ && ! isalnum (src[4]))
+ {
+ *mode = X_REG_N;
+ *reg = 11 + src[3] - '0';
+ return 4;
+ }
+ }
+ if (src[2] >= '0' && src[2] <= '8' && ! ((src[2] - '0') & 1)
+ && ! isalnum (src[3]))
+ {
+ *mode = X_REG_N;
+ *reg = (src[2] - '0') + 1;
+ return 3;
+ }
+ }
+ if (src[0] == 'f' && src[1] == 'v')
+ {
+ if (src[2] == '1'&& src[3] == '2' && ! isalnum (src[4]))
+ {
+ *mode = V_REG_N;
+ *reg = 12;
+ return 4;
+ }
+ if ((src[2] == '0' || src[2] == '4' || src[2] == '8') && ! isalnum (src[3]))
+ {
+ *mode = V_REG_N;
+ *reg = (src[2] - '0');
+ return 3;
+ }
+ }
if (src[0] == 'f' && src[1] == 'p' && src[2] == 'u' && src[3] == 'l'
&& ! isalnum (src[4]))
{
@@ -338,6 +412,13 @@ parse_reg (src, mode, reg)
return 5;
}
+ if (src[0] == 'x' && src[1] == 'm' && src[2] == 't' && src[3] == 'r'
+ && src[4] == 'x' && ! isalnum (src[5]))
+ {
+ *mode = XMTRX_M4;
+ return 5;
+ }
+
return 0;
}
@@ -662,6 +743,9 @@ get_specific (opcode, operands)
case A_IND_R0_REG_N:
case A_DISP_REG_N:
case F_REG_N:
+ case D_REG_N:
+ case X_REG_N:
+ case V_REG_N:
case FPUL_N:
case FPSCR_N:
/* Opcode needs rn */
@@ -669,11 +753,23 @@ get_specific (opcode, operands)
goto fail;
reg_n = user->reg;
break;
+ case FD_REG_N:
+ if (user->type != F_REG_N && user->type != D_REG_N)
+ goto fail;
+ reg_n = user->reg;
+ break;
+ case DX_REG_N:
+ if (user->type != D_REG_N && user->type != X_REG_N)
+ goto fail;
+ reg_n = user->reg;
+ break;
case A_GBR:
case A_SR:
case A_VBR:
case A_SSR:
case A_SPC:
+ case A_SGR:
+ case A_DBR:
if (user->type != arg)
goto fail;
break;
@@ -697,6 +793,9 @@ get_specific (opcode, operands)
break;
case F_REG_M:
+ case D_REG_M:
+ case X_REG_M:
+ case V_REG_M:
case FPUL_M:
case FPSCR_M:
/* Opcode needs rn */
@@ -704,6 +803,16 @@ get_specific (opcode, operands)
goto fail;
reg_m = user->reg;
break;
+ case DX_REG_M:
+ if (user->type != D_REG_N && user->type != X_REG_N)
+ goto fail;
+ reg_m = user->reg;
+ break;
+ case XMTRX_M4:
+ if (user->type != XMTRX_M4)
+ goto fail;
+ reg_m = 4;
+ break;
default:
printf ("unhandled %d\n", arg);
@@ -813,6 +922,9 @@ build_Mytes (opcode, operand)
case REG_M:
nbuf[index] = reg_m;
break;
+ case REG_NM:
+ nbuf[index] = reg_n | (reg_m >> 2);
+ break;
case REG_B:
nbuf[index] = reg_b | 0x08;
break;
@@ -1099,10 +1211,12 @@ CONST char *md_shortopts = "";
struct option md_longopts[] = {
#define OPTION_RELAX (OPTION_MD_BASE)
-#define OPTION_LITTLE (OPTION_MD_BASE+1)
+#define OPTION_LITTLE (OPTION_MD_BASE + 1)
+#define OPTION_SMALL (OPTION_LITTLE + 1)
{"relax", no_argument, NULL, OPTION_RELAX},
{"little", no_argument, NULL, OPTION_LITTLE},
+ {"small", no_argument, NULL, OPTION_SMALL},
{NULL, no_argument, NULL, 0}
};
size_t md_longopts_size = sizeof(md_longopts);
@@ -1117,11 +1231,16 @@ md_parse_option (c, arg)
case OPTION_RELAX:
sh_relax = 1;
break;
+
case OPTION_LITTLE:
shl = 1;
target_big_endian = 0;
break;
+ case OPTION_SMALL:
+ sh_small = 1;
+ break;
+
default:
return 0;
}
@@ -1136,7 +1255,8 @@ md_show_usage (stream)
fprintf(stream, "\
SH options:\n\
-little generate little endian code\n\
--relax alter jump instructions for long displacements\n");
+-relax alter jump instructions for long displacements\n\
+-small align sections to 4 byte boundaries, not 16\n");
}
int md_short_jump_size;
@@ -1560,8 +1680,70 @@ DEFUN (md_section_align, (seg, size),
#endif /* ! BFD_ASSEMBLER */
}
+/* This static variable is set by s_uacons to tell sh_cons_align that
+ the expession does not need to be aligned. */
+
+static int sh_no_align_cons = 0;
+
+/* This handles the unaligned space allocation pseudo-ops, such as
+ .uaword. .uaword is just like .word, but the value does not need
+ to be aligned. */
+
+static void
+s_uacons (bytes)
+ int bytes;
+{
+ /* Tell sh_cons_align not to align this value. */
+ sh_no_align_cons = 1;
+ cons (bytes);
+}
+
+/* If a .word, et. al., pseud-op is seen, warn if the value is not
+ aligned correctly. Note that this can cause warnings to be issued
+ when assembling initialized structured which were declared with the
+ packed attribute. FIXME: Perhaps we should require an option to
+ enable this warning? */
+
+void
+sh_cons_align (nbytes)
+ int nbytes;
+{
+ int nalign;
+ char *p;
+
+ if (sh_no_align_cons)
+ {
+ /* This is an unaligned pseudo-op. */
+ sh_no_align_cons = 0;
+ return;
+ }
+
+ nalign = 0;
+ while ((nbytes & 1) == 0)
+ {
+ ++nalign;
+ nbytes >>= 1;
+ }
+
+ if (nalign == 0)
+ return;
+
+ if (now_seg == absolute_section)
+ {
+ if ((abs_section_offset & ((1 << nalign) - 1)) != 0)
+ as_warn ("misaligned data");
+ return;
+ }
+
+ p = frag_var (rs_align_code, 1, 1, (relax_substateT) 0,
+ (symbolS *) NULL, (offsetT) nalign, (char *) NULL);
+
+ record_alignment (now_seg, nalign);
+}
+
/* When relaxing, we need to output a reloc for any .align directive
- that requests alignment to a four byte boundary or larger. */
+ that requests alignment to a four byte boundary or larger. This is
+ also where we check for misaligned data. */
void
sh_handle_align (frag)
@@ -1574,6 +1756,10 @@ sh_handle_align (frag)
&& now_seg != bss_section)
fix_new (frag, frag->fr_fix, 2, &abs_symbol, frag->fr_offset, 0,
BFD_RELOC_SH_ALIGN);
+
+ if (frag->fr_type == rs_align_code
+ && frag->fr_next->fr_address - frag->fr_address - frag->fr_fix != 0)
+ as_warn_where (frag->fr_file, frag->fr_line, "misaligned data");
}
/* This macro decides whether a particular reloc is an entry in a
@@ -1587,6 +1773,7 @@ sh_handle_align (frag)
#define SWITCH_TABLE_CONS(fix) \
((fix)->fx_r_type == 0 \
&& ((fix)->fx_size == 2 \
+ || (fix)->fx_size == 1 \
|| (fix)->fx_size == 4))
#endif
@@ -1597,6 +1784,7 @@ sh_handle_align (frag)
&& S_GET_SEGMENT ((fix)->fx_subsy) == text_section \
&& ((fix)->fx_r_type == BFD_RELOC_32 \
|| (fix)->fx_r_type == BFD_RELOC_16 \
+ || (fix)->fx_r_type == BFD_RELOC_8 \
|| SWITCH_TABLE_CONS (fix)))
/* See whether we need to force a relocation into the output file.
@@ -1639,6 +1827,8 @@ md_apply_fix (fixP, val)
#ifdef BFD_ASSEMBLER
long val = *valp;
#endif
+ long max, min;
+ int shift;
#ifndef BFD_ASSEMBLER
if (fixP->fx_r_type == 0)
@@ -1648,36 +1838,53 @@ md_apply_fix (fixP, val)
else if (fixP->fx_size == 4)
fixP->fx_r_type = BFD_RELOC_32;
else if (fixP->fx_size == 1)
- fixP->fx_r_type = BFD_RELOC_SH_IMM8;
+ fixP->fx_r_type = BFD_RELOC_8;
else
abort ();
}
#endif
+ max = min = 0;
+ shift = 0;
switch (fixP->fx_r_type)
{
case BFD_RELOC_SH_IMM4:
+ max = 0xf;
*buf = (*buf & 0xf0) | (val & 0xf);
break;
case BFD_RELOC_SH_IMM4BY2:
+ max = 0xf;
+ shift = 1;
*buf = (*buf & 0xf0) | ((val >> 1) & 0xf);
break;
case BFD_RELOC_SH_IMM4BY4:
+ max = 0xf;
+ shift = 2;
*buf = (*buf & 0xf0) | ((val >> 2) & 0xf);
break;
case BFD_RELOC_SH_IMM8BY2:
+ max = 0xff;
+ shift = 1;
*buf = val >> 1;
break;
case BFD_RELOC_SH_IMM8BY4:
+ max = 0xff;
+ shift = 2;
*buf = val >> 2;
break;
case BFD_RELOC_8:
case BFD_RELOC_SH_IMM8:
+ /* Sometimes the 8 bit value is sign extended (e.g., add) and
+ sometimes it is not (e.g., and). We permit any 8 bit value.
+ Note that adding further restrictions may invalidate
+ reasonable looking assembly code, such as ``and -0x1,r0''. */
+ max = 0xff;
+ min = - 0xff;
*buf++ = val;
break;
@@ -1770,6 +1977,19 @@ md_apply_fix (fixP, val)
abort ();
}
+ if (shift != 0)
+ {
+ if ((val & ((1 << shift) - 1)) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line, "misaligned offset");
+ if (val >= 0)
+ val >>= shift;
+ else
+ val = ((val >> shift)
+ | ((long) -1 & ~ ((long) -1 >> shift)));
+ }
+ if (max != 0 && (val < min || val > max))
+ as_bad_where (fixP->fx_file, fixP->fx_line, "offset out of range");
+
#ifdef BFD_ASSEMBLER
return 0;
#endif
@@ -1931,6 +2151,7 @@ static const struct reloc_map coff_reloc_map[] =
{ BFD_RELOC_SH_IMM8BY4, R_SH_IMM8BY4 },
{ BFD_RELOC_SH_PCRELIMM8BY2, R_SH_PCRELIMM8BY2 },
{ BFD_RELOC_SH_PCRELIMM8BY4, R_SH_PCRELIMM8BY4 },
+ { BFD_RELOC_8_PCREL, R_SH_SWITCH8 },
{ BFD_RELOC_SH_SWITCH16, R_SH_SWITCH16 },
{ BFD_RELOC_SH_SWITCH32, R_SH_SWITCH32 },
{ BFD_RELOC_SH_USES, R_SH_USES },
@@ -1977,6 +2198,8 @@ sh_coff_reloc_mangle (seg, fix, intr, paddr)
if (fix->fx_r_type == BFD_RELOC_16)
intr->r_type = R_SH_SWITCH16;
+ else if (fix->fx_r_type == BFD_RELOC_8)
+ intr->r_type = R_SH_SWITCH8;
else if (fix->fx_r_type == BFD_RELOC_32)
intr->r_type = R_SH_SWITCH32;
else
@@ -2077,6 +2300,8 @@ tc_gen_reloc (section, fixp)
rel->addend = rel->address - S_GET_VALUE (fixp->fx_subsy);
if (r_type == BFD_RELOC_16)
r_type = BFD_RELOC_SH_SWITCH16;
+ else if (r_type == BFD_RELOC_8)
+ r_type = BFD_RELOC_8_PCREL;
else if (r_type == BFD_RELOC_32)
r_type = BFD_RELOC_SH_SWITCH32;
else
OpenPOWER on IntegriCloud