mirror of
git://gcc.gnu.org/git/gcc.git
synced 2024-12-30 17:25:11 +08:00
expr.c (emit_block_move): Properly handle case where one of the block move arguments has a queued...
� * expr.c (emit_block_move): Properly handle case where one of the block move arguments has a queued increment or decrement. (clear_storage): Similarly. Fix formatting goof. From-SVN: r27822
This commit is contained in:
parent
5dd2921918
commit
4bc973aedd
170
gcc/expr.c
170
gcc/expr.c
@ -1710,6 +1710,37 @@ emit_block_move (x, y, size, align)
|
||||
}
|
||||
}
|
||||
|
||||
/* X, Y, or SIZE may have been passed through protect_from_queue.
|
||||
|
||||
It is unsafe to save the value generated by protect_from_queue
|
||||
and reuse it later. Consider what happens if emit_queue is
|
||||
called before the return value from protect_from_queue is used.
|
||||
|
||||
Expansion of the CALL_EXPR below will call emit_queue before
|
||||
we are finished emitting RTL for argument setup. So if we are
|
||||
not careful we could get the wrong value for an argument.
|
||||
|
||||
To avoid this problem we go ahead and emit code to copy X, Y &
|
||||
SIZE into new pseudos. We can then place those new pseudos
|
||||
into an RTL_EXPR and use them later, even after a call to
|
||||
emit_queue.
|
||||
|
||||
Note this is not strictly needed for library calls since they
|
||||
do not call emit_queue before loading their arguments. However,
|
||||
we may need to have library calls call emit_queue in the future
|
||||
since failing to do so could cause problems for targets which
|
||||
define SMALL_REGISTER_CLASSES and pass arguments in registers. */
|
||||
x = copy_to_mode_reg (Pmode, XEXP (x, 0));
|
||||
y = copy_to_mode_reg (Pmode, XEXP (y, 0));
|
||||
|
||||
#ifdef TARGET_MEM_FUNCTIONS
|
||||
size = copy_to_mode_reg (TYPE_MODE (sizetype), size);
|
||||
#else
|
||||
size = convert_to_mode (TYPE_MODE (integer_type_node), size,
|
||||
TREE_UNSIGNED (integer_type_node));
|
||||
size = copy_to_reg (size);
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_MEM_FUNCTIONS
|
||||
/* It is incorrect to use the libcall calling conventions to call
|
||||
memcpy in this context.
|
||||
@ -1748,12 +1779,10 @@ emit_block_move (x, y, size, align)
|
||||
the last is a size_t byte count for the copy. */
|
||||
arg_list
|
||||
= build_tree_list (NULL_TREE,
|
||||
make_tree (build_pointer_type (void_type_node),
|
||||
XEXP (x, 0)));
|
||||
make_tree (build_pointer_type (void_type_node), x));
|
||||
TREE_CHAIN (arg_list)
|
||||
= build_tree_list (NULL_TREE,
|
||||
make_tree (build_pointer_type (void_type_node),
|
||||
XEXP (y, 0)));
|
||||
make_tree (build_pointer_type (void_type_node), y));
|
||||
TREE_CHAIN (TREE_CHAIN (arg_list))
|
||||
= build_tree_list (NULL_TREE, make_tree (sizetype, size));
|
||||
TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arg_list))) = NULL_TREE;
|
||||
@ -1767,8 +1796,7 @@ emit_block_move (x, y, size, align)
|
||||
retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
|
||||
#else
|
||||
emit_library_call (bcopy_libfunc, 0,
|
||||
VOIDmode, 3, XEXP (y, 0), Pmode,
|
||||
XEXP (x, 0), Pmode,
|
||||
VOIDmode, 3, y, Pmode, x, Pmode
|
||||
convert_to_mode (TYPE_MODE (integer_type_node), size,
|
||||
TREE_UNSIGNED (integer_type_node)),
|
||||
TYPE_MODE (integer_type_node));
|
||||
@ -2444,69 +2472,97 @@ clear_storage (object, size, align)
|
||||
}
|
||||
}
|
||||
|
||||
/* OBJECT or SIZE may have been passed through protect_from_queue.
|
||||
|
||||
It is unsafe to save the value generated by protect_from_queue
|
||||
and reuse it later. Consider what happens if emit_queue is
|
||||
called before the return value from protect_from_queue is used.
|
||||
|
||||
Expansion of the CALL_EXPR below will call emit_queue before
|
||||
we are finished emitting RTL for argument setup. So if we are
|
||||
not careful we could get the wrong value for an argument.
|
||||
|
||||
To avoid this problem we go ahead and emit code to copy OBJECT
|
||||
and SIZE into new pseudos. We can then place those new pseudos
|
||||
into an RTL_EXPR and use them later, even after a call to
|
||||
emit_queue.
|
||||
|
||||
Note this is not strictly needed for library calls since they
|
||||
do not call emit_queue before loading their arguments. However,
|
||||
we may need to have library calls call emit_queue in the future
|
||||
since failing to do so could cause problems for targets which
|
||||
define SMALL_REGISTER_CLASSES and pass arguments in registers. */
|
||||
object = copy_to_mode_reg (Pmode, XEXP (object, 0));
|
||||
|
||||
#ifdef TARGET_MEM_FUNCTIONS
|
||||
/* It is incorrect to use the libcall calling conventions to call
|
||||
memset in this context.
|
||||
size = copy_to_mode_reg (TYPE_MODE (sizetype), size);
|
||||
#else
|
||||
size = convert_to_mode (TYPE_MODE (integer_type_node), size,
|
||||
TREE_UNSIGNED (integer_type_node));
|
||||
size = copy_to_reg (size);
|
||||
#endif
|
||||
|
||||
This could be a user call to memset and the user may wish to
|
||||
examine the return value from memset.
|
||||
|
||||
For targets where libcalls and normal calls have different conventions
|
||||
for returning pointers, we could end up generating incorrect code.
|
||||
#ifdef TARGET_MEM_FUNCTIONS
|
||||
/* It is incorrect to use the libcall calling conventions to call
|
||||
memset in this context.
|
||||
|
||||
So instead of using a libcall sequence we build up a suitable
|
||||
CALL_EXPR and expand the call in the normal fashion. */
|
||||
if (fn == NULL_TREE)
|
||||
{
|
||||
tree fntype;
|
||||
This could be a user call to memset and the user may wish to
|
||||
examine the return value from memset.
|
||||
|
||||
/* This was copied from except.c, I don't know if all this is
|
||||
necessary in this context or not. */
|
||||
fn = get_identifier ("memset");
|
||||
push_obstacks_nochange ();
|
||||
end_temporary_allocation ();
|
||||
fntype = build_pointer_type (void_type_node);
|
||||
fntype = build_function_type (fntype, NULL_TREE);
|
||||
fn = build_decl (FUNCTION_DECL, fn, fntype);
|
||||
DECL_EXTERNAL (fn) = 1;
|
||||
TREE_PUBLIC (fn) = 1;
|
||||
DECL_ARTIFICIAL (fn) = 1;
|
||||
make_decl_rtl (fn, NULL_PTR, 1);
|
||||
assemble_external (fn);
|
||||
pop_obstacks ();
|
||||
}
|
||||
For targets where libcalls and normal calls have different
|
||||
conventions for returning pointers, we could end up generating
|
||||
incorrect code.
|
||||
|
||||
/* We need to make an argument list for the function call.
|
||||
So instead of using a libcall sequence we build up a suitable
|
||||
CALL_EXPR and expand the call in the normal fashion. */
|
||||
if (fn == NULL_TREE)
|
||||
{
|
||||
tree fntype;
|
||||
|
||||
memset has three arguments, the first is a void * addresses, the
|
||||
second a integer with the initialization value, the last is a size_t
|
||||
byte count for the copy. */
|
||||
arg_list
|
||||
= build_tree_list (NULL_TREE,
|
||||
make_tree (build_pointer_type (void_type_node),
|
||||
XEXP (object, 0)));
|
||||
TREE_CHAIN (arg_list)
|
||||
= build_tree_list (NULL_TREE,
|
||||
make_tree (integer_type_node, const0_rtx));
|
||||
TREE_CHAIN (TREE_CHAIN (arg_list))
|
||||
= build_tree_list (NULL_TREE, make_tree (sizetype, size));
|
||||
TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arg_list))) = NULL_TREE;
|
||||
/* This was copied from except.c, I don't know if all this is
|
||||
necessary in this context or not. */
|
||||
fn = get_identifier ("memset");
|
||||
push_obstacks_nochange ();
|
||||
end_temporary_allocation ();
|
||||
fntype = build_pointer_type (void_type_node);
|
||||
fntype = build_function_type (fntype, NULL_TREE);
|
||||
fn = build_decl (FUNCTION_DECL, fn, fntype);
|
||||
DECL_EXTERNAL (fn) = 1;
|
||||
TREE_PUBLIC (fn) = 1;
|
||||
DECL_ARTIFICIAL (fn) = 1;
|
||||
make_decl_rtl (fn, NULL_PTR, 1);
|
||||
assemble_external (fn);
|
||||
pop_obstacks ();
|
||||
}
|
||||
|
||||
/* Now we have to build up the CALL_EXPR itself. */
|
||||
call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
|
||||
call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
|
||||
call_expr, arg_list, NULL_TREE);
|
||||
TREE_SIDE_EFFECTS (call_expr) = 1;
|
||||
/* We need to make an argument list for the function call.
|
||||
|
||||
retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
|
||||
memset has three arguments, the first is a void * addresses, the
|
||||
second a integer with the initialization value, the last is a
|
||||
size_t byte count for the copy. */
|
||||
arg_list
|
||||
= build_tree_list (NULL_TREE,
|
||||
make_tree (build_pointer_type (void_type_node),
|
||||
object));
|
||||
TREE_CHAIN (arg_list)
|
||||
= build_tree_list (NULL_TREE,
|
||||
make_tree (integer_type_node, const0_rtx));
|
||||
TREE_CHAIN (TREE_CHAIN (arg_list))
|
||||
= build_tree_list (NULL_TREE, make_tree (sizetype, size));
|
||||
TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arg_list))) = NULL_TREE;
|
||||
|
||||
/* Now we have to build up the CALL_EXPR itself. */
|
||||
call_expr = build1 (ADDR_EXPR,
|
||||
build_pointer_type (TREE_TYPE (fn)), fn);
|
||||
call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
|
||||
call_expr, arg_list, NULL_TREE);
|
||||
TREE_SIDE_EFFECTS (call_expr) = 1;
|
||||
|
||||
retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
|
||||
#else
|
||||
emit_library_call (bzero_libfunc, 0,
|
||||
VOIDmode, 2,
|
||||
XEXP (object, 0), Pmode,
|
||||
convert_to_mode
|
||||
(TYPE_MODE (integer_type_node), size,
|
||||
TREE_UNSIGNED (integer_type_node)),
|
||||
VOIDmode, 2, object, Pmode, size
|
||||
TYPE_MODE (integer_type_node));
|
||||
#endif
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user