--- /dev/null
+/* PR tree-optimization/119493 */
+/* { dg-do run } */
+/* { dg-options "-O2 -fdump-tree-tailr1" } */
+/* { dg-final { scan-tree-dump-times " = foo \\\(\[^\n\r;]*\\\);" 4 "tailr1" } } */
+/* { dg-final { scan-tree-dump-times " = bar \\\(\[^\n\r;]*\\\);" 4 "tailr1" } } */
+/* { dg-final { scan-tree-dump-not " = foo \\\(\[^\n\r;]*\\\); \\\[must tail call\\\]" "tailr1" } } */
+/* { dg-final { scan-tree-dump-not " = bar \\\(\[^\n\r;]*\\\); \\\[must tail call\\\]" "tailr1" } } */
+
+struct S { unsigned s; };
+struct T { struct S t[2]; };
+
+[[gnu::noinline, gnu::noclone]] struct S
+foo (struct S m)
+{
+ if (m.s == 0 || m.s == 42)
+ return m;
+ [[gnu::musttail]] return foo ((struct S) { m.s - 1 });
+}
+
+[[gnu::noinline, gnu::noclone]] struct S
+bar (struct T m, struct S n, int o, int p, int q)
+{
+ struct T r;
+ if (m.t[1].s != o || n.s != o)
+ __builtin_abort ();
+ if (o == 0 || o == 42)
+ return n;
+ r = m;
+ m.t[1].s -= p;
+ r.t[1].s -= q;
+ [[gnu::musttail]] return bar (r, m.t[1], o - 1, p, q);
+}
+
+int
+main ()
+{
+ if (foo ((struct S) { 0 }).s != 0)
+ __builtin_abort ();
+ if (foo ((struct S) { 4 }).s != 0)
+ __builtin_abort ();
+ if (foo ((struct S) { 42 }).s != 42)
+ __builtin_abort ();
+ if (foo ((struct S) { 51 }).s != 42)
+ __builtin_abort ();
+ if (bar ((struct T) { { { 0 }, { 0 } } }, (struct S) { 0 }, 0, 1, 1).s != 0)
+ __builtin_abort ();
+ if (bar ((struct T) { { { 7 }, { 7 } } }, (struct S) { 7 }, 7, 1, 1).s != 0)
+ __builtin_abort ();
+ if (bar ((struct T) { { { 42 }, { 42 } } },
+ (struct S) { 42 }, 42, 1, 1).s != 42)
+ __builtin_abort ();
+ if (bar ((struct T) { { { 48 }, { 48 } } },
+ (struct S) { 48 }, 48, 1, 1).s != 42)
+ __builtin_abort ();
+}
have a copyable type and the two arguments must have reasonably
equivalent types. The latter requirement could be relaxed if
we emitted a suitable type conversion statement. */
- if (!is_gimple_reg_type (TREE_TYPE (param))
+ if (TREE_ADDRESSABLE (TREE_TYPE (param))
|| !useless_type_conversion_p (TREE_TYPE (param),
TREE_TYPE (arg)))
break;
- /* The parameter should be a real operand, so that phi node
- created for it at the start of the function has the meaning
- of copying the value. This test implies is_gimple_reg_type
- from the previous condition, however this one could be
- relaxed by being more careful with copying the new value
- of the parameter (emitting appropriate GIMPLE_ASSIGN and
- updating the virtual operands). */
- if (!is_gimple_reg (param))
+ if (is_gimple_reg_type (TREE_TYPE (param))
+ ? !is_gimple_reg (param)
+ : (!is_gimple_variable (param)
+ || TREE_THIS_VOLATILE (param)
+ || may_be_aliased (param)
+ || !gimple_call_must_tail_p (call)))
break;
}
}
param = DECL_CHAIN (param), idx++)
{
tree ddef, arg = gimple_call_arg (call, idx);
- if (is_gimple_reg (param)
- && (ddef = ssa_default_def (cfun, param))
- && (arg != ddef))
+ if (!is_gimple_reg (param)
+ || ((ddef = ssa_default_def (cfun, param))
+ && arg != ddef))
bitmap_set_bit (tailr_arg_needs_copy, idx);
}
}
/* Add phi node entries for arguments. The ordering of the phi nodes should
be the same as the ordering of the arguments. */
+ auto_vec<tree> copies;
for (param = DECL_ARGUMENTS (current_function_decl),
idx = 0, gpi = gsi_start_phis (first);
param;
if (!bitmap_bit_p (tailr_arg_needs_copy, idx))
continue;
+ if (!is_gimple_reg_type (TREE_TYPE (param)))
+ {
+ if (param == gimple_call_arg (stmt, idx))
+ continue;
+ /* First check if param isn't used by any of the following
+ call arguments. If it is, we need to copy first to
+ a temporary and only after doing all the assignments copy it
+ to param. */
+ size_t idx2 = idx + 1;
+ tree param2 = DECL_CHAIN (param);
+ for (; param2; param2 = DECL_CHAIN (param2), idx2++)
+ if (!is_gimple_reg_type (TREE_TYPE (param)))
+ {
+ tree base = get_base_address (gimple_call_arg (stmt, idx2));
+ if (base == param)
+ break;
+ }
+ tree tmp = param;
+ if (param2)
+ {
+ tmp = create_tmp_var (TREE_TYPE (param));
+ copies.safe_push (param);
+ copies.safe_push (tmp);
+ }
+ gimple *g = gimple_build_assign (tmp, gimple_call_arg (stmt, idx));
+ gsi_insert_before (&t->call_gsi, g, GSI_SAME_STMT);
+ continue;
+ }
+
arg = gimple_call_arg (stmt, idx);
phi = gpi.phi ();
gcc_assert (param == SSA_NAME_VAR (PHI_RESULT (phi)));
add_phi_arg (phi, arg, e, gimple_location (stmt));
gsi_next (&gpi);
}
+ for (unsigned i = 0; i < copies.length (); i += 2)
+ {
+ gimple *g = gimple_build_assign (copies[i], copies[i + 1]);
+ gsi_insert_before (&t->call_gsi, g, GSI_SAME_STMT);
+ }
/* Update the values of accumulators. */
adjust_accumulator_values (t->call_gsi, t->mult, t->add, e);
or if there are existing degenerate PHI nodes. */
if (!single_pred_p (first)
|| !gimple_seq_empty_p (phi_nodes (first)))
- first =
- split_edge (single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
+ first
+ = split_edge (single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
/* Copy the args if needed. */
unsigned idx;
param = DECL_CHAIN (param), idx++)
if (bitmap_bit_p (tailr_arg_needs_copy, idx))
{
+ if (!is_gimple_reg_type (TREE_TYPE (param)))
+ continue;
tree name = ssa_default_def (cfun, param);
tree new_name = make_ssa_name (param, SSA_NAME_DEF_STMT (name));
gphi *phi;