tree-optimization/98464 - replace loop info with avail at uses

This does VN replacement in loop nb_iterations consistent with
the rest of the IL by using availability at the definition site
of uses.

2021-01-04  Richard Biener  <rguenther@suse.de>

	PR tree-optimization/98464
	* tree-ssa-sccvn.c (vn_valueize_for_srt): Rename from ...
	(vn_valueize_wrapper): ... this.  Temporarily adjust vn_context_bb.
	(process_bb): Adjust.

	* g++.dg/opt/pr98464.C: New testcase.
This commit is contained in:
Richard Biener 2021-01-04 13:02:24 +01:00
parent 7f2b731756
commit 39bd65faee
2 changed files with 171 additions and 5 deletions

View File

@ -0,0 +1,155 @@
// { dg-do compile }
// { dg-require-effective-target c++11 }
// { dg-options "-O3 -fno-tree-dce" }
template < typename, typename, template < typename > class _Op, typename... _Args > struct __detector {
using type = _Op< _Args... >;
};
template < typename _Default, template < typename > class _Op, typename... _Args > using __detected_or = __detector< _Default, void, _Op, _Args... >;
template < typename _Default, template < typename > class _Op, typename... _Args > using __detected_or_t = typename __detected_or< _Default, _Op, _Args... >::type;
template < typename, typename > struct __replace_first_arg;
template < template < typename > class _Template, typename _Up, typename _Tp, typename... _Types > struct __replace_first_arg< _Template< _Tp, _Types... >, _Up > {
using type = _Template< _Up >;
};
template < class > class min_pointer;
class MoveOnly;
struct pointer_traits {
template < typename _Up > using rebind = typename __replace_first_arg< min_pointer< int >, _Up >::type;
};
template < typename _Iterator > class __normal_iterator {
public: __normal_iterator(_Iterator);
};
struct __allocator_traits_base {
template < typename _Tp > using __pointer = typename _Tp::pointer;
};
template < typename _Alloc > struct allocator_traits : __allocator_traits_base {
typedef typename _Alloc::value_type value_type;
using pointer = __detected_or_t< value_type, __pointer, _Alloc >;
template < typename _Tp > struct _Ptr {
using type = pointer_traits::rebind< _Tp >;
};
using const_pointer = typename _Ptr< value_type >::type;
using size_type = int;
static pointer allocate(_Alloc __a, size_type __n) {
return __a.allocate(__n);
}
};
template < typename _ForwardIterator, typename _Allocator > void _Destroy(_ForwardIterator __first, _ForwardIterator __last, _Allocator) {
for (;
__first != __last;
++__first) ;
}
template < typename _InputIterator, typename _ForwardIterator, typename _Allocator > _ForwardIterator __uninitialized_copy_a(_InputIterator, _ForwardIterator, _Allocator);
template < typename _InputIterator, typename _ForwardIterator, typename _Allocator > _ForwardIterator __uninitialized_move_if_noexcept_a(_InputIterator __last, _ForwardIterator __result, _Allocator __alloc) {
return __uninitialized_copy_a(__last, __result, __alloc);
}
template < typename _ForwardIterator, typename _Size, typename _Allocator > _ForwardIterator __uninitialized_default_n_a(_ForwardIterator __first, _Size __n, _Allocator) {
for (;
__n;
--__n, ++__first) ;
return __first;
}
template < typename _Alloc > struct _Vector_base {
typedef _Alloc _Tp_alloc_type;
typedef typename _Tp_alloc_type ::pointer pointer;
struct _Vector_impl_data {
pointer _M_start;
pointer _M_finish;
pointer _M_end_of_storage;
};
struct _Vector_impl : _Tp_alloc_type, _Vector_impl_data {
_Vector_impl(_Tp_alloc_type) {
}
};
_Vector_base(long __n, _Alloc __a) : _M_impl(__a) {
_M_impl._M_end_of_storage = _M_impl._M_start + __n;
}
_Vector_impl _M_impl;
pointer _M_allocate(long __n) {
return __n ? allocator_traits< _Tp_alloc_type >::allocate(_M_impl, __n) : pointer();
}
};
template < typename, typename _Alloc > class vector : _Vector_base< _Alloc > {
public: typedef typename _Alloc::pointer pointer;
typedef __normal_iterator< typename allocator_traits< _Alloc >::const_pointer > const_iterator;
typedef _Alloc allocator_type;
vector(long __n, allocator_type __a = allocator_type()) : _Vector_base< _Alloc >(__n, __a) {
this->_M_impl._M_finish = __uninitialized_default_n_a(this->_M_impl._M_start, __n, 0);
}
~vector() {
_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, 0);
}
const_iterator cbegin() {
return this->_M_impl._M_start;
}
typename _Alloc::value_type operator[](long) {
return *this->_M_impl._M_start;
}
void insert(const_iterator, MoveOnly &&) {
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage) ;
else _M_realloc_insert();
}
template < typename... > void _M_realloc_insert();
};
template < typename _Tp, typename _Alloc > template < typename... > void vector< _Tp, _Alloc >::_M_realloc_insert() {
long __trans_tmp_6 = this->_M_impl._M_finish - this->_M_impl._M_start;
pointer __old_start = this->_M_impl._M_start;
pointer __old_finish = this->_M_impl._M_finish;
pointer __new_start(this->_M_allocate(__trans_tmp_6));
pointer __new_finish = __uninitialized_move_if_noexcept_a(__old_finish, __new_finish, 0);
_Destroy(__old_start, __old_finish, 0);
this->_M_impl._M_start = __new_start;
this->_M_impl._M_finish = __new_finish;
}
class MoveOnly {
int data_;
public: bool operator==(MoveOnly) {
return data_;
}
};
void __assert_fail();
template < class T > class min_pointer {
T *ptr_;
min_pointer(T *p) : ptr_(p) {
}
public: min_pointer() = default;
T operator*() {
return *ptr_;
}
void operator++() {
++ptr_;
}
void operator+=(long n) {
ptr_ += n;
}
min_pointer operator+(long n) {
min_pointer tmp(*this);
tmp += n;
return tmp;
}
friend long operator-(min_pointer x, min_pointer y) {
return x.ptr_ - y.ptr_;
}
friend bool operator==(min_pointer x, min_pointer y) {
return x.ptr_ == y.ptr_;
}
friend bool operator!=(min_pointer x, min_pointer y) {
return !(x == y);
}
friend class min_allocator;
};
class min_allocator {
public: typedef MoveOnly value_type;
typedef min_pointer< MoveOnly > pointer;
pointer allocate(long) {
return static_cast< MoveOnly * >(operator new(sizeof(MoveOnly)));
}
};
int main() {
vector< int, min_allocator > v(100);
v.insert(v.cbegin(), MoveOnly());
int j = 0;
for (;
j < 10;
++j) v[j] == MoveOnly() ? void() : __assert_fail();
}

