diff options
author | Richard Henderson <rth@redhat.com> | 1999-06-07 12:53:56 +0000 |
---|---|---|
committer | Richard Henderson <rth@redhat.com> | 1999-06-07 12:53:56 +0000 |
commit | 63fab58c7d582b41b75c33ef23a8b60ab5f4f3bf (patch) | |
tree | e219194d5a69f9812c5bd948a663d357dc44f027 /gas/config/tc-sparc.c | |
parent | 440034c99f17db548089e694de8330911152c767 (diff) | |
download | gdb-63fab58c7d582b41b75c33ef23a8b60ab5f4f3bf.zip gdb-63fab58c7d582b41b75c33ef23a8b60ab5f4f3bf.tar.gz gdb-63fab58c7d582b41b75c33ef23a8b60ab5f4f3bf.tar.bz2 |
Jakub Jelinek <jj@ultra.linux.cz>
* config/tc-sparc.c (md_assemble): Fix up setx, support setsw.
Optimize set if sizeof(bfd_vma) == 64.
(sparc_ip): Fix sethi - without %hi() it should generate
R_SPARC_32 reloc, not R_SPARC_HI22.
(tc_gen_reloc): Handle BFD_RELOC_SPARC22.
Diffstat (limited to 'gas/config/tc-sparc.c')
-rw-r--r-- | gas/config/tc-sparc.c | 480 |
1 files changed, 276 insertions, 204 deletions
diff --git a/gas/config/tc-sparc.c b/gas/config/tc-sparc.c index d483956..7810d61 100644 --- a/gas/config/tc-sparc.c +++ b/gas/config/tc-sparc.c @@ -913,6 +913,7 @@ static int special_case; /* Bit masks of various insns. */ #define NOP_INSN 0x01000000 #define OR_INSN 0x80100000 +#define XOR_INSN 0x80180000 #define FMOVS_INSN 0x81A00020 #define SETHI_INSN 0x01000000 #define SLLX_INSN 0x81281000 @@ -964,222 +965,295 @@ md_assemble (str) 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_SET: - { - int need_hi22_p = 0; + for (;;) + { + switch (special_case) + { + case SPECIAL_CASE_NONE: + /* normal insn */ + output_insn (insn, &the_insn); + return; - /* "set" is not defined for negative numbers in v9: it doesn't yield - what you expect it to. */ - if (SPARC_OPCODE_ARCH_V9_P (max_architecture) - && the_insn.exp.X_op == O_constant) - { - if (the_insn.exp.X_add_number < 0) - as_warn (_("set: used with negative number")); - else if (the_insn.exp.X_add_number > (offsetT) 0xffffffff) - as_warn (_("set: number larger than 4294967295")); - } + 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")); + + low32 = the_insn.exp.X_add_number; + + if (low32 < 0) + { + int rd = (the_insn.opcode & RD (~0)) >> 25; + int 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; + } - /* 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)) - { - 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 = (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; - the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (rd) : 0) - | RD (rd) - | IMMED - | (the_insn.exp.X_add_number - & (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); - } - break; - } - case SPECIAL_CASE_SETSW: - { - /* FIXME: Not finished. */ - break; - } - - case SPECIAL_CASE_SETX: - { -#define SIGNEXT32(x) ((((x) & 0xffffffff) ^ 0x80000000) - 0x80000000) - int upper32 = SIGNEXT32 (BSR (the_insn.exp.X_add_number, 32)); - int lower32 = SIGNEXT32 (the_insn.exp.X_add_number); -#undef SIGNEXT32 - int tmpreg = (the_insn.opcode & RS1 (~0)) >> 14; - int dstreg = (the_insn.opcode & RD (~0)) >> 25; - /* Output directly to dst reg if lower 32 bits are all zero. */ - int upper_dstreg = (the_insn.exp.X_op == O_constant - && lower32 == 0) ? dstreg : tmpreg; - int need_hh22_p = 0, need_hm10_p = 0, need_hi22_p = 0, need_lo10_p = 0; - - /* The tmp reg should not be the dst reg. */ - if (tmpreg == dstreg) - as_warn (_("setx: temporary register same as destination register")); - - /* 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; - - /* ??? 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) - need_hh22_p = need_hm10_p = need_hi22_p = need_lo10_p = 1; - else - { - /* 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 || lower32 >= 0))) - 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. */ - || (! need_hh22_p && ! need_hm10_p)) + if (the_insn.exp.X_op == O_constant) { - /* 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)) - need_hi22_p = 1; - - /* Does bottom part (after sethi) have bits? */ - 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; + 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; } - } - - 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_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); - } + /* 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 (need_hi22_p) - { - the_insn.opcode = (SETHI_INSN | RD (dstreg) - | ((lower32 >> 10) & 0x3fffff)); - the_insn.reloc = BFD_RELOC_HI22; - 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; } - - if (need_lo10_p) + + case SPECIAL_CASE_SETX: { - /* 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); - } + 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 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); + 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; } - - /* If we needed to build both upper and lower parts, OR them together. */ - if ((need_hh22_p || need_hm10_p) - && (need_hi22_p || need_lo10_p)) + + case SPECIAL_CASE_FDIV: { - the_insn.opcode = (OR_INSN | RS1 (dstreg) | RS2 (upper_dstreg) - | RD (dstreg)); - the_insn.reloc = BFD_RELOC_NONE; + int rd = (the_insn.opcode >> 25) & 0x1f; + output_insn (insn, &the_insn); - } - /* We didn't need both regs, but we may have to sign extend lower32. */ - else if (need_hi22_p && upper32 == -1) - { - the_insn.opcode = (SRA_INSN | RS1 (dstreg) | RD (dstreg) - | IMMED | 0); - the_insn.reloc = BFD_RELOC_NONE; + + /* 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; } - break; - } - - 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); - break; - } - - default: - as_fatal (_("failed special case insn sanity check")); + + default: + as_fatal (_("failed special case insn sanity check")); + } } } @@ -1891,14 +1965,10 @@ sparc_ip (str, pinsn) } break; - case '0': /* 64 bit immediate (setx insn) */ + case '0': /* 64 bit immediate (set, setsw, setx insn) */ the_insn.reloc = BFD_RELOC_NONE; /* reloc handled elsewhere */ goto immediate; - case 'h': /* high 22 bits */ - the_insn.reloc = BFD_RELOC_HI22; - goto immediate; - case 'l': /* 22 bit PC relative immediate */ the_insn.reloc = BFD_RELOC_SPARC_WDISP22; the_insn.pcrel = 1; @@ -1909,6 +1979,7 @@ sparc_ip (str, pinsn) the_insn.pcrel = 1; goto immediate; + case 'h': case 'n': /* 22 bit immediate */ the_insn.reloc = BFD_RELOC_SPARC22; goto immediate; @@ -2968,6 +3039,7 @@ tc_gen_reloc (section, fixp) case BFD_RELOC_LO10: case BFD_RELOC_32_PCREL_S2: case BFD_RELOC_SPARC13: + case BFD_RELOC_SPARC22: case BFD_RELOC_SPARC_BASE13: case BFD_RELOC_SPARC_WDISP16: case BFD_RELOC_SPARC_WDISP19: |