📄 tc-i960.c
字号:
The following supports generation of branch-prediction instrumentation (turned on by -b switch). The instrumentation collects counts of branches taken/not-taken for later input to a utility that will set the branch prediction bits of the instructions in accordance with the behavior observed. (Note that the KX series does not have brach-prediction.) The instrumentation consists of: (1) before and after each conditional branch, a call to an external routine that increments and steps over an inline counter. The counter itself, initialized to 0, immediately follows the call instruction. For each branch, the counter following the branch is the number of times the branch was not taken, and the difference between the counters is the number of times it was taken. An example of an instrumented conditional branch: call BR_CNT_FUNC .word 0 LBRANCH23: be label call BR_CNT_FUNC .word 0 (2) a table of pointers to the instrumented branches, so that an external postprocessing routine can locate all of the counters. the table begins with a 2-word header: a pointer to the next in a linked list of such tables (initialized to 0); and a count of the number of entries in the table (exclusive of the header. Note that input source code is expected to already contain calls an external routine that will link the branch local table into a list of such tables. *//* Number of branches instrumented so far. Also used to generate unique local labels for each instrumented branch. */static int br_cnt;#define BR_LABEL_BASE "LBRANCH"/* Basename of local labels on instrumented branches, to avoid conflict with compiler- generated local labels. */#define BR_CNT_FUNC "__inc_branch"/* Name of the external routine that will increment (and step over) an inline counter. */#define BR_TAB_NAME "__BRANCH_TABLE__"/* Name of the table of pointers to branches. A local (i.e., non-external) symbol. *//***************************************************************************** md_begin: One-time initialization. Set up hash tables. *************************************************************************** */voidmd_begin (){ int i; /* Loop counter */ const struct i960_opcode *oP; /* Pointer into opcode table */ const char *retval; /* Value returned by hash functions */ op_hash = hash_new (); reg_hash = hash_new (); areg_hash = hash_new (); /* For some reason, the base assembler uses an empty string for "no error message", instead of a NULL pointer. */ retval = 0; for (oP = i960_opcodes; oP->name && !retval; oP++) retval = hash_insert (op_hash, oP->name, (PTR) oP); for (i = 0; regnames[i].reg_name && !retval; i++) retval = hash_insert (reg_hash, regnames[i].reg_name, (char *) ®names[i].reg_num); for (i = 0; aregs[i].areg_name && !retval; i++) retval = hash_insert (areg_hash, aregs[i].areg_name, (char *) &aregs[i].areg_num); if (retval) as_fatal (_("Hashing returned \"%s\"."), retval);}/***************************************************************************** md_assemble: Assemble an instruction Assumptions about the passed-in text: - all comments, labels removed - text is an instruction - all white space compressed to single blanks - all character constants have been replaced with decimal *************************************************************************** */voidmd_assemble (textP) char *textP; /* Source text of instruction */{ /* Parsed instruction text, containing NO whitespace: arg[0]->opcode mnemonic arg[1-3]->operands, with char constants replaced by decimal numbers. */ char *args[4]; int n_ops; /* Number of instruction operands */ /* Pointer to instruction description */ struct i960_opcode *oP; /* TRUE iff opcode mnemonic included branch-prediction suffix (".f" or ".t"). */ int branch_predict; /* Setting of branch-prediction bit(s) to be OR'd into instruction opcode of CTRL/COBR format instructions. */ long bp_bits; int n; /* Offset of last character in opcode mnemonic */ const char *bp_error_msg = _("branch prediction invalid on this opcode"); /* Parse instruction into opcode and operands */ memset (args, '\0', sizeof (args)); n_ops = i_scan (textP, args); if (n_ops == -1) { return; /* Error message already issued */ } /* Do "macro substitution" (sort of) on 'ldconst' pseudo-instruction */ if (!strcmp (args[0], "ldconst")) { n_ops = parse_ldconst (args); if (n_ops == -1) { return; } } /* Check for branch-prediction suffix on opcode mnemonic, strip it off */ n = strlen (args[0]) - 1; branch_predict = 0; bp_bits = 0; if (args[0][n - 1] == '.' && (args[0][n] == 't' || args[0][n] == 'f')) { /* We could check here to see if the target architecture supports branch prediction, but why bother? The bit will just be ignored by processors that don't use it. */ branch_predict = 1; bp_bits = (args[0][n] == 't') ? BP_TAKEN : BP_NOT_TAKEN; args[0][n - 1] = '\0'; /* Strip suffix from opcode mnemonic */ } /* Look up opcode mnemonic in table and check number of operands. Check that opcode is legal for the target architecture. If all looks good, assemble instruction. */ oP = (struct i960_opcode *) hash_find (op_hash, args[0]); if (!oP || !targ_has_iclass (oP->iclass)) { as_bad (_("invalid opcode, \"%s\"."), args[0]); } else if (n_ops != oP->num_ops) { as_bad (_("improper number of operands. expecting %d, got %d"), oP->num_ops, n_ops); } else { switch (oP->format) { case FBRA: case CTRL: ctrl_fmt (args[1], oP->opcode | bp_bits, oP->num_ops); if (oP->format == FBRA) { /* Now generate a 'bno' to same arg */ ctrl_fmt (args[1], BNO | bp_bits, 1); } break; case COBR: case COJ: cobr_fmt (args, oP->opcode | bp_bits, oP); break; case REG: if (branch_predict) { as_warn (bp_error_msg); } reg_fmt (args, oP); break; case MEM1: if (args[0][0] == 'c' && args[0][1] == 'a') { if (branch_predict) { as_warn (bp_error_msg); } mem_fmt (args, oP, 1); break; } case MEM2: case MEM4: case MEM8: case MEM12: case MEM16: if (branch_predict) { as_warn (bp_error_msg); } mem_fmt (args, oP, 0); break; case CALLJ: if (branch_predict) { as_warn (bp_error_msg); } /* Output opcode & set up "fixup" (relocation); flag relocation as 'callj' type. */ know (oP->num_ops == 1); get_cdisp (args[1], "CTRL", oP->opcode, 24, 0, 1); break; default: BAD_CASE (oP->format); break; } }} /* md_assemble() *//***************************************************************************** md_number_to_chars: convert a number to target byte order *************************************************************************** */voidmd_number_to_chars (buf, value, n) char *buf; valueT value; int n;{ number_to_chars_littleendian (buf, value, n);}/***************************************************************************** md_chars_to_number: convert from target byte order to host byte order. *************************************************************************** */intmd_chars_to_number (val, n) unsigned char *val; /* Value in target byte order */ int n; /* Number of bytes in the input */{ int retval; for (retval = 0; n--;) { retval <<= 8; retval |= val[n]; } return retval;}#define MAX_LITTLENUMS 6#define LNUM_SIZE sizeof (LITTLENUM_TYPE)/***************************************************************************** md_atof: convert ascii to floating point Turn a string at input_line_pointer into a floating point constant of type 'type', and store the appropriate bytes at *litP. The number of LITTLENUMS emitted is returned at 'sizeP'. An error message is returned, or a pointer to an empty message if OK. Note we call the i386 floating point routine, rather than complicating things with more files or symbolic links. *************************************************************************** */char *md_atof (type, litP, sizeP) int type; char *litP; int *sizeP;{ LITTLENUM_TYPE words[MAX_LITTLENUMS]; LITTLENUM_TYPE *wordP; int prec; char *t; char *atof_ieee (); switch (type) { case 'f': case 'F': prec = 2; break; case 'd': case 'D': prec = 4; break; case 't': case 'T': prec = 5; type = 'x'; /* That's what atof_ieee() understands */ break; default: *sizeP = 0; return _("Bad call to md_atof()"); } t = atof_ieee (input_line_pointer, type, words); if (t) { input_line_pointer = t; } *sizeP = prec * LNUM_SIZE; /* Output the LITTLENUMs in REVERSE order in accord with i80960 word-order. (Dunno why atof_ieee doesn't do it in the right order in the first place -- probably because it's a hack of atof_m68k.) */ for (wordP = words + prec - 1; prec--;) { md_number_to_chars (litP, (long) (*wordP--), LNUM_SIZE); litP += sizeof (LITTLENUM_TYPE); } return 0;}/***************************************************************************** md_number_to_imm *************************************************************************** */voidmd_number_to_imm (buf, val, n) char *buf; long val; int n;{ md_number_to_chars (buf, val, n);}/***************************************************************************** md_number_to_disp *************************************************************************** */voidmd_number_to_disp (buf, val, n) char *buf; long val; int n;{ md_number_to_chars (buf, val, n);}/***************************************************************************** md_number_to_field: Stick a value (an address fixup) into a bit field of previously-generated instruction. *************************************************************************** */voidmd_number_to_field (instrP, val, bfixP) char *instrP; /* Pointer to instruction to be fixed */ long val; /* Address fixup value */ bit_fixS *bfixP; /* Description of bit field to be fixed up */{ int numbits; /* Length of bit field to be fixed */ long instr; /* 32-bit instruction to be fixed-up */ long sign; /* 0 or -1, according to sign bit of 'val' */ /* Convert instruction back to host byte order. */ instr = md_chars_to_number (instrP, 4); /* Surprise! -- we stored the number of bits to be modified rather than a pointer to a structure. */ numbits = (int) bfixP; if (numbits == 1) { /* This is a no-op, stuck here by reloc_callj() */ return; } know ((numbits == 13) || (numbits == 24)); /* Propagate sign bit of 'val' for the given number of bits. Result should be all 0 or all 1. */ sign = val >> ((int) numbits - 1); if (((val < 0) && (sign != -1)) || ((val > 0) && (sign != 0))) { as_bad (_("Fixup of %ld too large for field width of %d"), val, numbits); } else { /* Put bit field into instruction and write back in target * byte order. */ val &= ~(-1 << (int) numbits); /* Clear unused sign bits */ instr |= val; md_number_to_chars (instrP, instr, 4); }} /* md_number_to_field() *//***************************************************************************** md_parse_option Invocation line includes a switch not recognized by the base assembler. See if it's a processor-specific option. For the 960, these are: -norelax: Conditional branch instructions that require displacements greater than 13 bits (or that have external targets) should generate errors. The default is to replace each such instruction with the corresponding compare (or chkbit) and branch instructions. Note that the Intel "j" cobr directives are ALWAYS "de-optimized" in this way when necessary, regardless of the setting of this option. -b: Add code to collect information about branches taken, for later optimization of branch prediction bits by a separate tool. COBR and CNTL format instructions have branch prediction bits (in the CX architecture); if "BR" represents an instruction in one of these classes, the following rep- resents the code generated by the assembler: call <increment routine> .word 0 # pre-counter Label: BR call <increment routine> .word 0 # post-counter A table of all such "Labels" is also generated. -AKA, -AKB, -AKC, -ASA, -ASB, -AMC, -ACA: Select the 80960 architecture. Instructions or features not supported by the selected architecture cause fatal errors. The default is to generate code for any instruction or feature that is supported by SOME version of the 960 (even if this means mixing architectures!). ****************************************************************************/CONST char *md_shortopts = "A:b";struct option md_longopts[] ={#define OPTION_LINKRELAX (OPTION_MD_BASE) {"linkrelax", no_argument, NULL, OPTION_LINKRELAX}, {"link-relax", no_argument, NULL, OPTION_LINKRELAX},#define OPTION_NORELAX (OPTION_MD_BASE + 1) {"norelax", no_argument, NULL, OPTION_NORELAX}, {"no-relax", no_argument, NULL, OPTION_NORELAX}, {NULL, no_argument, NULL, 0}};size_t md_longopts_size = sizeof (md_longopts);struct tabentry { char *flag;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -