📄 tc-d30v.c
字号:
else if (flags & OPERAND_FLAG) used_reg[j][2] |= 1L << regno; else if (!(flags & OPERAND_CONTROL)) { int r, z; /* Need to check if there are two source registers, for example st2w. */ if (flags & OPERAND_2REG) z = 1; else z = 0; for (r = regno; r <= regno + z; r++) { if (r >= 32) used_reg[j][1] |= 1L << (r - 32); else used_reg[j][0] |= 1L << r; } } } } } } flags_set1 = op1->op->flags_set; flags_set2 = op2->op->flags_set; flags_used1 = op1->op->flags_used; flags_used2 = op2->op->flags_used; /* Check for illegal combinations with ADDppp/SUBppp. */ if (((flags_set1 & FLAG_NOT_WITH_ADDSUBppp) != 0 && (flags_used2 & FLAG_ADDSUBppp) != 0) || ((flags_set2 & FLAG_NOT_WITH_ADDSUBppp) != 0 && (flags_used1 & FLAG_ADDSUBppp) != 0)) return 0; /* Load instruction combined with half-word multiply is illegal. */ if (((flags_used1 & FLAG_MEM) != 0 && (flags_used2 & FLAG_MUL16)) || ((flags_used2 & FLAG_MEM) != 0 && (flags_used1 & FLAG_MUL16))) return 0; /* Specifically allow add || add by removing carry, overflow bits dependency. This is safe, even if an addc follows since the IU takes the argument in the right container, and it writes its results last. However, don't paralellize add followed by addc or sub followed by subb. */ if (mod_reg[0][2] == FLAG_CVVA && mod_reg[1][2] == FLAG_CVVA && (used_reg[0][2] & ~flag_reg[0]) == 0 && (used_reg[1][2] & ~flag_reg[1]) == 0 && op1->op->unit == EITHER && op2->op->unit == EITHER) { mod_reg[0][2] = mod_reg[1][2] = 0; } for (j = 0; j < 3; j++) { /* If the second instruction depends on the first, we obviously cannot parallelize. Note, the mod flag implies use, so check that as well. */ /* If flag_explicitly_parallel is set, then the case of the second instruction using a register the first instruction modifies is assumed to be okay; we trust the human. We don't trust the human if both instructions modify the same register but we do trust the human if they modify the same flags. */ /* We have now been requested not to trust the human if the instructions modify the same flag registers either. */ if (flag_explicitly_parallel) { if ((mod_reg[0][j] & mod_reg[1][j]) != 0) return 0; } else if ((mod_reg[0][j] & (mod_reg[1][j] | used_reg[1][j])) != 0) return 0; } return 1;}/* This is the main entry point for the machine-dependent assembler. STR points to a machine-dependent instruction. This function is supposed to emit the frags/bytes it assembles to. For the D30V, it mostly handles the special VLIW parsing and packing and leaves the difficult stuff to do_assemble (). */static long long prev_insn = -1;static struct d30v_insn prev_opcode;static subsegT prev_subseg;static segT prev_seg = 0;voidmd_assemble (str) char *str;{ struct d30v_insn opcode; long long insn; /* Execution type; parallel, etc. */ exec_type_enum extype = EXEC_UNKNOWN; /* Saved extype. Used for multiline instructions. */ static exec_type_enum etype = EXEC_UNKNOWN; char *str2; if ((prev_insn != -1) && prev_seg && ((prev_seg != now_seg) || (prev_subseg != now_subseg))) d30v_cleanup (false); if (d30v_current_align < 3) d30v_align (3, NULL, d30v_last_label); else if (d30v_current_align > 3) d30v_current_align = 3; d30v_last_label = NULL; flag_explicitly_parallel = 0; flag_xp_state = 0; if (etype == EXEC_UNKNOWN) { /* Look for the special multiple instruction separators. */ str2 = strstr (str, "||"); if (str2) { extype = EXEC_PARALLEL; flag_xp_state = 1; } else { str2 = strstr (str, "->"); if (str2) extype = EXEC_SEQ; else { str2 = strstr (str, "<-"); if (str2) extype = EXEC_REVSEQ; } } /* STR2 points to the separator, if one. */ if (str2) { *str2 = 0; /* If two instructions are present and we already have one saved, then first write it out. */ d30v_cleanup (false); /* Assemble first instruction and save it. */ prev_insn = do_assemble (str, &prev_opcode, 1, 0); if (prev_insn == -1) as_bad (_("Cannot assemble instruction")); if (prev_opcode.form != NULL && prev_opcode.form->form >= LONG) as_bad (_("First opcode is long. Unable to mix instructions as specified.")); fixups = fixups->next; str = str2 + 2; prev_seg = now_seg; prev_subseg = now_subseg; } } insn = do_assemble (str, &opcode, (extype != EXEC_UNKNOWN || etype != EXEC_UNKNOWN), extype == EXEC_PARALLEL); if (insn == -1) { if (extype != EXEC_UNKNOWN) etype = extype; as_bad (_("Cannot assemble instruction")); return; } if (etype != EXEC_UNKNOWN) { extype = etype; etype = EXEC_UNKNOWN; } /* Word multiply instructions must not be followed by either a load or a 16-bit multiply instruction in the next cycle. */ if ( (extype != EXEC_REVSEQ) && prev_mul32_p && (opcode.op->flags_used & (FLAG_MEM | FLAG_MUL16))) { /* However, load and multiply should able to be combined in a parallel operation, so check for that first. */ if (prev_insn != -1 && (opcode.op->flags_used & FLAG_MEM) && opcode.form->form < LONG && (extype == EXEC_PARALLEL || (Optimizing && extype == EXEC_UNKNOWN)) && parallel_ok (&prev_opcode, (long) prev_insn, &opcode, (long) insn, extype) && write_2_short (&prev_opcode, (long) prev_insn, &opcode, (long) insn, extype, fixups) == 0) { /* No instructions saved. */ prev_insn = -1; return; } else { /* Can't parallelize, flush previous instruction and emit a word of NOPS, unless the previous instruction is a NOP, in which case just flush it, as this will generate a word of NOPs for us. */ if (prev_insn != -1 && (strcmp (prev_opcode.op->name, "nop") == 0)) d30v_cleanup (false); else { char *f; if (prev_insn != -1) d30v_cleanup (true); else { f = frag_more (8); d30v_number_to_chars (f, NOP2, 8); if (warn_nops == NOP_ALL || warn_nops == NOP_MULTIPLY) { if (opcode.op->flags_used & FLAG_MEM) as_warn (_("word of NOPs added between word multiply and load")); else as_warn (_("word of NOPs added between word multiply and 16-bit multiply")); } } } extype = EXEC_UNKNOWN; } } else if ( (extype == EXEC_REVSEQ) && cur_mul32_p && (prev_opcode.op->flags_used & (FLAG_MEM | FLAG_MUL16))) { /* Can't parallelize, flush current instruction and add a sequential NOP. */ write_1_short (&opcode, (long) insn, fixups->next->next, true); /* Make the previous instruction the current one. */ extype = EXEC_UNKNOWN; insn = prev_insn; now_seg = prev_seg; now_subseg = prev_subseg; prev_insn = -1; cur_mul32_p = prev_mul32_p; prev_mul32_p = 0; memcpy (&opcode, &prev_opcode, sizeof (prev_opcode)); } /* If this is a long instruction, write it and any previous short instruction. */ if (opcode.form->form >= LONG) { if (extype != EXEC_UNKNOWN) as_bad (_("Instruction uses long version, so it cannot be mixed as specified")); d30v_cleanup (false); write_long (&opcode, insn, fixups); prev_insn = -1; } else if ((prev_insn != -1) && (write_2_short (&prev_opcode, (long) prev_insn, &opcode, (long) insn, extype, fixups) == 0)) { /* No instructions saved. */ prev_insn = -1; } else { if (extype != EXEC_UNKNOWN) as_bad (_("Unable to mix instructions as specified")); /* Save off last instruction so it may be packed on next pass. */ memcpy (&prev_opcode, &opcode, sizeof (prev_opcode)); prev_insn = insn; prev_seg = now_seg; prev_subseg = now_subseg; fixups = fixups->next; prev_mul32_p = cur_mul32_p; }}/* Assemble a single instruction and return an opcode. Return -1 (an invalid opcode) on error. */#define NAME_BUF_LEN 20static long longdo_assemble (str, opcode, shortp, is_parallel) char *str; struct d30v_insn *opcode; int shortp; int is_parallel;{ unsigned char *op_start; unsigned char *save; unsigned char *op_end; char name[NAME_BUF_LEN]; int cmp_hack; int nlen = 0; int fsize = (shortp ? FORCE_SHORT : 0); expressionS myops[6]; long long insn; /* Drop leading whitespace. */ while (*str == ' ') str++; /* Find the opcode end. */ for (op_start = op_end = (unsigned char *) (str); *op_end && nlen < (NAME_BUF_LEN - 1) && *op_end != '/' && !is_end_of_line[*op_end] && *op_end != ' '; op_end++) { name[nlen] = tolower (op_start[nlen]); nlen++; } if (nlen == 0) return -1; name[nlen] = 0; /* If there is an execution condition code, handle it. */ if (*op_end == '/') { int i = 0; while ((i < ECC_MAX) && strncasecmp (d30v_ecc_names[i], op_end + 1, 2)) i++; if (i == ECC_MAX) { char tmp[4]; strncpy (tmp, op_end + 1, 2); tmp[2] = 0; as_bad (_("unknown condition code: %s"), tmp); return -1; }#if 0 printf ("condition code=%d\n", i);#endif opcode->ecc = i; op_end += 3; } else opcode->ecc = ECC_AL; /* CMP and CMPU change their name based on condition codes. */ if (!strncmp (name, "cmp", 3)) { int p, i; char **str = (char **) d30v_cc_names; if (name[3] == 'u') p = 4; else p = 3; for (i = 1; *str && strncmp (*str, &name[p], 2); i++, str++) ; /* cmpu only supports some condition codes. */ if (p == 4) { if (i < 3 || i > 6) { name[p + 2] = 0; as_bad (_("cmpu doesn't support condition code %s"), &name[p]); } } if (!*str) { name[p + 2] = 0; as_bad (_("unknown condition code: %s"), &name[p]); } cmp_hack = i; name[p] = 0; } else cmp_hack = 0;#if 0 printf ("cmp_hack=%d\n", cmp_hack);#endif /* Need to look for .s or .l. */ if (name[nlen - 2] == '.') { switch (name[nlen - 1]) { case 's': fsize = FORCE_SHORT; break; case 'l': fsize = FORCE_LONG; break; } name[nlen - 2] = 0; } /* Find the first opcode with the proper name. */ opcode->op = (struct d30v_opcode *) hash_find (d30v_hash, name); if (opcode->op == NULL) { as_bad (_("unknown opcode: %s"), name); return -1; } save = input_line_pointer; input_line_pointer = op_end; while (!(opcode->form = find_format (opcode->op, myops, fsize, cmp_hack))) { opcode->op++; if (opcode->op->name == NULL || strcmp (opcode->op->name, name)) { as_bad (_("operands for opcode `%s' do not match any valid format"), name); return -1; } } input_line_pointer = save; insn = build_insn (opcode, myops); /* Propigate multiply status. */ if (insn != -1) { if (is_parallel && prev_mul32_p) cur_mul32_p = 1; else { prev_mul32_p = cur_mul32_p; cur_mul32_p = (opcode->op->flags_used & FLAG_MUL32) != 0; } } /* Propagate left_kills_right status. */ if (insn != -1) { prev_left_kills_right_p = cur_left_kills_right_p; if (opcode->op->flags_set & FLAG_LKR) { cur_left_kills_right_p = 1; if (strcmp (opcode->op->name, "mvtsys") == 0) { /* Left kills right for only mvtsys only for PSW/PSWH/PSWL/flags target. */ if ((myops[0].X_op == O_register) && ((myops[0].X_add_number == OPERAND_CONTROL) || /* psw */ (myops[0].X_add_number == OPERAND_CONTROL+MAX_CONTROL_REG+2) || /* pswh */ (myops[0].X_add_number == OPERAND_CONTROL+MAX_CONTROL_REG+1) || /* pswl */ (myops[0].X_add_number == OPERAND_FLAG+0) || /* f0 */ (myops[0].X_add_number == OPERAND_FLAG+1) || /* f1 */ (myops[0].X_add_number == OPERAND_FLAG+2) || /* f2 */ (myops[0].X_add_number == OPERAND_FLAG+3) || /* f3 */ (myops[0].X_add_number == OPERAND_FLAG+4) || /* f4 */ (myops[0].X_add_number == OPERAND_FLAG+5) || /* f5 */ (myops[0].X_add_number == OPERAND_FLAG+6) || /* f6 */ (myops[0].X_add_number == OPERAND_FLAG+7))) /* f7 */ { cur_left_kills_right_p = 1; } else { /* Other mvtsys target registers don't kill right instruction. */ cur_left_kills_right_p = 0; } } /* mvtsys */ } else cur_left_kills_right_p = 0; } return insn;}/* Get a pointer to an entry in the format table. It must look at all formats for an opcode and use the operands to choose the correct one. Return NULL on error. */static struct d30v_format *find_format (opcode, myops, fsize, cmp_hack) struct d30v_opcode *opcode; expressionS myops[]; int fsize; int cmp_hack;{ int numops, match, index, i = 0, j, k; struct d30v_format *fm; if (opcode == NULL) return NULL; /* Get all the operands and save them as expressions. */ numops = get_operands (myops, cmp_hack); while ((index = opcode->format[i++]) != 0) { if (fsize == FORCE_SHORT && index >= LONG) continue; if (fsize == FORCE_LONG && index < LONG) continue; fm = (struct d30v_format *) &d30v_format_table[index]; k = index; while (fm->form == index) { match = 1; /* Now check the operands for compatibility. */ for (j = 0; match && fm->operands[j]; j++) { int flags = d30v_operand_table[fm->operands[j]].flags; int bits = d30v_operand_table[fm->operands[j]].bits; int X_op = myops[j].X_op; int num = myops[j].X_add_number; if (flags & OPERAND_SPECIAL) break; else if (X_op == O_illegal) match = 0; else if (flags & OPERAND_REG) { if (X_op != O_register || ((flags & OPERAND_ACC) && !(num & OPERAND_ACC)) || (!(flags & OPERAND_ACC) && (num & OPERAND_ACC)) || ((flags & OPERAND_FLAG) && !(num & OPERAND_FLAG)) || (!(flags & (OPERAND_FLAG | OPERAND_CONTROL)) && (num & OPERAND_FLAG)) || ((flags & OPERAND_CONTROL) && !(num & (OPERAND_CONTROL | OPERAND_FLAG)))) { match = 0; } } else if (((flags & OPERAND_MINUS) && (X_op != O_absent || num != OPERAND_MINUS)) || ((flags & OPERAND_PLUS) && (X_op != O_absent || num != OPERAND_PLUS)) || ((flags & OPERAND_ATMINUS) && (X_op != O_absent || num != OPERAND_ATMINUS)) || ((flags & OPERAND_ATPAR) && (X_op != O_absent || num != OPERAND_ATPAR)) || ((flags & OPERAND_ATSIGN) && (X_op != O_absent || num != OPERAND_ATSIGN))) { match = 0; } else if (flags & OPERAND_NUM) { /* A number can be a constant or symbol expression. */ /* If we have found a register name, but that name also matches a symbol, then re-parse the name as an expression. */ if (X_op == O_register && symbol_find ((char *) myops[j].X_op_symbol)) { input_line_pointer = (char *) myops[j].X_op_symbol; expression (&myops[j]); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -