From 18ab4042ce75c2176f7133ea107e26c010be9481 Mon Sep 17 00:00:00 2001
From: huzife <634763349@qq.com>
Date: Tue, 20 May 2025 19:10:14 +0800
Subject: [PATCH] [Sync] Sync patches from openeuler/gcc
(cherry picked from commit 26d537981ea51f0f0b3ca2023b610f1d202c7743)
---
...4OE_oeAware-section-dup-in-namespace.patch | 55 +
...propagation-localize-array-array-dse.patch | 4997 +++++++++++++++++
gcc.spec | 10 +-
3 files changed, 5061 insertions(+), 1 deletion(-)
create mode 100644 0377-oeAware-Fix-.GCC4OE_oeAware-section-dup-in-namespace.patch
create mode 100644 0378-Add-alignment-propagation-localize-array-array-dse.patch
diff --git a/0377-oeAware-Fix-.GCC4OE_oeAware-section-dup-in-namespace.patch b/0377-oeAware-Fix-.GCC4OE_oeAware-section-dup-in-namespace.patch
new file mode 100644
index 0000000..bc78518
--- /dev/null
+++ b/0377-oeAware-Fix-.GCC4OE_oeAware-section-dup-in-namespace.patch
@@ -0,0 +1,55 @@
+From 81600a0743fd889363339bc1463a56bae84cda60 Mon Sep 17 00:00:00 2001
+From: liyancheng <412998149@qq.com>
+Date: Sun, 18 May 2025 11:12:12 +0800
+Subject: [PATCH 1/2] [oeAware] Fix .GCC4OE_oeAware section dup in namespace
+ main
+
+This resolves an ICE "section already exists" caused
+by incorrectly creating the .GCC4OE_oeAware section
+for non-global main functions.
+---
+ gcc/testsuite/gcc.dg/oeaware-main-in-namespace.cpp | 10 ++++++++++
+ gcc/varasm.cc | 8 +++++---
+ 2 files changed, 15 insertions(+), 3 deletions(-)
+ create mode 100644 gcc/testsuite/gcc.dg/oeaware-main-in-namespace.cpp
+
+diff --git a/gcc/testsuite/gcc.dg/oeaware-main-in-namespace.cpp b/gcc/testsuite/gcc.dg/oeaware-main-in-namespace.cpp
+new file mode 100644
+index 000000000..5e44f4a1c
+--- /dev/null
++++ b/gcc/testsuite/gcc.dg/oeaware-main-in-namespace.cpp
+@@ -0,0 +1,10 @@
++/* { dg-do compile { target *-*-linux* *-*-gnu* } } */
++/* { dg-options "-foeaware-policy=1" } */
++
++namespace radar8446940 {
++int main () {
++ return 0;
++}
++}
++
++/* { dg-final { scan-assembler-not "GCC4OE_oeAware" } } */
+diff --git a/gcc/varasm.cc b/gcc/varasm.cc
+index bdf02edea..5134c0c1f 100644
+--- a/gcc/varasm.cc
++++ b/gcc/varasm.cc
+@@ -8572,11 +8572,13 @@ handle_vtv_comdat_section (section *sect, const_tree decl ATTRIBUTE_UNUSED)
+ void
+ create_oeaware_section ()
+ {
+- /* To prevent inserting repeated segments and data,
+- we only perform the insertion in the file where the main
++ /* To prevent inserting repeated segments and data, we only perform
++ the insertion in the file where the GLOBAL main
+ function is located. */
+ if (!cfun || TREE_CODE (cfun->decl) != FUNCTION_DECL
+- || !DECL_NAME (cfun->decl) || !MAIN_NAME_P (DECL_NAME (cfun->decl)))
++ || !DECL_NAME (cfun->decl) || !MAIN_NAME_P (DECL_NAME (cfun->decl))
++ || (DECL_CONTEXT (cfun->decl) != NULL_TREE &&
++ TREE_CODE (DECL_CONTEXT (cfun->decl)) != TRANSLATION_UNIT_DECL))
+ return;
+
+ int flags = SECTION_STRINGS;
+--
+2.33.0
+
diff --git a/0378-Add-alignment-propagation-localize-array-array-dse.patch b/0378-Add-alignment-propagation-localize-array-array-dse.patch
new file mode 100644
index 0000000..3b5a056
--- /dev/null
+++ b/0378-Add-alignment-propagation-localize-array-array-dse.patch
@@ -0,0 +1,4997 @@
+From 83045e6ab5f34873af07ce6a9defd4dd7c9a1f8f Mon Sep 17 00:00:00 2001
+From: huzife <634763349@qq.com>
+Date: Tue, 20 May 2025 17:34:43 +0800
+Subject: [PATCH 2/2] Add alignment-propagation, localize-array, array-dse
+
+---
+ gcc/Makefile.in | 3 +
+ gcc/common.opt | 12 +
+ gcc/config/aarch64/aarch64.cc | 4 +
+ gcc/ipa-alignment-propagation.cc | 478 ++++
+ gcc/ipa-array-dse.cc | 3317 ++++++++++++++++++++++
+ gcc/ipa-array-dse.h | 263 ++
+ gcc/ipa-localize-array.cc | 614 ++++
+ gcc/ipa-struct-reorg/ipa-struct-reorg.cc | 37 +-
+ gcc/ipa-utils.cc | 44 +
+ gcc/ipa-utils.h | 15 +
+ gcc/passes.def | 14 +
+ gcc/timevar.def | 3 +
+ gcc/tree-pass.h | 3 +
+ 13 files changed, 4806 insertions(+), 1 deletion(-)
+ create mode 100644 gcc/ipa-alignment-propagation.cc
+ create mode 100644 gcc/ipa-array-dse.cc
+ create mode 100644 gcc/ipa-array-dse.h
+ create mode 100644 gcc/ipa-localize-array.cc
+
+diff --git a/gcc/Makefile.in b/gcc/Makefile.in
+index c7a503235..070e5e456 100644
+--- a/gcc/Makefile.in
++++ b/gcc/Makefile.in
+@@ -1451,6 +1451,9 @@ OBJS = \
+ inchash.o \
+ incpath.o \
+ init-regs.o \
++ ipa-alignment-propagation.o \
++ ipa-localize-array.o \
++ ipa-array-dse.o \
+ ipa-hardware-detection.o \
+ internal-fn.o \
+ ipa-struct-reorg/ipa-struct-reorg.o \
+diff --git a/gcc/common.opt b/gcc/common.opt
+index 4cd2574e4..ed4696b7a 100644
+--- a/gcc/common.opt
++++ b/gcc/common.opt
+@@ -2069,6 +2069,18 @@ fipa-matrix-reorg
+ Common Ignore
+ Does nothing. Preserved for backward compatibility.
+
++fipa-alignment-propagation
++Common Var(flag_ipa_alignment_propagation) Init(0) Optimization
++Propagate alignment of local variable's address
++
++fipa-localize-array
++Common Var(flag_ipa_localize_array) Init(0) Optimization
++Transform global calloced array to be specific function local.
++
++fipa-array-dse
++Common Var(flag_ipa_array_dse) Init(0) Optimization
++Array dead and redundant store elimination.
++
+ fipa-reorder-fields
+ Common Var(flag_ipa_reorder_fields) Init(0) Optimization
+ Perform structure fields reorder optimizations.
+diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
+index 80242ddf8..faa445e26 100644
+--- a/gcc/config/aarch64/aarch64.cc
++++ b/gcc/config/aarch64/aarch64.cc
+@@ -19090,6 +19090,9 @@ extern bool lang_c_p (void);
+ static void
+ override_C_optimize_options (struct gcc_options *opts)
+ {
++ opts->x_flag_ipa_alignment_propagation = 1;
++ opts->x_flag_ipa_localize_array = 1;
++ opts->x_flag_ipa_array_dse = 1;
+ opts->x_flag_ipa_reorder_fields = 1;
+ opts->x_flag_ipa_struct_reorg = 5;
+ opts->x_struct_layout_optimize_level = 5;
+@@ -19154,6 +19157,7 @@ override_CPP_optimize_options (struct gcc_options *opts)
+ opts->x_param_inline_unit_growth = 256;
+ opts->x_flag_cmlt_arith = 1;
+ opts->x_flag_if_conversion_gimple = 1;
++ opts->x_flag_find_with_sve = 1;
+ }
+
+ static void
+diff --git a/gcc/ipa-alignment-propagation.cc b/gcc/ipa-alignment-propagation.cc
+new file mode 100644
+index 000000000..3f14818ae
+--- /dev/null
++++ b/gcc/ipa-alignment-propagation.cc
+@@ -0,0 +1,478 @@
++/* Copyright (C) 2019-2022 Free Software Foundation, Inc.
++
++This file is part of GCC.
++
++GCC is free software; you can redistribute it and/or modify it under
++the terms of the GNU General Public License as published by the Free
++Software Foundation; either version 3, or (at your option) any later
++version.
++
++GCC is distributed in the hope that it will be useful, but WITHOUT ANY
++WARRANTY; without even the implied warranty of MERCHANTABILITY or
++FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++for more details.
++
++You should have received a copy of the GNU General Public License
++along with GCC; see the file COPYING3. If not see
++. */
++
++#include "config.h"
++#include "system.h"
++#include "coretypes.h"
++#include "tm.h"
++#include "tree.h"
++#include "tree-cfg.h"
++#include "tree-pass.h"
++#include "tm_p.h"
++#include "basic-block.h"
++#include "bitmap.h"
++#include "function.h"
++#include "cfg.h"
++#include "cgraph.h"
++#include "gimple.h"
++#include "gimple-iterator.h"
++#include "gimple-pretty-print.h"
++#include "gimple-ssa.h"
++#include "ipa-utils.h"
++
++class alignment_propagator
++{
++public:
++ alignment_propagator (cgraph_node *node);
++
++ void execute ();
++
++private:
++ void propagate_params_alignment ();
++ void transform ();
++ size_t get_param_alignment (unsigned param_index);
++ size_t get_var_alignment (tree var);
++ bool check_assign (gimple *stmt, auto_vec &worklist,
++ size_t &alignment);
++ bool check_param (tree t, auto_vec &worklist, size_t &alignment);
++ int get_param_index_from_ssa (tree var);
++ size_t get_arg_alignment (cgraph_node *caller, tree arg);
++ size_t new_alignment (size_t orig, size_t new_value);
++ bool pow2_or_zerop (size_t value);
++ size_t abs_value (tree t);
++ bool candidate_stmt_p (gimple *stmt);
++
++private:
++ cgraph_node *node = nullptr;
++ hash_map alignment_map;
++};
++
++alignment_propagator::alignment_propagator (cgraph_node *node)
++ : node (node)
++{
++}
++
++void
++alignment_propagator::execute ()
++{
++ if (dump_file)
++ {
++ fprintf (dump_file, "Start to rewrite function: %s\n",
++ node->dump_asm_name());
++ dump_function_to_file (node->decl, dump_file, dump_flags);
++ }
++
++ cfun_saver save (node);
++
++ propagate_params_alignment ();
++
++ /* If no alignment is propagated, there is no need to continue cause
++ the rest cases are covered by constant propagation. */
++ if (!alignment_map.is_empty ())
++ transform ();
++}
++
++void
++alignment_propagator::propagate_params_alignment ()
++{
++ unsigned i = 0;
++ tree param = DECL_ARGUMENTS (node->decl);
++ while (param)
++ {
++ size_t alignment = get_param_alignment (i);
++ if (alignment)
++ alignment_map.put (param, alignment);
++
++ param = DECL_CHAIN (param);
++ i++;
++ }
++}
++
++void
++alignment_propagator::transform ()
++{
++ basic_block bb;
++ FOR_EACH_BB_FN (bb, cfun)
++ {
++ for (auto gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
++ {
++ gimple *stmt = gsi_stmt (gsi);
++ if (!candidate_stmt_p (stmt))
++ continue;
++
++ if (dump_file)
++ {
++ fprintf (dump_file, "Rewrite stmt:\n ");
++ print_gimple_stmt (dump_file, stmt, 0, TDF_NONE);
++ }
++
++ tree lhs = gimple_assign_lhs (stmt);
++ tree new_rhs = build_int_cst (TREE_TYPE (lhs), 0);
++ gimple_assign_set_rhs_from_tree (&gsi, new_rhs);
++ update_stmt (stmt);
++
++ if (dump_file)
++ {
++ fprintf (dump_file, "To:\n ");
++ print_gimple_stmt (dump_file, stmt, 0, TDF_NONE);
++ fprintf (dump_file, "\n");
++ }
++ }
++ }
++}
++
++size_t
++alignment_propagator::get_param_alignment (unsigned param_index)
++{
++ size_t alignment = 0;
++ for (auto e = node->callers; e; e = e->next_caller)
++ {
++ if (e->caller == node)
++ continue;
++
++ if (gimple_call_num_args (e->call_stmt) <= param_index)
++ return 0;
++
++ tree arg = gimple_call_arg (e->call_stmt, param_index);
++ size_t arg_alignment = get_arg_alignment (e->caller, arg);
++ if (!arg_alignment)
++ return 0;
++
++ if (!alignment || arg_alignment < alignment)
++ alignment = arg_alignment;
++ }
++
++ return alignment;
++}
++
++size_t
++alignment_propagator::get_var_alignment (tree var)
++{
++ size_t alignment = 0;
++
++ auto_bitmap visited;
++ auto_vec worklist;
++ worklist.safe_push (var);
++
++ while (!worklist.is_empty ())
++ {
++ tree t = worklist.pop ();
++ if (TREE_CODE (t) == INTEGER_CST)
++ {
++ size_t value = abs_value (t);
++ if (!pow2_or_zerop (value))
++ return 0;
++
++ alignment = new_alignment (alignment, value);
++ continue;
++ }
++
++ if (TREE_CODE (t) != SSA_NAME)
++ return 0;
++
++ if (!bitmap_set_bit (visited, SSA_NAME_VERSION (t)))
++ continue;
++
++ gimple *stmt = SSA_NAME_DEF_STMT (t);
++ switch (gimple_code (stmt))
++ {
++ case GIMPLE_PHI:
++ for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++)
++ worklist.safe_push (gimple_phi_arg_def (stmt, i));
++ break;
++ case GIMPLE_ASSIGN:
++ if (!check_assign (stmt, worklist, alignment))
++ return 0;
++ break;
++ case GIMPLE_NOP:
++ /* If we reach a default def, try to get the argument's alignment
++ from caller node. */
++ if (!check_param (t, worklist, alignment))
++ return 0;
++ break;
++ default:
++ return 0;
++ }
++ }
++
++ return alignment;
++}
++
++bool
++alignment_propagator::check_assign (gimple *stmt, auto_vec &worklist,
++ size_t &alignment)
++{
++ if (gimple_assign_single_p (stmt) || gimple_assign_cast_p (stmt))
++ {
++ worklist.safe_push (gimple_assign_rhs1 (stmt));
++ return true;
++ }
++
++ switch (gimple_assign_rhs_code (stmt))
++ {
++ case NEGATE_EXPR:
++ worklist.safe_push (gimple_assign_rhs1 (stmt));
++ return true;
++ case MAX_EXPR:
++ [[fallthrough]];
++ case MIN_EXPR:
++ [[fallthrough]];
++ case POINTER_PLUS_EXPR:
++ [[fallthrough]];
++ case POINTER_DIFF_EXPR:
++ [[fallthrough]];
++ case PLUS_EXPR:
++ [[fallthrough]];
++ case MINUS_EXPR:
++ worklist.safe_push (gimple_assign_rhs1 (stmt));
++ worklist.safe_push (gimple_assign_rhs2 (stmt));
++ return true;
++ case MULT_EXPR:
++ break;
++ default:
++ return false;
++ }
++
++ /* For mult_expr, rhs2 must be an integer constant, so we can simply take
++ this constant as alignment. Otherwise, return false. */
++ tree rhs2 = gimple_assign_rhs2 (stmt);
++ if (TREE_CODE (rhs2) != INTEGER_CST)
++ return false;
++
++ alignment = new_alignment (alignment, abs_value (rhs2));
++ return true;
++}
++
++bool
++alignment_propagator::check_param (tree t, auto_vec &worklist,
++ size_t &alignment)
++{
++ int index = get_param_index_from_ssa (t);
++ if (index == -1)
++ return false;
++
++ for (cgraph_edge *e = node->callers; e; e = e->next_caller)
++ {
++ if (gimple_call_num_args (e->call_stmt) <= index)
++ return false;
++
++ tree arg = gimple_call_arg (e->call_stmt, index);
++ if (e->caller == node)
++ worklist.safe_push (arg);
++ else
++ {
++ auto *align = alignment_map.get (SSA_NAME_VAR (t));
++ if (!align)
++ return false;
++
++ alignment = new_alignment (alignment, *align);
++ }
++ }
++
++ return true;
++}
++
++/* Find param from VAR and return its index. Return -1 if fail. */
++
++int
++alignment_propagator::get_param_index_from_ssa (tree var)
++{
++ if (!SSA_NAME_IS_DEFAULT_DEF (var) || !SSA_NAME_VAR (var))
++ return -1;
++
++ tree param = DECL_ARGUMENTS (cfun->decl);
++ int index = 0;
++ while (param && param != SSA_NAME_VAR (var))
++ {
++ param = DECL_CHAIN (param);
++ index++;
++ }
++
++ return index;
++}
++
++/* Get alignment of an argument if it is calculated from the address of a
++ local variable. */
++
++size_t
++alignment_propagator::get_arg_alignment (cgraph_node *caller, tree arg)
++{
++ if (!caller || !arg)
++ return 0;
++
++ cfun_saver save (caller);
++
++ tree base = nullptr;
++ tree offset = nullptr;
++
++ /* Extract base and offset. */
++ if (TREE_CODE (arg) == ADDR_EXPR)
++ {
++ base = arg;
++ tree op0 = TREE_OPERAND (base, 0);
++ if (TREE_CODE (op0) == MEM_REF)
++ {
++ base = TREE_OPERAND (op0, 0);
++ offset = TREE_OPERAND (op0, 1);
++ }
++ }
++ else
++ {
++ if (TREE_CODE (arg) != SSA_NAME)
++ return 0;
++
++ gimple *stmt = SSA_NAME_DEF_STMT (arg);
++ if (!is_gimple_assign (stmt))
++ return 0;
++
++ if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
++ offset = gimple_assign_rhs2 (stmt);
++ else if (!gimple_assign_single_p (stmt))
++ return 0;
++
++ base = gimple_assign_rhs1 (stmt);
++ }
++
++ /* Check if ARG uses the address of a local variable. */
++ if (TREE_CODE (base) != ADDR_EXPR)
++ return 0;
++
++ tree decl = TREE_OPERAND (base, 0);
++ if (!decl || !VAR_P (decl)
++ || decl_function_context (decl) != current_function_decl)
++ return 0;
++
++ size_t alignment = LOCAL_DECL_ALIGNMENT (decl) / 8;
++
++ /* Update alignment if there is an offset. */
++ if (offset)
++ {
++ if (TREE_CODE (offset) != INTEGER_CST)
++ return 0;
++
++ auto value = abs_value (offset);
++ if (!pow2_or_zerop (value))
++ return 0;
++
++ alignment = new_alignment (alignment, value);
++ }
++
++ return alignment;
++}
++
++size_t
++alignment_propagator::new_alignment (size_t orig_value, size_t new_value)
++{
++ if (!new_value)
++ return orig_value;
++
++ if (!orig_value || new_value < orig_value)
++ return new_value;
++
++ return orig_value;
++}
++
++bool
++alignment_propagator::pow2_or_zerop (size_t value)
++{
++ return !(value & (value - 1));
++}
++
++size_t
++alignment_propagator::abs_value (tree t)
++{
++ gcc_assert (TREE_CODE (t) == INTEGER_CST);
++ auto value = TREE_INT_CST_LOW (t);
++
++ return std::abs (static_cast (value));
++}
++
++bool
++alignment_propagator::candidate_stmt_p (gimple *stmt)
++{
++ if (!is_gimple_assign (stmt)
++ || gimple_assign_rhs_code (stmt) != BIT_AND_EXPR)
++ return false;
++
++ tree var = gimple_assign_rhs1 (stmt);
++ tree rhs2 = gimple_assign_rhs2 (stmt);
++
++ return rhs2 && TREE_CODE (rhs2) == INTEGER_CST
++ && TREE_INT_CST_LOW (rhs2) < get_var_alignment (var);
++}
++
++
++static unsigned
++ipa_propagate_alignment (void)
++{
++ auto_vec candidate_nodes;
++ cgraph_node *cnode = NULL;
++ FOR_EACH_FUNCTION (cnode)
++ {
++ if (!cnode->real_symbol_p () || !cnode->definition
++ || !cnode->has_gimple_body_p () || cnode->inlined_to)
++ continue;
++
++ cnode->get_body ();
++ candidate_nodes.safe_push (cnode);
++ }
++
++ for (auto *node : candidate_nodes)
++ alignment_propagator (node).execute ();
++
++ return 0;
++}
++
++namespace {
++const pass_data pass_data_ipa_alignment_propagation = {
++ SIMPLE_IPA_PASS,
++ "alignment-propagation",
++ OPTGROUP_NONE,
++ TV_IPA_ALIGNMENT_PROPAGATION,
++ (PROP_cfg | PROP_ssa),
++ 0,
++ 0,
++ (TODO_update_ssa),
++ (TODO_verify_all),
++};
++
++class pass_ipa_alignment_propagation
++ : public simple_ipa_opt_pass
++{
++public:
++ pass_ipa_alignment_propagation (gcc::context *ctxt)
++ : simple_ipa_opt_pass (pass_data_ipa_alignment_propagation, ctxt)
++ {}
++
++ virtual bool gate (function *)
++ {
++ return optimize >= 3 && flag_ipa_alignment_propagation;
++ }
++
++ virtual unsigned execute (function *)
++ {
++ return ipa_propagate_alignment ();
++ }
++};
++} /* namespace. */
++
++simple_ipa_opt_pass *
++make_pass_ipa_alignment_propagation (gcc::context *ctxt)
++{
++ return new pass_ipa_alignment_propagation (ctxt);
++}
+diff --git a/gcc/ipa-array-dse.cc b/gcc/ipa-array-dse.cc
+new file mode 100644
+index 000000000..df973e849
+--- /dev/null
++++ b/gcc/ipa-array-dse.cc
+@@ -0,0 +1,3317 @@
++/* Array dead store elimination
++ Copyright (C) 2021-2022 Free Software Foundation, Inc.
++
++This file is part of GCC.
++
++GCC is free software; you can redistribute it and/or modify
++it under the terms of the GNU General Public License as published by
++the Free Software Foundation; either version 3, or (at your option)
++any later version.
++
++GCC is distributed in the hope that it will be useful,
++but WITHOUT ANY WARRANTY; without even the implied warranty of
++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++GNU General Public License for more details.
++
++You should have received a copy of the GNU General Public License
++along with GCC; see the file COPYING3. If not see
++. */
++
++#include "ipa-array-dse.h"
++
++#include "basic-block.h"
++#include "bitmap.h"
++#include "cgraph.h"
++#include "cfghooks.h"
++#include "cfgloop.h"
++#include "cfg.h"
++#include "fold-const.h"
++#include "gimple.h"
++#include "gimple-builder.h"
++#include "gimple-iterator.h"
++#include "gimple-pretty-print.h"
++#include "gimple-ssa.h"
++#include "gimple-walk.h"
++#include "gimplify-me.h"
++#include "ipa-utils.h"
++#include "tree-phinodes.h"
++#include "ssa-iterators.h"
++#include "stringpool.h"
++#include "tree-cfg.h"
++#include "tree-dfa.h"
++#include "tree-inline.h"
++#include "tree-pass.h"
++#include "tree-pretty-print.h"
++#include "tree-ssanames.h"
++#include "tree-vrp.h"
++#include "tree.h"
++
++namespace array_dse {
++
++#define RANGE_TYPE long_long_integer_type_node
++#define RANGE_INF LONG_LONG_MAX
++#define RANGE_NINF LONG_LONG_MIN
++
++static inline bool
++integer_cst_p (tree t)
++{
++ return TREE_CODE (t) == INTEGER_CST && !TREE_OVERFLOW (t);
++}
++
++static tree
++strip_base (tree addr)
++{
++ tree base = get_base_address (addr);
++ return TREE_CODE (base) == MEM_REF ? TREE_OPERAND (base, 0) : nullptr;
++}
++
++static tree
++strip_ssa_copy (tree var)
++{
++ if (!var || TREE_CODE (var) != SSA_NAME)
++ return var;
++
++ while (true)
++ {
++ gimple *stmt = SSA_NAME_DEF_STMT (var);
++ if (!gimple_assign_single_p (stmt) && !gimple_assign_cast_p (stmt))
++ break;
++
++ tree rhs = gimple_assign_rhs1 (stmt);
++ if (!rhs || TREE_CODE (rhs) != SSA_NAME)
++ break;
++
++ var = rhs;
++ }
++
++ return var;
++}
++
++static inline unsigned
++greatest_common_divisor (unsigned a, unsigned b)
++{
++ return b == 0 ? a : greatest_common_divisor (b, a % b);
++}
++
++static compare_result
++opposite_compare_result (compare_result result)
++{
++ switch (result)
++ {
++ case COMPARE_ERROR: return COMPARE_ERROR;
++ case LT: return GT;
++ case EQ: return NE;
++ case GT: return LT;
++ case LE: return GE;
++ case GE: return LE;
++ case NE: return EQ;
++ }
++}
++
++static tree_code
++opposite_cond_code (tree_code code)
++{
++ switch (code)
++ {
++ case LT_EXPR: return GE_EXPR;
++ case LE_EXPR: return GT_EXPR;
++ case GT_EXPR: return LE_EXPR;
++ case GE_EXPR: return LT_EXPR;
++ case EQ_EXPR: return NE_EXPR;
++ case NE_EXPR: return EQ_EXPR;
++ default:
++ return ERROR_MARK;
++ }
++}
++
++/* Calculate step of a loop variable, record all stmts that plus step. */
++
++static int
++calc_loop_var_step (tree loop_var, tree iterate_var,
++ hash_set *iterate_stmts = nullptr)
++{
++ int step = 0;
++ auto_bitmap visited;
++ auto_vec worklist;
++ worklist.safe_push (iterate_var);
++
++ while (!worklist.is_empty ())
++ {
++ tree t = worklist.pop ();
++ if (TREE_CODE (t) != SSA_NAME)
++ return 0;
++
++ if (t == loop_var || !bitmap_set_bit (visited, SSA_NAME_VERSION (t)))
++ continue;
++
++ gimple *stmt = SSA_NAME_DEF_STMT (t);
++ if (gimple_code (stmt) == GIMPLE_PHI)
++ {
++ for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++)
++ worklist.safe_push (gimple_phi_arg_def (stmt, i));
++ continue;
++ }
++
++ /* Check iterate stmts' pattern: _2 = _1 + step. */
++ if (!is_gimple_assign (stmt)
++ || (gimple_assign_rhs_code (stmt) != POINTER_PLUS_EXPR
++ && gimple_assign_rhs_code (stmt) != PLUS_EXPR))
++ return 0;
++
++ tree ptr = gimple_assign_rhs1 (stmt);
++ tree offset = gimple_assign_rhs2 (stmt);
++ if (TREE_CODE (offset) != INTEGER_CST)
++ return 0;
++
++ worklist.safe_push (ptr);
++ HOST_WIDE_INT offset_val = TREE_INT_CST_LOW (offset);
++ if (step && offset_val != step)
++ return 0;
++ step = offset_val;
++
++ if (iterate_stmts)
++ iterate_stmts->add (stmt);
++ }
++
++ return step;
++}
++
++/* VAR is a loop var when:
++ 1. VAR is defined by a phi in LOOP's header.
++ 2. The defination phi should have two args, one comes from preheader
++ and the other comes from latch. */
++
++static bool
++loop_var_p (loop_p loop, tree var)
++{
++ if (TREE_CODE (var) != SSA_NAME)
++ return false;
++
++ gimple *stmt = SSA_NAME_DEF_STMT (var);
++ if (gimple_code (stmt) != GIMPLE_PHI || gimple_bb (stmt) != loop->header)
++ return false;
++
++ edge preheader_edge = loop_preheader_edge (loop);
++ edge latch_edge = loop_latch_edge (loop);
++
++ return preheader_edge && latch_edge
++ && PHI_ARG_DEF_FROM_EDGE (stmt, preheader_edge)
++ && PHI_ARG_DEF_FROM_EDGE (stmt, latch_edge);
++}
++
++static inline tree
++build_value (HOST_WIDE_INT value)
++{
++ return build_int_cst (RANGE_TYPE, value);
++}
++
++static inline value_range
++make_range (HOST_WIDE_INT value)
++{
++ tree v = build_value (value);
++ return value_range{v, v};
++}
++
++static inline value_range
++make_range (HOST_WIDE_INT min, HOST_WIDE_INT max)
++{
++ return value_range{build_value (min), build_value (max)};
++}
++
++static infinite_kind
++infinite_p (tree value)
++{
++ tree type = TREE_TYPE (value);
++ if (TREE_CODE (value) != INTEGER_CST || TYPE_PRECISION (type) == 1)
++ return infinite_kind::NON_INF;
++
++ wide_int type_min = wi::min_value (TYPE_PRECISION (type), TYPE_SIGN (type));
++ wide_int type_max = wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type));
++
++ if (INTEGRAL_TYPE_P (type) && !TYPE_UNSIGNED (type)
++ && wi::to_wide (value) == type_min)
++ return infinite_kind::NINF;
++
++ if (wi::to_wide (value) == type_max)
++ return infinite_kind::INF;
++
++ return infinite_kind::NON_INF;
++}
++
++static inline HOST_WIDE_INT
++get_multiplier (tree t)
++{
++ if (TREE_CODE (t) != MULT_EXPR
++ || TREE_CODE (TREE_OPERAND (t, 1)) != INTEGER_CST)
++ return 0;
++
++ return TREE_INT_CST_LOW (TREE_OPERAND (t, 1));
++}
++
++static tree negate_tree (tree t);
++static tree minus_tree (tree t1, tree t2);
++
++/* Convert negative multiplier to positive. */
++
++static void
++handle_negate_multiplier (tree t)
++{
++ HOST_WIDE_INT multiplier = get_multiplier (t);
++ if (multiplier >= 0)
++ return;
++
++ tree lhs = TREE_OPERAND (t, 0);
++ if (TREE_CODE (lhs) == PLUS_EXPR)
++ {
++ TREE_OPERAND (t, 0) = minus_tree (negate_tree (TREE_OPERAND (lhs, 0)),
++ TREE_OPERAND (lhs, 1));
++ TREE_OPERAND (t, 1) = build_int_cst (RANGE_TYPE, -multiplier);
++ }
++ else if (TREE_CODE (lhs) == MINUS_EXPR)
++ {
++ TREE_OPERAND (t, 0) = negate_tree (lhs);
++ TREE_OPERAND (t, 1) = build_int_cst (RANGE_TYPE, -multiplier);
++ }
++}
++
++static tree
++negate_tree (tree t)
++{
++ if (!t)
++ return nullptr;
++
++ if (infinite_p (t) == infinite_kind::INF)
++ return build_value (RANGE_NINF);
++ else if (infinite_p (t) == infinite_kind::NINF)
++ return build_value (RANGE_INF);
++ else
++ return fold_build1 (NEGATE_EXPR, RANGE_TYPE, t);
++}
++
++static tree
++plus_tree (tree t1, tree t2)
++{
++ if (!t1 || !t2)
++ return nullptr;
++
++ infinite_kind inf1 = infinite_p (t1);
++ infinite_kind inf2 = infinite_p (t2);
++
++ if ((inf1 == infinite_kind::INF && inf2 == infinite_kind::NINF)
++ || (inf1 == infinite_kind::NINF && inf2 == infinite_kind::INF))
++ return nullptr;
++
++ if (inf1 == infinite_kind::NINF || inf2 == infinite_kind::NINF)
++ return build_value (RANGE_NINF);
++
++ if (inf1 == infinite_kind::INF || inf2 == infinite_kind::INF)
++ return build_value (RANGE_INF);
++
++ tree ret = fold_build2 (PLUS_EXPR, RANGE_TYPE, t1, t2);
++ handle_negate_multiplier (ret);
++
++ return ret;
++}
++
++static tree
++minus_tree (tree t1, tree t2)
++{
++ if (!t1 || !t2)
++ return nullptr;
++
++ infinite_kind inf1 = infinite_p (t1);
++ infinite_kind inf2 = infinite_p (t2);
++
++ if ((inf1 == infinite_kind::INF && inf2 == infinite_kind::INF)
++ || (inf1 == infinite_kind::NINF && inf2 == infinite_kind::NINF))
++ return nullptr;
++
++ if (inf1 == infinite_kind::NINF || inf2 == infinite_kind::INF)
++ return build_value (RANGE_NINF);
++
++ if (inf1 == infinite_kind::INF || inf2 == infinite_kind::NINF)
++ return build_value (RANGE_INF);
++
++ tree ret = fold_build2 (MINUS_EXPR, RANGE_TYPE, t1, t2);
++ handle_negate_multiplier (ret);
++
++ return ret;
++}
++
++/* Callback for walk_tree, usage:
++ walk_tree (&A, sub_expr_p, B, nullptr)
++
++ Check if B is sub expr of A.
++ */
++
++static tree
++sub_expr_p (tree *opnd_ptr, int *walk_subtrees ATTRIBUTE_UNUSED, void *data)
++{
++ tree opnd = *opnd_ptr;
++ tree var = static_cast (data);
++
++ if (opnd == var)
++ return var;
++
++ return NULL_TREE;
++}
++
++static unsigned
++get_ptr_layers (tree expr)
++{
++ unsigned layers = 0;
++ while (POINTER_TYPE_P (expr) || TREE_CODE (expr) == ARRAY_TYPE)
++ {
++ layers++;
++ expr = TREE_TYPE (expr);
++ }
++
++ return layers;
++}
++
++static bool
++find_base (gimple *stmt ATTRIBUTE_UNUSED, tree base,
++ tree var ATTRIBUTE_UNUSED, void *data)
++{
++ return (TREE_CODE (base) == MEM_REF
++ && TREE_OPERAND (base, 0) == static_cast (data));
++}
++
++static bool
++gimple_phi_arg_p (gimple *stmt, tree var)
++{
++ for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++)
++ if (gimple_phi_arg_def (stmt, i) == var)
++ return true;
++
++ return false;
++}
++
++/* Returns the number of FIELD_DECLs in TYPE. */
++
++static unsigned
++fields_length (const_tree type)
++{
++ tree t = TYPE_FIELDS (type);
++ return list_length (t);
++}
++
++/* Get unique base address of VAR. */
++
++tree
++addr_analyzer::get_address (tree var)
++{
++ if (tree *it = address_map.get (var))
++ return *it;
++
++ tree addr = analyze_address (var);
++ address_map.put (var, addr);
++
++ return addr;
++}
++
++/* Try to find the unique base address to which VAR is accessing in
++ current function. */
++
++tree
++addr_analyzer::analyze_address (tree var)
++{
++ tree addr = nullptr;
++ auto_bitmap visited;
++ auto_vec worklist;
++ worklist.safe_push (var);
++
++ while (!worklist.is_empty ())
++ {
++ tree t = worklist.pop ();
++ if (TREE_CODE (t) != SSA_NAME || !POINTER_TYPE_P (TREE_TYPE (t)))
++ return nullptr;
++
++ if (!bitmap_set_bit (visited, SSA_NAME_VERSION (t)))
++ continue;
++
++ if (SSA_NAME_IS_DEFAULT_DEF (t))
++ {
++ tree new_addr = SSA_NAME_VAR (t);
++ if (!new_addr || (addr && addr != new_addr))
++ return nullptr;
++
++ addr = new_addr;
++ continue;
++ }
++
++ gimple *def_stmt = SSA_NAME_DEF_STMT (t);
++ if (is_gimple_assign (def_stmt))
++ {
++ if (!gimple_assign_single_p (def_stmt)
++ && !gimple_assign_cast_p (def_stmt)
++ && gimple_assign_rhs_code (def_stmt) != POINTER_PLUS_EXPR)
++ return nullptr;
++
++ worklist.safe_push (gimple_assign_rhs1 (def_stmt));
++ }
++ else if (gimple_code (def_stmt) == GIMPLE_PHI)
++ {
++ for (unsigned i = 0; i < gimple_phi_num_args (def_stmt); i++)
++ worklist.safe_push (gimple_phi_arg_def (def_stmt, i));
++ }
++ else
++ return nullptr;
++ }
++
++ return addr;
++}
++
++array_dse_callee::array_dse_callee (cgraph_node *node)
++ : node (node)
++{
++}
++
++/* Check if the node could be a candidate callee for array dse. */
++
++bool
++array_dse_callee::analyze ()
++{
++ cfun_saver save (node, LOOPS_NORMAL | LOOPS_HAVE_RECORDED_EXITS);
++
++ return filter_function () && find_candidate_array ()
++ && find_length_param () && check_array_usage ();
++}
++
++unsigned HOST_WIDE_INT
++array_dse_callee::get_len_param_max () const
++{
++ return len_param_max;
++}
++
++tree
++array_dse_callee::mult_tree (basic_block bb, tree t1, tree t2)
++{
++ if (!bb || !t1 || !t2 || !integer_cst_p (t2)
++ || infinite_p (t2) != infinite_kind::NON_INF)
++ return nullptr;
++
++ if (integer_zerop (t1) || integer_zerop (t2))
++ return integer_zero_node;
++
++ auto range1 = calc_tree_range (bb, t1);
++ if (tree_to_shwi (range1.min ()) < 0)
++ return nullptr;
++
++ HOST_WIDE_INT multiplier = tree_to_shwi (t2);
++ if (infinite_p (t1) != infinite_kind::NON_INF)
++ return build_value (multiplier > 0 ? RANGE_INF : RANGE_NINF);
++
++ return fold_build2 (MULT_EXPR, RANGE_TYPE, t1, t2);
++}
++
++tree
++array_dse_callee::div_tree (basic_block bb, tree t1, tree t2)
++{
++ if (!bb || !t1 || !t2 || !integer_cst_p (t2)
++ || infinite_p (t2) != infinite_kind::NON_INF)
++ return nullptr;
++
++ if (integer_zerop (t2))
++ return nullptr;
++
++ if (integer_zerop (t1))
++ return integer_zero_node;
++
++ auto range1 = calc_tree_range (bb, t1);
++ if (tree_to_shwi (range1.min ()) < 0)
++ return nullptr;
++
++ HOST_WIDE_INT divisor = tree_to_shwi (t2);
++
++ if (infinite_p (t1) != infinite_kind::NON_INF)
++ return build_value (divisor > 0 ? RANGE_INF : RANGE_NINF);
++
++ return fold_build2 (TRUNC_DIV_EXPR, RANGE_TYPE, t1, t2);
++}
++
++tree
++array_dse_callee::lshift_tree (tree t1, tree t2)
++{
++ if (!t1 || !t2 || !integer_cst_p (t2))
++ return nullptr;
++
++ if (infinite_p (t1) != infinite_kind::NON_INF)
++ return t1;
++
++ return fold_build2 (LSHIFT_EXPR, RANGE_TYPE, t1, t2);
++}
++
++tree
++array_dse_callee::rshift_tree (tree t1, tree t2)
++{
++ if (!t1 || !t2 || !integer_cst_p (t2))
++ return nullptr;
++
++ if (infinite_p (t1) != infinite_kind::NON_INF)
++ return t1;
++
++ return fold_build2 (RSHIFT_EXPR, RANGE_TYPE, t1, t2);
++}
++
++tree
++array_dse_callee::max_tree (basic_block bb, tree t1, tree t2)
++{
++ if (!bb || !t1 || !t2)
++ return nullptr;
++
++ switch (compare_tree (bb, t1, t2))
++ {
++ case EQ: return t1;
++ case GT: return t1;
++ case GE: return t1;
++ case LT: return t2;
++ case LE: return t2;
++ default: return nullptr;
++ }
++}
++
++tree
++array_dse_callee::min_tree (basic_block bb, tree t1, tree t2)
++{
++ if (!bb || !t1 || !t2)
++ return nullptr;
++
++ switch (compare_tree (bb, t1, t2))
++ {
++ case EQ: return t2;
++ case GT: return t2;
++ case GE: return t2;
++ case LT: return t1;
++ case LE: return t1;
++ default: return nullptr;
++ }
++}
++
++/* Calculate the value of T, where T is an expression with len_main_var and
++ N_VALUE is len_main_var's value. */
++
++HOST_WIDE_INT
++array_dse_callee::calc_tree_value (tree t, HOST_WIDE_INT n_value)
++{
++ if (TREE_CODE (t) == INTEGER_CST)
++ return tree_to_shwi (t);
++
++ if (t == len_main_var || t == signed_len_var)
++ return n_value;
++
++ HOST_WIDE_INT op_value[2];
++ for (int i = 0; i < std::min (2, tree_operand_length (t)); i++)
++ op_value[i] = calc_tree_value (TREE_OPERAND (t, i), n_value);
++
++ switch (TREE_CODE (t))
++ {
++ case NEGATE_EXPR:
++ return -op_value[0];
++ case PLUS_EXPR:
++ return op_value[0] + op_value[1];
++ case MINUS_EXPR:
++ return op_value[0] - op_value[1];
++ case MULT_EXPR:
++ return op_value[0] * op_value[1];
++ case TRUNC_DIV_EXPR:
++ return op_value[0] / op_value[1];
++ case LSHIFT_EXPR:
++ return op_value[0] * (1 << op_value[1]);
++ case RSHIFT_EXPR:
++ return op_value[0] / (1 << op_value[1]);
++ default:
++ return 0;
++ }
++}
++
++/* Calculate expression T's range. */
++
++value_range
++array_dse_callee::calc_tree_range (basic_block bb, tree t)
++{
++ if (!t)
++ return value_range{RANGE_TYPE};
++
++ if (TREE_CODE (t) == INTEGER_CST)
++ return make_range (tree_to_shwi (t));
++
++ if (t == len_main_var || t == signed_len_var)
++ return len_range_map.get_or_insert (bb);
++
++ int len = tree_operand_length (t);
++ gcc_assert (len > 0);
++ value_range range1 = calc_tree_range (bb, TREE_OPERAND (t, 0));
++ value_range range2;
++ if (len == 2)
++ range2 = calc_tree_range (bb, TREE_OPERAND (t, 1));
++
++ switch (TREE_CODE (t))
++ {
++ /* Since the variable in both expressions is len_main_var and both
++ expressions are monotonically increasing, we can just substitute
++ the maximum and minimum values of len_main_var to calculate the
++ expression T's range. */
++ case PLUS_EXPR:
++ case MINUS_EXPR:
++ {
++ tree op[2] = {TREE_OPERAND (t, 0), TREE_OPERAND (t, 1)};
++ if (integer_cst_p (op[0]) || integer_cst_p (op[1]))
++ break;
++
++ auto len_range = len_range_map.get_or_insert (bb);
++ auto len_min = tree_to_shwi (len_range.min ());
++ auto len_max = tree_to_shwi (len_range.max ());
++ auto min1 = calc_tree_value (op[0], len_min);
++ auto max1 = calc_tree_value (op[0], len_max);
++ auto min2 = calc_tree_value (op[1], len_min);
++ auto max2 = calc_tree_value (op[1], len_max);
++
++ auto min = TREE_CODE (t) == PLUS_EXPR ? min1 + min2 : min1 - min2;
++ auto max = TREE_CODE (t) == PLUS_EXPR ? max1 + max2 : max1 - max2;
++
++ if (min > max)
++ std::swap (min, max);
++
++ return make_range (min, max);
++ }
++ default:
++ break;
++ }
++
++ return build_range (bb, TREE_CODE (t), range1, range2);
++}
++
++value_range
++array_dse_callee::build_range (basic_block bb, tree_code op,
++ const value_range &r1, const value_range &r2)
++{
++ tree min = nullptr;
++ tree max = nullptr;
++ switch (op)
++ {
++ case NEGATE_EXPR:
++ min = negate_tree (r1.max ());
++ max = negate_tree (r1.min ());
++ break;
++ case PLUS_EXPR:
++ [[fallthrough]];
++ case POINTER_PLUS_EXPR:
++ min = plus_tree (r1.min (), r2.min ());
++ max = plus_tree (r1.max (), r2.max ());
++ break;
++ case MINUS_EXPR:
++ [[fallthrough]];
++ case POINTER_DIFF_EXPR:
++ min = minus_tree (r1.min(), r2.max ());
++ max = minus_tree (r1.max(), r2.min ());
++ break;
++ case MULT_EXPR:
++ min = mult_tree (bb, r1.min (), r2.min ());
++ max = mult_tree (bb, r1.max (), r2.max ());
++ break;
++ case TRUNC_DIV_EXPR:
++ min = div_tree (bb, r1.min (), r2.max ());
++ max = div_tree (bb, r1.max (), r2.min ());
++ break;
++ case LSHIFT_EXPR:
++ min = lshift_tree (r1.min (), r2.min ());
++ max = lshift_tree (r1.max (), r2.max ());
++ break;
++ case RSHIFT_EXPR:
++ min = rshift_tree (r1.min (), r2.max ());
++ max = rshift_tree (r1.max (), r2.min ());
++ break;
++ case MAX_EXPR:
++ min = max_tree (bb, r1.min (), r2.min ());
++ max = max_tree (bb, r1.max (), r2.max ());
++ break;
++ case MIN_EXPR:
++ min = min_tree (bb, r1.min (), r2.min ());
++ max = min_tree (bb, r1.max (), r2.max ());
++ break;
++ default:
++ break;
++ }
++
++ return min && max ? value_range{min, max} : value_range{RANGE_TYPE};
++}
++
++/* Compare two pointer range value in BB. */
++
++compare_result
++array_dse_callee::compare_tree (basic_block bb, tree t1, tree t2)
++{
++ if (!bb || !t1 || !t2)
++ return COMPARE_ERROR;
++
++ if (operand_equal_p (t1, t2))
++ return EQ;
++
++ auto ret = compare_tree_by_minus (bb, t1, t2);
++ if (!ret)
++ ret = opposite_compare_result (compare_tree_by_minus (bb, t2, t1));
++
++ return ret;
++}
++
++compare_result
++array_dse_callee::compare_tree_by_minus (basic_block bb, tree t1, tree t2)
++{
++ tree expr = minus_tree (t1, t2);
++ auto range = calc_tree_range (bb, expr);
++ HOST_WIDE_INT min = tree_to_shwi (range.min ());
++ HOST_WIDE_INT max = tree_to_shwi (range.max ());
++ if (min == 0)
++ return GE;
++ if (min > 0)
++ return GT;
++ if (max == 0)
++ return LE;
++ if (max < 0)
++ return LT;
++
++ return COMPARE_ERROR;
++}
++
++bool
++array_dse_callee::filter_function () const
++{
++ return leaf_recursive_node_p (node) && no_return_p ()
++ /* There must be two params: array and length. */
++ && list_length (DECL_ARGUMENTS (node->decl)) == PARAM_NUM;
++}
++
++/* Candidate callee must return no value. Each return block can't have any
++ stmt except a return stmt. */
++
++bool
++array_dse_callee::no_return_p () const
++{
++ tree return_type = TREE_TYPE (TREE_TYPE (cfun->decl));
++ if (TREE_CODE (return_type) != VOID_TYPE)
++ return false;
++
++ for (auto return_edge : EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
++ {
++ basic_block return_bb = return_edge->src;
++ if (!single_succ_p (return_bb))
++ return false;
++
++ gimple *stmt = first_stmt (return_bb);
++ if (gimple_code (stmt) != GIMPLE_RETURN
++ || gimple_return_retval (as_a (stmt)))
++ return false;
++ }
++
++ return true;
++}
++
++bool
++array_dse_callee::find_main_vars ()
++{
++ auto_bitmap visited;
++ tree default_def[PARAM_NUM] = {nullptr, nullptr};
++
++ /* Collect all params' default def. */
++ unsigned i;
++ tree name;
++ FOR_EACH_SSA_NAME (i, name, cfun)
++ {
++ if (!SSA_NAME_IS_DEFAULT_DEF (name)
++ || SSA_NAME_IS_VIRTUAL_OPERAND (name))
++ continue;
++
++ gimple *stmt = SSA_NAME_DEF_STMT (name);
++ if (gimple_code (stmt) != GIMPLE_NOP)
++ return false;
++
++ /* Each param should have an unique default ssa def. */
++ int index = find_param_index (SSA_NAME_VAR (name));
++ if (index == -1 || !bitmap_set_bit (visited, index))
++ return false;
++
++ default_def[index] = name;
++ }
++
++ if (bitmap_count_bits (visited) != PARAM_NUM)
++ return false;
++
++ array_main_var = default_def[array_param_index];
++ len_main_var = default_def[len_param_index];
++
++ find_tail_recursive_loop (default_def);
++
++ signed_len_var = fold_convert (RANGE_TYPE, len_main_var);
++
++ return true;
++}
++
++/* Try to find a tail recursive loop. */
++
++void
++array_dse_callee::find_tail_recursive_loop (tree *default_def)
++{
++ tree main_loop_var[PARAM_NUM] = {nullptr, nullptr};
++ loop_p unique_loop = nullptr;
++
++ for (unsigned i = 0; i < PARAM_NUM; i++)
++ {
++ tree name = default_def[i];
++
++ use_operand_p use_p;
++ gimple *stmt = nullptr;
++ if (!single_imm_use (name, &use_p, &stmt)
++ || gimple_code (stmt) != GIMPLE_PHI)
++ return;
++
++ main_loop_var[i] = gimple_phi_result (stmt);
++
++ /* Check if all main vars are defined in the same loop header. */
++ basic_block bb = gimple_bb (stmt);
++ loop_p loop = bb->loop_father;
++ if (!loop || loop->num == 0 || !bb_loop_header_p (bb)
++ || (unique_loop && unique_loop != loop))
++ return;
++
++ unique_loop = loop;
++ }
++
++ /* Multiple latch is not allow. */
++ if (!unique_loop || !loop_latch_edge (unique_loop))
++ return;
++
++ /* The loop header must be the "first" block. There shouldn't be any
++ stmt before entering main loop. */
++ basic_block entry_bb = ENTRY_BLOCK_PTR_FOR_FN (cfun);
++ basic_block preheader = loop_preheader_edge (unique_loop)->src;
++ if (single_succ_p (preheader) && single_pred_p (preheader)
++ && single_pred (preheader) == entry_bb && single_succ_p (entry_bb)
++ && empty_block_p (preheader))
++ {
++ main_loop = unique_loop;
++ array_main_var = main_loop_var[array_param_index];
++ len_main_var = main_loop_var[len_param_index];
++ }
++}
++
++/* Check if the function only store to the array passed by its param. */
++
++bool
++array_dse_callee::find_candidate_array ()
++{
++ tree unique_array = nullptr;
++ basic_block bb;
++ FOR_EACH_BB_FN (bb, cfun)
++ {
++ for (auto gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
++ {
++ gimple *stmt = gsi_stmt (gsi);
++ if (gimple_clobber_p (stmt))
++ continue;
++
++ /* There are 3 kind of stmts may have store ops: GIMPLE_ASSIGN,
++ GIMPLE_CALL and GIMPLE_ASM. */
++ if (gimple_has_volatile_ops (stmt)
++ || gimple_code (stmt) == GIMPLE_ASM)
++ return false;
++
++ /* We have check that current function only has recursive call,
++ and it doesn't return a value, so we can skip call stmt. */
++ if (gimple_code (stmt) != GIMPLE_ASSIGN)
++ continue;
++
++ tree lhs = gimple_assign_lhs (stmt);
++ if (TREE_CODE (lhs) == SSA_NAME)
++ continue;
++
++ tree base = strip_base (lhs);
++ if (!base || TREE_CODE (base) != SSA_NAME)
++ return false;
++
++ tree array = analyzer.get_address (base);
++ if (!array || (unique_array && unique_array != array))
++ return false;
++
++ unique_array = array;
++ }
++ }
++
++ if (!unique_array)
++ return false;
++
++ int index = find_param_index (unique_array);
++ if (index < 0)
++ return false;
++
++ array_param = unique_array;
++ array_param_index = index;
++
++ if (dump_file)
++ {
++ fprintf (dump_file, "Found unique stored array: ");
++ print_generic_expr (dump_file, unique_array);
++ fprintf (dump_file, "\n");
++ }
++
++ return true;
++}
++
++/* Check if the function has length param. */
++
++bool
++array_dse_callee::find_length_param ()
++{
++ collect_read_write_ptrs ();
++
++ tree len = nullptr;
++ unsigned size = 0;
++ for (auto ptr : all_ptrs)
++ if (!check_pointer (ptr, len, size))
++ return false;
++
++ if (!len || TREE_CODE (len) != SSA_NAME || !SSA_NAME_VAR (len))
++ return false;
++
++ int index = find_param_index (SSA_NAME_VAR (len));
++ if (index < 0)
++ return false;
++
++ len_param = SSA_NAME_VAR (len);
++ len_param_index = index;
++ elem_size = build_int_cst (RANGE_TYPE, size);
++ elem_size_cst = size;
++ calc_length_param_max ();
++
++ if (len && dump_file)
++ {
++ fprintf (dump_file, "Found unique array length: ");
++ print_generic_expr (dump_file, len_param);
++ fprintf (dump_file, "\n");
++ }
++
++ return true;
++}
++
++void
++array_dse_callee::collect_read_write_ptrs ()
++{
++ basic_block bb;
++ FOR_EACH_BB_FN (bb, cfun)
++ {
++ for (auto gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
++ {
++ gimple *stmt = gsi_stmt (gsi);
++
++ for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
++ {
++ tree op = gimple_op (stmt, i);
++ if (!op)
++ continue;
++
++ tree base = strip_base (op);
++ if (!base || TREE_CODE (base) != SSA_NAME)
++ continue;
++
++ tree array = analyzer.get_address (base);
++ if (array != array_param)
++ continue;
++
++ all_ptrs.add (base);
++ }
++ }
++ }
++}
++
++/* We heuristically set upper bound of length param to the max value with
++ half bits of its data type.
++
++ TODO: Overflows may still occur, we need a better implement.
++ */
++
++void
++array_dse_callee::calc_length_param_max ()
++{
++ unsigned bits = TYPE_PRECISION (TREE_TYPE (len_param));
++ len_param_max = 1L << (bits / 2);
++}
++
++/* Check pointer pattern: ptr = ptr1 + offset1
++ |
++ ptr2 + offset2
++ |
++ ...
++ |
++ ARRAY + offset3
++
++ All ptrs we visited must be calculated by adding offset to array_param.
++ All offset must be an expression with the only variable len_param.
++ LEN will be set to the unique variable we founded.
++ SIZE will be set to the minimum offset unit, which will be treated as the
++ array element size.
++ */
++
++bool
++array_dse_callee::check_pointer (tree ptr, tree &len, unsigned &size)
++{
++ visited_offset.empty ();
++ auto_bitmap visited;
++ auto_vec worklist;
++ worklist.safe_push (ptr);
++
++ while (!worklist.is_empty ())
++ {
++ tree t = worklist.pop ();
++ if (!POINTER_TYPE_P (TREE_TYPE (t)) || TREE_CODE (t) != SSA_NAME)
++ return false;
++
++ if (!bitmap_set_bit (visited, SSA_NAME_VERSION (t)))
++ continue;
++
++ if (SSA_NAME_IS_DEFAULT_DEF (t))
++ {
++ tree var = SSA_NAME_VAR (t);
++ if (!var || var != array_param)
++ return false;
++
++ continue;
++ }
++
++ gimple *stmt = SSA_NAME_DEF_STMT (t);
++ if (is_gimple_assign (stmt))
++ {
++ worklist.safe_push (gimple_assign_rhs1 (stmt));
++ if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
++ && !check_offset (gimple_assign_rhs2 (stmt), len, size,
++ worklist))
++ return false;
++ }
++ else if (gimple_code (stmt) == GIMPLE_PHI)
++ {
++ for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++)
++ worklist.safe_push (gimple_phi_arg_def (stmt, i));
++ }
++ else
++ return false;
++ }
++
++ return true;
++}
++
++/* Check offset part. */
++
++bool
++array_dse_callee::check_offset (tree var, tree &len, unsigned &size,
++ auto_vec &worklist)
++{
++ if (visited_offset.contains (var))
++ return true;
++ visited_offset.add (var);
++
++ if (TREE_CODE (TREE_TYPE (var)) != INTEGER_TYPE)
++ return false;
++
++ tree offset = strip_ssa_copy (var);
++ if (TREE_CODE (offset) == INTEGER_CST)
++ {
++ HOST_WIDE_INT value = TREE_INT_CST_LOW (offset);
++ value = std::abs (value);
++ size = size ? greatest_common_divisor (size, value) : value;
++ return true;
++ }
++
++ if (TREE_CODE (offset) != SSA_NAME)
++ return false;
++
++ gimple *stmt = SSA_NAME_DEF_STMT (offset);
++ if (gimple_code (stmt) == GIMPLE_PHI)
++ {
++ for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++)
++ if (!check_offset (gimple_phi_arg_def (stmt, i), len, size, worklist))
++ return false;
++ }
++ else if (!is_gimple_assign (stmt))
++ return false;
++
++ switch (gimple_assign_rhs_code (stmt))
++ {
++ case MAX_EXPR:
++ [[fallthrough]];
++ case MIN_EXPR:
++ [[fallthrough]];
++ case PLUS_EXPR:
++ [[fallthrough]];
++ case MINUS_EXPR:
++ return check_offset (gimple_assign_rhs1 (stmt), len, size, worklist)
++ && check_offset (gimple_assign_rhs2 (stmt), len, size,
++ worklist);
++ case POINTER_DIFF_EXPR:
++ worklist.safe_push(gimple_assign_rhs1 (stmt));
++ worklist.safe_push(gimple_assign_rhs2 (stmt));
++ return true;
++ case NEGATE_EXPR:
++ return check_offset (gimple_assign_rhs1 (stmt), len, size, worklist);
++ case MULT_EXPR:
++ return check_mult_expr (stmt, len, size);
++ default:
++ return false;
++ }
++}
++
++/* Handle MULT_EXPR. */
++
++bool
++array_dse_callee::check_mult_expr (gimple *stmt, tree &len, unsigned &size)
++{
++ tree rhs1 = gimple_assign_rhs1 (stmt);
++ tree rhs2 = gimple_assign_rhs2 (stmt);
++
++ /* Handle size. */
++ if (TREE_CODE (rhs2) != INTEGER_CST)
++ return false;
++
++ HOST_WIDE_INT value = TREE_INT_CST_LOW (rhs2);
++ size = greatest_common_divisor (size, std::abs (value));
++
++ /* Handle index. */
++ rhs1 = strip_ssa_copy (rhs1);
++ if (TREE_CODE (rhs1) != SSA_NAME)
++ return false;
++
++ gimple *index_stmt = SSA_NAME_DEF_STMT (rhs1);
++ if (is_gimple_assign (index_stmt) && gimple_num_ops (index_stmt) > 2)
++ {
++ if (TREE_CODE (gimple_assign_rhs2 (index_stmt)) != INTEGER_CST)
++ return false;
++ rhs1 = gimple_assign_rhs1 (index_stmt);
++ }
++
++ if (len && len != rhs1)
++ return false;
++ len = rhs1;
++
++ return true;
++}
++
++/* Find the param index of VAR in current function.
++ Return -1 if not found. */
++
++int
++array_dse_callee::find_param_index (tree var)
++{
++ if (TREE_CODE (var) != PARM_DECL)
++ return -1;
++
++ tree param = DECL_ARGUMENTS (node->decl);
++ int index = 0;
++ while (param)
++ {
++ if (param == var)
++ return index;
++
++ param = DECL_CHAIN (param);
++ index++;
++ }
++
++ return -1;
++}
++
++bool
++array_dse_callee::check_array_usage ()
++{
++ find_main_vars ();
++
++ return calc_ptr_range () && check_ptr_range ()
++ && check_recursive_call_arg ();
++}
++
++/* Calculate len_param's value range in each block.
++ We assume its initial range is [1, len_param_max], we will validate this
++ range at each call to this callee. */
++
++void
++array_dse_callee::calc_len_range ()
++{
++ /* Init all blocks' len_range. */
++ auto full_len_range = make_range (len_param_min, len_param_max);
++ basic_block bb;
++ FOR_EACH_BB_FN (bb, cfun)
++ len_range_map.put (bb, full_len_range);
++
++ /* Calculate new range according to condition. */
++ FOR_EACH_BB_FN (bb, cfun)
++ {
++ gimple *stmt = gsi_stmt (gsi_last_bb (bb));
++ auto cond_range = get_range_from_cond (stmt);
++ if (cond_range.undefined_p ())
++ continue;
++
++ edge true_edge = nullptr;
++ edge false_edge = nullptr;
++ extract_true_false_edges_from_block (bb, &true_edge, &false_edge);
++ update_len_range (true_edge->dest, cond_range);
++ update_len_range (false_edge->dest, invert_range (cond_range));
++ }
++}
++
++/* Update len_param's range in all block dominated by START. */
++
++void
++array_dse_callee::update_len_range (basic_block start,
++ const value_range &new_range)
++{
++ for (auto bb : get_all_dominated_blocks (CDI_DOMINATORS, start))
++ (*len_range_map.get (bb)).intersect (new_range);
++}
++
++value_range
++array_dse_callee::invert_range (const value_range &range) const
++{
++ auto new_range = range;
++ new_range.invert ();
++
++ return new_range;
++}
++
++/* Get range of len_param from a condition. */
++
++value_range
++array_dse_callee::get_range_from_cond (gimple *stmt)
++{
++ if (!stmt || gimple_code (stmt) != GIMPLE_COND)
++ return value_range{};
++
++ gcond *cond = as_a (stmt);
++ tree_code code = gimple_cond_code (cond);
++ tree lhs = gimple_cond_lhs (cond);
++ tree rhs = gimple_cond_rhs (cond);
++
++ if (lhs != len_main_var || TREE_CODE (rhs) != INTEGER_CST)
++ return value_range{};
++
++ HOST_WIDE_INT value = TREE_INT_CST_LOW (rhs);
++
++ switch (code)
++ {
++ case LT_EXPR:
++ return make_range (RANGE_NINF, value - 1);
++ case LE_EXPR:
++ return make_range (RANGE_NINF, value);
++ case GT_EXPR:
++ return make_range (value + 1, RANGE_INF);
++ case GE_EXPR:
++ return make_range (value, RANGE_INF);
++ case EQ_EXPR:
++ return make_range (value);
++ case NE_EXPR:
++ return invert_range (make_range (value));
++ default:
++ return value_range{};
++ }
++}
++
++/* Get range of a variable, represented by len_param. If variable is a
++ pointer, return the range of its offset from array_param. */
++
++value_range
++array_dse_callee::get_var_range (basic_block bb, tree var)
++{
++ if (var == array_main_var)
++ return make_range (0, 0);
++
++ if (var == len_main_var)
++ return value_range{signed_len_var, signed_len_var};
++
++ if (find_var_range (var, bb))
++ return var_range[var][bb];
++
++ /* If we can't calculate its range, keep it varying. */
++ auto &range = var_range[var][bb];
++ range.set_varying (RANGE_TYPE);
++
++ if (TREE_CODE (var) == INTEGER_CST)
++ {
++ HOST_WIDE_INT value = TREE_INT_CST_LOW (var);
++ range = make_range (value);
++ return range;
++ }
++
++ if (TREE_CODE (var) != SSA_NAME)
++ return range;
++
++ /* Build range expression recursively. */
++ gimple *stmt = SSA_NAME_DEF_STMT (var);
++ if (gimple_code (stmt) == GIMPLE_PHI)
++ {
++ range = get_var_range (bb, gimple_phi_arg_def (stmt, 0));
++ for (unsigned i = 1; i < gimple_phi_num_args (stmt); i++)
++ {
++ tree arg = gimple_phi_arg_def (stmt, i);
++ auto arg_range = get_var_range (bb, arg);
++ tree min = min_tree (bb, range.min (), arg_range.min ());
++ tree max = max_tree (bb, range.max (), arg_range.max ());
++ if (!min || !max)
++ {
++ range.set_varying (RANGE_TYPE);
++ break;
++ }
++
++ range = value_range{min, max};
++ }
++ return range;
++ }
++
++ if (!is_gimple_assign (stmt))
++ return range;
++
++ tree rhs1 = gimple_assign_rhs1 (stmt);
++ tree rhs2 = gimple_num_ops (stmt) > 2 ? gimple_assign_rhs2 (stmt) : nullptr;
++ value_range range1 = get_var_range (bb, rhs1);
++ value_range range2 = value_range{RANGE_TYPE};
++ if (rhs2)
++ range2 = get_var_range (bb, rhs2);
++
++ if (gimple_assign_single_p (stmt) || gimple_assign_cast_p (stmt))
++ range = range1;
++ else
++ range = build_range (bb, gimple_assign_rhs_code (stmt), range1, range2);
++
++ return range;
++}
++
++/* Calculate pointer's offset range by checking loop condition. */
++
++bool
++array_dse_callee::calc_ptr_range ()
++{
++ calc_len_range ();
++
++ auto_bitmap visited;
++ auto_vec worklist;
++ loop_p l = main_loop ? main_loop : current_loops->tree_root;
++ worklist.safe_push (l->header);
++
++ while (!worklist.is_empty ())
++ {
++ basic_block bb = worklist.pop ();
++ if (bb == EXIT_BLOCK_PTR_FOR_FN (cfun)
++ || (main_loop && bb == main_loop->latch)
++ || unreachable_blocks.contains (bb))
++ continue;
++
++ if (bb->flags & BB_IRREDUCIBLE_LOOP)
++ return false;
++
++ if (!bitmap_set_bit (visited, bb->index))
++ continue;
++
++ if (loop_header_p (bb) && !calc_loop_var_range (bb->loop_father))
++ return false;
++
++ for (auto succ : bb->succs)
++ worklist.safe_push (succ->dest);
++ }
++
++ return true;
++}
++
++/* Check if offset range of all pointers calculated by array_param are
++ within [0, (len_param -1) * elem_size]. */
++
++bool
++array_dse_callee::check_ptr_range ()
++{
++ basic_block bb;
++ FOR_EACH_BB_FN (bb, cfun)
++ {
++ if (unreachable_blocks.contains (bb))
++ continue;
++
++ for (auto gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
++ {
++ gimple *stmt = gsi_stmt (gsi);
++
++ for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
++ {
++ tree op = gimple_op (stmt, i);
++ if (!op)
++ continue;
++
++ tree base = strip_base (op);
++ if (!base || TREE_CODE (base) != SSA_NAME
++ || !all_ptrs.contains (base))
++ continue;
++
++ auto range = get_var_range (bb, base);
++ /* offset >= 0. */
++ auto ret = compare_tree (bb, range.min (), integer_zero_node);
++ if (!ret || ret & LT)
++ return false;
++
++ /* offset <= (n - 1) * elem_size. */
++ tree tmp = minus_tree (signed_len_var, integer_one_node);
++ tmp = mult_tree (bb, tmp, elem_size);
++ ret = compare_tree (bb, range.max (), tmp);
++ if (!ret || ret & GT)
++ return false;
++ }
++ }
++ }
++
++ return true;
++}
++
++/* Check range of recursive call arguments:
++ void func(a, n) {
++ ...
++ func(a1, n1);
++ }
++
++ 1. (1) a1 >= a (tail recursive call)
++ (2) a1 == a (normal recursive call)
++ 2. n1 >= 1
++ 3. a1 + n1 * elem_size <= a + n * elem_size
++ */
++
++bool
++array_dse_callee::check_recursive_call_arg ()
++{
++ auto_vec array_args;
++ auto_vec len_args;
++ auto_vec blocks;
++ auto_vec is_tail_recursive_call;
++
++ collect_recursive_call_args (array_args, len_args, blocks,
++ is_tail_recursive_call);
++
++ for (unsigned i = 0; i < array_args.length (); i++)
++ {
++ basic_block bb = blocks[i];
++ update_branch_range (bb);
++ auto array_range = get_var_range (bb, array_args[i]);
++ auto len_range = get_var_range (bb, len_args[i]);
++
++ /* Check requirement 2. */
++ auto ret = compare_tree (bb, len_range.min (), integer_one_node);
++ if (!ret || ret & ~GE)
++ return false;
++
++ if (is_tail_recursive_call[i])
++ {
++ /* Check requirement 1.1. */
++ ret = compare_tree (bb, array_range.min (), integer_zero_node);
++ if (!ret || ret & ~GE)
++ return false;
++ }
++ else
++ {
++ /* Check requirement 1.2. */
++ if (!integer_zerop (array_range.min ())
++ || !integer_zerop (array_range.max ()))
++ return false;
++ }
++
++ /* Check requirement 3. */
++ tree offset = build_recursive_offset (len_args[i]);
++ if (!offset)
++ return false;
++
++ tree recursive_ptr_max
++ = build_recursive_ptr_range_max (bb, array_args[i], offset);
++ if (!recursive_ptr_max)
++ return false;
++
++ tree upper_bound = mult_tree (bb, signed_len_var, elem_size);
++ ret = compare_tree (bb, recursive_ptr_max, upper_bound);
++ if (!ret || ret & ~LE)
++ return false;
++ }
++
++ return true;
++}
++
++void
++array_dse_callee::collect_recursive_call_args (
++ auto_vec &array_args, auto_vec &len_args,
++ auto_vec &blocks, auto_vec &is_tail_recursive_call)
++{
++ for (cgraph_edge *edge = node->callees; edge; edge = edge->next_callee)
++ {
++ if (node != edge->callee)
++ continue;
++
++ gcall *call = edge->call_stmt;
++ tree array_arg = gimple_call_arg (call, array_param_index);
++ tree len_arg = gimple_call_arg (call, len_param_index);
++
++ array_args.safe_push (array_arg);
++ len_args.safe_push (len_arg);
++ blocks.safe_push (gimple_bb (call));
++ is_tail_recursive_call.safe_push (tail_recursive_call_p (call));
++ }
++
++ if (main_loop)
++ {
++ gimple *array_def_stmt = SSA_NAME_DEF_STMT (array_main_var);
++ gimple *len_def_stmt = SSA_NAME_DEF_STMT (len_main_var);
++ edge latch_edge = loop_latch_edge (main_loop);
++ tree array_arg = PHI_ARG_DEF_FROM_EDGE (array_def_stmt, latch_edge);
++ tree len_arg = PHI_ARG_DEF_FROM_EDGE (len_def_stmt, latch_edge);
++ array_args.safe_push (array_arg);
++ len_args.safe_push (len_arg);
++ blocks.safe_push (latch_edge->src);
++ is_tail_recursive_call.safe_push (true);
++ }
++}
++
++/* If BB is first block after a condition jump, try to update range according
++ to the condition. */
++
++void
++array_dse_callee::update_branch_range (basic_block bb)
++{
++ if (!single_pred_p (bb))
++ return;
++
++ basic_block pred = single_pred (bb);
++ gimple *stmt = gsi_stmt (gsi_last_bb (pred));
++ if (!stmt || gimple_code (stmt) != GIMPLE_COND)
++ return;
++
++ tree lhs = gimple_cond_lhs (stmt);
++ tree rhs = gimple_cond_rhs (stmt);
++ if (!integer_cst_p (rhs))
++ return;
++
++ tree_code code = gimple_cond_code (stmt);
++ if (single_pred_edge (bb)->flags & EDGE_FALSE_VALUE)
++ code = opposite_cond_code (code);
++
++ auto range = get_var_range (bb, lhs);
++ tree min = range.min ();
++ tree max = range.max ();
++ HOST_WIDE_INT value = TREE_INT_CST_LOW (rhs);
++
++ switch (code)
++ {
++ case LT_EXPR:
++ value--;
++ [[fallthrough]];
++ case LE_EXPR:
++ max = min_tree (bb, max, build_value (value));
++ break;
++ case GT_EXPR:
++ value++;
++ [[fallthrough]];
++ case GE_EXPR:
++ min = max_tree (bb, min, build_value (value));
++ break;
++ case EQ_EXPR:
++ var_range[lhs][bb] = make_range (value);
++ return;
++ default:
++ return;
++ }
++
++ var_range[lhs][bb] = value_range{min, max};
++}
++
++/* If LEN = (ptr1 - ptr2) / elem_size,
++ then recursive_offset = LEN * elem_size = (ptr1 - ptr2).
++
++ We can do this only when ptr1 and ptr2 comes from array_param,
++ so (ptr1 - ptr2) is an integer multiple of elem_size.
++ */
++
++tree
++array_dse_callee::build_recursive_offset (tree len_arg)
++{
++ if (TREE_CODE (len_arg) != SSA_NAME)
++ return nullptr;
++
++ gimple *stmt = SSA_NAME_DEF_STMT (len_arg);
++ if (!is_gimple_assign (stmt))
++ return nullptr;
++
++ /* Check pattern: (ptr1 - ptr2) / elem_size. */
++ tree_code code = gimple_assign_rhs_code (stmt);
++ if (code != TRUNC_DIV_EXPR && code != RSHIFT_EXPR)
++ return nullptr;
++
++ tree rhs1 = gimple_assign_rhs1 (stmt);
++ if (TREE_CODE (rhs1) != SSA_NAME)
++ return nullptr;
++
++ gimple *def = SSA_NAME_DEF_STMT (strip_ssa_copy (rhs1));
++ if (!is_gimple_assign (def)
++ || gimple_assign_rhs_code (def) != POINTER_DIFF_EXPR)
++ return nullptr;
++
++ /* Check ptr1 and ptr2. */
++ tree len = nullptr;
++ unsigned size = 0;
++ if (!check_pointer (gimple_assign_rhs1 (def), len, size)
++ || !check_pointer (gimple_assign_rhs2 (def), len, size)
++ || len != len_main_var || size != elem_size_cst)
++ return nullptr;
++
++ tree rhs2 = gimple_assign_rhs2 (stmt);
++ if (!integer_cst_p (rhs2))
++ return nullptr;
++
++ HOST_WIDE_INT value = TREE_INT_CST_LOW (rhs2);
++ if (code == RSHIFT_EXPR)
++ value = 1 << value;
++
++ if (value != elem_size_cst)
++ return nullptr;
++
++ return rhs1;
++}
++
++/* Build expression of recursive pointer range max. */
++
++tree
++array_dse_callee::build_recursive_ptr_range_max (basic_block bb,
++ tree array_arg,
++ tree offset)
++{
++ if (TREE_CODE (array_arg) != SSA_NAME)
++ return nullptr;
++
++ tree recursive_ptr_max = nullptr;
++ gimple *stmt = SSA_NAME_DEF_STMT (array_arg);
++
++ /* If ARRAY_ARG = rhs1 - offset, return rhs1's range max directly. */
++ if (is_gimple_assign (stmt)
++ && gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
++ {
++ tree rhs1 = gimple_assign_rhs1 (stmt);
++ tree rhs2 = gimple_assign_rhs2 (stmt);
++ if (TREE_CODE (rhs2) == SSA_NAME)
++ {
++ stmt = SSA_NAME_DEF_STMT (rhs2);
++ if (is_gimple_assign (stmt)
++ && gimple_assign_rhs_code (stmt) == NEGATE_EXPR
++ && gimple_assign_rhs1 (stmt) == offset)
++ recursive_ptr_max = get_var_range (bb, rhs1).max ();
++ }
++ }
++
++ if (!recursive_ptr_max)
++ {
++ auto range1 = get_var_range (bb, array_arg);
++ auto range2 = get_var_range (bb, offset);
++ recursive_ptr_max = plus_tree (range1.max (), range2.max ());
++ }
++
++ return recursive_ptr_max;
++}
++
++bool
++array_dse_callee::tail_recursive_call_p (gimple *stmt)
++{
++ if (stmt->next)
++ return false;
++
++ basic_block bb = gimple_bb (stmt);
++ return single_succ_p (bb) && return_bb_p (single_succ (bb));
++}
++
++bool
++array_dse_callee::return_bb_p (basic_block bb) const
++{
++ return bb && single_succ_p (bb) &&
++ single_succ (bb) == EXIT_BLOCK_PTR_FOR_FN (cfun);
++}
++
++/* Calculate the range of a loop variable according to initial value and
++ loop exit condition. */
++
++bool
++array_dse_callee::calc_loop_var_range (loop_p loop)
++{
++ if (!loops_state_satisfies_p (LOOPS_HAVE_PREHEADERS)
++ && loops_state_satisfies_p (LOOPS_MAY_HAVE_MULTIPLE_LATCHES))
++ return false;
++
++ basic_block header = loop->header;
++ for (auto gsi = gsi_start_phis (header); !gsi_end_p (gsi); gsi_next (&gsi))
++ {
++ gphi *phi = as_a (gsi_stmt (gsi));
++ tree result = gimple_phi_result (phi);
++ if (!loop_var_p (loop, result))
++ continue;
++
++ tree iterate_var = PHI_ARG_DEF_FROM_EDGE (phi, loop_latch_edge (loop));
++ int step = calc_loop_var_step (result, iterate_var);
++ unsigned abs_step = static_cast (std::abs (step));
++ if (!step)
++ continue;
++
++ if (POINTER_TYPE_P (TREE_TYPE (result)))
++ {
++ if (abs_step != elem_size_cst)
++ return false;
++ loop_ptrs[loop].add (result);
++ }
++ else if (TREE_CODE (TREE_TYPE (result)) != INTEGER_TYPE
++ || abs_step != 1)
++ return false;
++
++ tree init_var = PHI_ARG_DEF_FROM_EDGE (phi, loop_preheader_edge (loop));
++ auto init_range = get_var_range (header, init_var);
++ tree min = step > 0 ? init_range.min () : build_value (RANGE_NINF);
++ tree max = step > 0 ? build_value (RANGE_INF) : init_range.max ();
++ auto new_range = value_range{min, max};
++ for (auto bb : get_all_dominated_blocks (CDI_DOMINATORS, header))
++ {
++ if (!find_var_range (result, bb))
++ {
++ var_range[result][bb] = new_range;
++ continue;
++ }
++
++ auto &range = var_range[result][bb];
++ min = max_tree (bb, min, range.min ());
++ max = min_tree (bb, max, range.max ());
++ if (!min || !max)
++ return false;
++ range = value_range {min, max};
++ }
++ }
++
++ if (!check_loop_exits (loop))
++ return false;
++
++ return true;
++}
++
++bool
++array_dse_callee::check_loop_exits (loop_p loop)
++{
++ for (auto edge : get_loop_exit_edges (loop))
++ {
++ gimple *stmt = gsi_stmt (gsi_last_bb (edge->src));
++ if (gimple_code (stmt) != GIMPLE_COND)
++ continue;
++
++ gcond *cond = as_a (stmt);
++ tree lhs = gimple_cond_lhs (cond);
++ tree rhs = gimple_cond_rhs (cond);
++
++ bool lhs_cand_p = loop_var_p (loop, lhs) || iterate_var_p (loop, lhs);
++ bool rhs_cand_p = loop_var_p (loop, rhs) || iterate_var_p (loop, rhs);
++ if (!lhs_cand_p && !rhs_cand_p)
++ continue;
++
++ tree step = nullptr;
++ if (POINTER_TYPE_P (TREE_TYPE (lhs))
++ && POINTER_TYPE_P (TREE_TYPE (rhs)))
++ {
++ if (TREE_CODE (lhs) != SSA_NAME || TREE_CODE (rhs) != SSA_NAME)
++ return false;
++ step = elem_size;
++ }
++ else if (TREE_CODE (TREE_TYPE (lhs)) == INTEGER_TYPE
++ && TREE_CODE (TREE_TYPE (rhs)) == INTEGER_TYPE)
++ {
++ if (!lhs_cand_p && !integer_cst_p (rhs))
++ return false;
++ step = integer_one_node;
++ }
++ else
++ return false;
++
++ tree_code code = gimple_cond_code (cond);
++ if (edge->flags & EDGE_TRUE_VALUE)
++ code = opposite_cond_code (code);
++
++ if (!fill_loop_var_range (loop, code, lhs, rhs, step))
++ return false;
++
++ if (iterate_var_p (loop, lhs))
++ lhs = get_loop_var (loop, lhs);
++
++ if (!fill_loop_var_range (loop, code, lhs, rhs, step))
++ return false;
++ }
++
++ return true;
++}
++
++/* fill loop variable range according to loop exit's condition and step. */
++
++bool
++array_dse_callee::fill_loop_var_range (loop_p loop, tree_code code,
++ tree lhs, tree rhs, tree step)
++{
++ for (auto bb : get_all_dominated_blocks (CDI_DOMINATORS, loop->header))
++ {
++ auto lhs_range = get_var_range (bb, lhs);
++ auto rhs_range = get_var_range (bb, rhs);
++ tree lhs_min = lhs_range.min ();
++ tree lhs_max = lhs_range.max ();
++ tree rhs_min = rhs_range.min ();
++ tree rhs_max = rhs_range.max ();
++ bool in_loop = flow_bb_inside_loop_p (loop, bb);
++
++ switch (code)
++ {
++ case LT_EXPR:
++ lhs_max = in_loop ? minus_tree (rhs_max, step) : rhs_max;
++ rhs_min = in_loop ? plus_tree (lhs_min, step) : lhs_min;
++ break;
++ case LE_EXPR:
++ lhs_max = in_loop ? rhs_max : plus_tree (rhs_max, step);
++ rhs_min = in_loop ? lhs_min : minus_tree (lhs_min, step);
++ break;
++ case GT_EXPR:
++ lhs_min = in_loop ? plus_tree (rhs_min, step) : rhs_min;
++ rhs_max = in_loop ? minus_tree (lhs_max, step) : lhs_max;
++ break;
++ case GE_EXPR:
++ lhs_min = in_loop ? rhs_min : minus_tree (rhs_min, step);
++ rhs_max = in_loop ? lhs_max : plus_tree (lhs_max, step);
++ break;
++ default:
++ return false;
++ }
++
++ if (loop_var_p (loop, lhs) || iterate_var_p (loop, lhs))
++ var_range[lhs][bb] = value_range{lhs_min, lhs_max};
++ if (loop_var_p (loop, rhs) || iterate_var_p (loop, rhs))
++ var_range[rhs][bb] = value_range{rhs_min, rhs_max};
++
++ if (integer_onep (step) && loop_var_p (loop, lhs)
++ && !fill_loop_ptr_range (loop, bb, minus_tree (lhs_max, lhs_min)))
++ return false;
++ }
++
++ return true;
++}
++
++/* If the variable in loop exit's condition is a integer, like
++ for (i = 0; i < n; i++)
++
++ fill other pointers' range in the same loop.
++ */
++
++bool
++array_dse_callee::fill_loop_ptr_range (loop_p loop, basic_block bb,
++ tree loop_length)
++{
++ if (!loop_length)
++ return false;
++
++ auto ret = compare_tree (bb, loop_length, integer_zero_node);
++ if (!ret)
++ return false;
++
++ if (ret == LT)
++ {
++ unreachable_blocks.add (bb);
++ return true;
++ }
++
++ tree length = mult_tree (bb, loop_length, elem_size);
++ if (!length)
++ return false;
++
++ for (auto ptr : loop_ptrs[loop])
++ {
++ auto &range = var_range[ptr][bb];
++ tree min = range.min ();
++ tree max = range.max ();
++ if (infinite_p (min) != infinite_kind::NON_INF)
++ {
++ if (infinite_p (max) != infinite_kind::NON_INF)
++ return false;
++ min = minus_tree (max, length);
++ }
++ else if (infinite_p (max) != infinite_kind::NON_INF)
++ {
++ if (infinite_p (min) != infinite_kind::NON_INF)
++ return false;
++ max = plus_tree (min, length);
++ }
++ else
++ return false;
++
++ range = value_range{min, max};
++ }
++
++ return true;
++}
++
++bool
++array_dse_callee::loop_header_p (basic_block bb)
++{
++ return bb_loop_header_p (bb)
++ && (!main_loop || bb->loop_father != main_loop);
++}
++
++bool
++array_dse_callee::iterate_var_p (loop_p loop, tree var)
++{
++ if (!var)
++ return false;
++
++ tree loop_var = get_loop_var (loop, var);
++ return loop_var && loop_var != var;
++}
++
++/* Find the loop variable from the GIVEN var throught its def chain. */
++
++tree
++array_dse_callee::get_loop_var (loop_p loop, tree var)
++{
++ if (TREE_CODE (var) != SSA_NAME || !SSA_NAME_VAR (var))
++ return nullptr;
++
++ tree result = nullptr;
++
++ auto_bitmap visited;
++ auto_vec worklist;
++ worklist.safe_push (var);
++
++ while (!worklist.is_empty ())
++ {
++ tree t = worklist.pop ();
++ if (TREE_CODE (var) != SSA_NAME)
++ return nullptr;
++
++ if (!bitmap_set_bit (visited, SSA_NAME_VERSION (t)))
++ continue;
++
++ if (loop_var_p (loop, t) && SSA_NAME_VAR (t) == SSA_NAME_VAR (var))
++ {
++ if (result && result != t)
++ return nullptr;
++ result = t;
++ continue;
++ }
++
++ gimple *stmt = SSA_NAME_DEF_STMT (t);
++ basic_block bb = gimple_bb (stmt);
++ if (!bb || !flow_bb_inside_loop_p (loop, gimple_bb (stmt)))
++ return nullptr;
++
++ if (gimple_code (stmt) == GIMPLE_PHI)
++ {
++ for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++)
++ worklist.safe_push (gimple_phi_arg_def (stmt, i));
++ continue;
++ }
++
++ if (!is_gimple_assign (stmt)
++ || (gimple_assign_rhs_code (stmt) != POINTER_PLUS_EXPR
++ && gimple_assign_rhs_code (stmt) != PLUS_EXPR))
++ return nullptr;
++
++ worklist.safe_push (gimple_assign_rhs1 (stmt));
++ }
++
++ return result;
++}
++
++bool
++array_dse_callee::find_var_range (tree var, basic_block bb)
++{
++ auto iter1 = var_range.find (var);
++ if (iter1 == var_range.end ())
++ return false;
++
++ auto iter2 = iter1->second.find (bb);
++ return iter2 != iter1->second.end ();
++}
++
++array_dse_edge::array_dse_edge (cgraph_edge *edge, array_dse_callee *callee)
++ : call_edge (edge),
++ callee (callee)
++{
++}
++
++bool
++array_dse_edge::analyze ()
++{
++ cfun_saver save (call_edge->caller, LOOPS_NORMAL);
++
++ if (gimple_call_num_args (call_edge->call_stmt) != callee->PARAM_NUM)
++ return false;
++
++ return find_local_array_from_arg () && check_array_usage ()
++ && check_len_arg_range ();
++}
++
++bool
++array_dse_edge::fully_redundant ()
++{
++ return array_arg_start > read_upper_bound;
++}
++
++tree
++array_dse_edge::get_bound_addr ()
++{
++ unsigned HOST_WIDE_INT bound_size = (read_upper_bound + 1) * elem_size;
++ tree bound_size_expr = build_int_cst (size_type_node, bound_size);
++
++ tree addr_type = build_pointer_type (TREE_TYPE (array));
++ tree array_addr = build1 (ADDR_EXPR, addr_type, array);
++
++ return build2 (POINTER_PLUS_EXPR, addr_type, array_addr, bound_size_expr);
++}
++
++/* Find the local array used by call argument. */
++
++bool
++array_dse_edge::find_local_array_from_arg ()
++{
++ tree arg = gimple_call_arg (call_edge->call_stmt, callee->array_param_index);
++
++ while (TREE_CODE (arg) == ADDR_EXPR || TREE_CODE (arg) == MEM_REF)
++ arg = TREE_OPERAND (arg, 0);
++
++ if (!arg || !VAR_P (arg) || TREE_CODE (TREE_TYPE (arg)) != ARRAY_TYPE
++ || decl_function_context (arg) != current_function_decl)
++ return false;
++
++ array = arg;
++ elem_size = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (array))));
++ array_size = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (array))) / elem_size;
++
++ return true;
++}
++
++bool
++array_dse_edge::check_array_usage ()
++{
++ if (!collect_array_accesses ())
++ return false;
++
++ if (!find_inner_array ())
++ return false;
++
++ for (auto [var, stmt] : array_accesses)
++ if (!check_access_kind (stmt, var))
++ return false;
++
++ collect_call_block_succs ();
++ if (!calc_read_bound () || !calc_array_arg_start ())
++ return false;
++
++ if (call_block_succs.contains (gimple_bb (call_edge->call_stmt))
++ && !check_optimized_area_rewrite ())
++ return false;
++
++ return true;
++}
++
++bool
++array_dse_edge::collect_array_accesses ()
++{
++ basic_block bb;
++ FOR_EACH_BB_FN (bb, cfun)
++ {
++ if (bb->flags & BB_IRREDUCIBLE_LOOP)
++ return false;
++
++ for (auto gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
++ {
++ gphi *phi = as_a (gsi_stmt (gsi));
++ tree result = gimple_phi_result (phi);
++ if (walk_tree (&result, sub_expr_p, array, nullptr))
++ if (!check_array_access (phi, result))
++ return false;
++
++ for (unsigned i = 0; i < gimple_phi_num_args (phi); i++)
++ {
++ tree arg = gimple_phi_arg_def (phi, i);
++ if (!walk_tree (&arg, sub_expr_p, array, nullptr))
++ continue;
++
++ if (!check_array_access (phi, arg))
++ return false;
++ }
++ }
++
++ for (auto gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
++ {
++ gimple *stmt = gsi_stmt (gsi);
++ if (gimple_clobber_p (stmt) || call_stmt_p (stmt))
++ continue;
++
++ for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
++ {
++ tree var = gimple_op (stmt, i);
++ if (!var)
++ continue;
++
++ if (!walk_tree (&var, sub_expr_p, array, nullptr))
++ continue;
++
++ if (!is_gimple_assign (stmt))
++ return false;
++
++ if (!check_array_access (stmt, var))
++ return false;
++ }
++ }
++
++ }
++
++ renumber_gimple_stmt_uids (cfun);
++
++ return !array_accesses.is_empty ();
++}
++
++bool
++array_dse_edge::check_array_access (gimple *stmt, tree var)
++{
++ if (array_ref_p (var))
++ return gimple_assign_single_p (stmt) && !array_accesses.put (var, stmt);
++
++ if (array_addr_p (var))
++ return check_array_address (stmt, var);
++
++ return false;
++}
++
++bool
++array_dse_edge::check_array_address (gimple *stmt, tree addr)
++{
++ if (gimple_code (stmt) == GIMPLE_PHI)
++ return check_array_address (as_a (stmt), addr);
++
++ if (is_gimple_assign (stmt))
++ return check_array_address (as_a (stmt), addr);
++
++ return false;
++}
++
++bool
++array_dse_edge::check_array_address (gphi *phi, tree addr)
++{
++ tree result = gimple_phi_result (phi);
++ if (TREE_CODE (result) != SSA_NAME)
++ return false;
++
++ if (array_address_vars.contains (result))
++ return true;
++
++ for (unsigned i = 0; i < gimple_phi_num_args (phi); i++)
++ {
++ tree arg = gimple_phi_arg_def (phi, i);
++ if (arg == addr)
++ continue;
++
++ if (TREE_CODE (arg) != SSA_NAME)
++ return false;
++
++ /* Only support simple loop variable: VAR is the initial address of
++ phi RESULT and other ARG must be defined by RESULT + offset. */
++ gimple *stmt = SSA_NAME_DEF_STMT (arg);
++ if (!is_gimple_assign (stmt)
++ || gimple_assign_rhs_code (stmt) != POINTER_PLUS_EXPR
++ || gimple_assign_rhs1 (stmt) != result)
++ return false;
++ }
++
++ array_address_vars.add (result);
++
++ return check_access_from_address (result);
++}
++
++bool
++array_dse_edge::check_array_address (gassign *assign, tree addr)
++{
++ if (!gimple_assign_single_p (assign)
++ && gimple_assign_rhs_code (assign) != POINTER_PLUS_EXPR
++ && gimple_assign_rhs1 (assign) != addr)
++ return false;
++
++ tree lhs = gimple_assign_lhs (assign);
++ if (TREE_CODE (lhs) != SSA_NAME)
++ return false;
++
++ array_address_vars.add (lhs);
++
++ return check_access_from_address (lhs);
++}
++
++bool
++array_dse_edge::check_access_from_address (tree addr)
++{
++ gimple *stmt;
++ imm_use_iterator iter;
++ FOR_EACH_IMM_USE_STMT (stmt, iter, addr)
++ {
++ for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
++ {
++ tree op = gimple_op (stmt, i);
++ if (walk_tree (&op, sub_expr_p, addr, nullptr)
++ && !check_array_access (stmt, op))
++ return false;
++ }
++ }
++
++ return true;
++}
++
++bool
++array_dse_edge::check_access_kind (gimple *stmt, tree var)
++{
++ gcc_assert (gimple_assign_single_p (stmt));
++
++ auto &kind = access_kinds.get_or_insert (var);
++
++ tree lhs = gimple_assign_lhs (stmt);
++ if (var == lhs)
++ {
++ kind = WRITE;
++ return true;
++ }
++
++ gcc_assert (var == gimple_assign_rhs1 (stmt));
++
++ if (!inner_array)
++ {
++ kind = READ;
++ return true;
++ }
++
++ auto_bitmap visited;
++ kind = check_access_kind_iterate (lhs, visited);
++ return kind != ACCESS_ERROR;
++}
++
++access_kind
++array_dse_edge::check_access_kind_iterate (tree var, auto_bitmap &visited)
++{
++ if (!var || TREE_CODE (var) != SSA_NAME)
++ return ACCESS_ERROR;
++
++ if (!bitmap_set_bit (visited, SSA_NAME_VERSION (var)))
++ return NONE;
++
++ int kind = NONE;
++
++ imm_use_iterator iter;
++ gimple *stmt = nullptr;
++ FOR_EACH_IMM_USE_STMT (stmt, iter, var)
++ {
++ if (walk_stmt_load_store_ops (stmt, var, find_base, nullptr))
++ kind |= READ;
++
++ if (walk_stmt_load_store_ops (stmt, var, nullptr, find_base))
++ kind |= WRITE;
++
++ if (kind)
++ continue;
++
++ tree next_var = nullptr;
++ if (is_gimple_assign (stmt))
++ {
++ if ((!gimple_assign_single_p (stmt) && !gimple_assign_cast_p (stmt))
++ || gimple_assign_rhs1 (stmt) != var)
++ return ACCESS_ERROR;
++
++ tree lhs = gimple_assign_lhs (stmt);
++ if (array_ref_p (lhs))
++ {
++ kind |= READ;
++ continue;
++ }
++
++ next_var = lhs;
++ }
++ else if (gimple_code (stmt) == GIMPLE_PHI)
++ {
++ if (gimple_phi_arg_p (stmt, var))
++ next_var = gimple_phi_result (stmt);
++ }
++ else if (gimple_code (stmt) == GIMPLE_COND)
++ {
++ if (gimple_cond_lhs (stmt) == var || gimple_cond_rhs (stmt) == var)
++ {
++ kind |= READ;
++ continue;
++ }
++ }
++
++ access_kind next_kind = check_access_kind_iterate (next_var, visited);
++ if (next_kind == ACCESS_ERROR)
++ return ACCESS_ERROR;
++
++ kind |= next_kind;
++ }
++
++ return static_cast (kind);
++}
++
++bool
++array_dse_edge::find_inner_array ()
++{
++ tree type = TREE_TYPE (array);
++ unsigned ptr_layers = get_ptr_layers (type);
++ gcc_assert (ptr_layers);
++
++ /* No inner source array. */
++ if (ptr_layers == 1)
++ {
++ inner_elem_type = TREE_TYPE (array);
++ return true;
++ }
++
++ /* It's hard to trace all source of array. */
++ if (ptr_layers > 2
++ || TREE_CODE (TREE_TYPE (TREE_TYPE (type))) != RECORD_TYPE)
++ return false;
++
++ inner_elem_type = TREE_TYPE (TREE_TYPE (type));
++
++ for (auto [var, stmt] : array_accesses)
++ {
++ tree lhs = gimple_get_lhs (stmt);
++ if (lhs != var)
++ continue;
++
++ if (!array_ref_p (lhs) || !is_gimple_assign (stmt))
++ return false;
++
++ tree rhs = gimple_assign_rhs1 (stmt);
++ if (array_ref_p (rhs))
++ continue;
++
++ if (TREE_CODE (rhs) != SSA_NAME)
++ return false;
++
++ tree base = rhs;
++ gimple *def_stmt = SSA_NAME_DEF_STMT (base);
++ while (is_gimple_assign (def_stmt)
++ && gimple_assign_rhs_code (def_stmt) == POINTER_PLUS_EXPR)
++ {
++ base = gimple_assign_rhs1 (def_stmt);
++ if (TREE_CODE (base) != SSA_NAME)
++ return false;
++ def_stmt = SSA_NAME_DEF_STMT (base);
++ }
++
++ if (!gimple_call_builtin_p (def_stmt, BUILT_IN_CALLOC))
++ return false;
++
++ /* Only support unique source. The inner_array must be used only once,
++ assigned its address to the candidate array. */
++ if (inner_array)
++ return false;
++
++ /* array: T *[], base: T *. */
++ if (TREE_TYPE (TREE_TYPE (array)) != TREE_TYPE (base))
++ return false;
++
++ if (!unique_use_p (base, stmt) || !initialize_assign_p (stmt))
++ return false;
++
++ inner_array = base;
++ }
++
++ return true;
++}
++
++bool
++array_dse_edge::unique_use_p (tree var, gimple *unique_assign) const
++{
++ auto_vec worklist;
++ auto_bitmap visited;
++ worklist.safe_push (var);
++
++ while (!worklist.is_empty ())
++ {
++ tree t = worklist.pop ();
++ if (TREE_CODE (t) != SSA_NAME)
++ return false;
++
++ if (!bitmap_set_bit (visited, SSA_NAME_VERSION (t)))
++ continue;
++
++ imm_use_iterator iter;
++ gimple *stmt = nullptr;
++ FOR_EACH_IMM_USE_STMT (stmt, iter, t)
++ {
++ if (gimple_call_builtin_p (stmt, BUILT_IN_FREE))
++ continue;
++
++ if (!is_gimple_assign (stmt))
++ return false;
++
++ if (stmt == unique_assign)
++ continue;
++
++ worklist.safe_push (gimple_assign_lhs (stmt));
++ }
++ }
++
++ return true;
++}
++
++bool
++array_dse_edge::initialize_assign_p (gimple *stmt) const
++{
++ if (!stmt || !gimple_bb (stmt))
++ return false;
++
++ hash_set preds;
++ auto_vec worklist;
++ worklist.safe_push (gimple_bb (stmt));
++
++ while (!worklist.is_empty ())
++ {
++ basic_block bb = worklist.pop ();
++ if (preds.add (bb))
++ continue;
++
++ for (auto e : bb->preds)
++ worklist.safe_push (e->src);
++ }
++
++ for (auto [var, access_stmt] : array_accesses)
++ {
++ if (access_stmt == stmt)
++ continue;
++
++ if (preds.contains (gimple_bb (access_stmt)))
++ return false;
++ }
++
++ return true;
++}
++
++bool
++array_dse_edge::calc_read_bound ()
++{
++ for (auto [var, stmt] : array_accesses)
++ {
++ if (!after_call_stmt_p (stmt) || !read_array_p (var))
++ continue;
++
++ auto range = calc_ref_range (var);
++ if (!integer_cst_p (range.max ()))
++ return false;
++
++ auto max = tree_to_shwi (range.max ());
++ if (max % elem_size)
++ return false;
++
++ if (max / elem_size > read_upper_bound)
++ read_upper_bound = max / elem_size;
++ }
++
++ return true;
++}
++
++value_range
++array_dse_edge::calc_ref_range (tree var)
++{
++ tree_code code = TREE_CODE (var);
++ /* Array_ref's second op is an index. Convert it to address offset. */
++ if (code == ARRAY_REF)
++ {
++ auto r = calc_offset_range (TREE_OPERAND (var, 1));
++ if (r.varying_p ())
++ return r;
++
++ gcc_assert (integer_cst_p (r.min ()) && integer_cst_p (r.max ()));
++ return make_range (tree_to_shwi (r.min ()) * elem_size,
++ tree_to_shwi (r.max ()) * elem_size);
++ }
++
++ gcc_assert (code == MEM_REF);
++ auto r1 = calc_addr_range (TREE_OPERAND (var, 0));
++ auto r2 = calc_offset_range (TREE_OPERAND (var, 1));
++
++ return value_range{plus_tree (r1.min (), r2.min ()),
++ plus_tree (r1.max (), r2.max ())};
++}
++
++value_range
++array_dse_edge::calc_addr_range (tree var)
++{
++ if (array_address_vars.contains (var))
++ {
++ gcc_assert (TREE_CODE (var) == SSA_NAME);
++ gimple *stmt = SSA_NAME_DEF_STMT (var);
++ if (is_gimple_assign (stmt))
++ {
++ auto r1 = calc_addr_range (gimple_assign_rhs1 (stmt));
++ auto r2 = calc_offset_range (gimple_assign_rhs2 (stmt));
++ return value_range{plus_tree (r1.min (), r2.min ()),
++ plus_tree (r1.max (), r2.max ())};
++ }
++
++ return calc_simple_loop_range (var);
++ }
++
++ if (TREE_CODE (var) != ADDR_EXPR)
++ return value_range{RANGE_TYPE};
++
++ tree op = TREE_OPERAND (var, 0);
++ if (op == array)
++ return make_range (0);
++
++ if (!array_ref_p (op))
++ return value_range{RANGE_TYPE};
++
++ return calc_ref_range (op);
++}
++
++value_range
++array_dse_edge::calc_offset_range (tree offset)
++{
++ tree var = strip_ssa_copy (offset);
++ if (integer_cst_p (var))
++ return make_range (tree_to_shwi (var));
++
++ if (TREE_CODE (var) != SSA_NAME)
++ return value_range{RANGE_TYPE};
++
++ gimple *stmt = SSA_NAME_DEF_STMT (var);
++ if (gimple_code (stmt) == GIMPLE_PHI)
++ return calc_simple_loop_range (var);
++
++ if (!is_gimple_assign (stmt) || gimple_assign_rhs_code (stmt) != MULT_EXPR
++ || !integer_cst_p (gimple_assign_rhs2 (stmt))
++ || TREE_INT_CST_LOW (gimple_assign_rhs2 (stmt)) != elem_size)
++ return value_range{RANGE_TYPE};
++
++ auto range = calc_offset_range (gimple_assign_rhs1 (stmt));
++ if (!integer_cst_p (range.min ()) || !integer_cst_p (range.max ()))
++ return value_range{RANGE_TYPE};
++
++ return make_range (tree_to_shwi (range.min ()) * elem_size,
++ tree_to_shwi (range.max ()) * elem_size);
++}
++
++value_range
++array_dse_edge::calc_simple_loop_range (tree var)
++{
++ gimple *stmt = SSA_NAME_DEF_STMT (var);
++ basic_block bb = gimple_bb (stmt);
++ loop_p loop = bb->loop_father;
++
++ if (!loop || loop->header != bb || !loop->any_upper_bound)
++ return value_range{RANGE_TYPE};
++
++ tree init_var = PHI_ARG_DEF_FROM_EDGE (stmt, loop_preheader_edge (loop));
++ tree iterate_var = PHI_ARG_DEF_FROM_EDGE (stmt, loop_latch_edge (loop));
++
++ value_range init_range;
++ if (array_addr_p (init_var))
++ init_range = calc_addr_range (init_var);
++ else
++ init_range = calc_offset_range (init_var);
++
++ if (!init_range.singleton_p () || !integer_cst_p (init_range.min ()))
++ return value_range{RANGE_TYPE};
++
++ HOST_WIDE_INT init_value = tree_to_shwi (init_range.min ());
++ int step = calc_loop_var_step (var, iterate_var);
++ int upper_bound = loop->nb_iterations_upper_bound.to_shwi ();
++
++ return make_range (init_value, init_value + step * upper_bound);
++}
++
++void
++array_dse_edge::collect_call_block_succs ()
++{
++ basic_block call_block = gimple_bb (call_edge->call_stmt);
++ auto_vec worklist;
++ for (auto e : call_block->succs)
++ worklist.safe_push (e->dest);
++
++ while (!worklist.is_empty ())
++ {
++ basic_block bb = worklist.pop ();
++ if (call_block_succs.add (bb))
++ continue;
++
++ for (auto e : bb->succs)
++ worklist.safe_push (e->dest);
++ }
++}
++
++bool
++array_dse_edge::calc_array_arg_start ()
++{
++ tree array_arg = gimple_call_arg (call_edge->call_stmt,
++ callee->array_param_index);
++ if (!array_addr_p (array_arg))
++ return false;
++
++ auto range = calc_addr_range (array_arg);
++ if (!range.singleton_p () || !integer_cst_p (range.min ()))
++ return false;
++
++ auto value = tree_to_shwi (range.min ());
++ if (value % elem_size)
++ return false;
++
++ array_arg_start = value / elem_size;
++ return true;
++}
++
++bool
++array_dse_edge::check_optimized_area_rewrite ()
++{
++ tree arg = gimple_call_arg (call_edge->call_stmt, callee->len_param_index);
++ if (!arg)
++ return false;
++
++ tree var = strip_ssa_copy (arg);
++ if (!var || TREE_CODE (var) != SSA_NAME)
++ return false;
++
++ gimple *stmt = SSA_NAME_DEF_STMT (var);
++ loop_p loop = gimple_bb (stmt)->loop_father;
++ if (!loop || !loop_var_p (loop, var))
++ return false;
++
++ /* To make sure the optimized area of array is fully rewritten, the loop
++ step must be 1. We only support one iterate stmt now. */
++ tree iterate_var = PHI_ARG_DEF_FROM_EDGE (stmt, loop_latch_edge (loop));
++ hash_set iterate_stmts;
++ if (calc_loop_var_step (var, iterate_var, &iterate_stmts) != 1
++ || iterate_stmts.elements () != 1)
++ return false;
++
++ gimple *iter_stmt = *iterate_stmts.begin ();
++ if (gimple_assign_rhs1 (iter_stmt) != var)
++ return false;
++
++ /* Check if the element has been fully written. */
++ basic_block bb = gimple_bb (iter_stmt);
++ if (!check_full_write_elem (bb, gimple_assign_lhs (iter_stmt)))
++ return false;
++
++ /* Check if the start address is less equal than the read_upper_bound. */
++ tree init_var = PHI_ARG_DEF_FROM_EDGE (stmt, loop_preheader_edge (loop));
++ init_var = strip_init_var (init_var, var);
++ if (!init_var)
++ return false;
++
++ auto range = calc_offset_range (init_var);
++ if (!integer_cst_p (range.min ()) || !integer_cst_p (range.max ()))
++ return false;
++
++ len_arg_min = tree_to_shwi (range.min ());
++
++ return tree_to_shwi (range.max ()) + array_arg_start <= read_upper_bound;
++}
++
++bool
++array_dse_edge::check_full_write_elem (basic_block bb, tree index)
++{
++ hash_set visited_fields;
++
++ for (auto [var, stmt] : array_accesses)
++ {
++ /* Must in the same block. */
++ if (gimple_bb (stmt) != bb)
++ continue;
++
++ if (!write_array_p (var))
++ continue;
++
++ if (TREE_CODE (var) != MEM_REF
++ || !array_index_of_addr_p (index, TREE_OPERAND (var, 0))
++ || !integer_zerop (TREE_OPERAND (var, 1)))
++ continue;
++
++ /* Directly write to array. */
++ tree lhs = gimple_assign_lhs (stmt);
++ if (var == lhs)
++ return true;
++ else if (!inner_array)
++ continue;
++
++ imm_use_iterator iter;
++ gimple *use_stmt = nullptr;
++ FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
++ {
++ tree ref = gimple_get_lhs (use_stmt);
++ if (!ref || TREE_CODE (ref) != COMPONENT_REF)
++ continue;
++
++ visited_fields.add (TREE_OPERAND (ref, 1));
++ }
++ }
++
++ return inner_array
++ && visited_fields.elements () == fields_length (inner_elem_type);
++}
++
++bool
++array_dse_edge::array_index_of_addr_p (tree index, tree addr)
++{
++ if (TREE_CODE (index) != SSA_NAME || TREE_CODE (addr) != SSA_NAME)
++ return false;
++
++ /* Check pattern: addr = &array + offset. */
++ gimple *stmt = SSA_NAME_DEF_STMT (addr);
++ if (!is_gimple_assign (stmt)
++ || gimple_assign_rhs_code (stmt) != POINTER_PLUS_EXPR)
++ return false;
++
++ tree rhs1 = gimple_assign_rhs1 (stmt);
++ if (TREE_CODE (rhs1) != ADDR_EXPR || TREE_OPERAND (rhs1, 0) != array)
++ return false;
++
++ tree rhs2 = gimple_assign_rhs2 (stmt);
++ if (TREE_CODE (rhs2) != SSA_NAME)
++ return false;
++
++ /* Check pattern: offset = index * elem_size. */
++ stmt = SSA_NAME_DEF_STMT (rhs2);
++ if (!is_gimple_assign (stmt) || gimple_assign_rhs_code (stmt) != MULT_EXPR)
++ return false;
++
++ rhs1 = gimple_assign_rhs1 (stmt);
++ rhs2 = gimple_assign_rhs2 (stmt);
++
++ return strip_ssa_copy (rhs1) == index && integer_cst_p (rhs2)
++ && TREE_INT_CST_LOW (rhs2) == elem_size;
++}
++
++tree
++array_dse_edge::strip_init_var (tree init_var, tree var)
++{
++ tree last = var;
++ while (true)
++ {
++ if (TREE_CODE (init_var) != SSA_NAME)
++ break;
++
++ gimple *stmt = SSA_NAME_DEF_STMT (init_var);
++ loop_p loop = gimple_bb (stmt)->loop_father;
++ if (!loop || !loop_var_p (loop, init_var))
++ break;
++
++ auto latch_edge = loop_latch_edge (loop);
++ auto preheader_edge = loop_preheader_edge (loop);
++ if (!latch_edge || !preheader_edge
++ || PHI_ARG_DEF_FROM_EDGE (stmt, latch_edge) != last)
++ break;
++
++ last = init_var;
++ init_var = PHI_ARG_DEF_FROM_EDGE (stmt, preheader_edge);
++ }
++
++ return strip_ssa_copy (init_var);
++}
++
++bool
++array_dse_edge::check_len_arg_range ()
++{
++ return check_len_arg_lower_bound ()
++ && check_len_arg_upper_bound ();
++}
++
++/* Check: ARG >= 1 (assumption in callee analysis). */
++
++bool
++array_dse_edge::check_len_arg_lower_bound ()
++{
++ if (len_arg_min >= 1)
++ return true;
++
++ /* If the len_arg_min recorded previous doesn't meet the condition, try to
++ update it by checking condition jump. */
++ tree arg = gimple_call_arg (call_edge->call_stmt, callee->len_param_index);
++ if (!arg)
++ return false;
++
++ tree var = strip_ssa_copy (arg);
++ if (!var || TREE_CODE (var) != SSA_NAME)
++ return false;
++
++ basic_block call_block = gimple_bb (call_edge->call_stmt);
++ basic_block bb;
++ FOR_EACH_BB_FN (bb, cfun)
++ {
++ gimple *stmt = last_stmt (bb);
++ if (!stmt || gimple_code (stmt) != GIMPLE_COND)
++ continue;
++
++ tree_code code = gimple_cond_code (stmt);
++ tree lhs = gimple_cond_lhs (stmt);
++ tree rhs = gimple_cond_rhs (stmt);
++
++ if (lhs != var || !integer_cst_p (rhs))
++ continue;
++
++ edge true_edge;
++ edge false_edge;
++ extract_true_false_edges_from_block (bb, &true_edge, &false_edge);
++ if (!true_edge || !false_edge)
++ continue;
++
++ if (dominated_by_p (CDI_DOMINATORS, call_block, false_edge->dest))
++ code = opposite_cond_code (code);
++ else if (!dominated_by_p (CDI_DOMINATORS, call_block, true_edge->dest))
++ continue;
++
++ HOST_WIDE_INT rvalue = TREE_INT_CST_LOW (rhs);
++ switch (code)
++ {
++ case GT_EXPR:
++ len_arg_min = std::max (len_arg_min, rvalue + 1);
++ break;
++ case GE_EXPR:
++ [[fallthrough]];
++ case EQ_EXPR:
++ len_arg_min = std::max (len_arg_min, rvalue);
++ break;
++ case NE_EXPR:
++ if (len_arg_min == rvalue)
++ len_arg_min++;
++ break;
++ default:
++ break;
++ }
++ }
++
++ return len_arg_min >= 1;
++}
++
++/* We can assume that the array will not be accessed out of bounds.
++ So we use array_size as the upper bound of len arg. */
++
++bool
++array_dse_edge::check_len_arg_upper_bound ()
++{
++ return array_size <= callee->get_len_param_max ();
++}
++
++bool
++array_dse_edge::after_call_stmt_p (gimple *stmt)
++{
++ if (call_stmt_p (stmt))
++ return false;
++
++ basic_block bb = gimple_bb (stmt);
++ if (bb == gimple_bb (call_edge->call_stmt)
++ && gimple_uid (stmt) > gimple_uid (call_edge->call_stmt))
++ return true;
++
++ return call_block_succs.contains (bb);
++}
++
++bool
++array_dse_edge::write_array_p (tree var)
++{
++ auto *kind = access_kinds.get (var);
++ if (!kind)
++ return false;
++
++ return *kind & WRITE;
++}
++
++bool
++array_dse_edge::read_array_p (tree var)
++{
++ auto *kind = access_kinds.get (var);
++ if (!kind)
++ return false;
++
++ return *kind & READ;
++}
++
++bool
++array_dse_edge::call_stmt_p (gimple *stmt) const
++{
++ return stmt == call_edge->call_stmt;
++}
++
++bool
++array_dse_edge::array_ref_p (tree var)
++{
++ if (!var)
++ return false;
++
++ if (TREE_CODE (var) == ARRAY_REF)
++ return TREE_OPERAND (var, 0) == array;
++
++ return (TREE_CODE (var) == MEM_REF && array_addr_p (TREE_OPERAND (var, 0)));
++}
++
++bool
++array_dse_edge::array_addr_p (tree var)
++{
++ if (array_address_vars.contains (var))
++ return true;
++
++ if (TREE_CODE (var) != ADDR_EXPR)
++ return false;
++
++ tree op = TREE_OPERAND (var, 0);
++ return op == array || array_ref_p (op);
++}
++
++unsigned
++ipa_array_dse::execute ()
++{
++ cgraph_node *node;
++ FOR_EACH_FUNCTION (node)
++ {
++ if (!node->real_symbol_p () || !node->definition
++ || !node->has_gimple_body_p () || node->inlined_to)
++ continue;
++ node->get_body ();
++
++ if (!DECL_STRUCT_FUNCTION (node->decl))
++ continue;
++
++ nodes.safe_push (node);
++ }
++
++ if (!find_array_dse_candidate_callees ())
++ {
++ if (dump_file)
++ fprintf (dump_file, "Fail finding array dse candidate callees\n");
++ return 0;
++ }
++
++ if (!find_array_dse_candidate_edges ())
++ {
++ if (dump_file)
++ fprintf (dump_file, "Fail finding array dse candidate edges\n");
++ return 0;
++ }
++
++ for (auto edge : candidate_edges)
++ apply_array_dse (edge);
++
++ symtab->remove_unreachable_nodes (dump_file);
++
++ return TODO_update_ssa;
++}
++
++bool
++ipa_array_dse::find_array_dse_candidate_callees ()
++{
++ if (dump_file)
++ fprintf (dump_file, "Finding array dse candidate callees\n\n");
++
++ for (auto node : nodes)
++ {
++ if (!tree_versionable_function_p (node->decl)
++ || !opt_for_fn (node->decl, optimize))
++ continue;
++
++ const char *fn_name = node->dump_asm_name ();
++ if (dump_file)
++ fprintf (dump_file, "Analyzing callee: %s\n", fn_name);
++
++ auto *callee = new array_dse_callee (node);
++ if (!callee->analyze ())
++ {
++ delete callee;
++ continue;
++ }
++
++ if (dump_file)
++ {
++ fprintf (dump_file, "Found candidate callee: %s\n", fn_name);
++ if (dump_flags & TDF_DETAILS)
++ dump_function_to_file (node->decl, dump_file, dump_flags);
++ fprintf (dump_file, "\n");
++ }
++
++ candidate_callees.safe_push (callee);
++ }
++
++ return !candidate_callees.is_empty ();
++}
++
++bool
++ipa_array_dse::find_array_dse_candidate_edges ()
++{
++ if (dump_file)
++ fprintf (dump_file, "Finding array dse candidate call edges\n\n");
++
++ for (auto *callee : candidate_callees)
++ {
++ cgraph_edge *e = callee->node->callers;
++ while (e && e->caller == callee->node)
++ e = e->next_caller;
++
++ for (auto *c : candidate_callees)
++ if (e->caller == c->node)
++ return false;
++
++ auto *edge = new array_dse_edge (e, callee);
++ if (!edge->analyze ())
++ {
++ delete edge;
++ continue;
++ }
++
++ if (dump_file)
++ {
++ fprintf (dump_file, "Found candidate call edge: ");
++ print_gimple_stmt (dump_file, e->call_stmt, 0, TDF_NONE);
++ if (dump_flags & TDF_DETAILS)
++ dump_function_to_file (e->caller->decl, dump_file, dump_flags);
++ fprintf (dump_file, "\n");
++ }
++
++ candidate_edges.safe_push (edge);
++ }
++
++ return !candidate_edges.is_empty ();
++}
++
++bool
++ipa_array_dse::apply_array_dse (array_dse_edge *ad_edge)
++{
++ /* Remove call stmt if it's fully redundant. */
++ if (ad_edge->fully_redundant ())
++ {
++ cgraph_node *caller = ad_edge->call_edge->caller;
++ gimple *call_stmt = ad_edge->call_edge->call_stmt;
++
++ cfun_saver save (caller);
++
++ auto gsi = gsi_for_stmt (call_stmt);
++ basic_block call_bb = gimple_bb (call_stmt);
++ tree fndecl = gimple_call_fndecl (call_stmt);
++ caller->remove_stmt_references (call_stmt);
++ unlink_stmt_vdef (call_stmt);
++ if (gsi_remove (&gsi, true))
++ gimple_purge_dead_eh_edges (call_bb);
++ cgraph_update_edges_for_call_stmt (call_stmt, fndecl, nullptr);
++
++ return true;
++ }
++
++ /* Insert array redundant bound check to callee. */
++ array_dse_callee *callee = ad_edge->callee;
++ cgraph_node *orig_callee = ad_edge->callee->node;
++ cgraph_node *new_callee
++ = orig_callee->create_version_clone_with_body (vNULL, NULL, NULL, NULL,
++ NULL, "array_dse", NULL);
++
++ if (!transform_new_callee (callee, new_callee))
++ return false;
++
++ tree bound_addr = ad_edge->get_bound_addr ();
++ rewrite_call_edge (ad_edge->call_edge, new_callee, bound_addr);
++
++ return true;
++}
++
++tree
++ipa_array_dse::add_bound_param (tree param)
++{
++ vec *new_params = NULL;
++ auto_vec arg_decls;
++
++ push_function_arg_decls (&arg_decls, cfun->decl);
++ gcc_checking_assert (!arg_decls.is_empty ());
++ vec_safe_reserve (new_params, arg_decls.length () + 1);
++
++ for (unsigned i = 0; i < arg_decls.length (); ++i)
++ {
++ ipa_adjusted_param adj;
++
++ memset (&adj, 0, sizeof (adj));
++
++ adj.type = TREE_TYPE (arg_decls[i]);
++ adj.base_index = i;
++ adj.prev_clone_index = i;
++ adj.op = IPA_PARAM_OP_COPY;
++ new_params->quick_push (adj);
++ }
++
++ tree param_name = DECL_NAME (param);
++ const char *name = concat (IDENTIFIER_POINTER (param_name), ".bound", NULL);
++ ipa_adjusted_param adj;
++ adj.type = TREE_TYPE (param);
++ adj.base_index = arg_decls.length ();
++ adj.prev_clone_index = arg_decls.length ();
++ adj.op = IPA_PARAM_OP_NEW;
++ new_params->quick_push (adj);
++
++ auto adjustments = new ipa_param_body_adjustments (new_params, cfun->decl);
++ adjustments->modify_formal_parameters ();
++ delete adjustments;
++
++ arg_decls.truncate (0);
++ push_function_arg_decls (&arg_decls, cfun->decl);
++
++ tree new_param = arg_decls.last ();
++ DECL_NAME (new_param) = get_identifier (name);
++
++ return get_or_create_ssa_default_def (cfun, new_param);
++}
++
++tree
++ipa_array_dse::find_array_main_var (array_dse_callee *callee)
++{
++ int i = 0;
++ tree param = DECL_ARGUMENTS (cfun->decl);
++ while (i++ < callee->array_param_index)
++ param = DECL_CHAIN (param);
++
++ tree name;
++ FOR_EACH_SSA_NAME (i, name, cfun)
++ {
++ if (!SSA_NAME_IS_DEFAULT_DEF (name)
++ || SSA_NAME_VAR (name) != param)
++ continue;
++
++ if (!callee->main_loop)
++ return name;
++
++ use_operand_p use_p;
++ gimple *stmt;
++ if (!single_imm_use (name, &use_p, &stmt)
++ || gimple_code (stmt) != GIMPLE_PHI)
++ return nullptr;
++
++ return gimple_phi_result (stmt);
++ }
++
++ return nullptr;
++}
++
++bool
++ipa_array_dse::transform_new_callee (array_dse_callee *callee,
++ cgraph_node *new_node)
++{
++ cfun_saver save (new_node);
++
++ tree bound_ssa = add_bound_param (callee->array_param);
++ tree array = find_array_main_var (callee);
++ if (!array)
++ return false;
++
++ edge e;
++ if (callee->main_loop)
++ {
++ gimple *array_def_stmt = SSA_NAME_DEF_STMT (array);
++ basic_block array_def_bb = gimple_bb (array_def_stmt);
++ gcc_assert (gimple_code (array_def_stmt) == GIMPLE_PHI);
++ e = split_block_after_labels (array_def_bb);
++ }
++ else
++ {
++ basic_block entry_bb = ENTRY_BLOCK_PTR_FOR_FN (cfun);
++ gcc_assert (single_succ_p (entry_bb));
++ basic_block bb = split_edge (single_succ_edge (entry_bb));
++ e = single_succ_edge (bb);
++ }
++
++ auto gsi = gsi_last_bb (e->src);
++ gimple *cond = gimple_build_cond (GE_EXPR, array, bound_ssa, nullptr,
++ nullptr);
++ gsi_insert_after (&gsi, cond, GSI_NEW_STMT);
++
++ edge return_edge = make_edge (e->src, EXIT_BLOCK_PTR_FOR_FN (cfun), 0);
++ basic_block return_bb = split_edge (return_edge);
++ auto return_gsi = gsi_last_bb (return_bb);
++ gsi_insert_after (&return_gsi, gimple_build_return (nullptr), GSI_NEW_STMT);
++
++ e->flags &= ~EDGE_FALLTHRU;
++ e->flags |= EDGE_FALSE_VALUE;
++ single_pred_edge (return_bb)->flags |= EDGE_TRUE_VALUE;
++ single_succ_edge (return_bb)->flags = 0;
++
++ for (auto call_edge = new_node->callees; call_edge;
++ call_edge = call_edge->next_callee)
++ rewrite_call_edge (call_edge, new_node, bound_ssa);
++
++ return true;
++}
++
++void
++ipa_array_dse::rewrite_call_edge (cgraph_edge *edge, cgraph_node *new_node,
++ tree bound_addr)
++{
++ auto_vec args;
++ gcall *call_stmt = edge->call_stmt;
++ gimple_stmt_iterator gsi = gsi_for_stmt (call_stmt);
++ cgraph_node *caller = edge->caller;
++ cfun_saver save (caller);
++
++ for (unsigned i = 0; i < gimple_call_num_args (call_stmt); i++)
++ args.safe_push (gimple_call_arg (call_stmt, i));
++
++ bound_addr = force_gimple_operand_gsi (&gsi, bound_addr, true,
++ NULL_TREE, true,
++ GSI_SAME_STMT);
++ args.safe_push (bound_addr);
++
++ gcall *new_call = gimple_build_call_vec (new_node->decl, args);
++
++ if (tree vdef = gimple_vdef (call_stmt))
++ {
++ gimple_set_vdef (new_call, vdef);
++ SSA_NAME_DEF_STMT (vdef) = new_call;
++ }
++
++ gimple_set_vuse (new_call, gimple_vuse (call_stmt));
++ gimple_call_copy_flags (new_call, call_stmt);
++ gimple_call_set_chain (new_call, gimple_call_chain (call_stmt));
++ gsi_replace (&gsi, new_call, false);
++
++ cgraph_update_edges_for_call_stmt (call_stmt,
++ gimple_call_fndecl (call_stmt), new_call);
++
++ caller->remove_stmt_references (call_stmt);
++ caller->record_stmt_references (new_call);
++}
++
++} // namespace array_dse
++
++namespace {
++
++const pass_data pass_data_ipa_array_dse =
++{
++ SIMPLE_IPA_PASS, /* type */
++ "array-dse", /* name */
++ OPTGROUP_NONE, /* optinfo_flags */
++ TV_IPA_ARRAY_DSE, /* tv_id */
++ PROP_cfg | PROP_ssa, /* properties_required */
++ 0, /* properties_provided */
++ 0, /* properties_destroyed */
++ 0, /* todo_flags_start */
++ 0, /* todo_flags_finish */
++};
++
++class pass_ipa_array_dse : public simple_ipa_opt_pass
++{
++public:
++ pass_ipa_array_dse (gcc::context *ctxt)
++ : simple_ipa_opt_pass (pass_data_ipa_array_dse, ctxt)
++ {}
++
++ /* opt_pass methods: */
++ virtual bool gate (function *)
++ {
++ return optimize >= 3 && flag_ipa_array_dse;
++ }
++
++ virtual unsigned int execute (function *)
++ {
++ return array_dse::ipa_array_dse ().execute ();
++ }
++
++}; // class pass_ipa_array_dse
++
++} // anon namespace
++
++simple_ipa_opt_pass *
++make_pass_ipa_array_dse (gcc::context *ctxt)
++{
++ return new pass_ipa_array_dse (ctxt);
++}
+diff --git a/gcc/ipa-array-dse.h b/gcc/ipa-array-dse.h
+new file mode 100644
+index 000000000..b1f5ee611
+--- /dev/null
++++ b/gcc/ipa-array-dse.h
+@@ -0,0 +1,263 @@
++/* Array dead store elimination
++ Copyright (C) 2021-2022 Free Software Foundation, Inc.
++
++This file is part of GCC.
++
++GCC is free software; you can redistribute it and/or modify
++it under the terms of the GNU General Public License as published by
++the Free Software Foundation; either version 3, or (at your option)
++any later version.
++
++GCC is distributed in the hope that it will be useful,
++but WITHOUT ANY WARRANTY; without even the implied warranty of
++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++GNU General Public License for more details.
++
++You should have received a copy of the GNU General Public License
++along with GCC; see the file COPYING3. If not see
++. */
++
++#ifndef IPA_ARRAY_DSE_H
++#define IPA_ARRAY_DSE_H
++
++#include "config.h"
++#include "system.h"
++#include "coretypes.h"
++#include "options.h"
++#include "function.h"
++#include "cfgloop.h"
++#include "hash-map.h"
++#include "tree-core.h"
++#include "tree.h"
++#include "bitmap.h"
++#include "value-range.h"
++#include