View File

@ -304,12 +304,23 @@ static vn_nary_op_t last_inserted_nary;
static vn_tables_t valid_info;
/* Valueization hook. Valueize NAME if it is an SSA name, otherwise
just return it. */
/* Valueization hook for simplify_replace_tree. Valueize NAME if it is
an SSA name, otherwise just return it. */
tree (*vn_valueize) (tree);
tree vn_valueize_wrapper (tree t, void* context ATTRIBUTE_UNUSED)
static tree
vn_valueize_for_srt (tree t, void* context ATTRIBUTE_UNUSED)
{
return vn_valueize (t);
basic_block saved_vn_context_bb = vn_context_bb;
/* Look for sth available at the definition block of the argument.
This avoids inconsistencies between availability there which
decides if the stmt can be removed and availability at the
use site. The SSA property ensures that things available
at the definition are also available at uses. */
if (!SSA_NAME_IS_DEFAULT_DEF (t))
vn_context_bb = gimple_bb (SSA_NAME_DEF_STMT (t));
tree res = vn_valueize (t);
vn_context_bb = saved_vn_context_bb;
return res;
}
@ -6995,7 +7006,7 @@ process_bb (rpo_elim &avail, basic_block bb,
if (bb->loop_father->nb_iterations)
bb->loop_father->nb_iterations
= simplify_replace_tree (bb->loop_father->nb_iterations,
NULL_TREE, NULL_TREE, &vn_valueize_wrapper);
NULL_TREE, NULL_TREE, &vn_valueize_for_srt);
}
/* Value-number all defs in the basic-block. */