📄 ns32k.c
字号:
intcalc_address_cost (operand) rtx operand;{ int i; int cost = 0; if (GET_CODE (operand) == MEM) cost += 3; if (GET_CODE (operand) == MULT) cost += 2; switch (GET_CODE (operand)) { case REG: cost += 1; break; case POST_DEC: case PRE_DEC: break; case CONST_INT: if (INTVAL (operand) <= 7 && INTVAL (operand) >= -8) break; if (INTVAL (operand) < 0x2000 && INTVAL (operand) >= -0x2000) { cost +=1; break; } case CONST: case LABEL_REF: case SYMBOL_REF: cost +=3; break; case CONST_DOUBLE: cost += 5; break; case MEM: cost += calc_address_cost (XEXP (operand, 0)); break; case MULT: case PLUS: for (i = 0; i < GET_RTX_LENGTH (GET_CODE (operand)); i++) { cost += calc_address_cost (XEXP (operand, i)); } default: break; } return cost;}/* Return the register class of a scratch register needed to copy IN into or out of a register in CLASS in MODE. If it can be done directly, NO_REGS is returned. */enum reg_classsecondary_reload_class (class, mode, in) enum reg_class class; enum machine_mode mode ATTRIBUTE_UNUSED; rtx in;{ int regno = true_regnum (in); if (regno >= FIRST_PSEUDO_REGISTER) regno = -1; if ((class == FRAME_POINTER_REG && regno == STACK_POINTER_REGNUM) || ( class == STACK_POINTER_REG && regno == FRAME_POINTER_REGNUM)) return GENERAL_REGS; else return NO_REGS;}/* Generate the rtx that comes from an address expression in the md file *//* The expression to be build is BASE[INDEX:SCALE]. To recognize this, scale must be converted from an exponent (from ASHIFT) to a multiplier (for MULT). */static rtxgen_indexed_expr (base, index, scale) rtx base, index, scale;{ rtx addr; /* This generates an invalid addressing mode, if BASE is fp or sp. This is handled by PRINT_OPERAND_ADDRESS. */ if (GET_CODE (base) != REG && GET_CODE (base) != CONST_INT) base = gen_rtx_MEM (SImode, base); addr = gen_rtx_MULT (SImode, index, GEN_INT (1 << INTVAL (scale))); addr = gen_rtx_PLUS (SImode, base, addr); return addr;}/* Split one or more DImode RTL references into pairs of SImode references. The RTL can be REG, offsettable MEM, integer constant, or CONST_DOUBLE. "operands" is a pointer to an array of DImode RTL to split and "num" is its length. lo_half and hi_half are output arrays that parallel "operands". */voidsplit_di (operands, num, lo_half, hi_half) rtx operands[]; int num; rtx lo_half[], hi_half[];{ while (num--) { if (GET_CODE (operands[num]) == REG) { lo_half[num] = gen_rtx_REG (SImode, REGNO (operands[num])); hi_half[num] = gen_rtx_REG (SImode, REGNO (operands[num]) + 1); } else if (CONSTANT_P (operands[num])) { split_double (operands[num], &lo_half[num], &hi_half[num]); } else if (offsettable_memref_p (operands[num])) { lo_half[num] = operands[num]; hi_half[num] = adjust_address (operands[num], SImode, 4); } else abort (); }}/* Return the best assembler insn template for moving operands[1] into operands[0] as a fullword. */static const char *singlemove_string (operands) rtx *operands;{ if (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) <= 7 && INTVAL (operands[1]) >= -8) return "movqd %1,%0"; return "movd %1,%0";}const char *output_move_double (operands) rtx *operands;{ enum anon1 { REGOP, OFFSOP, PUSHOP, CNSTOP, RNDOP } optype0, optype1; rtx latehalf[2]; /* First classify both operands. */ if (REG_P (operands[0])) optype0 = REGOP; else if (offsettable_memref_p (operands[0])) optype0 = OFFSOP; else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) optype0 = PUSHOP; else optype0 = RNDOP; if (REG_P (operands[1])) optype1 = REGOP; else if (CONSTANT_P (operands[1]) || GET_CODE (operands[1]) == CONST_DOUBLE) optype1 = CNSTOP; else if (offsettable_memref_p (operands[1])) optype1 = OFFSOP; else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) optype1 = PUSHOP; else optype1 = RNDOP; /* Check for the cases that the operand constraints are not supposed to allow to happen. Abort if we get one, because generating code for these cases is painful. */ if (optype0 == RNDOP || optype1 == RNDOP) abort (); /* Ok, we can do one word at a time. Normally we do the low-numbered word first, but if either operand is autodecrementing then we do the high-numbered word first. In either case, set up in LATEHALF the operands to use for the high-numbered word and in some cases alter the operands in OPERANDS to be suitable for the low-numbered word. */ if (optype0 == REGOP) latehalf[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1); else if (optype0 == OFFSOP) latehalf[0] = adjust_address (operands[0], SImode, 4); else latehalf[0] = operands[0]; if (optype1 == REGOP) latehalf[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 1); else if (optype1 == OFFSOP) latehalf[1] = adjust_address (operands[1], SImode, 4); else if (optype1 == CNSTOP) split_double (operands[1], &operands[1], &latehalf[1]); else latehalf[1] = operands[1]; /* If insn is effectively movd N(sp),tos then we will do the high word first. We should use the adjusted operand 1 (which is N+4(sp)) for the low word as well, to compensate for the first decrement of sp. Given this, it doesn't matter which half we do "first". */ if (optype0 == PUSHOP && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) operands[1] = latehalf[1]; /* If one or both operands autodecrementing, do the two words, high-numbered first. */ else if (optype0 == PUSHOP || optype1 == PUSHOP) { output_asm_insn (singlemove_string (latehalf), latehalf); return singlemove_string (operands); } /* If the first move would clobber the source of the second one, do them in the other order. */ /* Overlapping registers. */ if (optype0 == REGOP && optype1 == REGOP && REGNO (operands[0]) == REGNO (latehalf[1])) { /* Do that word. */ output_asm_insn (singlemove_string (latehalf), latehalf); /* Do low-numbered word. */ return singlemove_string (operands); } /* Loading into a register which overlaps a register used in the address. */ else if (optype0 == REGOP && optype1 != REGOP && reg_overlap_mentioned_p (operands[0], operands[1])) { if (reg_mentioned_p (operands[0], XEXP (operands[1], 0)) && reg_mentioned_p (latehalf[0], XEXP (operands[1], 0))) { /* If both halves of dest are used in the src memory address, load the destination address into the low reg (operands[0]). Then it works to load latehalf first. */ rtx xops[2]; xops[0] = XEXP (operands[1], 0); xops[1] = operands[0]; output_asm_insn ("addr %a0,%1", xops); operands[1] = gen_rtx_MEM (DImode, operands[0]); latehalf[1] = adjust_address (operands[1], SImode, 4); /* The first half has the overlap, Do the late half first. */ output_asm_insn (singlemove_string (latehalf), latehalf); /* Then clobber. */ return singlemove_string (operands); } if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))) { /* The first half has the overlap, Do the late half first. */ output_asm_insn (singlemove_string (latehalf), latehalf); /* Then clobber. */ return singlemove_string (operands); } } /* Normal case. Do the two words, low-numbered first. */ output_asm_insn (singlemove_string (operands), operands); operands[0] = latehalf[0]; operands[1] = latehalf[1]; return singlemove_string (operands);}#define MAX_UNALIGNED_COPY (32)/* Expand string/block move operations. operands[0] is the pointer to the destination. operands[1] is the pointer to the source. operands[2] is the number of bytes to move. operands[3] is the alignment. */static voidmove_tail (operands, bytes, offset) rtx operands[]; int bytes; int offset;{ if (bytes & 2) { emit_move_insn (adjust_address (operands[0], HImode, offset), adjust_address (operands[1], HImode, offset)); offset += 2; } if (bytes & 1) emit_move_insn (adjust_address (operands[0], QImode, offset), adjust_address (operands[1], QImode, offset));}voidexpand_block_move (operands) rtx operands[];{ rtx bytes_rtx = operands[2]; rtx align_rtx = operands[3]; int constp = (GET_CODE (bytes_rtx) == CONST_INT); int bytes = (constp ? INTVAL (bytes_rtx) : 0); int align = INTVAL (align_rtx); rtx src_reg = gen_rtx_REG (Pmode, 1); rtx dest_reg = gen_rtx_REG (Pmode, 2); rtx count_reg = gen_rtx_REG (SImode, 0); if (constp && bytes <= 0) return; if (constp && bytes < 20) { int words = bytes >> 2; if (words) { if (words < 3 || flag_unroll_loops) { int offset = 0; for (; words; words--, offset += 4) emit_move_insn (adjust_address (operands[0], SImode, offset), adjust_address (operands[1], SImode, offset)); } else { /* Use movmd. It is slower than multiple movd's but more compact. It is also slower than movsd for large copies but causes less registers reloading so is better than movsd for small copies. */ rtx src, dest; dest = copy_addr_to_reg (XEXP (operands[0], 0)); src = copy_addr_to_reg (XEXP (operands[1], 0)); emit_insn (gen_movstrsi2(dest, src, GEN_INT (words))); } } move_tail (operands, bytes & 3, bytes & ~3); return; } if (align > UNITS_PER_WORD) align = UNITS_PER_WORD; /* Move the address into scratch registers. */ emit_insn (gen_rtx_CLOBBER (VOIDmode, dest_reg)); emit_move_insn (dest_reg, XEXP (operands[0], 0)); operands[0] = gen_rtx_MEM (SImode, dest_reg); emit_insn (gen_rtx_CLOBBER (VOIDmode, src_reg)); emit_move_insn (src_reg, XEXP (operands[1], 0)); operands[1] = gen_rtx_MEM (SImode, src_reg); emit_insn (gen_rtx_CLOBBER (VOIDmode, count_reg)); if (constp && (align == UNITS_PER_WORD || bytes < MAX_UNALIGNED_COPY)) { /* constant no of bytes and aligned or small enough copy to not bother * aligning. Emit insns to copy by words. */ if (bytes >> 2) { emit_move_insn (count_reg, GEN_INT (bytes >> 2)); emit_insn (gen_movstrsi1 (GEN_INT (4))); } /* insns to copy rest */ move_tail (operands, bytes & 3, 0); } else if (align == UNITS_PER_WORD) { /* insns to copy by words */ emit_insn (gen_lshrsi3 (count_reg, bytes_rtx, GEN_INT (2))); emit_insn (gen_movstrsi1 (GEN_INT (4))); if (constp) { move_tail (operands, bytes & 3, 0); } else { /* insns to copy rest */ emit_insn (gen_andsi3 (count_reg, bytes_rtx, GEN_INT (3))); emit_insn (gen_movstrsi1 (const1_rtx)); } } else { /* Not aligned and we may have a lot to copy so it is worth * aligning. */ rtx aligned_label = gen_label_rtx (); rtx bytes_reg; bytes_reg = copy_to_mode_reg (SImode, bytes_rtx); if (!constp) { /* Emit insns to test and skip over the alignment if it is * not worth it. This doubles as a test to ensure that the alignment * operation can't copy too many bytes */ emit_insn (gen_cmpsi (bytes_reg, GEN_INT (MAX_UNALIGNED_COPY))); emit_jump_insn (gen_blt (aligned_label)); } /* Emit insns to do alignment at run time */ emit_insn (gen_negsi2 (count_reg, src_reg)); emit_insn (gen_andsi3 (count_reg, count_reg, GEN_INT (3))); emit_insn (gen_subsi3 (bytes_reg, bytes_reg, count_reg)); emit_insn (gen_movstrsi1 (const1_rtx)); if (!constp) emit_label (aligned_label); /* insns to copy by words */ emit_insn (gen_lshrsi3 (count_reg, bytes_reg, GEN_INT (2))); emit_insn (gen_movstrsi1 (GEN_INT (4))); /* insns to copy rest */ emit_insn (gen_andsi3 (count_reg, bytes_reg, GEN_INT (3))); emit_insn (gen_movstrsi1 (const1_rtx)); }}/* Returns 1 if OP contains a global symbol reference */intglobal_symbolic_reference_mentioned_p (op, f) rtx op; int f;{ register const char *fmt; register int i; if (GET_CODE (op) == SYMBOL_REF) { if (! SYMBOL_REF_FLAG (op)) return 1; else return 0; } else if (f && GET_CODE (op) != CONST) return 0; fmt = GET_RTX_FORMAT (GET_CODE (op)); for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--) { if (fmt[i] == 'E') { register int j; for (j = XVECLEN (op, i) - 1; j >= 0; j--) if (global_symbolic_reference_mentioned_p (XVECEXP (op, i, j), 0)) return 1; } else if (fmt[i] == 'e' && global_symbolic_reference_mentioned_p (XEXP (op, i), 0)) return 1; } return 0;}/* Returns 1 if OP contains a symbol reference */intsymbolic_reference_mentioned_p (op) rtx op;{ register const char *fmt; register int i; if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF) return 1; fmt = GET_RTX_FORMAT (GET_CODE (op)); for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--) { if (fmt[i] == 'E') { register int j; for (j = XVECLEN (op, i) - 1; j >= 0; j--) if (symbolic_reference_mentioned_p (XVECEXP (op, i, j))) return 1; } else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i))) return 1; } return 0;}/* Table of machine-specific attributes. */const struct attribute_spec ns32k_attribute_table[] ={ /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ /* Stdcall attribute says callee is responsible for popping arguments if they are not variable. */ { "stdcall", 0, 0, false, true, true, ns32k_handle_fntype_attribute }, /* Cdecl attribute says the callee is a normal C declaration */ { "cdecl", 0, 0, false, true, true, ns32k_handle_fntype_attribute }, { NULL, 0, 0, false, false, false, NULL }};/* Handle an attribute requiring a FUNCTION_TYPE, FIELD_DECL or TYPE_DECL; arguments as in struct attribute_spec.handler. */static treens32k_handle_fntype_attribute (node, name, args, flags, no_add_attrs) tree *node; tree name; tree args ATTRIBUTE_UNUSED; int flags ATTRIBUTE_UNUSED; bool *no_add_attrs;{ if (TREE_CODE (*node) != FUNCTION_TYPE && TREE_CODE (*node) != FIELD_DECL && TREE_CODE (*node) != TYPE_DECL) { warning ("`%s' attribute only applies to functions", IDENTIFIER_POINTER (name)); *no_add_attrs = true; } return NULL_TREE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -