📄 tc-d10v.c
字号:
{ int i, bits, shift, flags, format; unsigned long number; /* The insn argument is only used for the DIVS kludge. */ if (insn) format = LONG_R; else { insn = opcode->opcode; format = opcode->format; } for (i = 0; opcode->operands[i]; i++) { flags = d10v_operands[opcode->operands[i]].flags; bits = d10v_operands[opcode->operands[i]].bits; shift = d10v_operands[opcode->operands[i]].shift; number = opers[i].X_add_number; if (flags & OPERAND_REG) { number &= REGISTER_MASK; if (format == LONG_L) shift += 15; } if (opers[i].X_op != O_register && opers[i].X_op != O_constant) { /* Now create a fixup. */ if (fixups->fc >= MAX_INSN_FIXUPS) as_fatal (_("too many fixups")); if (AT_WORD_P (&opers[i])) { /* Reconize XXX>>1+N aka XXX@word+N as special (AT_WORD). */ fixups->fix[fixups->fc].reloc = BFD_RELOC_D10V_18; opers[i].X_op = O_symbol; opers[i].X_op_symbol = NULL; /* Should free it. */ /* number is left shifted by AT_WORD_RIGHT_SHIFT so that, it is aligned with the symbol's value. Later, BFD_RELOC_D10V_18 will right shift (symbol_value + X_add_number). */ number <<= AT_WORD_RIGHT_SHIFT; opers[i].X_add_number = number; } else fixups->fix[fixups->fc].reloc = get_reloc ((struct d10v_operand *) &d10v_operands[opcode->operands[i]]); if (fixups->fix[fixups->fc].reloc == BFD_RELOC_16 || fixups->fix[fixups->fc].reloc == BFD_RELOC_D10V_18) fixups->fix[fixups->fc].size = 2; else fixups->fix[fixups->fc].size = 4; fixups->fix[fixups->fc].exp = opers[i]; fixups->fix[fixups->fc].operand = opcode->operands[i]; fixups->fix[fixups->fc].pcrel = (flags & OPERAND_ADDR) ? true : false; (fixups->fc)++; } /* Truncate to the proper number of bits. */ if ((opers[i].X_op == O_constant) && check_range (number, bits, flags)) as_bad (_("operand out of range: %d"), number); number &= 0x7FFFFFFF >> (31 - bits); insn = insn | (number << shift); } /* kludge: for DIVS, we need to put the operands in twice */ /* on the second pass, format is changed to LONG_R to force the second set of operands to not be shifted over 15. */ if ((opcode->opcode == OPCODE_DIVS) && (format == LONG_L)) insn = build_insn (opcode, opers, insn); return insn;}/* Write out a long form instruction. */static voidwrite_long (insn, fx) unsigned long insn; Fixups *fx;{ int i, where; char *f = frag_more (4); insn |= FM11; number_to_chars_bigendian (f, insn, 4); for (i = 0; i < fx->fc; i++) { if (fx->fix[i].reloc) { where = f - frag_now->fr_literal; if (fx->fix[i].size == 2) where += 2; if (fx->fix[i].reloc == BFD_RELOC_D10V_18) fx->fix[i].operand |= 4096; fix_new_exp (frag_now, where, fx->fix[i].size, &(fx->fix[i].exp), fx->fix[i].pcrel, fx->fix[i].operand|2048); } } fx->fc = 0;}/* Write out a short form instruction by itself. */static voidwrite_1_short (opcode, insn, fx) struct d10v_opcode *opcode; unsigned long insn; Fixups *fx;{ char *f = frag_more (4); int i, where; if (opcode->exec_type & PARONLY) as_fatal (_("Instruction must be executed in parallel with another instruction.")); /* The other container needs to be NOP. */ /* According to 4.3.1: for FM=00, sub-instructions performed only by IU cannot be encoded in L-container. */ if (opcode->unit == IU) insn |= FM00 | (NOP << 15); /* Right container. */ else insn = FM00 | (insn << 15) | NOP; /* Left container. */ number_to_chars_bigendian (f, insn, 4); for (i = 0; i < fx->fc; i++) { if (fx->fix[i].reloc) { where = f - frag_now->fr_literal; if (fx->fix[i].size == 2) where += 2; if (fx->fix[i].reloc == BFD_RELOC_D10V_18) fx->fix[i].operand |= 4096; /* If it's an R reloc, we may have to switch it to L. */ if ((fx->fix[i].reloc == BFD_RELOC_D10V_10_PCREL_R) && (opcode->unit != IU)) fx->fix[i].operand |= 1024; fix_new_exp (frag_now, where, fx->fix[i].size, &(fx->fix[i].exp), fx->fix[i].pcrel, fx->fix[i].operand|2048); } } fx->fc = 0;}/* Expects two short instructions. If possible, writes out both as a single packed instruction. Otherwise, writes out the first one, packed with a NOP. Returns number of instructions not written out. */static intwrite_2_short (opcode1, insn1, opcode2, insn2, exec_type, fx) struct d10v_opcode *opcode1, *opcode2; unsigned long insn1, insn2; packing_type exec_type; Fixups *fx;{ unsigned long insn; char *f; int i, j, where; if ((exec_type != PACK_PARALLEL) && ((opcode1->exec_type & PARONLY) || (opcode2->exec_type & PARONLY))) as_fatal (_("Instruction must be executed in parallel")); if ((opcode1->format & LONG_OPCODE) || (opcode2->format & LONG_OPCODE)) as_fatal (_("Long instructions may not be combined.")); switch (exec_type) { case PACK_UNSPEC: /* Order not specified. */ if (opcode1->exec_type & ALONE) { /* Case of a short branch on a separate GAS line. Pack with NOP. */ write_1_short (opcode1, insn1, fx->next); return 1; } if (Optimizing && parallel_ok (opcode1, insn1, opcode2, insn2, exec_type)) { /* Parallel. */ if (opcode1->unit == IU) insn = FM00 | (insn2 << 15) | insn1; else if (opcode2->unit == MU) insn = FM00 | (insn2 << 15) | insn1; else { insn = FM00 | (insn1 << 15) | insn2; /* Advance over dummy fixup since packed insn1 in L. */ fx = fx->next; } } else if (opcode1->unit == IU) /* Reverse sequential with IU opcode1 on right and done first. */ insn = FM10 | (insn2 << 15) | insn1; else { /* Sequential with non-IU opcode1 on left and done first. */ insn = FM01 | (insn1 << 15) | insn2; /* Advance over dummy fixup since packed insn1 in L. */ fx = fx->next; } break; case PACK_PARALLEL: if (opcode1->exec_type & SEQ || opcode2->exec_type & SEQ) as_fatal (_("One of these instructions may not be executed in parallel.")); if (opcode1->unit == IU) { if (opcode2->unit == IU) as_fatal (_("Two IU instructions may not be executed in parallel")); if (!flag_warn_suppress_instructionswap) as_warn (_("Swapping instruction order")); insn = FM00 | (insn2 << 15) | insn1; } else if (opcode2->unit == MU) { if (opcode1->unit == MU) as_fatal (_("Two MU instructions may not be executed in parallel")); if (!flag_warn_suppress_instructionswap) as_warn (_("Swapping instruction order")); insn = FM00 | (insn2 << 15) | insn1; } else { insn = FM00 | (insn1 << 15) | insn2; /* Advance over dummy fixup since packed insn1 in L. */ fx = fx->next; } break; case PACK_LEFT_RIGHT: if (opcode1->unit != IU) insn = FM01 | (insn1 << 15) | insn2; else if (opcode2->unit == MU || opcode2->unit == EITHER) { if (!flag_warn_suppress_instructionswap) as_warn (_("Swapping instruction order")); insn = FM10 | (insn2 << 15) | insn1; } else as_fatal (_("IU instruction may not be in the left container")); if (opcode1->exec_type & ALONE) as_warn (_("Instruction in R container is squashed by flow control instruction in L container.")); /* Advance over dummy fixup. */ fx = fx->next; break; case PACK_RIGHT_LEFT: if (opcode2->unit != MU) insn = FM10 | (insn1 << 15) | insn2; else if (opcode1->unit == IU || opcode1->unit == EITHER) { if (!flag_warn_suppress_instructionswap) as_warn (_("Swapping instruction order")); insn = FM01 | (insn2 << 15) | insn1; } else as_fatal (_("MU instruction may not be in the right container")); if (opcode2->exec_type & ALONE) as_warn (_("Instruction in R container is squashed by flow control instruction in L container.")); /* Advance over dummy fixup. */ fx = fx->next; break; default: as_fatal (_("unknown execution type passed to write_2_short()")); } f = frag_more (4); number_to_chars_bigendian (f, insn, 4); /* Process fixup chains. Note that the packing code above advanced fx conditionally. dlindsay@cygnus.com: There's something subtle going on here involving _dummy_first_bfd_reloc_code_real. This is related to the difference between BFD_RELOC_D10V_10_PCREL_R and _L, ie whether a fixup is done in the L or R container. A bug in this code can pass Plum Hall fine, yet still affect hand-written assembler. */ for (j = 0; j < 2; j++) { for (i = 0; i < fx->fc; i++) { if (fx->fix[i].reloc) { where = f - frag_now->fr_literal; if (fx->fix[i].size == 2) where += 2; if ((fx->fix[i].reloc == BFD_RELOC_D10V_10_PCREL_R) && (j == 0)) fx->fix[i].operand |= 1024; if (fx->fix[i].reloc == BFD_RELOC_D10V_18) fx->fix[i].operand |= 4096; fix_new_exp (frag_now, where, fx->fix[i].size, &(fx->fix[i].exp), fx->fix[i].pcrel, fx->fix[i].operand|2048); } } fx->fc = 0; fx = fx->next; } return (0);}/* Check 2 instructions and determine if they can be safely executed in parallel. Return 1 if they can be. */static intparallel_ok (op1, insn1, op2, insn2, exec_type) struct d10v_opcode *op1, *op2; unsigned long insn1, insn2; packing_type exec_type;{ int i, j, flags, mask, shift, regno; unsigned long ins, mod[2], used[2]; struct d10v_opcode *op; if ((op1->exec_type & SEQ) != 0 || (op2->exec_type & SEQ) != 0 || (op1->exec_type & PAR) == 0 || (op2->exec_type & PAR) == 0 || (op1->unit == BOTH) || (op2->unit == BOTH) || (op1->unit == IU && op2->unit == IU) || (op1->unit == MU && op2->unit == MU)) return 0; /* If this is auto parallization, and either instruction is a branch, don't parallel. */ if (exec_type == PACK_UNSPEC && (op1->exec_type & ALONE || op2->exec_type & ALONE)) return 0; /* The idea here is to create two sets of bitmasks (mod and used) which indicate which registers are modified or used by each instruction. The operation can only be done in parallel if instruction 1 and instruction 2 modify different registers, and the first instruction does not modify registers that the second is using (The second instruction can modify registers that the first is using as they are only written back after the first instruction has completed). Accesses to control registers, PSW, and memory are treated as accesses to a single register. So if both instructions write memory or if the first instruction writes memory and the second reads, then they cannot be done in parallel. Likewise, if the first instruction mucks with the psw and the second reads the PSW (which includes C, F0, and F1), then they cannot operate safely in parallel. */ /* The bitmasks (mod and used) look like this (bit 31 = MSB). */ /* r0-r15 0-15 */ /* a0-a1 16-17 */ /* cr (not psw) 18 */ /* psw 19 */ /* mem 20 */ for (j = 0; j < 2; j++) { if (j == 0) { op = op1; ins = insn1; } else { op = op2; ins = insn2; } mod[j] = used[j] = 0; if (op->exec_type & BRANCH_LINK) mod[j] |= 1 << 13; for (i = 0; op->operands[i]; i++) { flags = d10v_operands[op->operands[i]].flags; shift = d10v_operands[op->operands[i]].shift; mask = 0x7FFFFFFF >> (31 - d10v_operands[op->operands[i]].bits); if (flags & OPERAND_REG) { regno = (ins >> shift) & mask; if (flags & (OPERAND_ACC0 | OPERAND_ACC1)) regno += 16; else if (flags & OPERAND_CONTROL) /* mvtc or mvfc. */ { if (regno == 0) regno = 19; else regno = 18; } else if (flags & (OPERAND_FFLAG | OPERAND_CFLAG)) regno = 19; if (flags & OPERAND_DEST) { mod[j] |= 1 << regno; if (flags & OPERAND_EVEN) mod[j] |= 1 << (regno + 1); } else { used[j] |= 1 << regno; if (flags & OPERAND_EVEN) used[j] |= 1 << (regno + 1); /* Auto inc/dec also modifies the register. */ if (op->operands[i + 1] != 0 && (d10v_operands[op->operands[i + 1]].flags & (OPERAND_PLUS | OPERAND_MINUS)) != 0) mod[j] |= 1 << regno; } } else if (flags & OPERAND_ATMINUS) { /* SP implicitly used/modified. */ mod[j] |= 1 << 15; used[j] |= 1 << 15; } } if (op->exec_type & RMEM) used[j] |= 1 << 20; else if (op->exec_type & WMEM) mod[j] |= 1 << 20; else if (op->exec_type & RF0) used[j] |= 1 << 19; else if (op->exec_type & WF0) mod[j] |= 1 << 19; else if (op->exec_type & WCAR) mod[j] |= 1 << 19; } if ((mod[0] & mod[1]) == 0 && (mod[0] & used[1]) == 0) return 1; return 0;}/* 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 D10V, it mostly handles the special VLIW parsing and packing and leaves the difficult stuff to do_assemble(). */static unsigned long prev_insn;static struct d10v_opcode *prev_opcode = 0;static subsegT prev_subseg;static segT prev_seg = 0;;voidmd_assemble (str) char *str;{ /* etype is saved extype. For multi-line instructions. */ packing_type extype = PACK_UNSPEC; /* Parallel, etc. */ struct d10v_opcode *opcode; unsigned long insn; char *str2; if (etype == PACK_UNSPEC) { /* Look for the special multiple instruction separators. */ str2 = strstr (str, "||"); if (str2) extype = PACK_PARALLEL; else { str2 = strstr (str, "->"); if (str2) extype = PACK_LEFT_RIGHT; else { str2 = strstr (str, "<-"); if (str2) extype = PACK_RIGHT_LEFT; } } /* STR2 points to the separator, if there is one. */ if (str2) { *str2 = 0; /* If two instructions are present and we already have one saved, then first write out the saved one. */ d10v_cleanup (); /* Assemble first instruction and save it. */ prev_insn = do_assemble (str, &prev_opcode); prev_seg = now_seg; prev_subseg = now_subseg; if (prev_insn == (unsigned long) -1) as_fatal (_("can't find opcode ")); fixups = fixups->next; str = str2 + 2; } } insn = do_assemble (str, &opcode); if (insn == (unsigned long) -1) { if (extype != PACK_UNSPEC) { etype = extype; return; } as_fatal (_("can't find opcode ")); } if (etype != PACK_UNSPEC) { extype = etype; etype = PACK_UNSPEC; } /* If this is a long instruction, write it and any previous short instruction. */ if (opcode->format & LONG_OPCODE) { if (extype != PACK_UNSPEC) as_fatal (_("Unable to mix instructions as specified")); d10v_cleanup (); write_long (insn, fixups); prev_opcode = NULL; return; } if (prev_opcode && prev_seg && ((prev_seg != now_seg) || (prev_subseg != now_subseg))) d10v_cleanup (); if (prev_opcode && (write_2_short (prev_opcode, prev_insn, opcode, insn, extype, fixups) == 0)) { /* No instructions saved. */ prev_opcode = NULL; } else { if (extype != PACK_UNSPEC) as_fatal (_("Unable to mix instructions as specified")); /* Save last instruction so it may be packed on next pass. */ prev_opcode = opcode; prev_insn = insn; prev_seg = now_seg; prev_subseg = now_subseg; fixups = fixups->next; }}/* Assemble a single instruction. Return an opcode, or -1 (an invalid opcode) on error. */static unsigned longdo_assemble (str, opcode) char *str; struct d10v_opcode **opcode;{ unsigned char *op_start, *save; unsigned char *op_end; char name[20]; int nlen = 0; expressionS myops[6]; unsigned long insn; /* Drop leading whitespace. */ while (*str == ' ')
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -