For the testcase added with this patch, we would end up losing the:
#pragma GCC unroll 4
and emitting "warning: ignoring loop annotation". That warning comes
from tree-cfg.cc:replace_loop_annotate, and means that we failed to
process the ANNOTATE_EXPR in tree-cfg.cc:replace_loop_annotate_in_block.
That function walks backwards over the GIMPLE in an exiting BB for a
loop, skipping over the final gcond, and looks for any ANNOTATE_EXPRS
immediately preceding the gcond.
The function documents the following pre-condition:
/* [...] We assume that the annotations come immediately before the
condition in BB, if any. */
now looking at the exiting BB of the loop, we have:
<bb 8> :
D.4524 = .ANNOTATE (iftmp.1, 1, 4);
retval.0 = D.4524;
if (retval.0 != 0)
goto <bb 3>; [INV]
else
goto <bb 9>; [INV]
and crucially there is an intervening assignment between the gcond and
the preceding .ANNOTATE ifn call. To see where this comes from, we can
look to the IR given by -fdump-tree-original:
if (<<cleanup_point ANNOTATE_EXPR <first != last && !use_find(short
int*)::<lambda(short int)>::operator() (&pred, *first), unroll 4>>>)
goto <D.4518>;
else
goto <D.4516>;
here the problem is that we've wrapped a CLEANUP_POINT_EXPR around the
ANNOTATE_EXPR, meaning the ANNOTATE_EXPR is no longer the outermost
expression in the condition.
The CLEANUP_POINT_EXPR gets added by the following call chain:
finish_while_stmt_cond
-> maybe_convert_cond
-> condition_conversion
-> fold_build_cleanup_point_expr
this patch chooses to fix the issue in maybe_convert_cond by walking through
any ANNOTATE_EXPRs and doing any condition conversion on the inner expression,
leaving the ANNOTATE_EXPRs (if any) as the outermost expressions in the
condition.
With this patch, we don't get any such warning and the loop gets unrolled as
expected at -O2.
Bootstrapped/regtested on aarch64-linux-gnu, OK for trunk?
gcc/cp/ChangeLog:
PR libstdc++/116140
* semantics.cc (maybe_convert_cond): Ensure any ANNOTATE_EXPRs
remain the outermost expression(s) of the condition.
gcc/testsuite/ChangeLog:
PR libstdc++/116140
* g++.dg/ext/pragma-unroll-lambda.C: New test.
---
gcc/cp/semantics.cc | 26 ++++++++++++-------
.../g++.dg/ext/pragma-unroll-lambda.C | 17 ++++++++++++
2 files changed, 34 insertions(+), 9 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/ext/pragma-unroll-lambda.C
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index e58612660c9..8634a188458 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -966,25 +966,33 @@ maybe_convert_cond (tree cond)
if (type_dependent_expression_p (cond))
return cond;
+ /* If the condition has any ANNOTATE_EXPRs, those must remain the outermost
+ expressions of the condition. Walk through these, performing the condition
+ conversion in place on the inner expression. */
+ tree *condp = &cond;
+ while (TREE_CODE (*condp) == ANNOTATE_EXPR)
+ condp = &TREE_OPERAND (*condp, 0);
+
/* For structured binding used in condition, the conversion needs to be
evaluated before the individual variables are initialized in the
std::tuple_{size,elemenet} case. cp_finish_decomp saved the conversion
result in a TARGET_EXPR, pick it up from there. */
- if (DECL_DECOMPOSITION_P (cond)
- && DECL_DECOMP_IS_BASE (cond)
- && DECL_DECOMP_BASE (cond)
- && TREE_CODE (DECL_DECOMP_BASE (cond)) == TARGET_EXPR)
- cond = TARGET_EXPR_SLOT (DECL_DECOMP_BASE (cond));
+ if (DECL_DECOMPOSITION_P (*condp)
+ && DECL_DECOMP_IS_BASE (*condp)
+ && DECL_DECOMP_BASE (*condp)
+ && TREE_CODE (DECL_DECOMP_BASE (*condp)) == TARGET_EXPR)
+ *condp = TARGET_EXPR_SLOT (DECL_DECOMP_BASE (*condp));
if (warn_sequence_point && !processing_template_decl)
- verify_sequence_points (cond);
+ verify_sequence_points (*condp);
- maybe_warn_unparenthesized_assignment (cond, /*nested_p=*/false,
+ maybe_warn_unparenthesized_assignment (*condp, /*nested_p=*/false,
tf_warning_or_error);
/* Do the conversion. */
- cond = convert_from_reference (cond);
- return condition_conversion (cond);
+ *condp = convert_from_reference (*condp);
+ *condp = condition_conversion (*condp);
+ return cond;
}
/* Finish an expression-statement, whose EXPRESSION is as indicated. */
diff --git a/gcc/testsuite/g++.dg/ext/pragma-unroll-lambda.C b/gcc/testsuite/g++.dg/ext/pragma-unroll-lambda.C
new file mode 100644
index 00000000000..f410f6d8d25
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/pragma-unroll-lambda.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target c++11 } }
+
+template<typename Iter, typename Pred>
+inline Iter
+my_find(Iter first, Iter last, Pred pred)
+{
+#pragma GCC unroll 4
+ while (first != last && !pred(*first))
+ ++first;
+ return first;
+}
+
+short *use_find(short *p)
+{
+ auto pred = [](short x) { return x == 42; };
+ return my_find(p, p + 1024, pred);
+}