aboutsummaryrefslogtreecommitdiff
path: root/gcc/gimple-fold.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/gimple-fold.cc')
-rw-r--r--gcc/gimple-fold.cc131
1 files changed, 131 insertions, 0 deletions
diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index 37ca085..2f64de2 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -5252,6 +5252,127 @@ gimple_fold_builtin_assume_aligned (gimple_stmt_iterator *gsi)
return true;
}
+/* If va_list type is a simple pointer and nothing special is needed,
+ optimize __builtin_va_start (&ap, 0) into ap = __builtin_next_arg (0),
+ __builtin_va_end (&ap) out as NOP and __builtin_va_copy into a simple
+ pointer assignment. Returns true if a change happened. */
+
+static bool
+gimple_fold_builtin_stdarg (gimple_stmt_iterator *gsi, gcall *call)
+{
+ /* These shouldn't be folded before pass_stdarg. */
+ if (!(cfun->curr_properties & PROP_last_full_fold))
+ return false;
+
+ tree callee, lhs, rhs, cfun_va_list;
+ bool va_list_simple_ptr;
+ location_t loc = gimple_location (call);
+ gimple *nstmt0, *nstmt;
+ tree tlhs, oldvdef, newvdef;
+
+ callee = gimple_call_fndecl (call);
+
+ cfun_va_list = targetm.fn_abi_va_list (callee);
+ va_list_simple_ptr = POINTER_TYPE_P (cfun_va_list)
+ && (TREE_TYPE (cfun_va_list) == void_type_node
+ || TREE_TYPE (cfun_va_list) == char_type_node);
+
+ switch (DECL_FUNCTION_CODE (callee))
+ {
+ case BUILT_IN_VA_START:
+ if (!va_list_simple_ptr
+ || targetm.expand_builtin_va_start != NULL
+ || !builtin_decl_explicit_p (BUILT_IN_NEXT_ARG))
+ return false;
+
+ if (gimple_call_num_args (call) != 2)
+ return false;
+
+ lhs = gimple_call_arg (call, 0);
+ if (!POINTER_TYPE_P (TREE_TYPE (lhs))
+ || TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (lhs)))
+ != TYPE_MAIN_VARIANT (cfun_va_list))
+ return false;
+ /* Create `tlhs = __builtin_next_arg(0);`. */
+ tlhs = make_ssa_name (cfun_va_list);
+ nstmt0 = gimple_build_call (builtin_decl_explicit (BUILT_IN_NEXT_ARG), 1, integer_zero_node);
+ lhs = fold_build2 (MEM_REF, cfun_va_list, lhs, build_zero_cst (TREE_TYPE (lhs)));
+ gimple_call_set_lhs (nstmt0, tlhs);
+ gimple_set_location (nstmt0, loc);
+ gimple_move_vops (nstmt0, call);
+ gsi_replace (gsi, nstmt0, false);
+ oldvdef = gimple_vdef (nstmt0);
+ newvdef = make_ssa_name (gimple_vop (cfun), nstmt0);
+ gimple_set_vdef (nstmt0, newvdef);
+
+ /* Create `*lhs = tlhs;`. */
+ nstmt = gimple_build_assign (lhs, tlhs);
+ gimple_set_location (nstmt, loc);
+ gimple_set_vuse (nstmt, newvdef);
+ gimple_set_vdef (nstmt, oldvdef);
+ SSA_NAME_DEF_STMT (oldvdef) = nstmt;
+ gsi_insert_after (gsi, nstmt, GSI_NEW_STMT);
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Simplified\n ");
+ print_gimple_stmt (dump_file, call, 0, dump_flags);
+ fprintf (dump_file, "into\n ");
+ print_gimple_stmt (dump_file, nstmt0, 0, dump_flags);
+ fprintf (dump_file, " ");
+ print_gimple_stmt (dump_file, nstmt, 0, dump_flags);
+ }
+ return true;
+
+ case BUILT_IN_VA_COPY:
+ if (!va_list_simple_ptr)
+ return false;
+
+ if (gimple_call_num_args (call) != 2)
+ return false;
+
+ lhs = gimple_call_arg (call, 0);
+ if (!POINTER_TYPE_P (TREE_TYPE (lhs))
+ || TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (lhs)))
+ != TYPE_MAIN_VARIANT (cfun_va_list))
+ return false;
+ rhs = gimple_call_arg (call, 1);
+ if (TYPE_MAIN_VARIANT (TREE_TYPE (rhs))
+ != TYPE_MAIN_VARIANT (cfun_va_list))
+ return false;
+
+ lhs = fold_build2 (MEM_REF, cfun_va_list, lhs, build_zero_cst (TREE_TYPE (lhs)));
+ nstmt = gimple_build_assign (lhs, rhs);
+ gimple_set_location (nstmt, loc);
+ gimple_move_vops (nstmt, call);
+ gsi_replace (gsi, nstmt, false);
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Simplified\n ");
+ print_gimple_stmt (dump_file, call, 0, dump_flags);
+ fprintf (dump_file, "into\n ");
+ print_gimple_stmt (dump_file, nstmt, 0, dump_flags);
+ }
+ return true;
+
+ case BUILT_IN_VA_END:
+ /* No effect, so the statement will be deleted. */
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Removed\n ");
+ print_gimple_stmt (dump_file, call, 0, dump_flags);
+ }
+ unlink_stmt_vdef (call);
+ release_defs (call);
+ gsi_replace (gsi, gimple_build_nop (), true);
+ return true;
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
/* Fold the non-target builtin at *GSI and return whether any simplification
was made. */
@@ -5270,6 +5391,10 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
enum built_in_function fcode = DECL_FUNCTION_CODE (callee);
switch (fcode)
{
+ case BUILT_IN_VA_START:
+ case BUILT_IN_VA_END:
+ case BUILT_IN_VA_COPY:
+ return gimple_fold_builtin_stdarg (gsi, stmt);
case BUILT_IN_BCMP:
return gimple_fold_builtin_bcmp (gsi);
case BUILT_IN_BCOPY:
@@ -5886,6 +6011,12 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
tree overflow = NULL_TREE;
switch (gimple_call_internal_fn (stmt))
{
+ case IFN_ASSUME:
+ /* Remove .ASSUME calls during the last fold since it is no
+ longer needed. */
+ if (cfun->curr_properties & PROP_last_full_fold)
+ replace_call_with_value (gsi, NULL_TREE);
+ break;
case IFN_BUILTIN_EXPECT:
result = fold_builtin_expect (gimple_location (stmt),
gimple_call_arg (stmt, 0),