📄 tc-sparc.c
字号:
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 voidsynthetize_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 voidsynthetize_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. */voidmd_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; 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")); }}/* Subroutine of md_assemble to do the actual parsing. */static intsparc_ip (str, pinsn) char *str; const struct sparc_opcode **pinsn;{ char *error_message = ""; char *s; const char *args; char c; const struct sparc_opcode *insn; char *argsStart; unsigned long opcode; unsigned int mask = 0; int match = 0; int comma = 0; int v9_arg_p; int special_case = SPECIAL_CASE_NONE; s = str; if (islower ((unsigned char) *s)) { do ++s; while (islower ((unsigned char) *s) || isdigit ((unsigned char) *s)); } switch (*s) { case '\0': break; case ',': comma = 1; /* Fall through. */ case ' ': *s++ = '\0'; break; default: as_fatal (_("Unknown opcode: `%s'"), str); } insn = (struct sparc_opcode *) hash_find (op_hash, str); *pinsn = insn; if (insn == NULL) { as_bad (_("Unknown opcode: `%s'"), str); return special_case; } if (comma) { *--s = ','; } argsStart = s; for (;;) { opcode = insn->match; memset (&the_insn, '\0', sizeof (the_insn)); the_insn.reloc = BFD_RELOC_NONE; v9_arg_p = 0; /* Build the opcode, checking as we go to make sure that the operands match. */ for (args = insn->args;; ++args) { switch (*args) { case 'K': { int kmask = 0; /* Parse a series of masks. */ if (*s == '#') { while (*s == '#') { int mask; if (! parse_keyword_arg (sparc_encode_membar, &s, &mask)) { error_message = _(": invalid membar mask name"); goto error; } kmask |= mask; while (*s == ' ') ++s; if (*s == '|' || *s == '+') ++s; while (*s == ' ') ++s; } } else { if (! parse_const_expr_arg (&s, &kmask)) { error_message = _(": invalid membar mask expression"); goto error; } if (kmask < 0 || kmask > 127) { error_message = _(": invalid membar mask number"); goto error; } } opcode |= MEMBAR (kmask); continue; } case '3': { int smask = 0; if (! parse_const_expr_arg (&s, &smask)) { error_message = _(": invalid siam mode expression"); goto error; } if (smask < 0 || smask > 7) { error_message = _(": invalid siam mode number"); goto error; } opcode |= smask; continue; } case '*': { int fcn = 0; /* Parse a prefetch function. */ if (*s == '#') { if (! parse_keyword_arg (sparc_encode_prefetch, &s, &fcn)) { error_message = _(": invalid prefetch function name"); goto error; } } else { if (! parse_const_expr_arg (&s, &fcn)) { error_message = _(": invalid prefetch function expression"); goto error; } if (fcn < 0 || fcn > 31) { error_message = _(": invalid prefetch function number"); goto error; } } opcode |= RD (fcn); continue; } case '!': case '?': /* Parse a sparc64 privileged register. */ if (*s == '%') { struct priv_reg_entry *p = priv_reg_table; unsigned int len = 9999999; /* Init to make gcc happy. */ s += 1; while (p->name[0] > s[0]) p++; while (p->name[0] == s[0]) { len = strlen (p->name); if (strncmp (p->name, s, len) == 0) break; p++; } if (p->name[0] != s[0]) { error_message = _(": unrecognizable privileged register"); goto error; } if (*args == '?') opcode |= (p->regnum << 14); else opcode |= (p->regnum << 25); s += len; continue; } else { error_message = _(": unrecognizable privileged register"); goto error; } case '_': case '/': /* Parse a v9a/v9b ancillary state register. */ if (*s == '%') { struct priv_reg_entry *p = v9a_asr_table; unsigned int len = 9999999; /* Init to make gcc happy. */ s += 1; while (p->name[0] > s[0]) p++; while (p->name[0] == s[0]) { len = strlen (p->name); if (strncmp (p->name, s, len) == 0) break; p++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -