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:
Jeff Law 1999-06-28 18:42:41 -06:00
parent 5dd2921918
commit 4bc973aedd

View File

@ -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
}