Jakub Jelinek <jj@ultra.linux.cz>

* config/tc-sparc.c (sparc_ip): Don't use side-effect expression
        with isoctal.
        * config/tc-sparc.c (synthetize_setuw, synthetize_setsw,
        synthetize_setx): New functions.
        (md_assemble): Broken the special cases into the above
        functions. Make compiler happy if sizeof(bfd_vma)==4.
        Fix sethi generated from set/setuw. If instructions have a relloc,
        always clear the fields to be relocated in the opcode.
        (sparc_ip): Remove special_case global variable.
This commit is contained in:
Richard Henderson
1999-06-10 21:11:39 +00:00
parent b3fb1136bf
commit a22b281cd7
2 changed files with 347 additions and 304 deletions

@ -1,3 +1,16 @@
1999-06-10 Jakub Jelinek <jj@ultra.linux.cz>
* config/tc-sparc.c (sparc_ip): Don't use side-effect expression
with isoctal.
* config/tc-sparc.c (synthetize_setuw, synthetize_setsw,
synthetize_setx): New functions.
(md_assemble): Broken the special cases into the above
functions. Make compiler happy if sizeof(bfd_vma)==4.
Fix sethi generated from set/setuw. If instructions have a relloc,
always clear the fields to be relocated in the opcode.
(sparc_ip): Remove special_case global variable.
1999-06-10 Ian Lance Taylor <ian@zembu.com>
Based on patches from John W. Woznack <jwoznack@concentric.net>:

