📄 expr.c
字号:
= (GET_CODE (to_addr) == PRE_DEC || GET_CODE (to_addr) == POST_DEC); if (data.reverse) data.offset = len; data.len = len; data.to_struct = MEM_IN_STRUCT_P (to); data.from_struct = MEM_IN_STRUCT_P (from); /* If copying requires more than two move insns, copy addresses to registers (to make displacements shorter) and use post-increment if available. */ if (!(data.autinc_from && data.autinc_to) && move_by_pieces_ninsns (len, align) > 2) {#ifdef HAVE_PRE_DECREMENT if (data.reverse && ! data.autinc_from) { data.from_addr = copy_addr_to_reg (plus_constant (from_addr, len)); data.autinc_from = 1; data.explicit_inc_from = -1; }#endif#ifdef HAVE_POST_INCREMENT if (! data.autinc_from) { data.from_addr = copy_addr_to_reg (from_addr); data.autinc_from = 1; data.explicit_inc_from = 1; }#endif if (!data.autinc_from && CONSTANT_P (from_addr)) data.from_addr = copy_addr_to_reg (from_addr);#ifdef HAVE_PRE_DECREMENT if (data.reverse && ! data.autinc_to) { data.to_addr = copy_addr_to_reg (plus_constant (to_addr, len)); data.autinc_to = 1; data.explicit_inc_to = -1; }#endif#ifdef HAVE_POST_INCREMENT if (! data.reverse && ! data.autinc_to) { data.to_addr = copy_addr_to_reg (to_addr); data.autinc_to = 1; data.explicit_inc_to = 1; }#endif if (!data.autinc_to && CONSTANT_P (to_addr)) data.to_addr = copy_addr_to_reg (to_addr); } if (! SLOW_UNALIGNED_ACCESS || align > MOVE_MAX || align >= BIGGEST_ALIGNMENT / BITS_PER_UNIT) align = MOVE_MAX; /* First move what we can in the largest integer mode, then go to successively smaller modes. */ while (max_size > 1) { enum machine_mode mode = VOIDmode, tmode; enum insn_code icode; for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT); tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode)) if (GET_MODE_SIZE (tmode) < max_size) mode = tmode; if (mode == VOIDmode) break; icode = mov_optab->handlers[(int) mode].insn_code; if (icode != CODE_FOR_nothing && align >= MIN (BIGGEST_ALIGNMENT / BITS_PER_UNIT, GET_MODE_SIZE (mode))) move_by_pieces_1 (GEN_FCN (icode), mode, &data); max_size = GET_MODE_SIZE (mode); } /* The code above should have handled everything. */ if (data.len > 0) abort ();}/* Return number of insns required to move L bytes by pieces. ALIGN (in bytes) is maximum alignment we can assume. */static intmove_by_pieces_ninsns (l, align) unsigned int l; int align;{ register int n_insns = 0; int max_size = MOVE_MAX + 1; if (! SLOW_UNALIGNED_ACCESS || align > MOVE_MAX || align >= BIGGEST_ALIGNMENT / BITS_PER_UNIT) align = MOVE_MAX; while (max_size > 1) { enum machine_mode mode = VOIDmode, tmode; enum insn_code icode; for (tmode = GET_CLASS_NARROWEST_MODE (MODE_INT); tmode != VOIDmode; tmode = GET_MODE_WIDER_MODE (tmode)) if (GET_MODE_SIZE (tmode) < max_size) mode = tmode; if (mode == VOIDmode) break; icode = mov_optab->handlers[(int) mode].insn_code; if (icode != CODE_FOR_nothing && align >= MIN (BIGGEST_ALIGNMENT / BITS_PER_UNIT, GET_MODE_SIZE (mode))) n_insns += l / GET_MODE_SIZE (mode), l %= GET_MODE_SIZE (mode); max_size = GET_MODE_SIZE (mode); } return n_insns;}/* Subroutine of move_by_pieces. Move as many bytes as appropriate with move instructions for mode MODE. GENFUN is the gen_... function to make a move insn for that mode. DATA has all the other info. */static voidmove_by_pieces_1 (genfun, mode, data) rtx (*genfun) (); enum machine_mode mode; struct move_by_pieces *data;{ register int size = GET_MODE_SIZE (mode); register rtx to1, from1; while (data->len >= size) { if (data->reverse) data->offset -= size; to1 = (data->autinc_to ? gen_rtx (MEM, mode, data->to_addr) : copy_rtx (change_address (data->to, mode, plus_constant (data->to_addr, data->offset)))); MEM_IN_STRUCT_P (to1) = data->to_struct; from1 = (data->autinc_from ? gen_rtx (MEM, mode, data->from_addr) : copy_rtx (change_address (data->from, mode, plus_constant (data->from_addr, data->offset)))); MEM_IN_STRUCT_P (from1) = data->from_struct;#ifdef HAVE_PRE_DECREMENT if (data->explicit_inc_to < 0) emit_insn (gen_add2_insn (data->to_addr, GEN_INT (-size))); if (data->explicit_inc_from < 0) emit_insn (gen_add2_insn (data->from_addr, GEN_INT (-size)));#endif emit_insn ((*genfun) (to1, from1));#ifdef HAVE_POST_INCREMENT if (data->explicit_inc_to > 0) emit_insn (gen_add2_insn (data->to_addr, GEN_INT (size))); if (data->explicit_inc_from > 0) emit_insn (gen_add2_insn (data->from_addr, GEN_INT (size)));#endif if (! data->reverse) data->offset += size; data->len -= size; }}/* Emit code to move a block Y to a block X. This may be done with string-move instructions, with multiple scalar move instructions, or with a library call. Both X and Y must be MEM rtx's (perhaps inside VOLATILE) with mode BLKmode. SIZE is an rtx that says how long they are. ALIGN is the maximum alignment we can assume they have, measured in bytes. Return the address of the new block, if memcpy is called and returns it, 0 otherwise. */rtxemit_block_move (x, y, size, align) rtx x, y; rtx size; int align;{ rtx retval = 0; if (GET_MODE (x) != BLKmode) abort (); if (GET_MODE (y) != BLKmode) abort (); x = protect_from_queue (x, 1); y = protect_from_queue (y, 0); size = protect_from_queue (size, 0); if (GET_CODE (x) != MEM) abort (); if (GET_CODE (y) != MEM) abort (); if (size == 0) abort (); if (GET_CODE (size) == CONST_INT && (move_by_pieces_ninsns (INTVAL (size), align) < MOVE_RATIO)) move_by_pieces (x, y, INTVAL (size), align); else { /* Try the most limited insn first, because there's no point including more than one in the machine description unless the more limited one has some advantage. */ rtx opalign = GEN_INT (align); enum machine_mode mode; for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode; mode = GET_MODE_WIDER_MODE (mode)) { enum insn_code code = movstr_optab[(int) mode]; if (code != CODE_FOR_nothing /* We don't need MODE to be narrower than BITS_PER_HOST_WIDE_INT here because if SIZE is less than the mode mask, as it is returned by the macro, it will definitely be less than the actual mode mask. */ && ((GET_CODE (size) == CONST_INT && ((unsigned HOST_WIDE_INT) INTVAL (size) <= GET_MODE_MASK (mode))) || GET_MODE_BITSIZE (mode) >= BITS_PER_WORD) && (insn_operand_predicate[(int) code][0] == 0 || (*insn_operand_predicate[(int) code][0]) (x, BLKmode)) && (insn_operand_predicate[(int) code][1] == 0 || (*insn_operand_predicate[(int) code][1]) (y, BLKmode)) && (insn_operand_predicate[(int) code][3] == 0 || (*insn_operand_predicate[(int) code][3]) (opalign, VOIDmode))) { rtx op2; rtx last = get_last_insn (); rtx pat; op2 = convert_to_mode (mode, size, 1); if (insn_operand_predicate[(int) code][2] != 0 && ! (*insn_operand_predicate[(int) code][2]) (op2, mode)) op2 = copy_to_mode_reg (mode, op2); pat = GEN_FCN ((int) code) (x, y, op2, opalign); if (pat) { emit_insn (pat); return 0; } else delete_insns_since (last); } }#ifdef TARGET_MEM_FUNCTIONS retval = emit_library_call_value (memcpy_libfunc, NULL_RTX, 0, ptr_mode, 3, XEXP (x, 0), Pmode, XEXP (y, 0), Pmode, convert_to_mode (TYPE_MODE (sizetype), size, TREE_UNSIGNED (sizetype)), TYPE_MODE (sizetype));#else emit_library_call (bcopy_libfunc, 0, VOIDmode, 3, XEXP (y, 0), Pmode, XEXP (x, 0), Pmode, convert_to_mode (TYPE_MODE (integer_type_node), size, TREE_UNSIGNED (integer_type_node)), TYPE_MODE (integer_type_node));#endif } return retval;}/* Copy all or part of a value X into registers starting at REGNO. The number of registers to be filled is NREGS. */voidmove_block_to_reg (regno, x, nregs, mode) int regno; rtx x; int nregs; enum machine_mode mode;{ int i; rtx pat, last; if (nregs == 0) return; if (CONSTANT_P (x) && ! LEGITIMATE_CONSTANT_P (x)) x = validize_mem (force_const_mem (mode, x)); /* See if the machine can do this with a load multiple insn. */#ifdef HAVE_load_multiple if (HAVE_load_multiple) { last = get_last_insn (); pat = gen_load_multiple (gen_rtx (REG, word_mode, regno), x, GEN_INT (nregs)); if (pat) { emit_insn (pat); return; } else delete_insns_since (last); }#endif for (i = 0; i < nregs; i++) emit_move_insn (gen_rtx (REG, word_mode, regno + i), operand_subword_force (x, i, mode));}/* Copy all or part of a BLKmode value X out of registers starting at REGNO. The number of registers to be filled is NREGS. SIZE indicates the number of bytes in the object X. */voidmove_block_from_reg (regno, x, nregs, size) int regno; rtx x; int nregs; int size;{ int i; rtx pat, last; enum machine_mode mode; /* If SIZE is that of a mode no bigger than a word, just use that mode's store operation. */ if (size <= UNITS_PER_WORD && (mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0)) != BLKmode) { emit_move_insn (change_address (x, mode, NULL), gen_rtx (REG, mode, regno)); return; } /* Blocks smaller than a word on a BYTES_BIG_ENDIAN machine must be aligned to the left before storing to memory. Note that the previous test doesn't handle all cases (e.g. SIZE == 3). */ if (size < UNITS_PER_WORD && BYTES_BIG_ENDIAN) { rtx tem = operand_subword (x, 0, 1, BLKmode); rtx shift; if (tem == 0) abort (); shift = expand_shift (LSHIFT_EXPR, word_mode, gen_rtx (REG, word_mode, regno), build_int_2 ((UNITS_PER_WORD - size) * BITS_PER_UNIT, 0), NULL_RTX, 0); emit_move_insn (tem, shift); return; } /* See if the machine can do this with a store multiple insn. */#ifdef HAVE_store_multiple if (HAVE_store_multiple) { last = get_last_insn (); pat = gen_store_multiple (x, gen_rtx (REG, word_mode, regno), GEN_INT (nregs)); if (pat) { emit_insn (pat); return; } else delete_insns_since (last); }#endif for (i = 0; i < nregs; i++) { rtx tem = operand_subword (x, i, 1, BLKmode); if (tem == 0) abort (); emit_move_insn (tem, gen_rtx (REG, word_mode, regno + i)); }}/* Emit code to move a block Y to a block X, where X is non-consecutive registers represented by a PARALLEL. */voidemit_group_load (x, y) rtx x, y;{ rtx target_reg, source; int i; if (GET_CODE (x) != PARALLEL) abort (); /* Check for a NULL entry, used to indicate that the parameter goes both on the stack and in registers. */ if (XEXP (XVECEXP (x, 0, 0), 0)) i = 0; else i = 1; for (; i < XVECLEN (x, 0); i++) { rtx element = XVECEXP (x, 0, i); target_reg = XEXP (element, 0); if (GET_CODE (y) == MEM) source = change_address (y, GET_MODE (target_reg), plus_constant (XEXP (y, 0), INTVAL (XEXP (element, 1)))); else if (XEXP (element, 1) == const0_rtx) { if (GET_MODE (target_reg) == GET_MODE (y)) source = y; /* Allow for the target_reg to be smaller than the input register to allow for AIX with 4 DF arguments after a single SI arg. The last DF argument will only load 1 word into the integer registers, but load a DF value into the float registers. */ else if ((GET_MODE_SIZE (GET_MODE (target_reg)) <= GET_MODE_SIZE (GET_MODE (y))) && GET_MODE (target_reg) == word_mode) /* This might be a const_double, so we can't just use SUBREG. */ source = operand_subword (y, 0, 0, VOIDmode); else if (GET_MODE_SIZE (GET_MODE (target_reg)) == GET_MODE_SIZE (GET_MODE (y))) source = gen_lowpart (GET_MODE (target_reg), y); else abort (); } else abort (); emit_move_insn (target_reg, source); }}/* Emit code to move a block Y to a block X, where Y is non-consecutive registers represented by a PARALLEL. */voidemit_group_store (x, y) rtx x, y;{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -