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:
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,6 +924,281 @@ static const struct sparc_opcode *last_insn;
|
|||
/* The assembled opcode of `last_insn'. */
|
||||
static unsigned long last_opcode;
|
||||
|
||||
/* 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;
|
||||
|
||||
if (the_insn.exp.X_op == O_constant)
|
||||
{
|
||||
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)
|
||||
{
|
||||
synthetize_setuw (insn);
|
||||
return;
|
||||
}
|
||||
|
||||
opc = OR_INSN;
|
||||
|
||||
the_insn.reloc = BFD_RELOC_NONE;
|
||||
/* See if operand is absolute and small; skip sethi if so. */
|
||||
if (low32 < -(1 << 12))
|
||||
{
|
||||
the_insn.opcode = (SETHI_INSN | RD (rd)
|
||||
| (((~the_insn.exp.X_add_number) >> 10) & 0x3fffff));
|
||||
output_insn (insn, &the_insn);
|
||||
low32 = 0x1c00 | (low32 & 0x3ff);
|
||||
opc = RS1 (rd) | XOR_INSN;
|
||||
}
|
||||
|
||||
the_insn.opcode = (opc | RD (rd) | IMMED
|
||||
| (low32 & 0x1fff));
|
||||
output_insn (insn, &the_insn);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
int upper_dstreg;
|
||||
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)
|
||||
lower32 = SIGNEXT32 (the_insn.exp.X_add_number);
|
||||
upper32 = SIGNEXT32 (BSR (the_insn.exp.X_add_number, 32));
|
||||
#undef SIGNEXT32
|
||||
|
||||
upper_dstreg = tmpreg;
|
||||
/* The tmp reg should not be the dst reg. */
|
||||
if (tmpreg == dstreg)
|
||||
as_warn (_("setx: temporary register same as destination register"));
|
||||
|
||||
/* ??? Obviously there are other optimizations we can do
|
||||
(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)
|
||||
{
|
||||
if (sparc_arch_size == 32)
|
||||
{
|
||||
/* When arch size is 32, we want setx to be equivalent
|
||||
to setuw for anything but constants. */
|
||||
the_insn.exp.X_add_number &= 0xffffffff;
|
||||
synthetize_setuw (insn);
|
||||
return;
|
||||
}
|
||||
need_hh22_p = need_hm10_p = need_hi22_p = need_lo10_p = 1;
|
||||
lower32 = 0; upper32 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Reset X_add_number, we've extracted it as upper32/lower32.
|
||||
Otherwise fixup_segment will complain about not being able to
|
||||
write an 8 byte number in a 4 byte field. */
|
||||
the_insn.exp.X_add_number = 0;
|
||||
|
||||
/* Only need hh22 if `or' insn can't handle constant. */
|
||||
if (upper32 < -(1 << 12) || upper32 >= (1 << 12))
|
||||
need_hh22_p = 1;
|
||||
|
||||
/* Does bottom part (after sethi) have bits? */
|
||||
if ((need_hh22_p && (upper32 & 0x3ff) != 0)
|
||||
/* No hh22, but does upper32 still have bits we can't set
|
||||
from lower32? */
|
||||
|| (! need_hh22_p && upper32 != 0 && upper32 != -1))
|
||||
need_hm10_p = 1;
|
||||
|
||||
/* If the lower half is all zero, we build the upper half directly
|
||||
into the dst reg. */
|
||||
if (lower32 != 0
|
||||
/* Need lower half if number is zero or 0xffffffff00000000. */
|
||||
|| (! need_hh22_p && ! need_hm10_p))
|
||||
{
|
||||
/* No need for sethi if `or' insn can handle constant. */
|
||||
if (lower32 < -(1 << 12) || lower32 >= (1 << 12)
|
||||
/* Note that we can't use a negative constant in the `or'
|
||||
insn unless the upper 32 bits are all ones. */
|
||||
|| (lower32 < 0 && upper32 != -1)
|
||||
|| (lower32 >= 0 && upper32 == -1))
|
||||
need_hi22_p = 1;
|
||||
|
||||
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. */
|
||||
|| (! need_hi22_p && (lower32 & 0x1fff) != 0)
|
||||
/* Need `or' if we didn't set anything else. */
|
||||
|| (! need_hi22_p && ! need_hh22_p && ! need_hm10_p))
|
||||
need_lo10_p = 1;
|
||||
}
|
||||
else
|
||||
/* 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)
|
||||
| ((upper32 >> 10) & 0x3fffff));
|
||||
the_insn.reloc = (the_insn.exp.X_op != O_constant
|
||||
? BFD_RELOC_SPARC_HH22 : BFD_RELOC_NONE);
|
||||
output_insn (insn, &the_insn);
|
||||
}
|
||||
|
||||
if (need_hi22_p)
|
||||
{
|
||||
the_insn.opcode = (SETHI_INSN | RD (dstreg)
|
||||
| (((need_xor10_p ? ~lower32 : lower32)
|
||||
>> 10) & 0x3fffff));
|
||||
the_insn.reloc = (the_insn.exp.X_op != O_constant
|
||||
? BFD_RELOC_SPARC_LM22 : BFD_RELOC_NONE);
|
||||
output_insn (insn, &the_insn);
|
||||
}
|
||||
|
||||
if (need_hm10_p)
|
||||
{
|
||||
the_insn.opcode = (OR_INSN
|
||||
| (need_hh22_p ? RS1 (upper_dstreg) : 0)
|
||||
| RD (upper_dstreg)
|
||||
| IMMED
|
||||
| (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);
|
||||
}
|
||||
|
||||
if (need_lo10_p)
|
||||
{
|
||||
/* FIXME: One nice optimization to do here is to OR the low part
|
||||
with the highpart if hi22 isn't needed and the low part is
|
||||
positive. */
|
||||
the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (dstreg) : 0)
|
||||
| RD (dstreg)
|
||||
| IMMED
|
||||
| (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);
|
||||
}
|
||||
|
||||
/* If we needed to build the upper part, shift it into place. */
|
||||
if (need_hh22_p || need_hm10_p)
|
||||
{
|
||||
the_insn.opcode = (SLLX_INSN | RS1 (upper_dstreg) | RD (upper_dstreg)
|
||||
| IMMED | 32);
|
||||
the_insn.reloc = BFD_RELOC_NONE;
|
||||
output_insn (insn, &the_insn);
|
||||
}
|
||||
|
||||
/* To get -1 in upper32, we do sethi %hi(~x), r; xor r, -0x400 | x, r. */
|
||||
if (need_xor10_p)
|
||||
{
|
||||
the_insn.opcode = (XOR_INSN | RS1 (dstreg) | RD (dstreg) | IMMED
|
||||
| 0x1c00 | (lower32 & 0x3ff));
|
||||
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))
|
||||
{
|
||||
the_insn.opcode = (OR_INSN | RS1 (dstreg) | RS2 (upper_dstreg)
|
||||
| RD (dstreg));
|
||||
the_insn.reloc = BFD_RELOC_NONE;
|
||||
output_insn (insn, &the_insn);
|
||||
}
|
||||
}
|
||||
|
||||
/* Main entry point to assemble one instruction. */
|
||||
|
||||
void
|
||||
|
@ -931,10 +1206,10 @@ md_assemble (str)
|
|||
char *str;
|
||||
{
|
||||
const struct sparc_opcode *insn;
|
||||
int special_case;
|
||||
|
||||
know (str);
|
||||
special_case = SPECIAL_CASE_NONE;
|
||||
sparc_ip (str, &insn);
|
||||
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. */
|
||||
|
@ -965,301 +1240,50 @@ md_assemble (str)
|
|||
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;
|
||||
switch (special_case)
|
||||
{
|
||||
case SPECIAL_CASE_NONE:
|
||||
/* normal insn */
|
||||
output_insn (insn, &the_insn);
|
||||
break;
|
||||
|
||||
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)
|
||||
as_warn (_("setsw: number not in -2147483648..4294967295 range"));
|
||||
case SPECIAL_CASE_SETSW:
|
||||
synthetize_setsw (insn);
|
||||
break;
|
||||
|
||||
case SPECIAL_CASE_SET:
|
||||
synthetize_setuw (insn);
|
||||
break;
|
||||
|
||||
low32 = the_insn.exp.X_add_number;
|
||||
|
||||
if (low32 < 0)
|
||||
{
|
||||
int rd = (the_insn.opcode & RD (~0)) >> 25;
|
||||
int opc = OR_INSN;
|
||||
case SPECIAL_CASE_SETX:
|
||||
synthetize_setx (insn);
|
||||
break;
|
||||
|
||||
case SPECIAL_CASE_FDIV:
|
||||
{
|
||||
int rd = (the_insn.opcode >> 25) & 0x1f;
|
||||
|
||||
the_insn.reloc = BFD_RELOC_NONE;
|
||||
/* See if operand is absolute and small; skip sethi if so. */
|
||||
if (low32 < -(1 << 12))
|
||||
{
|
||||
the_insn.opcode = (SETHI_INSN | RD (rd)
|
||||
| (((~the_insn.exp.X_add_number) >> 10) & 0x3fffff));
|
||||
output_insn (insn, &the_insn);
|
||||
low32 = 0x1c00 | (low32 & 0x3ff);
|
||||
opc = RS1 (rd) | XOR_INSN;
|
||||
}
|
||||
|
||||
the_insn.opcode = (opc | RD (rd) | IMMED
|
||||
| (low32 & 0x1fff));
|
||||
output_insn (insn, &the_insn);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
output_insn (insn, &the_insn);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
/* According to information leaked from Sun, the "fdiv" instructions
|
||||
on early SPARC machines would produce incorrect results sometimes.
|
||||
The workaround is to add an fmovs of the destination register to
|
||||
itself just after the instruction. This was true on machines
|
||||
with Weitek 1165 float chips, such as the Sun-4/260 and /280. */
|
||||
assert (the_insn.reloc == BFD_RELOC_NONE);
|
||||
the_insn.opcode = FMOVS_INSN | rd | RD (rd);
|
||||
output_insn (insn, &the_insn);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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:
|
||||
{
|
||||
int upper32, lower32;
|
||||
int tmpreg = (the_insn.opcode & RS1 (~0)) >> 14;
|
||||
int dstreg = (the_insn.opcode & RD (~0)) >> 25;
|
||||
int upper_dstreg;
|
||||
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)
|
||||
lower32 = SIGNEXT32 (the_insn.exp.X_add_number);
|
||||
upper32 = SIGNEXT32 (BSR (the_insn.exp.X_add_number, 32));
|
||||
#undef SIGNEXT32
|
||||
|
||||
upper_dstreg = tmpreg;
|
||||
/* The tmp reg should not be the dst reg. */
|
||||
if (tmpreg == dstreg)
|
||||
as_warn (_("setx: temporary register same as destination register"));
|
||||
|
||||
/* ??? Obviously there are other optimizations we can do
|
||||
(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)
|
||||
{
|
||||
if (sparc_arch_size == 32)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
need_hh22_p = need_hm10_p = need_hi22_p = need_lo10_p = 1;
|
||||
lower32 = 0; upper32 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Reset X_add_number, we've extracted it as upper32/lower32.
|
||||
Otherwise fixup_segment will complain about not being able to
|
||||
write an 8 byte number in a 4 byte field. */
|
||||
the_insn.exp.X_add_number = 0;
|
||||
|
||||
/* Only need hh22 if `or' insn can't handle constant. */
|
||||
if (upper32 < -(1 << 12) || upper32 >= (1 << 12))
|
||||
need_hh22_p = 1;
|
||||
|
||||
/* Does bottom part (after sethi) have bits? */
|
||||
if ((need_hh22_p && (upper32 & 0x3ff) != 0)
|
||||
/* No hh22, but does upper32 still have bits we can't set
|
||||
from lower32? */
|
||||
|| (! need_hh22_p && upper32 != 0 && upper32 != -1))
|
||||
need_hm10_p = 1;
|
||||
|
||||
/* If the lower half is all zero, we build the upper half directly
|
||||
into the dst reg. */
|
||||
if (lower32 != 0
|
||||
/* Need lower half if number is zero or 0xffffffff00000000. */
|
||||
|| (! need_hh22_p && ! need_hm10_p))
|
||||
{
|
||||
/* No need for sethi if `or' insn can handle constant. */
|
||||
if (lower32 < -(1 << 12) || lower32 >= (1 << 12)
|
||||
/* Note that we can't use a negative constant in the `or'
|
||||
insn unless the upper 32 bits are all ones. */
|
||||
|| (lower32 < 0 && upper32 != -1)
|
||||
|| (lower32 >= 0 && upper32 == -1))
|
||||
need_hi22_p = 1;
|
||||
|
||||
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. */
|
||||
|| (! need_hi22_p && (lower32 & 0x1fff) != 0)
|
||||
/* Need `or' if we didn't set anything else. */
|
||||
|| (! need_hi22_p && ! need_hh22_p && ! need_hm10_p))
|
||||
need_lo10_p = 1;
|
||||
}
|
||||
else
|
||||
/* Output directly to dst reg if lower 32 bits are all
|
||||
zero. */
|
||||
upper_dstreg = dstreg;
|
||||
}
|
||||
|
||||
if (need_hh22_p)
|
||||
{
|
||||
the_insn.opcode = (SETHI_INSN | RD (upper_dstreg)
|
||||
| ((upper32 >> 10) & 0x3fffff));
|
||||
the_insn.reloc = (the_insn.exp.X_op != O_constant
|
||||
? BFD_RELOC_SPARC_HH22 : BFD_RELOC_NONE);
|
||||
output_insn (insn, &the_insn);
|
||||
}
|
||||
|
||||
if (need_hi22_p)
|
||||
{
|
||||
the_insn.opcode = (SETHI_INSN | RD (dstreg)
|
||||
| (((need_xor10_p ? ~lower32 : lower32)
|
||||
>> 10) & 0x3fffff));
|
||||
the_insn.reloc = (the_insn.exp.X_op != O_constant
|
||||
? BFD_RELOC_SPARC_LM22 : BFD_RELOC_NONE);
|
||||
output_insn (insn, &the_insn);
|
||||
}
|
||||
|
||||
if (need_hm10_p)
|
||||
{
|
||||
the_insn.opcode = (OR_INSN
|
||||
| (need_hh22_p ? RS1 (upper_dstreg) : 0)
|
||||
| RD (upper_dstreg)
|
||||
| IMMED
|
||||
| (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);
|
||||
}
|
||||
|
||||
if (need_lo10_p)
|
||||
{
|
||||
/* FIXME: One nice optimization to do here is to OR the low part
|
||||
with the highpart if hi22 isn't needed and the low part is
|
||||
positive. */
|
||||
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;
|
||||
output_insn (insn, &the_insn);
|
||||
}
|
||||
|
||||
/* If we needed to build the upper part, shift it into place. */
|
||||
if (need_hh22_p || need_hm10_p)
|
||||
{
|
||||
the_insn.opcode = (SLLX_INSN | RS1 (upper_dstreg) | RD (upper_dstreg)
|
||||
| IMMED | 32);
|
||||
the_insn.reloc = BFD_RELOC_NONE;
|
||||
output_insn (insn, &the_insn);
|
||||
}
|
||||
|
||||
/* To get -1 in upper32, we do sethi %hi(~x), r; xor r, -0x400 | x, r. */
|
||||
if (need_xor10_p)
|
||||
{
|
||||
the_insn.opcode = (XOR_INSN | RS1 (dstreg) | RD (dstreg) | IMMED
|
||||
| 0x1c00 | (lower32 & 0x3ff));
|
||||
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))
|
||||
{
|
||||
the_insn.opcode = (OR_INSN | RS1 (dstreg) | RS2 (upper_dstreg)
|
||||
| RD (dstreg));
|
||||
the_insn.reloc = BFD_RELOC_NONE;
|
||||
output_insn (insn, &the_insn);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case SPECIAL_CASE_FDIV:
|
||||
{
|
||||
int rd = (the_insn.opcode >> 25) & 0x1f;
|
||||
|
||||
output_insn (insn, &the_insn);
|
||||
|
||||
/* According to information leaked from Sun, the "fdiv" instructions
|
||||
on early SPARC machines would produce incorrect results sometimes.
|
||||
The workaround is to add an fmovs of the destination register to
|
||||
itself just after the instruction. This was true on machines
|
||||
with Weitek 1165 float chips, such as the Sun-4/260 and /280. */
|
||||
assert (the_insn.reloc == BFD_RELOC_NONE);
|
||||
the_insn.opcode = FMOVS_INSN | rd | RD (rd);
|
||||
output_insn (insn, &the_insn);
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
as_fatal (_("failed special case insn sanity check"));
|
||||
}
|
||||
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.
|
||||
|
|
Loading…
Reference in a new issue