* builtins.c (expand_builtin_memcmp): Do not emit the call here.
(expand_builtin_trap): Emit a regular call.
(set_builtin_user_assembler_name): Remove obsolete cases.
* dse.c (scan_insn): Adjust.
* except.c: Include calls.h.
(sjlj_emit_function_enter): If DONT_USE_BUILTIN_SETJMP is defined,
emit a regular call to setjmp.
* expr.c (emit_block_move_hints): Call emit_block_copy_via_libcall.
(block_move_libcall_safe_for_call_parm): Use memcpy builtin.
(emit_block_move_via_libcall): Delete.
(block_move_fn): Delete.
(init_block_move_fn): Likewise.
(emit_block_move_libcall_fn): Likewise.
(emit_block_op_via_libcall): New function.
(set_storage_via_libcall): Tidy up and use memset builtin.
(block_clear_fn): Delete.
(init_block_clear_fn): Likewise.
(clear_storage_libcall_fn): Likewise.
(expand_assignment): Call emit_block_move_via_libcall.
Do not include gt-expr.h.
* expr.h (emit_block_op_via_libcall): Declare.
(emit_block_copy_via_libcall): New inline function.
(emit_block_move_via_libcall): Likewise.
(emit_block_comp_via_libcall): Likewise.
(block_clear_fn): Delete.
(init_block_move_fn): Likewise.
(init_block_clear_fn): Likewise.
(emit_block_move_via_libcall): Likewise.
(set_storage_via_libcall): Add default parameter value.
* libfuncs.h (enum libfunc_index): Remove obsolete values.
(abort_libfunc): Delete.
(memcpy_libfunc): Likewise.
(memmove_libfunc): Likewise.
(memcmp_libfunc): Likewise.
(memset_libfunc): Likewise.
(setbits_libfunc): Likewise.
(setjmp_libfunc): Likewise.
(longjmp_libfunc): Likewise.
(profile_function_entry_libfunc): Likewise.
(profile_function_exit_libfunc): Likewise.
(gcov_flush_libfunc): Likewise.
* optabs-libfuncs.c (build_libfunc_function): Set DECL_ARTIFICIAL
and DECL_VISIBILITY on the declaration.
(init_optabs): Do not initialize obsolete libfuncs.
* optabs.c (prepare_cmp_insn): Call emit_block_comp_via_libcall.
* tree-core.h (ECF_RET1): Define.
(ECF_TM_PURE): Adjust.
(ECF_TM_BUILTIN): Likewise.
* tree.c (set_call_expr_flags): Deal with ECF_RET1.
(build_common_builtin_nodes): Initialize abort builtin.
Add ECF_RET1 on memcpy, memmove and memset builtins.
Pass final flags for alloca and alloca_with_align builtins.
* config/alpha/alpha.c (alpha_init_libfuncs): Do not initialize
obsolete builtins.
* config/ia64/ia64.c (ia64_vms_init_libfuncs): Likewise.
* config/i386/i386.c (ix86_expand_set_or_movmem): Adjust call to
set_storage_via_libcall and call emit_block_copy_via_libcall.
From-SVN: r236195
+2016-05-13 Eric Botcazou <ebotcazou@adacore.com>
+
+ * builtins.c (expand_builtin_memcmp): Do not emit the call here.
+ (expand_builtin_trap): Emit a regular call.
+ (set_builtin_user_assembler_name): Remove obsolete cases.
+ * dse.c (scan_insn): Adjust.
+ * except.c: Include calls.h.
+ (sjlj_emit_function_enter): If DONT_USE_BUILTIN_SETJMP is defined,
+ emit a regular call to setjmp.
+ * expr.c (emit_block_move_hints): Call emit_block_copy_via_libcall.
+ (block_move_libcall_safe_for_call_parm): Use memcpy builtin.
+ (emit_block_move_via_libcall): Delete.
+ (block_move_fn): Delete.
+ (init_block_move_fn): Likewise.
+ (emit_block_move_libcall_fn): Likewise.
+ (emit_block_op_via_libcall): New function.
+ (set_storage_via_libcall): Tidy up and use memset builtin.
+ (block_clear_fn): Delete.
+ (init_block_clear_fn): Likewise.
+ (clear_storage_libcall_fn): Likewise.
+ (expand_assignment): Call emit_block_move_via_libcall.
+ Do not include gt-expr.h.
+ * expr.h (emit_block_op_via_libcall): Declare.
+ (emit_block_copy_via_libcall): New inline function.
+ (emit_block_move_via_libcall): Likewise.
+ (emit_block_comp_via_libcall): Likewise.
+ (block_clear_fn): Delete.
+ (init_block_move_fn): Likewise.
+ (init_block_clear_fn): Likewise.
+ (emit_block_move_via_libcall): Likewise.
+ (set_storage_via_libcall): Add default parameter value.
+ * libfuncs.h (enum libfunc_index): Remove obsolete values.
+ (abort_libfunc): Delete.
+ (memcpy_libfunc): Likewise.
+ (memmove_libfunc): Likewise.
+ (memcmp_libfunc): Likewise.
+ (memset_libfunc): Likewise.
+ (setbits_libfunc): Likewise.
+ (setjmp_libfunc): Likewise.
+ (longjmp_libfunc): Likewise.
+ (profile_function_entry_libfunc): Likewise.
+ (profile_function_exit_libfunc): Likewise.
+ (gcov_flush_libfunc): Likewise.
+ * optabs-libfuncs.c (build_libfunc_function): Set DECL_ARTIFICIAL
+ and DECL_VISIBILITY on the declaration.
+ (init_optabs): Do not initialize obsolete libfuncs.
+ * optabs.c (prepare_cmp_insn): Call emit_block_comp_via_libcall.
+ * tree-core.h (ECF_RET1): Define.
+ (ECF_TM_PURE): Adjust.
+ (ECF_TM_BUILTIN): Likewise.
+ * tree.c (set_call_expr_flags): Deal with ECF_RET1.
+ (build_common_builtin_nodes): Initialize abort builtin.
+ Add ECF_RET1 on memcpy, memmove and memset builtins.
+ Pass final flags for alloca and alloca_with_align builtins.
+ * config/alpha/alpha.c (alpha_init_libfuncs): Do not initialize
+ obsolete builtins.
+ * config/ia64/ia64.c (ia64_vms_init_libfuncs): Likewise.
+ * config/i386/i386.c (ix86_expand_set_or_movmem): Adjust call to
+ set_storage_via_libcall and call emit_block_copy_via_libcall.
+
2016-05-12 Uros Bizjak <ubizjak@gmail.com>
* config/i386/i386.md (*call_got_x32): Change operand 0 to
return convert_to_mode (mode, result, 0);
}
- result = target;
- if (! (result != 0
- && REG_P (result) && GET_MODE (result) == mode
- && REGNO (result) >= FIRST_PSEUDO_REGISTER))
- result = gen_reg_rtx (mode);
-
- emit_library_call_value (memcmp_libfunc, result, LCT_PURE,
- TYPE_MODE (integer_type_node), 3,
- XEXP (arg1_rtx, 0), Pmode,
- XEXP (arg2_rtx, 0), Pmode,
- convert_to_mode (TYPE_MODE (sizetype), arg3_rtx,
- TYPE_UNSIGNED (sizetype)),
- TYPE_MODE (sizetype));
- return result;
+ return NULL_RTX;
}
/* Expand expression EXP, which is a call to the strcmp builtin. Return NULL_RTX
add_reg_note (insn, REG_ARGS_SIZE, GEN_INT (stack_pointer_delta));
}
else
- emit_library_call (abort_libfunc, LCT_NORETURN, VOIDmode, 0);
+ {
+ tree fn = builtin_decl_implicit (BUILT_IN_ABORT);
+ tree call_expr = build_call_expr (fn, 0);
+ expand_call (call_expr, NULL_RTX, false);
+ }
+
emit_barrier ();
}
void
set_builtin_user_assembler_name (tree decl, const char *asmspec)
{
- tree builtin;
gcc_assert (TREE_CODE (decl) == FUNCTION_DECL
&& DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
&& asmspec != 0);
- builtin = builtin_decl_explicit (DECL_FUNCTION_CODE (decl));
+ tree builtin = builtin_decl_explicit (DECL_FUNCTION_CODE (decl));
set_user_assembler_name (builtin, asmspec);
- switch (DECL_FUNCTION_CODE (decl))
+
+ if (DECL_FUNCTION_CODE (decl) == BUILT_IN_FFS
+ && INT_TYPE_SIZE < BITS_PER_WORD)
{
- case BUILT_IN_MEMCPY:
- init_block_move_fn (asmspec);
- memcpy_libfunc = set_user_assembler_libfunc ("memcpy", asmspec);
- break;
- case BUILT_IN_MEMSET:
- init_block_clear_fn (asmspec);
- memset_libfunc = set_user_assembler_libfunc ("memset", asmspec);
- break;
- case BUILT_IN_MEMMOVE:
- memmove_libfunc = set_user_assembler_libfunc ("memmove", asmspec);
- break;
- case BUILT_IN_MEMCMP:
- memcmp_libfunc = set_user_assembler_libfunc ("memcmp", asmspec);
- break;
- case BUILT_IN_ABORT:
- abort_libfunc = set_user_assembler_libfunc ("abort", asmspec);
- break;
- case BUILT_IN_FFS:
- if (INT_TYPE_SIZE < BITS_PER_WORD)
- {
- set_user_assembler_libfunc ("ffs", asmspec);
- set_optab_libfunc (ffs_optab, mode_for_size (INT_TYPE_SIZE,
- MODE_INT, 0), "ffs");
- }
- break;
- default:
- break;
+ set_user_assembler_libfunc ("ffs", asmspec);
+ set_optab_libfunc (ffs_optab, mode_for_size (INT_TYPE_SIZE, MODE_INT, 0),
+ "ffs");
}
}
set_optab_libfunc (smod_optab, DImode, "OTS$REM_L");
set_optab_libfunc (umod_optab, SImode, "OTS$REM_UI");
set_optab_libfunc (umod_optab, DImode, "OTS$REM_UL");
- abort_libfunc = init_one_libfunc ("decc$abort");
- memcmp_libfunc = init_one_libfunc ("decc$memcmp");
#ifdef MEM_LIBFUNCS_INIT
MEM_LIBFUNCS_INIT;
#endif
{
if (UINTVAL (count_exp) >= (unsigned HOST_WIDE_INT)dynamic_check)
{
- emit_block_move_via_libcall (dst, src, count_exp, false);
+ emit_block_copy_via_libcall (dst, src, count_exp);
count_exp = const0_rtx;
goto epilogue;
}
1, hot_label);
predict_jump (REG_BR_PROB_BASE * 90 / 100);
if (issetmem)
- set_storage_via_libcall (dst, count_exp, val_exp, false);
+ set_storage_via_libcall (dst, count_exp, val_exp);
else
- emit_block_move_via_libcall (dst, src, count_exp, false);
+ emit_block_copy_via_libcall (dst, src, count_exp);
emit_jump (jump_around_label);
emit_label (hot_label);
}
set_optab_libfunc (smod_optab, DImode, "OTS$REM_L");
set_optab_libfunc (umod_optab, SImode, "OTS$REM_UI");
set_optab_libfunc (umod_optab, DImode, "OTS$REM_UL");
- abort_libfunc = init_one_libfunc ("decc$abort");
- memcmp_libfunc = init_one_libfunc ("decc$memcmp");
#ifdef MEM_LIBFUNCS_INIT
MEM_LIBFUNCS_INIT;
#endif
if (CALL_P (insn))
{
bool const_call;
+ rtx call, sym;
tree memset_call = NULL_TREE;
insn_info->cannot_delete = true;
been pushed onto the stack.
memset and bzero don't read memory either. */
const_call = RTL_CONST_CALL_P (insn);
- if (!const_call)
- {
- rtx call = get_call_rtx_from (insn);
- if (call && GET_CODE (XEXP (XEXP (call, 0), 0)) == SYMBOL_REF)
- {
- rtx symbol = XEXP (XEXP (call, 0), 0);
- if (SYMBOL_REF_DECL (symbol)
- && TREE_CODE (SYMBOL_REF_DECL (symbol)) == FUNCTION_DECL)
- {
- if ((DECL_BUILT_IN_CLASS (SYMBOL_REF_DECL (symbol))
- == BUILT_IN_NORMAL
- && (DECL_FUNCTION_CODE (SYMBOL_REF_DECL (symbol))
- == BUILT_IN_MEMSET))
- || SYMBOL_REF_DECL (symbol) == block_clear_fn)
- memset_call = SYMBOL_REF_DECL (symbol);
- }
- }
- }
+ if (!const_call
+ && (call = get_call_rtx_from (insn))
+ && (sym = XEXP (XEXP (call, 0), 0))
+ && GET_CODE (sym) == SYMBOL_REF
+ && SYMBOL_REF_DECL (sym)
+ && TREE_CODE (SYMBOL_REF_DECL (sym)) == FUNCTION_DECL
+ && DECL_BUILT_IN_CLASS (SYMBOL_REF_DECL (sym)) == BUILT_IN_NORMAL
+ && DECL_FUNCTION_CODE (SYMBOL_REF_DECL (sym)) == BUILT_IN_MEMSET)
+ memset_call = SYMBOL_REF_DECL (sym);
+
if (const_call || memset_call)
{
insn_info_t i_ptr = active_local_stores;
#include "explow.h"
#include "stmt.h"
#include "expr.h"
+#include "calls.h"
#include "libfuncs.h"
#include "except.h"
#include "output.h"
if (dispatch_label)
{
+ rtx addr = plus_constant (Pmode, XEXP (fc, 0), sjlj_fc_jbuf_ofs);
+
#ifdef DONT_USE_BUILTIN_SETJMP
- rtx x;
- x = emit_library_call_value (setjmp_libfunc, NULL_RTX, LCT_RETURNS_TWICE,
- TYPE_MODE (integer_type_node), 1,
- plus_constant (Pmode, XEXP (fc, 0),
- sjlj_fc_jbuf_ofs), Pmode);
+ addr = copy_addr_to_reg (addr);
+ addr = convert_memory_address (ptr_mode, addr);
+ tree addr_tree = make_tree (ptr_type_node, addr);
+
+ tree fn = builtin_decl_implicit (BUILT_IN_SETJMP);
+ tree call_expr = build_call_expr (fn, 1, addr_tree);
+ rtx x = expand_call (call_expr, NULL_RTX, false);
emit_cmp_and_jump_insns (x, const0_rtx, NE, 0,
TYPE_MODE (integer_type_node), 0,
dispatch_label, REG_BR_PROB_BASE / 100);
#else
- expand_builtin_setjmp_setup (plus_constant (Pmode, XEXP (fc, 0),
- sjlj_fc_jbuf_ofs),
- dispatch_label);
+ expand_builtin_setjmp_setup (addr, dispatch_label);
#endif
}
static bool emit_block_move_via_movmem (rtx, rtx, rtx, unsigned, unsigned, HOST_WIDE_INT,
unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT);
-static tree emit_block_move_libcall_fn (int);
static void emit_block_move_via_loop (rtx, rtx, rtx, unsigned);
static rtx clear_by_pieces_1 (void *, HOST_WIDE_INT, machine_mode);
static void clear_by_pieces (rtx, unsigned HOST_WIDE_INT, unsigned int);
static void store_by_pieces_1 (struct store_by_pieces_d *, unsigned int);
static void store_by_pieces_2 (insn_gen_fn, machine_mode,
struct store_by_pieces_d *);
-static tree clear_storage_libcall_fn (int);
static rtx_insn *compress_float_constant (rtx, rtx);
static rtx get_subtarget (rtx);
static void store_constructor_field (rtx, unsigned HOST_WIDE_INT,
mark_addressable (y_expr);
if (x_expr)
mark_addressable (x_expr);
- retval = emit_block_move_via_libcall (x, y, size,
+ retval = emit_block_copy_via_libcall (x, y, size,
method == BLOCK_OP_TAILCALL);
}
/* If registers go on the stack anyway, any argument is sure to clobber
an outgoing argument. */
#if defined (REG_PARM_STACK_SPACE)
- fn = emit_block_move_libcall_fn (false);
+ fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
/* Avoid set but not used warning if *REG_PARM_STACK_SPACE doesn't
depend on its argument. */
(void) fn;
cumulative_args_t args_so_far;
tree fn, arg;
- fn = emit_block_move_libcall_fn (false);
+ fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
INIT_CUMULATIVE_ARGS (args_so_far_v, TREE_TYPE (fn), NULL_RTX, 0, 3);
args_so_far = pack_cumulative_args (&args_so_far_v);
return false;
}
-/* A subroutine of emit_block_move. Expand a call to memcpy.
- Return the return value from memcpy, 0 otherwise. */
-
-rtx
-emit_block_move_via_libcall (rtx dst, rtx src, rtx size, bool tailcall)
-{
- rtx dst_addr, src_addr;
- tree call_expr, fn, src_tree, dst_tree, size_tree;
- machine_mode size_mode;
- rtx retval;
-
- /* Emit code to copy the addresses of DST and SRC and SIZE into new
- pseudos. We can then place those new pseudos into a VAR_DECL and
- use them later. */
-
- dst_addr = copy_addr_to_reg (XEXP (dst, 0));
- src_addr = copy_addr_to_reg (XEXP (src, 0));
-
- dst_addr = convert_memory_address (ptr_mode, dst_addr);
- src_addr = convert_memory_address (ptr_mode, src_addr);
-
- dst_tree = make_tree (ptr_type_node, dst_addr);
- src_tree = make_tree (ptr_type_node, src_addr);
-
- size_mode = TYPE_MODE (sizetype);
-
- size = convert_to_mode (size_mode, size, 1);
- size = copy_to_mode_reg (size_mode, size);
-
- /* It is incorrect to use the libcall calling conventions to call
- memcpy in this context. This could be a user call to memcpy and
- the user may wish to examine the return value from memcpy. For
- targets where libcalls and normal calls have different conventions
- for returning pointers, we could end up generating incorrect code. */
-
- size_tree = make_tree (sizetype, size);
-
- fn = emit_block_move_libcall_fn (true);
- call_expr = build_call_expr (fn, 3, dst_tree, src_tree, size_tree);
- CALL_EXPR_TAILCALL (call_expr) = tailcall;
-
- retval = expand_normal (call_expr);
-
- return retval;
-}
-
-/* A subroutine of emit_block_move_via_libcall. Create the tree node
- for the function we use for block copies. */
-
-static GTY(()) tree block_move_fn;
-
-void
-init_block_move_fn (const char *asmspec)
-{
- if (!block_move_fn)
- {
- tree args, fn, attrs, attr_args;
-
- fn = get_identifier ("memcpy");
- args = build_function_type_list (ptr_type_node, ptr_type_node,
- const_ptr_type_node, sizetype,
- NULL_TREE);
-
- fn = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, fn, args);
- DECL_EXTERNAL (fn) = 1;
- TREE_PUBLIC (fn) = 1;
- DECL_ARTIFICIAL (fn) = 1;
- TREE_NOTHROW (fn) = 1;
- DECL_VISIBILITY (fn) = VISIBILITY_DEFAULT;
- DECL_VISIBILITY_SPECIFIED (fn) = 1;
-
- attr_args = build_tree_list (NULL_TREE, build_string (1, "1"));
- attrs = tree_cons (get_identifier ("fn spec"), attr_args, NULL);
-
- decl_attributes (&fn, attrs, ATTR_FLAG_BUILT_IN);
-
- block_move_fn = fn;
- }
-
- if (asmspec)
- set_user_assembler_name (block_move_fn, asmspec);
-}
-
-static tree
-emit_block_move_libcall_fn (int for_call)
-{
- static bool emitted_extern;
-
- if (!block_move_fn)
- init_block_move_fn (NULL);
-
- if (for_call && !emitted_extern)
- {
- emitted_extern = true;
- make_decl_rtl (block_move_fn);
- }
-
- return block_move_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. */
true, top_label, REG_BR_PROB_BASE * 90 / 100);
}
\f
+/* Expand a call to memcpy or memmove or memcmp, and return the result.
+ TAILCALL is true if this is a tail call. */
+
+rtx
+emit_block_op_via_libcall (enum built_in_function fncode, rtx dst, rtx src,
+ rtx size, bool tailcall)
+{
+ rtx dst_addr, src_addr;
+ tree call_expr, dst_tree, src_tree, size_tree;
+ machine_mode size_mode;
+
+ dst_addr = copy_addr_to_reg (XEXP (dst, 0));
+ dst_addr = convert_memory_address (ptr_mode, dst_addr);
+ dst_tree = make_tree (ptr_type_node, dst_addr);
+
+ src_addr = copy_addr_to_reg (XEXP (src, 0));
+ src_addr = convert_memory_address (ptr_mode, src_addr);
+ src_tree = make_tree (ptr_type_node, src_addr);
+
+ size_mode = TYPE_MODE (sizetype);
+ size = convert_to_mode (size_mode, size, 1);
+ size = copy_to_mode_reg (size_mode, size);
+ size_tree = make_tree (sizetype, size);
+
+ /* It is incorrect to use the libcall calling conventions for calls to
+ memcpy/memmove/memcmp because they can be provided by the user. */
+ tree fn = builtin_decl_implicit (fncode);
+ call_expr = build_call_expr (fn, 3, dst_tree, src_tree, size_tree);
+ CALL_EXPR_TAILCALL (call_expr) = tailcall;
+
+ return expand_call (call_expr, NULL_RTX, false);
+}
+\f
/* Copy all or part of a value X into registers starting at REGNO.
The number of registers to be filled is NREGS. */
{
tree call_expr, fn, object_tree, size_tree, val_tree;
machine_mode size_mode;
- rtx retval;
-
- /* Emit code to copy OBJECT and SIZE into new pseudos. We can then
- place those into new pseudos into a VAR_DECL and use them later. */
object = copy_addr_to_reg (XEXP (object, 0));
+ object_tree = make_tree (ptr_type_node, object);
+
+ if (!CONST_INT_P (val))
+ val = convert_to_mode (TYPE_MODE (integer_type_node), val, 1);
+ val_tree = make_tree (integer_type_node, val);
size_mode = TYPE_MODE (sizetype);
size = convert_to_mode (size_mode, size, 1);
size = copy_to_mode_reg (size_mode, size);
-
- /* It is incorrect to use the libcall calling conventions to call
- memset in this context. 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. */
-
- object_tree = make_tree (ptr_type_node, object);
- if (!CONST_INT_P (val))
- val = convert_to_mode (TYPE_MODE (integer_type_node), val, 1);
size_tree = make_tree (sizetype, size);
- val_tree = make_tree (integer_type_node, val);
- fn = clear_storage_libcall_fn (true);
+ /* It is incorrect to use the libcall calling conventions for calls to
+ memset because it can be provided by the user. */
+ fn = builtin_decl_implicit (BUILT_IN_MEMSET);
call_expr = build_call_expr (fn, 3, object_tree, val_tree, size_tree);
CALL_EXPR_TAILCALL (call_expr) = tailcall;
- retval = expand_normal (call_expr);
-
- return retval;
-}
-
-/* A subroutine of set_storage_via_libcall. Create the tree node
- for the function we use for block clears. */
-
-tree block_clear_fn;
-
-void
-init_block_clear_fn (const char *asmspec)
-{
- if (!block_clear_fn)
- {
- tree fn, args;
-
- fn = get_identifier ("memset");
- args = build_function_type_list (ptr_type_node, ptr_type_node,
- integer_type_node, sizetype,
- NULL_TREE);
-
- fn = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL, fn, args);
- DECL_EXTERNAL (fn) = 1;
- TREE_PUBLIC (fn) = 1;
- DECL_ARTIFICIAL (fn) = 1;
- TREE_NOTHROW (fn) = 1;
- DECL_VISIBILITY (fn) = VISIBILITY_DEFAULT;
- DECL_VISIBILITY_SPECIFIED (fn) = 1;
-
- block_clear_fn = fn;
- }
-
- if (asmspec)
- set_user_assembler_name (block_clear_fn, asmspec);
-}
-
-static tree
-clear_storage_libcall_fn (int for_call)
-{
- static bool emitted_extern;
-
- if (!block_clear_fn)
- init_block_clear_fn (NULL);
-
- if (for_call && !emitted_extern)
- {
- emitted_extern = true;
- make_decl_rtl (block_clear_fn);
- }
-
- return block_clear_fn;
+ return expand_call (call_expr, NULL_RTX, false);
}
\f
/* Expand a setmem pattern; return true if successful. */
size = expr_size (from);
from_rtx = expand_normal (from);
- emit_library_call (memmove_libfunc, LCT_NORMAL,
- VOIDmode, 3, XEXP (to_rtx, 0), Pmode,
- XEXP (from_rtx, 0), Pmode,
- convert_to_mode (TYPE_MODE (sizetype),
- size, TYPE_UNSIGNED (sizetype)),
- TYPE_MODE (sizetype));
+ emit_block_move_via_libcall (XEXP (to_rtx, 0), XEXP (from_rtx, 0), size);
preserve_temp_slots (to_rtx);
pop_temp_slots ();
return tree_to_shwi (size);
}
-
-#include "gt-expr.h"
/* Convert an rtx to MODE from OLDMODE and return the result. */
extern rtx convert_modes (machine_mode, machine_mode, rtx, int);
-/* Emit code to move a block Y to a block X. */
+/* Expand a call to memcpy or memmove or memcmp, and return the result. */
+extern rtx emit_block_op_via_libcall (enum built_in_function, rtx, rtx, rtx,
+ bool);
+
+static inline rtx
+emit_block_copy_via_libcall (rtx dst, rtx src, rtx size, bool tailcall = false)
+{
+ return emit_block_op_via_libcall (BUILT_IN_MEMCPY, dst, src, size, tailcall);
+}
+static inline rtx
+emit_block_move_via_libcall (rtx dst, rtx src, rtx size, bool tailcall = false)
+{
+ return emit_block_op_via_libcall (BUILT_IN_MEMMOVE, dst, src, size, tailcall);
+}
+
+static inline rtx
+emit_block_comp_via_libcall (rtx dst, rtx src, rtx size, bool tailcall = false)
+{
+ return emit_block_op_via_libcall (BUILT_IN_MEMCMP, dst, src, size, tailcall);
+}
+
+/* Emit code to move a block Y to a block X. */
enum block_op_methods
{
BLOCK_OP_NORMAL,
BLOCK_OP_TAILCALL
};
-extern GTY(()) tree block_clear_fn;
-extern void init_block_move_fn (const char *);
-extern void init_block_clear_fn (const char *);
-
extern rtx emit_block_move (rtx, rtx, rtx, enum block_op_methods);
-extern rtx emit_block_move_via_libcall (rtx, rtx, rtx, bool);
extern rtx emit_block_move_hints (rtx, rtx, rtx, enum block_op_methods,
unsigned int, HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT);
/* The same, but always output an library call. */
-rtx set_storage_via_libcall (rtx, rtx, rtx, bool);
+extern rtx set_storage_via_libcall (rtx, rtx, rtx, bool = false);
/* Expand a setmem pattern; return true if successful. */
extern bool set_storage_via_setmem (rtx, rtx, rtx, unsigned int,
/* Enumeration of indexes into libfunc_table. */
enum libfunc_index
{
- LTI_abort,
- LTI_memcpy,
- LTI_memmove,
- LTI_memcmp,
- LTI_memset,
- LTI_setbits,
-
- LTI_setjmp,
- LTI_longjmp,
LTI_unwind_sjlj_register,
LTI_unwind_sjlj_unregister,
-
- LTI_profile_function_entry,
- LTI_profile_function_exit,
-
LTI_synchronize,
-
- LTI_gcov_flush,
-
LTI_MAX
};
/* Accessor macros for libfunc_table. */
-#define abort_libfunc (libfunc_table[LTI_abort])
-#define memcpy_libfunc (libfunc_table[LTI_memcpy])
-#define memmove_libfunc (libfunc_table[LTI_memmove])
-#define memcmp_libfunc (libfunc_table[LTI_memcmp])
-#define memset_libfunc (libfunc_table[LTI_memset])
-#define setbits_libfunc (libfunc_table[LTI_setbits])
-
-#define setjmp_libfunc (libfunc_table[LTI_setjmp])
-#define longjmp_libfunc (libfunc_table[LTI_longjmp])
#define unwind_sjlj_register_libfunc (libfunc_table[LTI_unwind_sjlj_register])
#define unwind_sjlj_unregister_libfunc \
(libfunc_table[LTI_unwind_sjlj_unregister])
-
-#define profile_function_entry_libfunc (libfunc_table[LTI_profile_function_entry])
-#define profile_function_exit_libfunc (libfunc_table[LTI_profile_function_exit])
-
#define synchronize_libfunc (libfunc_table[LTI_synchronize])
-#define gcov_flush_libfunc (libfunc_table[LTI_gcov_flush])
-
/* In explow.c */
extern void set_stack_check_libfunc (const char *);
tree
build_libfunc_function (const char *name)
{
+ /* ??? We don't have any type information; pretend this is "int foo ()". */
tree decl = build_decl (UNKNOWN_LOCATION, FUNCTION_DECL,
get_identifier (name),
build_function_type (integer_type_node, NULL_TREE));
- /* ??? We don't have any type information except for this is
- a function. Pretend this is "int foo ()". */
- DECL_ARTIFICIAL (decl) = 1;
DECL_EXTERNAL (decl) = 1;
TREE_PUBLIC (decl) = 1;
+ DECL_ARTIFICIAL (decl) = 1;
+ DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT;
+ DECL_VISIBILITY_SPECIFIED (decl) = 1;
gcc_assert (DECL_ASSEMBLER_NAME (decl));
/* Zap the nonsensical SYMBOL_REF_DECL for this. What we're left with
set_optab_libfunc (abs_optab, TYPE_MODE (complex_double_type_node),
"cabs");
- abort_libfunc = init_one_libfunc ("abort");
- memcpy_libfunc = init_one_libfunc ("memcpy");
- memmove_libfunc = init_one_libfunc ("memmove");
- memcmp_libfunc = init_one_libfunc ("memcmp");
- memset_libfunc = init_one_libfunc ("memset");
- setbits_libfunc = init_one_libfunc ("__setbits");
-
-#ifndef DONT_USE_BUILTIN_SETJMP
- setjmp_libfunc = init_one_libfunc ("__builtin_setjmp");
- longjmp_libfunc = init_one_libfunc ("__builtin_longjmp");
-#else
- setjmp_libfunc = init_one_libfunc ("setjmp");
- longjmp_libfunc = init_one_libfunc ("longjmp");
-#endif
unwind_sjlj_register_libfunc = init_one_libfunc ("_Unwind_SjLj_Register");
unwind_sjlj_unregister_libfunc
= init_one_libfunc ("_Unwind_SjLj_Unregister");
- /* For function entry/exit instrumentation. */
- profile_function_entry_libfunc
- = init_one_libfunc ("__cyg_profile_func_enter");
- profile_function_exit_libfunc
- = init_one_libfunc ("__cyg_profile_func_exit");
-
- gcov_flush_libfunc = init_one_libfunc ("__gcov_flush");
-
/* Allow the target to add more libcalls or rename some, etc. */
targetm.init_libfuncs ();
}
{
machine_mode result_mode;
enum insn_code cmp_code;
- tree length_type;
- rtx libfunc;
rtx result;
rtx opalign
= GEN_INT (MIN (MEM_ALIGN (x), MEM_ALIGN (y)) / BITS_PER_UNIT);
if (methods != OPTAB_LIB && methods != OPTAB_LIB_WIDEN)
goto fail;
- /* Otherwise call a library function, memcmp. */
- libfunc = memcmp_libfunc;
- length_type = sizetype;
- result_mode = TYPE_MODE (integer_type_node);
- cmp_mode = TYPE_MODE (length_type);
- size = convert_to_mode (TYPE_MODE (length_type), size,
- TYPE_UNSIGNED (length_type));
-
- result = emit_library_call_value (libfunc, 0, LCT_PURE,
- result_mode, 3,
- XEXP (x, 0), Pmode,
- XEXP (y, 0), Pmode,
- size, cmp_mode);
+ /* Otherwise call a library function. */
+ result = emit_block_comp_via_libcall (XEXP (x, 0), XEXP (y, 0), size);
+
x = result;
y = const0_rtx;
- mode = result_mode;
+ mode = TYPE_MODE (integer_type_node);
methods = OPTAB_LIB_WIDEN;
unsignedp = false;
}
/* The function does not lead to calls within current function unit. */
#define ECF_LEAF (1 << 10)
+/* Nonzero if this call returns its first argument. */
+#define ECF_RET1 (1 << 11)
+
/* Nonzero if this call does not affect transactions. */
-#define ECF_TM_PURE (1 << 11)
+#define ECF_TM_PURE (1 << 12)
/* Nonzero if this call is into the transaction runtime library. */
-#define ECF_TM_BUILTIN (1 << 12)
+#define ECF_TM_BUILTIN (1 << 13)
/* Call argument flags. */
/* Nonzero if the argument is not dereferenced recursively, thus only
if (flags & ECF_LEAF)
DECL_ATTRIBUTES (decl) = tree_cons (get_identifier ("leaf"),
NULL, DECL_ATTRIBUTES (decl));
+ if (flags & ECF_RET1)
+ DECL_ATTRIBUTES (decl)
+ = tree_cons (get_identifier ("fn spec"),
+ build_tree_list (NULL_TREE, build_string (1, "1")),
+ DECL_ATTRIBUTES (decl));
if ((flags & ECF_TM_PURE) && flag_tm)
apply_tm_attr (decl, get_identifier ("transaction_pure"));
/* Looping const or pure is implied by noreturn.
tree tmp, ftype;
int ecf_flags;
- if (!builtin_decl_explicit_p (BUILT_IN_UNREACHABLE))
+ if (!builtin_decl_explicit_p (BUILT_IN_UNREACHABLE)
+ || !builtin_decl_explicit_p (BUILT_IN_ABORT))
{
ftype = build_function_type (void_type_node, void_list_node);
- local_define_builtin ("__builtin_unreachable", ftype, BUILT_IN_UNREACHABLE,
- "__builtin_unreachable",
- ECF_NOTHROW | ECF_LEAF | ECF_NORETURN
- | ECF_CONST);
+ if (!builtin_decl_explicit_p (BUILT_IN_UNREACHABLE))
+ local_define_builtin ("__builtin_unreachable", ftype,
+ BUILT_IN_UNREACHABLE,
+ "__builtin_unreachable",
+ ECF_NOTHROW | ECF_LEAF | ECF_NORETURN
+ | ECF_CONST);
+ if (!builtin_decl_explicit_p (BUILT_IN_ABORT))
+ local_define_builtin ("__builtin_abort", ftype, BUILT_IN_ABORT,
+ "abort",
+ ECF_LEAF | ECF_NORETURN | ECF_CONST);
}
if (!builtin_decl_explicit_p (BUILT_IN_MEMCPY)
if (!builtin_decl_explicit_p (BUILT_IN_MEMCPY))
local_define_builtin ("__builtin_memcpy", ftype, BUILT_IN_MEMCPY,
- "memcpy", ECF_NOTHROW | ECF_LEAF);
+ "memcpy", ECF_NOTHROW | ECF_LEAF | ECF_RET1);
if (!builtin_decl_explicit_p (BUILT_IN_MEMMOVE))
local_define_builtin ("__builtin_memmove", ftype, BUILT_IN_MEMMOVE,
- "memmove", ECF_NOTHROW | ECF_LEAF);
+ "memmove", ECF_NOTHROW | ECF_LEAF | ECF_RET1);
}
if (!builtin_decl_explicit_p (BUILT_IN_MEMCMP))
ptr_type_node, integer_type_node,
size_type_node, NULL_TREE);
local_define_builtin ("__builtin_memset", ftype, BUILT_IN_MEMSET,
- "memset", ECF_NOTHROW | ECF_LEAF);
+ "memset", ECF_NOTHROW | ECF_LEAF | ECF_RET1);
}
+ /* If we're checking the stack, `alloca' can throw. */
+ const int alloca_flags
+ = ECF_MALLOC | ECF_LEAF | (flag_stack_check ? 0 : ECF_NOTHROW);
+
if (!builtin_decl_explicit_p (BUILT_IN_ALLOCA))
{
ftype = build_function_type_list (ptr_type_node,
size_type_node, NULL_TREE);
local_define_builtin ("__builtin_alloca", ftype, BUILT_IN_ALLOCA,
- "alloca", ECF_MALLOC | ECF_NOTHROW | ECF_LEAF);
+ "alloca", alloca_flags);
}
ftype = build_function_type_list (ptr_type_node, size_type_node,
local_define_builtin ("__builtin_alloca_with_align", ftype,
BUILT_IN_ALLOCA_WITH_ALIGN,
"__builtin_alloca_with_align",
- ECF_MALLOC | ECF_NOTHROW | ECF_LEAF);
-
- /* If we're checking the stack, `alloca' can throw. */
- if (flag_stack_check)
- {
- TREE_NOTHROW (builtin_decl_explicit (BUILT_IN_ALLOCA)) = 0;
- TREE_NOTHROW (builtin_decl_explicit (BUILT_IN_ALLOCA_WITH_ALIGN)) = 0;
- }
+ alloca_flags);
ftype = build_function_type_list (void_type_node,
ptr_type_node, ptr_type_node,