diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 1c9311fa473..b2dcdf12dc5 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,24 @@ +2002-08-29 Richard Henderson + + * expr.h (enum block_op_methods): New. + (emit_block_move): Update prototype. + * expr.c (block_move_libcall_safe_for_call_parm): New. + (emit_block_move_via_loop): New. + (emit_block_move): Use them. New argument METHOD. + (emit_push_insn): Always respect the given alignment. + (expand_assignment): Update call to emit_block_move. + (store_expr, store_field, expand_expr): Likewise. + * builtins.c (expand_builtin_apply): Likewise. + (expand_builtin_memcpy, expand_builtin_va_copy): Likewise. + * function.c (expand_function_end): Likewise. + * config/sh/sh.c (sh_initialize_trampoline): Likewise. + * config/sparc/sparc.c (sparc_va_arg): Likewise. + * calls.c (expand_call, emit_library_call_value_1): Likewise. + (save_fixed_argument_area): Use emit_block_move with + BLOCK_OP_CALL_PARM instead of move_by_pieces. + (restore_fixed_argument_area): Likewise. + (store_one_arg): Fix alignment parameter to emit_push_insn. + 2002-08-29 John David Anglin * install.texi (hppa64-hp-hpux11*): Document installation procedure. diff --git a/gcc/builtins.c b/gcc/builtins.c index a295a9aab34..b2ad5375b77 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -1225,7 +1225,7 @@ expand_builtin_apply (function, arguments, argsize) set_mem_align (dest, PARM_BOUNDARY); src = gen_rtx_MEM (BLKmode, incoming_args); set_mem_align (src, PARM_BOUNDARY); - emit_block_move (dest, src, argsize); + emit_block_move (dest, src, argsize, BLOCK_OP_NORMAL); /* Refer to the argument block. */ apply_args_size (); @@ -2000,7 +2000,8 @@ expand_builtin_memcpy (arglist, target, mode) set_mem_align (src_mem, src_align); /* Copy word part most expediently. */ - dest_addr = emit_block_move (dest_mem, src_mem, len_rtx); + dest_addr = emit_block_move (dest_mem, src_mem, len_rtx, + BLOCK_OP_NORMAL); if (dest_addr == 0) { @@ -3298,7 +3299,7 @@ expand_builtin_va_copy (arglist) set_mem_align (srcb, TYPE_ALIGN (va_list_type_node)); /* Copy. */ - emit_block_move (dstb, srcb, size); + emit_block_move (dstb, srcb, size, BLOCK_OP_NORMAL); } return const0_rtx; diff --git a/gcc/calls.c b/gcc/calls.c index 5979747a68b..2e238094ffb 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -967,11 +967,8 @@ save_fixed_argument_area (reg_parm_stack_space, argblock, if (save_mode == BLKmode) { save_area = assign_stack_temp (BLKmode, num_to_save, 0); - /* Cannot use emit_block_move here because it can be done by a - library call which in turn gets into this place again and deadly - infinite recursion happens. */ - move_by_pieces (validize_mem (save_area), stack_area, num_to_save, - PARM_BOUNDARY); + emit_block_move (validize_mem (save_area), stack_area, + GEN_INT (num_to_save), BLOCK_OP_CALL_PARM); } else { @@ -1008,11 +1005,9 @@ restore_fixed_argument_area (save_area, argblock, high_to_save, low_to_save) if (save_mode != BLKmode) emit_move_insn (stack_area, save_area); else - /* Cannot use emit_block_move here because it can be done by a library - call which in turn gets into this place again and deadly infinite - recursion happens. */ - move_by_pieces (stack_area, validize_mem (save_area), - high_to_save - low_to_save + 1, PARM_BOUNDARY); + emit_block_move (stack_area, validize_mem (save_area), + GEN_INT (high_to_save - low_to_save + 1), + BLOCK_OP_CALL_PARM); } #endif /* REG_PARM_STACK_SPACE */ @@ -3317,9 +3312,9 @@ expand_call (exp, target, ignore) if (save_mode != BLKmode) emit_move_insn (stack_area, args[i].save_area); else - emit_block_move (stack_area, - validize_mem (args[i].save_area), - GEN_INT (args[i].size.constant)); + emit_block_move (stack_area, args[i].save_area, + GEN_INT (args[i].size.constant), + BLOCK_OP_CALL_PARM); } highest_outgoing_arg_in_use = initial_highest_arg_in_use; @@ -3909,8 +3904,8 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p) { save_area = assign_stack_temp (BLKmode, num_to_save, 0); set_mem_align (save_area, PARM_BOUNDARY); - emit_block_move (validize_mem (save_area), stack_area, - GEN_INT (num_to_save)); + emit_block_move (save_area, stack_area, GEN_INT (num_to_save), + BLOCK_OP_CALL_PARM); } else { @@ -3978,8 +3973,9 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p) } } - emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0, - argblock, GEN_INT (argvec[argnum].offset.constant), + emit_push_insn (val, mode, NULL_TREE, NULL_RTX, PARM_BOUNDARY, + partial, reg, 0, argblock, + GEN_INT (argvec[argnum].offset.constant), reg_parm_stack_space, ARGS_SIZE_RTX (alignment_pad)); /* Now mark the segment we just used. */ @@ -4180,8 +4176,9 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p) if (save_mode != BLKmode) emit_move_insn (stack_area, save_area); else - emit_block_move (stack_area, validize_mem (save_area), - GEN_INT (high_to_save - low_to_save + 1)); + emit_block_move (stack_area, save_area, + GEN_INT (high_to_save - low_to_save + 1), + BLOCK_OP_CALL_PARM); } #endif @@ -4358,7 +4355,8 @@ store_one_arg (arg, argblock, flags, variable_size, reg_parm_stack_space) arg->save_area = assign_temp (nt, 0, 1, 1); preserve_temp_slots (arg->save_area); emit_block_move (validize_mem (arg->save_area), stack_area, - expr_size (arg->tree_value)); + expr_size (arg->tree_value), + BLOCK_OP_CALL_PARM); } else { @@ -4479,8 +4477,8 @@ store_one_arg (arg, argblock, flags, variable_size, reg_parm_stack_space) /* This isn't already where we want it on the stack, so put it there. This can either be done with push or copy insns. */ - emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX, 0, - partial, reg, used - size, argblock, + emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX, + PARM_BOUNDARY, partial, reg, used - size, argblock, ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space, ARGS_SIZE_RTX (arg->alignment_pad)); @@ -4574,18 +4572,18 @@ store_one_arg (arg, argblock, flags, variable_size, reg_parm_stack_space) { rtx size_rtx1 = GEN_INT (reg_parm_stack_space - arg->offset.constant); emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx1, - TYPE_ALIGN (TREE_TYPE (pval)), partial, reg, - excess, argblock, ARGS_SIZE_RTX (arg->offset), - reg_parm_stack_space, + MAX (PARM_BOUNDARY, TYPE_ALIGN (TREE_TYPE (pval))), + partial, reg, excess, argblock, + ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space, ARGS_SIZE_RTX (arg->alignment_pad)); } } emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx, - TYPE_ALIGN (TREE_TYPE (pval)), partial, reg, excess, - argblock, ARGS_SIZE_RTX (arg->offset), - reg_parm_stack_space, + MAX (PARM_BOUNDARY, TYPE_ALIGN (TREE_TYPE (pval))), + partial, reg, excess, argblock, + ARGS_SIZE_RTX (arg->offset), reg_parm_stack_space, ARGS_SIZE_RTX (arg->alignment_pad)); /* Unless this is a partially-in-register argument, the argument is now diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c index e49881ba866..a6ce2be9d17 100644 --- a/gcc/config/sh/sh.c +++ b/gcc/config/sh/sh.c @@ -7301,7 +7301,7 @@ sh_initialize_trampoline (tramp, fnaddr, cxt) src = gen_rtx_MEM (BLKmode, tramp_templ); set_mem_align (dst, 256); set_mem_align (src, 64); - emit_block_move (dst, src, GEN_INT (fixed_len)); + emit_block_move (dst, src, GEN_INT (fixed_len), BLOCK_OP_NORMAL); emit_move_insn (gen_rtx_MEM (Pmode, plus_constant (tramp, fixed_len)), fnaddr); diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c index 971b6f2e5f7..4a83b8a5612 100644 --- a/gcc/config/sparc/sparc.c +++ b/gcc/config/sparc/sparc.c @@ -5347,7 +5347,8 @@ sparc_va_arg (valist, type) PUT_MODE (tmp, BLKmode); set_mem_alias_set (tmp, 0); - dest_addr = emit_block_move (tmp, addr_rtx, GEN_INT (rsize)); + dest_addr = emit_block_move (tmp, addr_rtx, GEN_INT (rsize), + BLOCK_OP_NORMAL); if (dest_addr != NULL_RTX) addr_rtx = dest_addr; else diff --git a/gcc/expr.c b/gcc/expr.c index b95de65fb27..700b84084da 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -132,9 +132,11 @@ static unsigned HOST_WIDE_INT move_by_pieces_ninsns unsigned int)); static void move_by_pieces_1 PARAMS ((rtx (*) (rtx, ...), enum machine_mode, struct move_by_pieces *)); +static bool block_move_libcall_safe_for_call_parm PARAMS ((void)); static bool emit_block_move_via_movstr PARAMS ((rtx, rtx, rtx, unsigned)); static rtx emit_block_move_via_libcall PARAMS ((rtx, rtx, rtx)); static tree emit_block_move_libcall_fn PARAMS ((int)); +static void emit_block_move_via_loop PARAMS ((rtx, rtx, rtx, unsigned)); static rtx clear_by_pieces_1 PARAMS ((PTR, HOST_WIDE_INT, enum machine_mode)); static void clear_by_pieces PARAMS ((rtx, unsigned HOST_WIDE_INT, @@ -1677,16 +1679,43 @@ move_by_pieces_1 (genfun, mode, data) 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. + METHOD describes what kind of copy this is, and what mechanisms may be used. Return the address of the new block, if memcpy is called and returns it, 0 otherwise. */ rtx -emit_block_move (x, y, size) +emit_block_move (x, y, size, method) rtx x, y, size; + enum block_op_methods method; { + bool may_use_call; rtx retval = 0; - unsigned int align = MIN (MEM_ALIGN (x), MEM_ALIGN (y)); + unsigned int align; + + switch (method) + { + case BLOCK_OP_NORMAL: + may_use_call = true; + break; + + case BLOCK_OP_CALL_PARM: + may_use_call = block_move_libcall_safe_for_call_parm (); + + /* Make inhibit_defer_pop nonzero around the library call + to force it to pop the arguments right away. */ + NO_DEFER_POP; + break; + + case BLOCK_OP_NO_LIBCALL: + may_use_call = false; + break; + + default: + abort (); + } + + align = MIN (MEM_ALIGN (x), MEM_ALIGN (y)); if (GET_MODE (x) != BLKmode) abort (); @@ -1708,12 +1737,77 @@ emit_block_move (x, y, size) move_by_pieces (x, y, INTVAL (size), align); else if (emit_block_move_via_movstr (x, y, size, align)) ; - else + else if (may_use_call) retval = emit_block_move_via_libcall (x, y, size); + else + emit_block_move_via_loop (x, y, size, align); + + if (method == BLOCK_OP_CALL_PARM) + OK_DEFER_POP; return retval; } +/* A subroutine of emit_block_move. Returns true if calling the + block move libcall will not clobber any parameters which may have + already been placed on the stack. */ + +static bool +block_move_libcall_safe_for_call_parm () +{ + if (PUSH_ARGS) + return true; + else + { + /* Check to see whether memcpy takes all register arguments. */ + static enum { + takes_regs_uninit, takes_regs_no, takes_regs_yes + } takes_regs = takes_regs_uninit; + + switch (takes_regs) + { + case takes_regs_uninit: + { + CUMULATIVE_ARGS args_so_far; + tree fn, arg; + + fn = emit_block_move_libcall_fn (false); + INIT_CUMULATIVE_ARGS (args_so_far, TREE_TYPE (fn), NULL_RTX, 0); + + arg = TYPE_ARG_TYPES (TREE_TYPE (fn)); + for ( ; arg != void_list_node ; arg = TREE_CHAIN (arg)) + { + enum machine_mode mode + = TYPE_MODE (TREE_TYPE (TREE_VALUE (arg))); + rtx tmp = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1); + if (!tmp || !REG_P (tmp)) + goto fail_takes_regs; +#ifdef FUNCTION_ARG_PARTIAL_NREGS + if (FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, + NULL_TREE, 1)) + goto fail_takes_regs; +#endif + FUNCTION_ARG_ADVANCE (args_so_far, mode, NULL_TREE, 1); + } + } + takes_regs = takes_regs_yes; + /* FALLTHRU */ + + case takes_regs_yes: + return true; + + fail_takes_regs: + takes_regs = takes_regs_no; + /* FALLTHRU */ + case takes_regs_no: + return false; + + default: + abort (); + } + } +} + /* A subroutine of emit_block_move. Expand a movstr pattern; return true if successful. */ @@ -1919,6 +2013,59 @@ emit_block_move_libcall_fn (for_call) return fn; } + +/* A subroutine of emit_block_move. Copy the data via an explicit + loop. This is used only when libcalls are forbidden. */ +/* ??? It'd be nice to copy in hunks larger than QImode. */ + +static void +emit_block_move_via_loop (x, y, size, align) + rtx x, y, size; + unsigned int align ATTRIBUTE_UNUSED; +{ + rtx cmp_label, top_label, iter, x_addr, y_addr, tmp; + enum machine_mode iter_mode; + + iter_mode = GET_MODE (size); + if (iter_mode == VOIDmode) + iter_mode = word_mode; + + top_label = gen_label_rtx (); + cmp_label = gen_label_rtx (); + iter = gen_reg_rtx (iter_mode); + + emit_move_insn (iter, const0_rtx); + + x_addr = force_operand (XEXP (x, 0), NULL_RTX); + y_addr = force_operand (XEXP (y, 0), NULL_RTX); + do_pending_stack_adjust (); + + emit_note (NULL, NOTE_INSN_LOOP_BEG); + + emit_jump (cmp_label); + emit_label (top_label); + + tmp = convert_modes (Pmode, iter_mode, iter, true); + x_addr = gen_rtx_PLUS (Pmode, x_addr, tmp); + y_addr = gen_rtx_PLUS (Pmode, y_addr, tmp); + x = change_address (x, QImode, x_addr); + y = change_address (y, QImode, y_addr); + + emit_move_insn (x, y); + + tmp = expand_simple_binop (iter_mode, PLUS, iter, const1_rtx, iter, + true, OPTAB_LIB_WIDEN); + if (tmp != iter) + emit_move_insn (iter, tmp); + + emit_note (NULL, NOTE_INSN_LOOP_CONT); + emit_label (cmp_label); + + emit_cmp_and_jump_insns (iter, size, LT, NULL_RTX, iter_mode, + true, top_label); + + emit_note (NULL, NOTE_INSN_LOOP_END); +} /* Copy all or part of a value X into registers starting at REGNO. The number of registers to be filled is NREGS. */ @@ -3623,16 +3770,12 @@ emit_push_insn (x, mode, type, size, align, partial, reg, extra, of sibling calls. */ set_mem_alias_set (target, 0); } - else - set_mem_align (target, align); - /* Make inhibit_defer_pop nonzero around the library call - to force it to pop the bcopy-arguments right away. */ - NO_DEFER_POP; + /* ALIGN may well be better aligned than TYPE, e.g. due to + PARM_BOUNDARY. Assume the caller isn't lying. */ + set_mem_align (target, align); - emit_block_move (target, xinner, size); - - OK_DEFER_POP; + emit_block_move (target, xinner, size, BLOCK_OP_CALL_PARM); } } else if (partial > 0) @@ -3951,7 +4094,7 @@ expand_assignment (to, from, want_value, suggest_reg) if (GET_CODE (to_rtx) == PARALLEL) emit_group_load (to_rtx, value, int_size_in_bytes (TREE_TYPE (from))); else if (GET_MODE (to_rtx) == BLKmode) - emit_block_move (to_rtx, value, expr_size (from)); + emit_block_move (to_rtx, value, expr_size (from), BLOCK_OP_NORMAL); else { #ifdef POINTERS_EXTEND_UNSIGNED @@ -4312,7 +4455,7 @@ store_expr (exp, target, want_value) if (GET_CODE (size) == CONST_INT && INTVAL (size) < TREE_STRING_LENGTH (exp)) - emit_block_move (target, temp, size); + emit_block_move (target, temp, size, BLOCK_OP_NORMAL); else { /* Compute the size of the data to copy from the string. */ @@ -4326,7 +4469,7 @@ store_expr (exp, target, want_value) /* Copy that much. */ copy_size_rtx = convert_to_mode (ptr_mode, copy_size_rtx, 0); - emit_block_move (target, temp, copy_size_rtx); + emit_block_move (target, temp, copy_size_rtx, BLOCK_OP_NORMAL); /* Figure out how much is left in TARGET that we have to clear. Do all calculations in ptr_mode. */ @@ -4367,7 +4510,7 @@ store_expr (exp, target, want_value) else if (GET_CODE (target) == PARALLEL) emit_group_load (target, temp, int_size_in_bytes (TREE_TYPE (exp))); else if (GET_MODE (temp) == BLKmode) - emit_block_move (target, temp, expr_size (exp)); + emit_block_move (target, temp, expr_size (exp), BLOCK_OP_NORMAL); else emit_move_insn (target, temp); } @@ -5295,7 +5438,8 @@ store_field (target, bitsize, bitpos, mode, exp, value_mode, unsignedp, type, target = adjust_address (target, VOIDmode, bitpos / BITS_PER_UNIT); emit_block_move (target, temp, GEN_INT ((bitsize + BITS_PER_UNIT - 1) - / BITS_PER_UNIT)); + / BITS_PER_UNIT), + BLOCK_OP_NORMAL); return value_mode == VOIDmode ? const0_rtx : target; } @@ -7218,7 +7362,8 @@ expand_expr (exp, target, tmode, modifier) emit_block_move (target, op0, GEN_INT ((bitsize + BITS_PER_UNIT - 1) - / BITS_PER_UNIT)); + / BITS_PER_UNIT), + BLOCK_OP_NORMAL); return target; } @@ -7634,7 +7779,8 @@ expand_expr (exp, target, tmode, modifier) if (GET_MODE (op0) == BLKmode) emit_block_move (new_with_op0_mode, op0, - GEN_INT (GET_MODE_SIZE (TYPE_MODE (type)))); + GEN_INT (GET_MODE_SIZE (TYPE_MODE (type))), + BLOCK_OP_NORMAL); else emit_move_insn (new_with_op0_mode, op0); @@ -8856,7 +9002,8 @@ expand_expr (exp, target, tmode, modifier) if (TYPE_ALIGN_OK (inner_type)) abort (); - emit_block_move (new, op0, expr_size (TREE_OPERAND (exp, 0))); + emit_block_move (new, op0, expr_size (TREE_OPERAND (exp, 0)), + BLOCK_OP_NORMAL); op0 = new; } diff --git a/gcc/expr.h b/gcc/expr.h index 76b5c809e8e..c3a1009a12a 100644 --- a/gcc/expr.h +++ b/gcc/expr.h @@ -394,7 +394,15 @@ extern rtx convert_modes PARAMS ((enum machine_mode, enum machine_mode, rtx, int)); /* Emit code to move a block Y to a block X. */ -extern rtx emit_block_move PARAMS ((rtx, rtx, rtx)); + +enum block_op_methods +{ + BLOCK_OP_NORMAL, + BLOCK_OP_CALL_PARM, + BLOCK_OP_NO_LIBCALL +}; + +extern rtx emit_block_move PARAMS ((rtx, rtx, rtx, enum block_op_methods)); /* Copy all or part of a value X into registers starting at REGNO. The number of registers to be filled is NREGS. */ diff --git a/gcc/function.c b/gcc/function.c index 5d5300192fe..f1381fb9351 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -6807,7 +6807,7 @@ expand_function_end (filename, line, end_bindings) #ifdef TRAMPOLINE_TEMPLATE blktramp = replace_equiv_address (initial_trampoline, tramp); emit_block_move (blktramp, initial_trampoline, - GEN_INT (TRAMPOLINE_SIZE)); + GEN_INT (TRAMPOLINE_SIZE), BLOCK_OP_NORMAL); #endif INITIALIZE_TRAMPOLINE (tramp, XEXP (DECL_RTL (function), 0), context); seq = get_insns ();