Now that we have a way of explicitly marking a variable as dead, we can
use that to indicate the end of a temporary's lifetime by adding it as a
cleanup for that temporary. Since gimple_push_cleanup still deals in
trees I needed to tweak a couple of places to avoid trying to treat a
clobber as a real CONSTRUCTOR, but the changes were small.
One somewhat surprising thing that showed up as a result of this change
were some failures in the libstdc++ testsuite. When I investigated, I
found that the tests were relying on temporaries living longer than they
should:
const int& x = std::max(1, 2);
Since std::max takes its arguments by const reference and returns one of
them, x ends up as a dangling reference to the temporary containing 2,
which dies at the end of the declaration-statement. This patch breaks
the testcase because now the temporary stack slots get reused by the
next statement, so by the time we look at x it no longer points to a 2.
I've fixed the tests by removing the references on the variables.
Tested x86_64-pc-linux-gnu, applying to trunk.
commit 007e0cccc9c084265ea24ca76db72083d90b3280
Author: Jason Merrill <ja...@redhat.com>
Date: Fri Nov 11 17:42:30 2011 -0500
PR c++/51060
* gimplify.c (gimplify_target_expr): Add a clobber to the cleanup.
(gimplify_modify_expr): Don't try to simplify it.
* cp/cp-gimplify.c (cp_gimplify_expr): Leave clobbers alone.
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index af45f59..9968c3d 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -569,7 +569,8 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
else if ((is_gimple_lvalue (op1) || INDIRECT_REF_P (op1)
|| (TREE_CODE (op1) == CONSTRUCTOR
- && CONSTRUCTOR_NELTS (op1) == 0)
+ && CONSTRUCTOR_NELTS (op1) == 0
+ && !TREE_CLOBBER_P (op1))
|| (TREE_CODE (op1) == CALL_EXPR
&& !CALL_EXPR_RETURN_SLOT_OPT (op1)))
&& is_really_empty_class (TREE_TYPE (op0)))
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 9845b69..cfe6696 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -4554,6 +4554,16 @@ gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
gcc_assert (TREE_CODE (*expr_p) == MODIFY_EXPR
|| TREE_CODE (*expr_p) == INIT_EXPR);
+ /* Trying to simplify a clobber using normal logic doesn't work,
+ so handle it here. */
+ if (TREE_CLOBBER_P (*from_p))
+ {
+ gcc_assert (!want_value && TREE_CODE (*to_p) == VAR_DECL);
+ gimplify_seq_add_stmt (pre_p, gimple_build_assign (*to_p, *from_p));
+ *expr_p = NULL;
+ return GS_ALL_DONE;
+ }
+
/* Insert pointer conversions required by the middle-end that are not
required by the frontend. This fixes middle-end type checking for
for example gcc.dg/redecl-6.c. */
@@ -5335,6 +5345,8 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
if (init)
{
+ tree cleanup = NULL_TREE;
+
/* TARGET_EXPR temps aren't part of the enclosing block, so add it
to the temps list. Handle also variable length TARGET_EXPRs. */
if (TREE_CODE (DECL_SIZE (temp)) != INTEGER_CST)
@@ -5369,8 +5381,30 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
/* If needed, push the cleanup for the temp. */
if (TARGET_EXPR_CLEANUP (targ))
- gimple_push_cleanup (temp, TARGET_EXPR_CLEANUP (targ),
- CLEANUP_EH_ONLY (targ), pre_p);
+ {
+ if (CLEANUP_EH_ONLY (targ))
+ gimple_push_cleanup (temp, TARGET_EXPR_CLEANUP (targ),
+ CLEANUP_EH_ONLY (targ), pre_p);
+ else
+ cleanup = TARGET_EXPR_CLEANUP (targ);
+ }
+
+ /* Add a clobber for the temporary going out of scope, like
+ gimplify_bind_expr. */
+ if (needs_to_live_in_memory (temp))
+ {
+ tree clobber = build_constructor (TREE_TYPE (temp), NULL);
+ TREE_THIS_VOLATILE (clobber) = true;
+ clobber = build2 (MODIFY_EXPR, TREE_TYPE (temp), temp, clobber);
+ if (cleanup)
+ cleanup = build2 (COMPOUND_EXPR, void_type_node, cleanup,
+ clobber);
+ else
+ cleanup = clobber;
+ }
+
+ if (cleanup)
+ gimple_push_cleanup (temp, cleanup, false, pre_p);
/* Only expand this once. */
TREE_OPERAND (targ, 3) = init;
diff --git a/gcc/testsuite/g++.dg/opt/stack2.C b/gcc/testsuite/g++.dg/opt/stack2.C
new file mode 100644
index 0000000..8468e1a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/opt/stack2.C
@@ -0,0 +1,33 @@
+// PR c++/51060
+// { dg-options "-Os -Wframe-larger-than=2000 -Werror" }
+
+// Shows a problem of not re-using stack space:
+// Compile as: g++ -c test_stack_reuse.cpp -o /dev/null -Wframe-larger-than=2048 -Werror -Os
+// Result: warning: the frame size of 10240 bytes is larger than 2048 bytes [-Wframe-larger-than=]
+//
+
+struct StackObject
+{
+ StackObject();
+ char buffer[1024];
+};
+
+void Test()
+{
+#define TEST_SUB() \
+ StackObject();
+
+#define TEST() \
+ TEST_SUB() \
+ TEST_SUB() \
+ TEST_SUB() \
+ TEST_SUB() \
+ TEST_SUB() \
+ TEST_SUB() \
+ TEST_SUB() \
+ TEST_SUB() \
+ TEST_SUB() \
+ TEST_SUB()
+
+ TEST()
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/max/1.cc b/libstdc++-v3/testsuite/25_algorithms/max/1.cc
index 978466f..1757b6a 100644
--- a/libstdc++-v3/testsuite/25_algorithms/max/1.cc
+++ b/libstdc++-v3/testsuite/25_algorithms/max/1.cc
@@ -25,13 +25,13 @@ void test01()
{
bool test __attribute__((unused)) = true;
- const int& x = std::max(1, 2);
- const int& y = std::max(4, 3);
+ const int x = std::max(1, 2);
+ const int y = std::max(4, 3);
VERIFY( x == 2 );
VERIFY( y == 4 );
- const int& xc = std::max(1, 2, std::greater<int>());
- const int& yc = std::max(4, 3, std::greater<int>());
+ const int xc = std::max(1, 2, std::greater<int>());
+ const int yc = std::max(4, 3, std::greater<int>());
VERIFY( xc == 1 );
VERIFY( yc == 3 );
}
diff --git a/libstdc++-v3/testsuite/25_algorithms/min/1.cc b/libstdc++-v3/testsuite/25_algorithms/min/1.cc
index 23bc721..ca52e49 100644
--- a/libstdc++-v3/testsuite/25_algorithms/min/1.cc
+++ b/libstdc++-v3/testsuite/25_algorithms/min/1.cc
@@ -25,13 +25,13 @@ void test01()
{
bool test __attribute__((unused)) = true;
- const int& z = std::min(1, 2);
- const int& w = std::min(4, 3);
+ const int z = std::min(1, 2);
+ const int w = std::min(4, 3);
VERIFY( z == 1 );
VERIFY( w == 3 );
- const int& zc = std::min(1, 2, std::greater<int>());
- const int& wc = std::min(4, 3, std::greater<int>());
+ const int zc = std::min(1, 2, std::greater<int>());
+ const int wc = std::min(4, 3, std::greater<int>());
VERIFY( zc == 2 );
VERIFY( wc == 4 );
}
diff --git a/libstdc++-v3/testsuite/25_algorithms/minmax/1.cc b/libstdc++-v3/testsuite/25_algorithms/minmax/1.cc
index 5212241..5e58192 100644
--- a/libstdc++-v3/testsuite/25_algorithms/minmax/1.cc
+++ b/libstdc++-v3/testsuite/25_algorithms/minmax/1.cc
@@ -27,15 +27,15 @@ void test01()
{
bool test __attribute__((unused)) = true;
- std::pair<const int&, const int&> z = std::minmax(1, 2);
- std::pair<const int&, const int&> w = std::minmax(4, 3);
+ std::pair<const int, const int> z = std::minmax(1, 2);
+ std::pair<const int, const int> w = std::minmax(4, 3);
VERIFY( z.first == 1 );
VERIFY( z.second == 2 );
VERIFY( w.first == 3 );
VERIFY( w.second == 4 );
- std::pair<const int&, const int&> zc = std::minmax(1, 2, std::greater<int>());
- std::pair<const int&, const int&> wc = std::minmax(4, 3, std::greater<int>());
+ std::pair<const int, const int> zc = std::minmax(1, 2, std::greater<int>());
+ std::pair<const int, const int> wc = std::minmax(4, 3, std::greater<int>());
VERIFY( zc.first == 2 );
VERIFY( zc.second == 1 );
VERIFY( wc.first == 4 );