📄 tc-sparc.c
字号:
the_insn.reloc = BFD_RELOC_NONE; /* reloc handled elsewhere */ goto immediate; case 'l': /* 22 bit PC relative immediate */ the_insn.reloc = BFD_RELOC_SPARC_WDISP22; the_insn.pcrel = 1; goto immediate; case 'L': /* 30 bit immediate */ the_insn.reloc = BFD_RELOC_32_PCREL_S2; the_insn.pcrel = 1; goto immediate; case 'h': case 'n': /* 22 bit immediate */ the_insn.reloc = BFD_RELOC_SPARC22; goto immediate; case 'i': /* 13 bit immediate */ the_insn.reloc = BFD_RELOC_SPARC13; /* fallthrough */ immediate: if (*s == ' ') s++; { char *s1; char *op_arg = NULL; expressionS op_exp; bfd_reloc_code_real_type old_reloc = the_insn.reloc; /* Check for %hi, etc. */ if (*s == '%') { static const struct ops { /* The name as it appears in assembler. */ char *name; /* strlen (name), precomputed for speed */ int len; /* The reloc this pseudo-op translates to. */ int reloc; /* Non-zero if for v9 only. */ int v9_p; /* Non-zero if can be used in pc-relative contexts. */ int pcrel_p;/*FIXME:wip*/ } ops[] = { /* hix/lox must appear before hi/lo so %hix won't be mistaken for %hi. */ { "hix", 3, BFD_RELOC_SPARC_HIX22, 1, 0 }, { "lox", 3, BFD_RELOC_SPARC_LOX10, 1, 0 }, { "hi", 2, BFD_RELOC_HI22, 0, 1 }, { "lo", 2, BFD_RELOC_LO10, 0, 1 }, { "hh", 2, BFD_RELOC_SPARC_HH22, 1, 1 }, { "hm", 2, BFD_RELOC_SPARC_HM10, 1, 1 }, { "lm", 2, BFD_RELOC_SPARC_LM22, 1, 1 }, { "h44", 3, BFD_RELOC_SPARC_H44, 1, 0 }, { "m44", 3, BFD_RELOC_SPARC_M44, 1, 0 }, { "l44", 3, BFD_RELOC_SPARC_L44, 1, 0 }, { "uhi", 3, BFD_RELOC_SPARC_HH22, 1, 0 }, { "ulo", 3, BFD_RELOC_SPARC_HM10, 1, 0 }, { NULL, 0, 0, 0, 0 } }; const struct ops *o; for (o = ops; o->name; o++) if (strncmp (s + 1, o->name, o->len) == 0) break; if (o->name == NULL) break; if (s[o->len + 1] != '(') { as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name); return special_case; } op_arg = o->name; the_insn.reloc = o->reloc; s += o->len + 2; v9_arg_p = o->v9_p; } /* Note that if the get_expression() fails, we will still have created U entries in the symbol table for the 'symbols' in the input string. Try not to create U symbols for registers, etc. */ /* This stuff checks to see if the expression ends in +%reg. If it does, it removes the register from the expression, and re-sets 's' to point to the right place. */ if (op_arg) { int npar = 0; for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++) if (*s1 == '(') npar++; else if (*s1 == ')') { if (!npar) break; npar--; } if (*s1 != ')') { as_bad (_("Illegal operands: %%%s requires arguments in ()"), op_arg); return special_case; } *s1 = '\0'; (void) get_expression (s); *s1 = ')'; s = s1 + 1; if (*s == ',' || *s == ']' || !*s) continue; if (*s != '+' && *s != '-') { as_bad (_("Illegal operands: Can't do arithmetics other than + and - involving %%%s()"), op_arg); return special_case; } *s1 = '0'; s = s1; op_exp = the_insn.exp; memset (&the_insn.exp, 0, sizeof (the_insn.exp)); } for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++) ; if (s1 != s && isdigit ((unsigned char) s1[-1])) { if (s1[-2] == '%' && s1[-3] == '+') s1 -= 3; else if (strchr ("goli0123456789", s1[-2]) && s1[-3] == '%' && s1[-4] == '+') s1 -= 4; else s1 = NULL; if (s1) { *s1 = '\0'; if (op_arg && s1 == s + 1) the_insn.exp.X_op = O_absent; else (void) get_expression (s); *s1 = '+'; if (op_arg) *s = ')'; s = s1; } } else s1 = NULL; if (!s1) { (void) get_expression (s); if (op_arg) *s = ')'; s = expr_end; } if (op_arg) { the_insn.exp2 = the_insn.exp; the_insn.exp = op_exp; if (the_insn.exp2.X_op == O_absent) the_insn.exp2.X_op = O_illegal; else if (the_insn.exp.X_op == O_absent) { the_insn.exp = the_insn.exp2; the_insn.exp2.X_op = O_illegal; } else if (the_insn.exp.X_op == O_constant) { valueT val = the_insn.exp.X_add_number; switch (the_insn.reloc) { default: break; case BFD_RELOC_SPARC_HH22: val = BSR (val, 32); /* Fall through. */ case BFD_RELOC_SPARC_LM22: case BFD_RELOC_HI22: val = (val >> 10) & 0x3fffff; break; case BFD_RELOC_SPARC_HM10: val = BSR (val, 32); /* Fall through. */ case BFD_RELOC_LO10: val &= 0x3ff; break; case BFD_RELOC_SPARC_H44: val >>= 22; val &= 0x3fffff; break; case BFD_RELOC_SPARC_M44: val >>= 12; val &= 0x3ff; break; case BFD_RELOC_SPARC_L44: val &= 0xfff; break; case BFD_RELOC_SPARC_HIX22: val = ~val; val = (val >> 10) & 0x3fffff; break; case BFD_RELOC_SPARC_LOX10: val = (val & 0x3ff) | 0x1c00; break; } the_insn.exp = the_insn.exp2; the_insn.exp.X_add_number += val; the_insn.exp2.X_op = O_illegal; the_insn.reloc = old_reloc; } else if (the_insn.exp2.X_op != O_constant) { as_bad (_("Illegal operands: Can't add non-constant expression to %%%s()"), op_arg); return special_case; } else { if (old_reloc != BFD_RELOC_SPARC13 || the_insn.reloc != BFD_RELOC_LO10 || sparc_arch_size != 64 || sparc_pic_code) { as_bad (_("Illegal operands: Can't do arithmetics involving %%%s() of a relocatable symbol"), op_arg); return special_case; } the_insn.reloc = BFD_RELOC_SPARC_OLO10; } } } /* Check for constants that don't require emitting a reloc. */ if (the_insn.exp.X_op == O_constant && the_insn.exp.X_add_symbol == 0 && the_insn.exp.X_op_symbol == 0) { /* For pc-relative call instructions, we reject constants to get better code. */ if (the_insn.pcrel && the_insn.reloc == BFD_RELOC_32_PCREL_S2 && in_signed_range (the_insn.exp.X_add_number, 0x3fff)) { error_message = _(": PC-relative operand can't be a constant"); goto error; } /* Constants that won't fit are checked in md_apply_fix3 and bfd_install_relocation. ??? It would be preferable to install the constants into the insn here and save having to create a fixS for each one. There already exists code to handle all the various cases (e.g. in md_apply_fix3 and bfd_install_relocation) so duplicating all that code here isn't right. */ } continue; case 'a': if (*s++ == 'a') { opcode |= ANNUL; continue; } break; case 'A': { int asi = 0; /* Parse an asi. */ if (*s == '#') { if (! parse_keyword_arg (sparc_encode_asi, &s, &asi)) { error_message = _(": invalid ASI name"); goto error; } } else { if (! parse_const_expr_arg (&s, &asi)) { error_message = _(": invalid ASI expression"); goto error; } if (asi < 0 || asi > 255) { error_message = _(": invalid ASI number"); goto error; } } opcode |= ASI (asi); continue; } /* Alternate space. */ case 'p': if (strncmp (s, "%psr", 4) == 0) { s += 4; continue; } break; case 'q': /* Floating point queue. */ if (strncmp (s, "%fq", 3) == 0) { s += 3; continue; } break; case 'Q': /* Coprocessor queue. */ if (strncmp (s, "%cq", 3) == 0) { s += 3; continue; } break; case 'S': if (strcmp (str, "set") == 0 || strcmp (str, "setuw") == 0) { special_case = SPECIAL_CASE_SET; continue; } else if (strcmp (str, "setsw") == 0) { special_case = SPECIAL_CASE_SETSW; continue; } else if (strcmp (str, "setx") == 0) { special_case = SPECIAL_CASE_SETX; continue; } else if (strncmp (str, "fdiv", 4) == 0) { special_case = SPECIAL_CASE_FDIV; continue; } break; case 'o': if (strncmp (s, "%asi", 4) != 0) break; s += 4; continue; case 's': if (strncmp (s, "%fprs", 5) != 0) break; s += 5; continue; case 'E': if (strncmp (s, "%ccr", 4) != 0) break; s += 4; continue; case 't': if (strncmp (s, "%tbr", 4) != 0) break; s += 4; continue; case 'w': if (strncmp (s, "%wim", 4) != 0) break; s += 4; continue; case 'x': { char *push = input_line_pointer; expressionS e; input_line_pointer = s; expression (&e); if (e.X_op == O_constant) { int n = e.X_add_number; if (n != e.X_add_number || (n & ~0x1ff) != 0) as_bad (_("OPF immediate operand out of range (0-0x1ff)")); else opcode |= e.X_add_number << 5; } else as_bad (_("non-immediate OPF operand, ignored")); s = input_line_pointer; input_line_pointer = push; continue; } case 'y': if (strncmp (s, "%y", 2) != 0) break; s += 2; continue; case 'u': case 'U': { /* Parse a sparclet cpreg. */ int cpreg; if (! parse_keyword_arg (sparc_encode_sparclet_cpreg, &s, &cpreg)) { error_message = _(": invalid cpreg name"); goto error; } opcode |= (*args == 'U' ? RS1 (cpreg) : RD (cpreg)); continue; } default: as_fatal (_("failed sanity check.")); } /* switch on arg code. */ /* Break out of for() loop. */ break; } /* For each arg that we expect. */ error: if (match == 0) { /* Args don't match. */ if (&insn[1] - sparc_opcodes < sparc_num_opcodes && (insn->name == insn[1].name || !strcmp (insn->name, insn[1].name))) { ++insn; s = argsStart; continue; } else { as_bad (_("Illegal operands%s"), error_message); return special_case; } } else { /* We have a match. Now see if the architecture is OK. */ int needed_arch_mask = insn->architecture; if (v9_arg_p) { needed_arch_mask &= ~(SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9) - 1); if (! needed_arch_mask) needed_arch_mask = SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9); } if (needed_arch_mask & SPARC_OPCODE_SUPPORTED (current_architecture)) /* OK. */ ; /* Can we bump up the architecture? */ else if (needed_arch_mask & SPARC_OPCODE_SUPPORTED (max_architecture)) { enum sparc_opcode_arch_val needed_architecture = sparc_ffs (SPARC_OPCODE_SUPPORTED (max_architecture) & needed_arch_mask); assert (needed_architecture <= SPARC_OPCODE_ARCH_MAX); if (warn_on_bump && needed_architecture > warn_after_architecture) { as_warn (_("architecture bumped from \"%s\" to \"%s\" on \"%s\""), sparc_opcode_archs[current_architecture].name, sparc_opcode_archs[needed_architecture].name, str); warn_after_architecture = needed_architecture; } current_architecture = needed_architecture; } /* Conflict. */ /* ??? This seems to be a bit fragile. What if the next entry in the opcode table is the one we want and it is supported? It is possible to arrange the table today so that this can't happen but what about tomorrow? */ else { int arch, printed_one_p = 0; char *p; char required_archs[SPARC_OPCODE_ARCH_MAX * 16]; /* Create a list of the architectures that support the insn. */ needed_arch_mask &= ~SPARC_OPCODE_SUPPORTED (max_architecture); p = required_archs; arch = sparc_ffs (needed_arch_mask); while ((1 << arch) <= needed_arch_mask) { if ((1 << arch) & needed_arch_mask) { if (printed_one_p) *p++ = '|'; strcpy (p, sparc_opcode_archs[arch].name); p += strlen (p); printed_one_p = 1; } ++arch; } as_bad (_("Architecture mismatch o
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -