aboutsummaryrefslogtreecommitdiff
path: root/gas/config/tc-sh.c
diff options
context:
space:
mode:
Diffstat (limited to 'gas/config/tc-sh.c')
-rw-r--r--gas/config/tc-sh.c169
1 files changed, 127 insertions, 42 deletions
diff --git a/gas/config/tc-sh.c b/gas/config/tc-sh.c
index 4003f5b..d468215 100644
--- a/gas/config/tc-sh.c
+++ b/gas/config/tc-sh.c
@@ -244,14 +244,12 @@ static int reg_x, reg_y;
static int reg_efg;
static int reg_b;
-static expressionS immediate; /* absolute expression */
-
typedef struct
{
sh_arg_type type;
int reg;
+ expressionS immediate;
}
-
sh_operand_info;
#define IDENT_CHAR(c) (isalnum (c) || (c) == '_')
@@ -466,7 +464,9 @@ parse_reg (src, mode, reg)
}
if (src[0] == 'p' && src[1] == 'c' && ! IDENT_CHAR ((unsigned char) src[2]))
{
- *mode = A_DISP_PC;
+ /* Don't use A_DISP_PC here - that would accept stuff like 'mova pc,r0'
+ and use an uninitialized immediate. */
+ *mode = A_PC;
return 2;
}
if (src[0] == 'g' && src[1] == 'b' && src[2] == 'r'
@@ -619,16 +619,17 @@ static symbolS *dot()
static
char *
-parse_exp (s)
+parse_exp (s, op)
char *s;
+ sh_operand_info *op;
{
char *save;
char *new;
save = input_line_pointer;
input_line_pointer = s;
- expression (&immediate);
- if (immediate.X_op == O_absent)
+ expression (&op->immediate);
+ if (op->immediate.X_op == O_absent)
as_bad (_("missing operand"));
new = input_line_pointer;
input_line_pointer = save;
@@ -710,7 +711,7 @@ parse_at (src, op)
else
{
/* Must be an @(disp,.. thing) */
- src = parse_exp (src);
+ src = parse_exp (src, op);
if (src[0] == ',')
src++;
/* Now can be rn, gbr or pc */
@@ -725,12 +726,12 @@ parse_at (src, op)
{
op->type = A_DISP_GBR;
}
- else if (mode == A_DISP_PC)
+ else if (mode == A_PC)
{
/* Turn a plain @(4,pc) into @(.+4,pc) */
- if (immediate.X_op == O_constant) {
- immediate.X_add_symbol = dot();
- immediate.X_op = O_symbol;
+ if (op->immediate.X_op == O_constant) {
+ op->immediate.X_add_symbol = dot();
+ op->immediate.X_op = O_symbol;
}
op->type = A_DISP_PC;
}
@@ -795,7 +796,7 @@ get_operand (ptr, op)
if (src[0] == '#')
{
src++;
- *ptr = parse_exp (src);
+ *ptr = parse_exp (src, op);
op->type = A_IMM;
return;
}
@@ -815,7 +816,7 @@ get_operand (ptr, op)
else
{
/* Not a reg, the only thing left is a displacement */
- *ptr = parse_exp (src);
+ *ptr = parse_exp (src, op);
op->type = A_DISP_PC;
return;
}
@@ -1177,22 +1178,24 @@ check (operand, low, high)
static void
-insert (where, how, pcrel)
+insert (where, how, pcrel, op)
char *where;
int how;
int pcrel;
+ sh_operand_info *op;
{
fix_new_exp (frag_now,
where - frag_now->fr_literal,
2,
- &immediate,
+ &op->immediate,
pcrel,
how);
}
static void
-build_relax (opcode)
+build_relax (opcode, op)
sh_opcode_info *opcode;
+ sh_operand_info *op;
{
int high_byte = target_big_endian ? 0 : 1;
char *p;
@@ -1204,8 +1207,8 @@ build_relax (opcode)
md_relax_table[C (what, COND32)].rlx_length,
md_relax_table[C (what, COND8)].rlx_length,
C (what, 0),
- immediate.X_add_symbol,
- immediate.X_add_number,
+ op->immediate.X_add_symbol,
+ op->immediate.X_add_number,
0);
p[high_byte] = (opcode->nibbles[0] << 4) | (opcode->nibbles[1]);
}
@@ -1215,14 +1218,63 @@ build_relax (opcode)
md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length,
md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length,
C (UNCOND_JUMP, 0),
- immediate.X_add_symbol,
- immediate.X_add_number,
+ op->immediate.X_add_symbol,
+ op->immediate.X_add_number,
0);
p[high_byte] = (opcode->nibbles[0] << 4);
}
}
+/* insert ldrs & ldre with fancy relocations that relaxation can recognize. */
+static char *
+insert_loop_bounds (output, operand)
+ char *output;
+ sh_operand_info *operand;
+{
+ char *name;
+ symbolS *end_sym;
+
+ /* Since the low byte of the opcode will be overwritten by the reloc, we
+ can just stash the high byte into both bytes and ignore endianness. */
+ output[0] = 0x8c;
+ output[1] = 0x8c;
+ insert (output, BFD_RELOC_SH_LOOP_START, 1, operand);
+ insert (output, BFD_RELOC_SH_LOOP_END, 1, operand + 1);
+
+ if (sh_relax)
+ {
+ static int count = 0;
+
+ /* If the last loop insn is a two-byte-insn, it is in danger of being
+ swapped with the insn after it. To prevent this, create a new
+ symbol - complete with SH_LABEL reloc - after the last loop insn.
+ If the last loop insn is four bytes long, the symbol will be
+ right in the middle, but four byte insns are not swapped anyways. */
+ /* A REPEAT takes 6 bytes. The SH has a 32 bit address space.
+ Hence a 9 digit number should be enough to count all REPEATs. */
+ name = alloca (11);
+ sprintf (name, "_R%x", count++ & 0x3fffffff);
+ end_sym = symbol_new (name, undefined_section, 0, &zero_address_frag);
+ /* Make this a local symbol. */
+#ifdef OBJ_COFF
+ SF_SET_LOCAL (end_sym);
+#endif /* OBJ_COFF */
+ symbol_table_insert (end_sym);
+ end_sym->sy_value = operand[1].immediate;
+ end_sym->sy_value.X_add_number += 2;
+ fix_new (frag_now, frag_now_fix (), 2, end_sym, 0, 1, BFD_RELOC_SH_LABEL);
+ }
+
+ output = frag_more (2);
+ output[0] = 0x8e;
+ output[1] = 0x8e;
+ insert (output, BFD_RELOC_SH_LOOP_START, 1, operand);
+ insert (output, BFD_RELOC_SH_LOOP_END, 1, operand + 1);
+
+ return frag_more (2);
+}
+
/* Now we know what sort of opcodes it is, lets build the bytes -
*/
static void
@@ -1268,32 +1320,52 @@ build_Mytes (opcode, operand)
case REG_B:
nbuf[index] = reg_b | 0x08;
break;
- case DISP_4:
- insert (output + low_byte, BFD_RELOC_SH_IMM4, 0);
+ case IMM0_4BY4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0, operand);
+ break;
+ case IMM0_4BY2:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0, operand);
+ break;
+ case IMM0_4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4, 0, operand);
+ break;
+ case IMM1_4BY4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0, operand + 1);
+ break;
+ case IMM1_4BY2:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0, operand + 1);
break;
- case IMM_4BY4:
- insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0);
+ case IMM1_4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4, 0, operand + 1);
break;
- case IMM_4BY2:
- insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0);
+ case IMM0_8BY4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8BY4, 0, operand);
break;
- case IMM_4:
- insert (output + low_byte, BFD_RELOC_SH_IMM4, 0);
+ case IMM0_8BY2:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0, operand);
break;
- case IMM_8BY4:
- insert (output + low_byte, BFD_RELOC_SH_IMM8BY4, 0);
+ case IMM0_8:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8, 0, operand);
break;
- case IMM_8BY2:
- insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0);
+ case IMM1_8BY4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8BY4, 0, operand + 1);
break;
- case IMM_8:
- insert (output + low_byte, BFD_RELOC_SH_IMM8, 0);
+ case IMM1_8BY2:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0, operand + 1);
+ break;
+ case IMM1_8:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8, 0, operand + 1);
break;
case PCRELIMM_8BY4:
- insert (output, BFD_RELOC_SH_PCRELIMM8BY4, 1);
+ insert (output, BFD_RELOC_SH_PCRELIMM8BY4, 1, operand);
break;
case PCRELIMM_8BY2:
- insert (output, BFD_RELOC_SH_PCRELIMM8BY2, 1);
+ insert (output, BFD_RELOC_SH_PCRELIMM8BY2, 1, operand);
+ break;
+ case REPEAT:
+ output = insert_loop_bounds (output, operand);
+ nbuf[index] = opcode->nibbles[3];
+ operand += 2;
break;
default:
printf (_("failed for %d\n"), i);
@@ -1457,10 +1529,10 @@ assemble_ppi (op_end, opcode)
break;
case PSH:
- if (immediate.X_op != O_constant)
+ if (operand[0].immediate.X_op != O_constant)
as_bad (_("dsp immediate shift value not constant"));
field_b = ((opcode->nibbles[2] << 12)
- | (immediate.X_add_number & 127) << 4
+ | (operand[0].immediate.X_add_number & 127) << 4
| reg_n);
break;
case PPI3:
@@ -1604,8 +1676,8 @@ md_assemble (str)
if (opcode->arg[0] == A_BDISP12
|| opcode->arg[0] == A_BDISP8)
{
- parse_exp (op_end + 1);
- build_relax (opcode);
+ parse_exp (op_end + 1, &operand[0]);
+ build_relax (opcode, &operand[0]);
}
else
{
@@ -2407,7 +2479,9 @@ sh_force_relocation (fix)
{
if (fix->fx_r_type == BFD_RELOC_VTABLE_INHERIT
- || fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ || fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+ || fix->fx_r_type == BFD_RELOC_SH_LOOP_START
+ || fix->fx_r_type == BFD_RELOC_SH_LOOP_END)
return 1;
if (! sh_relax)
@@ -2643,6 +2717,9 @@ md_apply_fix (fixP, val)
/* Nothing to do here. */
break;
+ case BFD_RELOC_SH_LOOP_START:
+ case BFD_RELOC_SH_LOOP_END:
+
case BFD_RELOC_VTABLE_INHERIT:
case BFD_RELOC_VTABLE_ENTRY:
fixP->fx_done = 0;
@@ -2993,6 +3070,14 @@ tc_gen_reloc (section, fixp)
else if (r_type == BFD_RELOC_VTABLE_INHERIT
|| r_type == BFD_RELOC_VTABLE_ENTRY)
rel->addend = fixp->fx_offset;
+ else if (r_type == BFD_RELOC_SH_LOOP_START
+ || r_type == BFD_RELOC_SH_LOOP_END)
+ rel->addend = fixp->fx_offset;
+ else if (r_type == BFD_RELOC_SH_LABEL && fixp->fx_pcrel)
+ {
+ rel->addend = 0;
+ rel->address = rel->addend = fixp->fx_offset;
+ }
else if (fixp->fx_pcrel)
rel->addend = fixp->fx_addnumber;
else