@ -31,11 +31,14 @@
static struct sparc_arch *lookup_arch PARAMS ((char *));
static void init_default_arch PARAMS ((void));
static void sparc_ip PARAMS ((char *, const struct sparc_opcode **));
static int sparc_ip PARAMS ((char *, const struct sparc_opcode **));
static int in_signed_range PARAMS ((bfd_signed_vma, bfd_signed_vma));
static int in_unsigned_range PARAMS ((bfd_vma, bfd_vma));
static int in_bitfield_range PARAMS ((bfd_signed_vma, bfd_signed_vma));
static int sparc_ffs PARAMS ((unsigned int));
static void synthetize_setuw PARAMS ((const struct sparc_opcode *));
static void synthetize_setsw PARAMS ((const struct sparc_opcode *));
static void synthetize_setx PARAMS ((const struct sparc_opcode *));
static bfd_vma BSR PARAMS ((bfd_vma, int));
static int cmp_reg_entry PARAMS ((const PTR, const PTR));
static int parse_keyword_arg PARAMS ((int (*) (const char *), char **, int *));
@ -176,7 +179,7 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
changed in read.c. Ideally it shouldn't have to know about it at all,
but nothing is ideal around here. */
#define isoctal(c) ((c) >= '0' && (c) < '8')
#define isoctal(c) ((unsigned)((c) - '0') < '8')
struct sparc_it
{
@ -897,9 +900,6 @@ BSR (val, amount)
/* For communication between sparc_ip and get_expression. */
static char *expr_end;
/* For communication between md_assemble and sparc_ip. */
static int special_case;
/* Values for `special_case'.
Instructions that require wierd handling because they're longer than
4 bytes. */
@ -924,70 +924,99 @@ static const struct sparc_opcode *last_insn;
/* The assembled opcode of `last_insn'. */
static unsigned long last_opcode;
/* Main entry point to assemble one instruction. */
void
md_assemble (str)
char *str;
{
/* Handle the set and setuw synthetic instructions. */
static void
synthetize_setuw (insn)
const struct sparc_opcode *insn;
{
int need_hi22_p = 0;
int rd = (the_insn.opcode & RD (~0)) >> 25;
know (str);
special_case = SPECIAL_CASE_NONE;
sparc_ip (str, &insn);
/* We warn about attempts to put a floating point branch in a delay slot,
unless the delay slot has been annulled. */
if (insn != NULL
&& last_insn != NULL
&& (insn->flags & F_FBR) != 0
&& (last_insn->flags & F_DELAYED) != 0
/* ??? This test isn't completely accurate. We assume anything with
F_{UNBR,CONDBR,FBR} set is annullable. */
&& ((last_insn->flags & (F_UNBR | F_CONDBR | F_FBR)) == 0
|| (last_opcode & ANNUL) == 0))
as_warn (_("FP branch in delay slot"));
/* SPARC before v9 requires a nop instruction between a floating
point instruction and a floating point branch. We insert one
automatically, with a warning. */
if (max_architecture < SPARC_OPCODE_ARCH_V9
&& insn != NULL
&& last_insn != NULL
&& (insn->flags & F_FBR) != 0
&& (last_insn->flags & F_FLOAT) != 0)
{
struct sparc_it nop_insn;
nop_insn.opcode = NOP_INSN;
nop_insn.reloc = BFD_RELOC_NONE;
output_insn (insn, &nop_insn);
as_warn (_("FP branch preceded by FP instruction; NOP inserted"));
}
for (;;)
{
switch (special_case)
{
case SPECIAL_CASE_NONE:
/* normal insn */
output_insn (insn, &the_insn);
return;
case SPECIAL_CASE_SETSW:
if (the_insn.exp.X_op == O_constant)
{
int low32;
if (the_insn.exp.X_add_number < -(offsetT)0x80000000
|| the_insn.exp.X_add_number > (offsetT) 0xffffffff)
if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
{
if (sizeof(offsetT) > 4
&& (the_insn.exp.X_add_number < 0
|| the_insn.exp.X_add_number > (offsetT) 0xffffffff))
as_warn (_("set: number not in 0..4294967295 range"));
}
else
{
if (sizeof(offsetT) > 4
&& (the_insn.exp.X_add_number < -(offsetT) 0x80000000
|| the_insn.exp.X_add_number > (offsetT) 0xffffffff))
as_warn (_("set: number not in -2147483648..4294967295 range"));
the_insn.exp.X_add_number = (int)the_insn.exp.X_add_number;
}
}
/* See if operand is absolute and small; skip sethi if so. */
if (the_insn.exp.X_op != O_constant
|| the_insn.exp.X_add_number >= (1 << 12)
|| the_insn.exp.X_add_number < -(1 << 12))
{
the_insn.opcode = (SETHI_INSN | RD (rd)
| ((the_insn.exp.X_add_number >> 10)
& (the_insn.exp.X_op == O_constant ? 0x3fffff : 0)));
the_insn.reloc = (the_insn.exp.X_op != O_constant
? BFD_RELOC_HI22
: BFD_RELOC_NONE);
output_insn (insn, &the_insn);
need_hi22_p = 1;
}
/* See if operand has no low-order bits; skip OR if so. */
if (the_insn.exp.X_op != O_constant
|| (need_hi22_p && (the_insn.exp.X_add_number & 0x3FF) != 0)
|| ! need_hi22_p)
{
the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (rd) : 0)
| RD (rd) | IMMED
| (the_insn.exp.X_add_number
& (the_insn.exp.X_op != O_constant ? 0 :
need_hi22_p ? 0x3ff : 0x1fff)));
the_insn.reloc = (the_insn.exp.X_op != O_constant
? BFD_RELOC_LO10
: BFD_RELOC_NONE);
output_insn (insn, &the_insn);
}
}
/* Handle the setsw synthetic instruction. */
static void
synthetize_setsw (insn)
const struct sparc_opcode *insn;
{
int low32, rd, opc;
rd = (the_insn.opcode & RD (~0)) >> 25;
if (the_insn.exp.X_op != O_constant)
{
synthetize_setuw (insn);
/* Need to sign extend it. */
the_insn.opcode = (SRA_INSN | RS1 (rd) | RD (rd));
the_insn.reloc = BFD_RELOC_NONE;
output_insn (insn, &the_insn);
return;
}
if (sizeof(offsetT) > 4
&& (the_insn.exp.X_add_number < -(offsetT) 0x80000000
|| the_insn.exp.X_add_number > (offsetT) 0xffffffff))
as_warn (_("setsw: number not in -2147483648..4294967295 range"));
low32 = the_insn.exp.X_add_number;
if (low32 < 0)
if (low32 >= 0)
{
int rd = (the_insn.opcode & RD (~0)) >> 25;
int opc = OR_INSN;
synthetize_setuw (insn);
return;
}
opc = OR_INSN;
the_insn.reloc = BFD_RELOC_NONE;
/* See if operand is absolute and small; skip sethi if so. */
@ -1003,77 +1032,13 @@ md_assemble (str)
the_insn.opcode = (opc | RD (rd) | IMMED
| (low32 & 0x1fff));
output_insn (insn, &the_insn);
return;
}
}
/* FALLTHROUGH */
}
case SPECIAL_CASE_SET:
{
int need_hi22_p = 0;
int rd = (the_insn.opcode & RD (~0)) >> 25;
if (the_insn.exp.X_op == O_constant)
{
if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
{
if (the_insn.exp.X_add_number < 0
|| the_insn.exp.X_add_number > (offsetT) 0xffffffff)
as_warn (_("set: number not in 0..4294967295 range"));
}
else
{
if (the_insn.exp.X_add_number < (offsetT)-0x80000000
|| the_insn.exp.X_add_number > (offsetT) 0xffffffff)
as_warn (_("set: number not in -2147483648..4294967295 range"));
if (the_insn.exp.X_add_number >= (offsetT)0x80000000)
the_insn.exp.X_add_number -= (offsetT)0x100000000;
}
}
/* See if operand is absolute and small; skip sethi if so. */
if (the_insn.exp.X_op != O_constant
|| the_insn.exp.X_add_number >= (1 << 12)
|| the_insn.exp.X_add_number < -(1 << 12))
{
the_insn.opcode = (SETHI_INSN | RD (rd)
| ((the_insn.exp.X_add_number >> 10)
& the_insn.exp.X_op == O_constant ? 0x3fffff : 0));
the_insn.reloc = BFD_RELOC_HI22;
output_insn (insn, &the_insn);
need_hi22_p = 1;
}
/* See if operand has no low-order bits; skip OR if so. */
if (the_insn.exp.X_op != O_constant
|| (need_hi22_p && (the_insn.exp.X_add_number & 0x3FF) != 0)
|| ! need_hi22_p)
{
the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (rd) : 0)
| RD (rd)
| IMMED
| (the_insn.exp.X_add_number
& (the_insn.exp.X_op != O_constant ? 0 :
need_hi22_p ? 0x3ff : 0x1fff)));
the_insn.reloc = (the_insn.exp.X_op != O_constant
? BFD_RELOC_LO10
: BFD_RELOC_NONE);
output_insn (insn, &the_insn);
}
if (special_case == SPECIAL_CASE_SETSW
&& the_insn.exp.X_op != O_constant)
{
/* Need to sign extend it. */
the_insn.opcode = (SRA_INSN | RS1 (rd) | RD (rd));
the_insn.reloc = BFD_RELOC_NONE;
output_insn (insn, &the_insn);
}
return;
}
case SPECIAL_CASE_SETX:
{
/* Handle the setsw synthetic instruction. */
static void
synthetize_setx (insn)
const struct sparc_opcode *insn;
{
int upper32, lower32;
int tmpreg = (the_insn.opcode & RS1 (~0)) >> 14;
int dstreg = (the_insn.opcode & RD (~0)) >> 25;
@ -1081,10 +1046,10 @@ md_assemble (str)
int need_hh22_p = 0, need_hm10_p = 0, need_hi22_p = 0, need_lo10_p = 0;
int need_xor10_p = 0;
#define SIGNEXT32(x) ((((x) & 0xffffffff) ^ 0x80000000) - 0x80000000)
#define SIGNEXT32(x) ((((x) & 0xffffffff) ^ 0x80000000) - 0x80000000)
lower32 = SIGNEXT32 (the_insn.exp.X_add_number);
upper32 = SIGNEXT32 (BSR (the_insn.exp.X_add_number, 32));
#undef SIGNEXT32
#undef SIGNEXT32
upper_dstreg = tmpreg;
/* The tmp reg should not be the dst reg. */
@ -1095,7 +1060,6 @@ md_assemble (str)
(e.g. sethi+shift for 0x1f0000000) and perhaps we shouldn't be
doing some of these. Later. If you do change things, try to
change all of this to be table driven as well. */
/* What to output depends on the number if it's constant.
Compute that first, then output what we've decided upon. */
if (the_insn.exp.X_op != O_constant)
@ -1105,8 +1069,8 @@ md_assemble (str)
/* When arch size is 32, we want setx to be equivalent
to setuw for anything but constants. */
the_insn.exp.X_add_number &= 0xffffffff;
special_case = SPECIAL_CASE_SET;
continue;
synthetize_setuw (insn);
return;
}
need_hh22_p = need_hm10_p = need_hi22_p = need_lo10_p = 1;
lower32 = 0; upper32 = 0;
@ -1145,6 +1109,7 @@ md_assemble (str)
if (need_hi22_p && upper32 == -1)
need_xor10_p = 1;
/* Does bottom part (after sethi) have bits? */
else if ((need_hi22_p && (lower32 & 0x3ff) != 0)
/* No sethi. */
@ -1154,11 +1119,13 @@ md_assemble (str)
need_lo10_p = 1;
}
else
/* Output directly to dst reg if lower 32 bits are all
zero. */
/* Output directly to dst reg if lower 32 bits are all zero. */
upper_dstreg = dstreg;
}
if (!upper_dstreg && dstreg)
as_warn (_("setx: illegal temporary register g0"));
if (need_hh22_p)
{
the_insn.opcode = (SETHI_INSN | RD (upper_dstreg)
@ -1184,8 +1151,7 @@ md_assemble (str)
| (need_hh22_p ? RS1 (upper_dstreg) : 0)
| RD (upper_dstreg)
| IMMED
| (upper32
& (need_hh22_p ? 0x3ff : 0x1fff)));
| (upper32 & (need_hh22_p ? 0x3ff : 0x1fff)));
the_insn.reloc = (the_insn.exp.X_op != O_constant
? BFD_RELOC_SPARC_HM10 : BFD_RELOC_NONE);
output_insn (insn, &the_insn);
@ -1199,9 +1165,9 @@ md_assemble (str)
the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (dstreg) : 0)
| RD (dstreg)
| IMMED
| (lower32
& (need_hi22_p ? 0x3ff : 0x1fff)));
the_insn.reloc = BFD_RELOC_LO10;
| (lower32 & (need_hi22_p ? 0x3ff : 0x1fff)));
the_insn.reloc = (the_insn.exp.X_op != O_constant
? BFD_RELOC_LO10 : BFD_RELOC_NONE);
output_insn (insn, &the_insn);
}
@ -1222,18 +1188,77 @@ md_assemble (str)
the_insn.reloc = BFD_RELOC_NONE;
output_insn (insn, &the_insn);
}
/* If we needed to build both upper and lower parts, OR them together. */
else if ((need_hh22_p || need_hm10_p)
&& (need_hi22_p || need_lo10_p))
else if ((need_hh22_p || need_hm10_p) && (need_hi22_p || need_lo10_p))
{
the_insn.opcode = (OR_INSN | RS1 (dstreg) | RS2 (upper_dstreg)
| RD (dstreg));
the_insn.reloc = BFD_RELOC_NONE;
output_insn (insn, &the_insn);
}
return;
}
/* Main entry point to assemble one instruction. */
void
md_assemble (str)
char *str;
{
const struct sparc_opcode *insn;
int special_case;
know (str);
special_case = sparc_ip (str, &insn);
/* We warn about attempts to put a floating point branch in a delay slot,
unless the delay slot has been annulled. */
if (insn != NULL
&& last_insn != NULL
&& (insn->flags & F_FBR) != 0
&& (last_insn->flags & F_DELAYED) != 0
/* ??? This test isn't completely accurate. We assume anything with
F_{UNBR,CONDBR,FBR} set is annullable. */
&& ((last_insn->flags & (F_UNBR | F_CONDBR | F_FBR)) == 0
|| (last_opcode & ANNUL) == 0))
as_warn (_("FP branch in delay slot"));
/* SPARC before v9 requires a nop instruction between a floating
point instruction and a floating point branch. We insert one
automatically, with a warning. */
if (max_architecture < SPARC_OPCODE_ARCH_V9
&& insn != NULL
&& last_insn != NULL
&& (insn->flags & F_FBR) != 0
&& (last_insn->flags & F_FLOAT) != 0)
{
struct sparc_it nop_insn;
nop_insn.opcode = NOP_INSN;
nop_insn.reloc = BFD_RELOC_NONE;
output_insn (insn, &nop_insn);
as_warn (_("FP branch preceded by FP instruction; NOP inserted"));
}
switch (special_case)
{
case SPECIAL_CASE_NONE:
/* normal insn */
output_insn (insn, &the_insn);
break;
case SPECIAL_CASE_SETSW:
synthetize_setsw (insn);
break;
case SPECIAL_CASE_SET:
synthetize_setuw (insn);
break;
case SPECIAL_CASE_SETX:
synthetize_setx (insn);
break;
case SPECIAL_CASE_FDIV:
{
int rd = (the_insn.opcode >> 25) & 0x1f;
@ -1254,12 +1279,11 @@ md_assemble (str)
default:
as_fatal (_("failed special case insn sanity check"));
}
}
}
/* Subroutine of md_assemble to do the actual parsing. */
static void
static int
sparc_ip (str, pinsn)
char *str;
const struct sparc_opcode **pinsn;
@ -1275,6 +1299,7 @@ sparc_ip (str, pinsn)
int match = 0;
int comma = 0;
int v9_arg_p;
int special_case = SPECIAL_CASE_NONE;
s = str;
if (islower ((unsigned char) *s))
@ -1306,7 +1331,7 @@ sparc_ip (str, pinsn)
if (insn == NULL)
{
as_bad (_("Unknown opcode: `%s'"), str);
return;
return special_case;
}
if (comma)
{
@ -1765,7 +1790,8 @@ sparc_ip (str, pinsn)
goto error;
case 'g': /* global register */
if (isoctal (c = *s++))
c = *s++;
if (isoctal (c))
{
mask = c - '0';
break;
@ -1773,7 +1799,8 @@ sparc_ip (str, pinsn)
goto error;
case 'i': /* in register */
if (isoctal (c = *s++))
c = *s++;
if (isoctal (c))
{
mask = c - '0' + 24;
break;
@ -1781,7 +1808,8 @@ sparc_ip (str, pinsn)
goto error;
case 'l': /* local register */
if (isoctal (c = *s++))
c = *s++;
if (isoctal (c))
{
mask = (c - '0' + 16);
break;
@ -1789,7 +1817,8 @@ sparc_ip (str, pinsn)
goto error;
case 'o': /* out register */
if (isoctal (c = *s++))
c = *s++;
if (isoctal (c))
{
mask = (c - '0' + 8);
break;
@ -2041,7 +2070,7 @@ sparc_ip (str, pinsn)
if (s[o->len + 1] != '(')
{
as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name);
return;
return special_case;
}
op_arg = o->name;
@ -2077,7 +2106,7 @@ sparc_ip (str, pinsn)
if (*s1 != ')')
{
as_bad (_("Illegal operands: %%%s requires arguments in ()"), op_arg);
return;
return special_case;
}
*s1 = '\0';
@ -2089,7 +2118,7 @@ sparc_ip (str, pinsn)
if (*s != '+' && *s != '-')
{
as_bad (_("Illegal operands: Can't do arithmetics other than + and - involving %%%s()"), op_arg);
return;
return special_case;
}
*s1 = '0';
s = s1;
@ -2192,7 +2221,7 @@ sparc_ip (str, pinsn)
else if (the_insn.exp2.X_op != O_constant)
{
as_bad (_("Illegal operands: Can't add non-constant expression to %%%s()"), op_arg);
return;
return special_case;
}
else
{
@ -2202,7 +2231,7 @@ sparc_ip (str, pinsn)
|| sparc_pic_code)
{
as_bad (_("Illegal operands: Can't do arithmetics involving %%%s() of a relocatable symbol"), op_arg);
return;
return special_case;
}
the_insn.reloc = BFD_RELOC_SPARC_OLO10;
}
@ -2416,7 +2445,7 @@ sparc_ip (str, pinsn)
else
{
as_bad (_("Illegal operands%s"), error_message);
return;
return special_case;
}
}
else
@ -2484,7 +2513,7 @@ sparc_ip (str, pinsn)
as_tsktsk (_(" (Requires %s; requested architecture is %s.)"),
required_archs,
sparc_opcode_archs[max_architecture].name);
return;
return special_case;
}
} /* if no match */
@ -2492,6 +2521,7 @@ sparc_ip (str, pinsn)
} /* forever looking for a match */
the_insn.opcode = opcode;
return special_case;
}
/* Parse an argument that can be expressed as a keyword.