Commit 4bc973ae by Jeff Law

expr.c (emit_block_move): Properly handle case where one of the block move…

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
parent 5dd29219
...@@ -1710,6 +1710,37 @@ emit_block_move (x, y, size, align) ...@@ -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 #ifdef TARGET_MEM_FUNCTIONS
/* It is incorrect to use the libcall calling conventions to call /* It is incorrect to use the libcall calling conventions to call
memcpy in this context. memcpy in this context.
...@@ -1748,12 +1779,10 @@ emit_block_move (x, y, size, align) ...@@ -1748,12 +1779,10 @@ emit_block_move (x, y, size, align)
the last is a size_t byte count for the copy. */ the last is a size_t byte count for the copy. */
arg_list arg_list
= build_tree_list (NULL_TREE, = build_tree_list (NULL_TREE,
make_tree (build_pointer_type (void_type_node), make_tree (build_pointer_type (void_type_node), x));
XEXP (x, 0)));
TREE_CHAIN (arg_list) TREE_CHAIN (arg_list)
= build_tree_list (NULL_TREE, = build_tree_list (NULL_TREE,
make_tree (build_pointer_type (void_type_node), make_tree (build_pointer_type (void_type_node), y));
XEXP (y, 0)));
TREE_CHAIN (TREE_CHAIN (arg_list)) TREE_CHAIN (TREE_CHAIN (arg_list))
= build_tree_list (NULL_TREE, make_tree (sizetype, size)); = build_tree_list (NULL_TREE, make_tree (sizetype, size));
TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arg_list))) = NULL_TREE; TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arg_list))) = NULL_TREE;
...@@ -1767,8 +1796,7 @@ emit_block_move (x, y, size, align) ...@@ -1767,8 +1796,7 @@ emit_block_move (x, y, size, align)
retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0); retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
#else #else
emit_library_call (bcopy_libfunc, 0, emit_library_call (bcopy_libfunc, 0,
VOIDmode, 3, XEXP (y, 0), Pmode, VOIDmode, 3, y, Pmode, x, Pmode
XEXP (x, 0), Pmode,
convert_to_mode (TYPE_MODE (integer_type_node), size, convert_to_mode (TYPE_MODE (integer_type_node), size,
TREE_UNSIGNED (integer_type_node)), TREE_UNSIGNED (integer_type_node)),
TYPE_MODE (integer_type_node)); TYPE_MODE (integer_type_node));
...@@ -2444,69 +2472,97 @@ clear_storage (object, size, align) ...@@ -2444,69 +2472,97 @@ clear_storage (object, size, align)
} }
} }
/* OBJECT or SIZE may have been passed through protect_from_queue.
#ifdef TARGET_MEM_FUNCTIONS It is unsafe to save the value generated by protect_from_queue
/* It is incorrect to use the libcall calling conventions to call and reuse it later. Consider what happens if emit_queue is
memset in this context. called before the return value from protect_from_queue is used.
This could be a user call to memset and the user may wish to Expansion of the CALL_EXPR below will call emit_queue before
examine the return value from memset. we are finished emitting RTL for argument setup. So if we are
not careful we could get the wrong value for an argument.
For targets where libcalls and normal calls have different conventions To avoid this problem we go ahead and emit code to copy OBJECT
for returning pointers, we could end up generating incorrect code. 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.
So instead of using a libcall sequence we build up a suitable Note this is not strictly needed for library calls since they
CALL_EXPR and expand the call in the normal fashion. */ do not call emit_queue before loading their arguments. However,
if (fn == NULL_TREE) we may need to have library calls call emit_queue in the future
{ since failing to do so could cause problems for targets which
tree fntype; define SMALL_REGISTER_CLASSES and pass arguments in registers. */
object = copy_to_mode_reg (Pmode, XEXP (object, 0));
/* This was copied from except.c, I don't know if all this is #ifdef TARGET_MEM_FUNCTIONS
necessary in this context or not. */ size = copy_to_mode_reg (TYPE_MODE (sizetype), size);
fn = get_identifier ("memset"); #else
push_obstacks_nochange (); size = convert_to_mode (TYPE_MODE (integer_type_node), size,
end_temporary_allocation (); TREE_UNSIGNED (integer_type_node));
fntype = build_pointer_type (void_type_node); size = copy_to_reg (size);
fntype = build_function_type (fntype, NULL_TREE); #endif
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 ();
}
/* We need to make an argument list for the function call.
memset has three arguments, the first is a void * addresses, the #ifdef TARGET_MEM_FUNCTIONS
second a integer with the initialization value, the last is a size_t /* It is incorrect to use the libcall calling conventions to call
byte count for the copy. */ memset in this context.
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;
/* Now we have to build up the CALL_EXPR itself. */ This could be a user call to memset and the user may wish to
call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn); examine the return value from memset.
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); For targets where libcalls and normal calls have different
conventions for returning pointers, we could end up generating
incorrect code.
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 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 ();
}
/* We need to make an argument list for the function call.
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 #else
emit_library_call (bzero_libfunc, 0, emit_library_call (bzero_libfunc, 0,
VOIDmode, 2, VOIDmode, 2, object, Pmode, size
XEXP (object, 0), Pmode,
convert_to_mode
(TYPE_MODE (integer_type_node), size,
TREE_UNSIGNED (integer_type_node)),
TYPE_MODE (integer_type_node)); TYPE_MODE (integer_type_node));
#endif #endif
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment