On 10/09/2024 10:29, Jason Merrill wrote: > On 9/10/24 6:10 AM, Alex Coplan wrote: > > On 27/08/2024 10:55, Alex Coplan wrote: > > > Hi, > > > > > > This is a v3 that hopefully addresses the feedback from both Jason and > > > Jakub. v2 was posted here: > > > https://gcc.gnu.org/pipermail/gcc-patches/2024-August/660191.html > > > > Gentle ping on this C++ patch: > > https://gcc.gnu.org/pipermail/gcc-patches/2024-August/661559.html > > > > Jason, are you OK with this approach, or would you prefer to not make the > > INTEGER_CST assumption and do something along the lines of your last > > suggestion > > instead: > > > > > Perhaps we want a recompute_expr_flags like the existing > > > recompute_constructor_flags, so we don't need to duplicate PROCESS_ARG > > > logic elsewhere. > > > > ? Sorry, I'd missed that reply when I wrote the v3 patch. > > I still think that function would be nice to have, but the patch is OK as > is.
Thanks, I've pushed the patch and the rest of the series as: 3fd07d4f04f libstdc++: Restore unrolling in std::find using pragma [PR116140] 9759f6299d9 lto: Stream has_unroll flag during LTO [PR116140] 31ff173c708 testsuite: Ensure ltrans dump files get cleaned up properly [PR116140] f97d86242b8 c++: Ensure ANNOTATE_EXPRs remain outermost expressions in conditions [PR116140] Alex > > > Thanks, > > Alex > > > > > > > > (Sorry for the delay in posting the re-spin, I was away last week.) > > > > > > In this version we refactor to introudce a helper class (annotate_saver) > > > which is much less invasive to the caller (maybe_convert_cond) and > > > should (at least in theory) be reuseable elsewhere. > > > > > > This version also relies on the assumption that operands 1 and 2 of > > > ANNOTATE_EXPRs are INTEGER_CSTs, which simplifies the flag updates > > > without having to rely on assumptions about the specific changes made > > > in maybe_convert_cond. > > > > > > Bootstrapped/regtested on aarch64-linux-gnu, OK for trunk? > > > > > > Thanks, > > > Alex > > > > > > -- >8 -- > > > > > > 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 by first introducing a new helper > > > class (annotate_saver) to save and restore outer chains of > > > ANNOTATE_EXPRs and then using it in maybe_convert_cond. > > > > > > With this patch, we don't get any such warning and the loop gets unrolled > > > as > > > expected at -O2. > > > > > > gcc/cp/ChangeLog: > > > > > > PR libstdc++/116140 > > > * semantics.cc (anotate_saver): New. Use it ... > > > (maybe_convert_cond): ... here, to 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. > > > > > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc > > > index 5ab2076b673..b1a49b14238 100644 > > > --- a/gcc/cp/semantics.cc > > > +++ b/gcc/cp/semantics.cc > > > @@ -951,6 +951,86 @@ maybe_warn_unparenthesized_assignment (tree t, bool > > > nested_p, > > > } > > > } > > > +/* Helper class for saving/restoring ANNOTATE_EXPRs. For a tree node t, > > > users > > > + can construct one of these like so: > > > + > > > + annotate_saver s (&t); > > > + > > > + and t will be updated to have any annotations removed. The user can > > > then > > > + transform t, and later restore the ANNOTATE_EXPRs with: > > > + > > > + t = s.restore (t). > > > + > > > + The intent is to ensure that any ANNOTATE_EXPRs remain the outermost > > > + expressions following any operations on t. */ > > > + > > > +class annotate_saver { > > > + /* The chain of saved annotations, if there were any. Otherwise null. > > > */ > > > + tree m_annotations; > > > + > > > + /* If M_ANNOTATIONS is non-null, then M_INNER points to TREE_OPERAND > > > (A, 0) > > > + for the innermost annotation A. */ > > > + tree *m_inner; > > > + > > > +public: > > > + annotate_saver (tree *); > > > + tree restore (tree); > > > +}; > > > + > > > +/* If *COND is an ANNOTATE_EXPR, walk through the chain of annotations, > > > and set > > > + *COND equal to the first non-ANNOTATE_EXPR (saving a pointer to the > > > + original chain of annotations for later use in restore). */ > > > + > > > +annotate_saver::annotate_saver (tree *cond) : m_annotations (nullptr) > > > +{ > > > + tree *t = cond; > > > + while (TREE_CODE (*t) == ANNOTATE_EXPR) > > > + t = &TREE_OPERAND (*t, 0); > > > + > > > + if (t != cond) > > > + { > > > + m_annotations = *cond; > > > + *cond = *t; > > > + m_inner = t; > > > + } > > > +} > > > + > > > +/* If we didn't strip any annotations on construction, return NEW_INNER > > > + unmodified. Otherwise, wrap the saved annotations around NEW_INNER > > > (updating > > > + the types and flags of the annotations if needed) and return the > > > resulting > > > + expression. */ > > > + > > > +tree > > > +annotate_saver::restore (tree new_inner) > > > +{ > > > + if (!m_annotations) > > > + return new_inner; > > > + > > > + /* If the type of the inner expression changed, we need to update the > > > types > > > + of all the ANNOTATE_EXPRs. We may need to update the flags too, > > > but we > > > + assume they only change if the type of the inner expression changes. > > > + The flag update logic assumes that the other operands to the > > > + ANNOTATE_EXPRs are always INTEGER_CSTs. */ > > > + if (TREE_TYPE (new_inner) != TREE_TYPE (*m_inner)) > > > + { > > > + const bool new_readonly > > > + = TREE_READONLY (new_inner) || CONSTANT_CLASS_P (new_inner); > > > + > > > + for (tree c = m_annotations; c != *m_inner; c = TREE_OPERAND (c, > > > 0)) > > > + { > > > + gcc_checking_assert (TREE_CODE (c) == ANNOTATE_EXPR > > > + && TREE_CODE (TREE_OPERAND (c, 1)) == INTEGER_CST > > > + && TREE_CODE (TREE_OPERAND (c, 2)) == > > > INTEGER_CST); > > > + TREE_TYPE (c) = TREE_TYPE (new_inner); > > > + TREE_SIDE_EFFECTS (c) = TREE_SIDE_EFFECTS (new_inner); > > > + TREE_READONLY (c) = new_readonly; > > > + } > > > + } > > > + > > > + *m_inner = new_inner; > > > + return m_annotations; > > > +} > > > + > > > /* COND is the condition-expression for an if, while, etc., > > > statement. Convert it to a boolean value, if appropriate. > > > In addition, verify sequence points if -Wsequence-point is enabled. > > > */ > > > @@ -966,6 +1046,9 @@ maybe_convert_cond (tree cond) > > > if (type_dependent_expression_p (cond)) > > > return cond; > > > + /* Strip any ANNOTATE_EXPRs from COND. */ > > > + annotate_saver annotations (&cond); > > > + > > > /* 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 > > > @@ -984,7 +1067,10 @@ maybe_convert_cond (tree cond) > > > /* Do the conversion. */ > > > cond = convert_from_reference (cond); > > > - return condition_conversion (cond); > > > + cond = condition_conversion (cond); > > > + > > > + /* Restore any ANNOTATE_EXPRs around COND. */ > > > + return annotations.restore (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); > > > +} > > >