https://gcc.gnu.org/g:da96cf650a1496ba0d4a4fe6d54d7753c7229e55

commit da96cf650a1496ba0d4a4fe6d54d7753c7229e55
Author: Josef Melcr <[email protected]>
Date:   Tue Jul 7 15:44:17 2026 +0200

    initial impl of rewritten ipa_jf's
    
    gcc/ChangeLog:
    
            * ipa-prop.cc (ipa_dump_expr_bytecode): Printing fn.
            (ipa_dump_jf_expr): Likewise.
            (ipa_dump_new_jump_function): Likewise.
            (ipa_set_new_jf_constant): Setter.
            (ipa_set_new_jf_simple_pass_through): Likewise.
            (get_ancestor_addr_info): Reposition in file.
            (linearize_gimple_stmt): New fn, creates SSA bytecode chain.
            (compute_complex_assign_jump_func): Integrate new jf.
            (compute_complex_ancestor_jump_func): Likewise.
            (analyze_agg_content_value): Likewise.
            (extract_mem_content): Likewise.
            (determine_known_aggregate_parts): Likewise.
            (ipa_compute_jump_functions_for_edge): Likewise.
            * ipa-prop.h (enum ipa_jf_bytecode): Add bytecode enum.
            (ipa_jf_bytecode_valid_p): Checks if bytecode is in valid range.
            (struct ipa_bytecode_seq): Struct holding the bytecode.
            (tree_code_to_ipa_jf_bytecode): Convertor fn.
            (struct ipa_jf_expr): Struct representing abstracted
            pass-through jf.
            (struct ipa_jf_agg_data): Struct representing the new aggregate
            jf.
            (enum new_jump_func_type): Enum for new jf.
            (struct ipa_jf): New jf.
    
    Signed-off-by: Josef Melcr <[email protected]>

Diff:
---
 gcc/ipa-prop.cc | 744 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
 gcc/ipa-prop.h  | 207 ++++++++++++++++
 2 files changed, 892 insertions(+), 59 deletions(-)

diff --git a/gcc/ipa-prop.cc b/gcc/ipa-prop.cc
index 3e05f659f305..9cdff358b062 100644
--- a/gcc/ipa-prop.cc
+++ b/gcc/ipa-prop.cc
@@ -599,6 +599,170 @@ ipa_dump_jump_function (FILE *f, ipa_jump_func *jump_func,
     fprintf (f, "         Unknown VR\n");
 }
 
+DEBUG_FUNCTION void
+ipa_dump_expr_bytecode (FILE *f, const struct ipa_jf_expr &expr,
+                       const char *prefix)
+{
+  const ipa_bytecode_seq &bytecode = expr.bytecode;
+  unsigned bytecode_len = bytecode.size ();
+  unsigned offset = 0;
+
+  while (offset < bytecode_len)
+    {
+      unsigned inst_offset = offset;
+      ipa_jf_bytecode inst
+       = bytecode.read_and_advance<ipa_jf_bytecode> (offset);
+      gcc_checking_assert (ipa_jf_bytecode_valid_p (inst));
+
+      switch (inst)
+       {
+       case IPA_JF_CONSTANT_LOAD:
+         {
+           fprintf (f, "%s(%d): CONSTANT_LOAD: ", prefix, inst_offset);
+
+           tree type = bytecode.read_and_advance<tree> (offset);
+           tree value = bytecode.read_and_advance<tree> (offset);
+
+           ipa_print_constant_value (f, value);
+           fprintf (f, " (");
+           print_generic_expr (f, type);
+           fprintf (f, ")");
+           break;
+         }
+       case IPA_JF_PARAM_LOAD:
+         {
+           fprintf (f, "%s(%d): PARAM_LOAD: ", prefix, inst_offset);
+
+           tree type = bytecode.read_and_advance<tree> (offset);
+           unsigned formal_id = bytecode.read_and_advance<unsigned> (offset);
+
+           fprintf (f, "%u (", formal_id);
+           print_generic_expr (f, type);
+           fprintf (f, ")");
+           break;
+         }
+       case IPA_JF_TYPE_CAST:
+         {
+           fprintf (f, "%s(%d): TYPE_CAST: ", prefix, inst_offset);
+
+           tree type = bytecode.read_and_advance<tree> (offset);
+
+           print_generic_expr (f, type);
+           break;
+         }
+       case IPA_JF_MINUS_EXPR:
+         {
+           fprintf (f, "%s(%d): MINUS_EXPR: ", prefix, inst_offset);
+
+           tree type = bytecode.read_and_advance<tree> (offset);
+
+           print_generic_expr (f, type);
+           break;
+         }
+       case IPA_JF_PLUS_EXPR:
+         {
+           fprintf (f, "%s(%d): PLUS_EXPR: ", prefix, inst_offset);
+
+           tree type = bytecode.read_and_advance<tree> (offset);
+
+           print_generic_expr (f, type);
+           break;
+         }
+       case IPA_JF_SUB_EXPR:
+         {
+           fprintf (f, "%s(%d): SUB_EXPR: ", prefix, inst_offset);
+
+           tree type = bytecode.read_and_advance<tree> (offset);
+
+           print_generic_expr (f, type);
+           break;
+         }
+       case IPA_JF_MULT_EXPR:
+         {
+           fprintf (f, "%s(%d): MULT_EXPR: ", prefix, inst_offset);
+
+           tree type = bytecode.read_and_advance<tree> (offset);
+
+           print_generic_expr (f, type);
+           break;
+         }
+       case IPA_JF_DIV_EXPR:
+         {
+           fprintf (f, "%s(%d): DIV_EXPR: ", prefix, inst_offset);
+
+           tree type = bytecode.read_and_advance<tree> (offset);
+
+           print_generic_expr (f, type);
+           break;
+         }
+       default:
+         gcc_unreachable ();
+       }
+      fprintf (f, "\n");
+    }
+}
+
+DEBUG_FUNCTION void
+ipa_dump_jf_expr (FILE *f, const struct ipa_jf_expr &expr)
+{
+  bool is_simple = expr.bytecode.is_empty ();
+  fprintf (f, "PASS-THROUGH: %s\n", is_simple ? "SIMPLE" : "LINEARIZED");
+  fprintf (f, "    offset: " HOST_WIDE_INT_PRINT_DEC "\n", expr.offset);
+  fprintf (f, "    agg_preserved: %d\n", expr.agg_preserved);
+  if (is_simple)
+    {
+      /* Simple pass-through jump function.  */
+      fprintf (f, "    formal_id: %d\n", expr.formal_id);
+      // TODO: flags?
+    }
+  else
+    {
+      /* Arithmetic jump function with a linearized ssa chain.  */
+      ipa_dump_expr_bytecode (f, expr, "        ");
+      // TODO: flags?
+    }
+}
+
+DEBUG_FUNCTION void
+ipa_dump_new_jump_function (FILE *f, struct ipa_jf *new_jf)
+{
+  enum new_jump_func_type type = new_jf->type;
+
+  if (type == NEW_IPA_JF_UNKNOWN)
+    fprintf (f, "UNKNOWN\n");
+  else if (type == NEW_IPA_JF_CONST)
+    {
+      fprintf (f, "CONST: ");
+      ipa_print_constant_value (f, new_jf->constant.value);
+      fprintf (f, "\n");
+    }
+  else if (type == NEW_IPA_JF_PASS_THROUGH)
+    {
+      ipa_dump_jf_expr (f, new_jf->pass_through);
+    }
+  else /* type == NEW_IPA_JF_AGGREGATE */
+    {
+      gcc_checking_assert (type == NEW_IPA_JF_AGGREGATE);
+      struct ipa_jf_expr *expr;
+      int i;
+
+      fprintf (f, "AGGREGATE JUMP FUNCTION:\n");
+      FOR_EACH_VEC_ELT (new_jf->aggregate.agg_parts, i, expr)
+       {
+         ipa_dump_jf_expr (f, *expr);
+       }
+    }
+
+  if (new_jf->vr)
+    {
+      fprintf (f, "    ");
+      new_jf->vr->dump (f);
+      fprintf (f, "\n");
+    }
+  else
+    fprintf (f, "    Unknown VR\n");
+}
+
 /* Print the jump functions associated with call graph edge CS to file F.  */
 
 static void
@@ -749,6 +913,13 @@ ipa_set_jf_constant (struct ipa_jump_func *jfunc, tree 
constant,
     jfunc->value.constant.rdesc = NULL;
 }
 
+static void
+ipa_set_new_jf_constant (struct ipa_jf *jf, tree constant, struct cgraph_edge 
*)
+{
+  jf->type = NEW_IPA_JF_CONST;
+  jf->constant.value = unshare_expr_without_location (constant);
+}
+
 /* Set JFUNC to be a simple pass-through jump function.  */
 static void
 ipa_set_jf_simple_pass_through (struct ipa_jump_func *jfunc, int formal_id,
@@ -763,6 +934,16 @@ ipa_set_jf_simple_pass_through (struct ipa_jump_func 
*jfunc, int formal_id,
   jfunc->value.pass_through.refdesc_decremented = false;
 }
 
+static void
+ipa_set_new_jf_simple_pass_through (struct ipa_jf *jf, int formal_id,
+                                   bool agg_preserved)
+{
+  jf->type = NEW_IPA_JF_PASS_THROUGH;
+  jf->pass_through.formal_id = formal_id;
+  jf->pass_through.agg_preserved = agg_preserved;
+  jf->pass_through.offset = ipa_jf_expr::SCALAR_SENTINEL;
+}
+
 /* Set JFUNC to be an unary pass through jump function.  */
 
 static void
@@ -1507,6 +1688,428 @@ unadjusted_ptr_and_unit_offset (tree op, tree *ret, 
poly_int64 *offset_ret)
   return offset_known;
 }
 
+
+/* Extract the base, offset and MEM_REF expression from a statement ASSIGN if
+   it looks like:
+
+   iftmp.1_3 = &obj_2(D)->D.1762;
+
+   The base of the MEM_REF must be a default definition SSA NAME of a
+   parameter.  Return NULL_TREE if it looks otherwise.  If case of success, the
+   whole MEM_REF expression is returned and the offset calculated from any
+   handled components and the MEM_REF itself is stored into *OFFSET.  The whole
+   RHS stripped off the ADDR_EXPR is stored into *OBJ_P.  */
+
+static tree
+get_ancestor_addr_info (gimple *assign, tree *obj_p, HOST_WIDE_INT *offset)
+{
+  HOST_WIDE_INT size;
+  tree expr, parm, obj;
+  bool reverse;
+
+  if (!gimple_assign_single_p (assign))
+    return NULL_TREE;
+  expr = gimple_assign_rhs1 (assign);
+
+  if (TREE_CODE (expr) != ADDR_EXPR)
+    return NULL_TREE;
+  expr = TREE_OPERAND (expr, 0);
+  obj = expr;
+  expr = get_ref_base_and_extent_hwi (expr, offset, &size, &reverse);
+
+  offset_int mem_offset;
+  if (!expr
+      || TREE_CODE (expr) != MEM_REF
+      || !mem_ref_offset (expr).is_constant (&mem_offset))
+    return NULL_TREE;
+  parm = TREE_OPERAND (expr, 0);
+  if (TREE_CODE (parm) != SSA_NAME
+      || !SSA_NAME_IS_DEFAULT_DEF (parm)
+      || TREE_CODE (SSA_NAME_VAR (parm)) != PARM_DECL)
+    return NULL_TREE;
+
+  *offset += mem_offset.to_short_addr () * BITS_PER_UNIT;
+  *obj_p = obj;
+  return expr;
+}
+
+static bool
+linearize_gimple_stmt (struct ipa_func_body_info *fbi, struct ipa_jf_expr 
*expr,
+                      tree t, gimple *gs)
+{
+  ipa_node_params *info = fbi->info;
+  ipa_bytecode_seq &bytecode = expr->bytecode;
+
+  tree_code tc;
+  tree lhs;
+  ipa_jf_bytecode op;
+  enum gimple_rhs_class cls;
+  int rhs_count;
+
+  if (gimple_nop_p (gs))
+    {
+      if (TREE_CODE (t) == SSA_NAME)
+       {
+         int formal_id = ipa_get_param_decl_index (info, SSA_NAME_VAR (t));
+         if (formal_id != -1)
+           {
+             bytecode.push (IPA_JF_PARAM_LOAD);
+             bytecode.push (TREE_TYPE (t));
+             bytecode.push (formal_id);
+             return true;
+           }
+         else
+           goto fail;
+       }
+      goto fail;
+    }
+
+  if (!is_gimple_assign (gs))
+    goto fail;
+
+  cls = gimple_assign_rhs_class (gs);
+  switch (cls)
+    {
+    case GIMPLE_SINGLE_RHS:
+      {
+       tree rhs = gimple_assign_rhs1 (gs);
+
+       if (is_gimple_ip_invariant (rhs))
+         {
+           bytecode.push (IPA_JF_CONSTANT_LOAD);
+           bytecode.push (TREE_TYPE (rhs));
+           bytecode.push (rhs);
+           return true;
+         }
+
+       while (gimple_assign_rhs_class (gs) == GIMPLE_SINGLE_RHS)
+         {
+           if (TREE_CODE (rhs) != SSA_NAME || SSA_NAME_IS_DEFAULT_DEF (rhs))
+             break;
+
+           gs = SSA_NAME_DEF_STMT (rhs);
+           if (!is_gimple_assign (gs))
+             break;
+
+           rhs = gimple_assign_rhs1 (gs);
+         }
+
+       // TODO: handle addr_expr
+       if (TREE_CODE (rhs) == ADDR_EXPR)
+         rhs = TREE_OPERAND (rhs, 0);
+       tree_code code = TREE_CODE (rhs);
+
+       // check if code is supported
+       switch (code)
+         {
+         case COMPONENT_REF:
+           {
+             tree obj;
+             HOST_WIDE_INT off; // field offset, lower to PLUS_EXPR
+             tree base = get_ancestor_addr_info (gs, &obj, &off);
+             if (!base)
+               goto fail;
+
+             rhs = SSA_NAME_VAR (TREE_OPERAND (base, 0));
+             int formal_id = ipa_get_param_decl_index (info, rhs);
+             if (formal_id == -1)
+               goto fail;
+
+             // confirmed ancestor, lower to plus expr
+             bytecode.push (IPA_JF_PARAM_LOAD);
+             bytecode.push (rhs);
+             bytecode.push (formal_id);
+
+             bytecode.push (IPA_JF_CONSTANT_LOAD);
+             bytecode.push (integer_type_node); // ????
+             bytecode.push (off);
+
+             bytecode.push (IPA_JF_PLUS_EXPR);
+             bytecode.push (rhs);
+             return true;
+           }
+         case VAR_DECL:
+           {
+             int formal_id = ipa_get_param_decl_index (info, rhs);
+             if (formal_id == -1)
+               goto fail;
+
+             bytecode.push (IPA_JF_PARAM_LOAD);
+             bytecode.push (rhs);
+             bytecode.push (formal_id);
+             return true;
+           }
+         case SSA_NAME:
+           {
+             return linearize_gimple_stmt (fbi, expr, rhs,
+                                           SSA_NAME_DEF_STMT (rhs));
+           }
+         default:
+           goto fail;
+         };
+      }
+    case GIMPLE_UNARY_RHS:
+      {
+       rhs_count = 1;
+       break;
+      }
+    case GIMPLE_BINARY_RHS:
+      {
+       rhs_count = 2;
+       break;
+      }
+    default:
+      gcc_checking_assert (0 && "currently unhandled");
+    }
+
+  for (int i = 0; i < rhs_count; i++)
+    {
+      tree operand = gimple_op (gs, i + 1);
+      if (is_gimple_ip_invariant (operand))
+       {
+         bytecode.push (IPA_JF_CONSTANT_LOAD);
+         bytecode.push (TREE_TYPE (operand));
+         bytecode.push (operand);
+       }
+      else if (TREE_CODE (operand) == SSA_NAME)
+       linearize_gimple_stmt (fbi, expr, operand, SSA_NAME_DEF_STMT (operand));
+    }
+
+  tc = gimple_assign_rhs_code (gs);
+  lhs = gimple_assign_lhs (gs);
+
+  op = tree_code_to_ipa_jf_bytecode (tc);
+  if (op == IPA_JF_BYTECODE_END)
+    goto fail;
+
+  bytecode.push (op);
+  bytecode.push (TREE_TYPE (lhs));
+
+  return true;
+
+fail:
+  bytecode.destroy ();
+  return false;
+}
+
+// static void
+// build_jf_expr_walk_ssa (struct ipa_func_body_info *fbi,
+//                     struct ipa_jf_expr *expr, gimple *stmt)
+// {
+//   // used as lazy gate, has nothing to do with openmp
+//   if (!flag_openmp)
+//     return;
+//
+//   unsigned cost = 0;
+//   const unsigned budget = 25; // arbitrary number
+//
+//   auto_vec<gimple *> qs;
+//   qs.safe_push (stmt);
+//
+//   int formal_id = -1;
+//   gimple *gs;
+//
+//   class ipa_node_params *info = fbi->info;
+//
+//   for (gs = stmt; gs && cost < budget && cost < qs.length (); cost++)
+//     {
+//       gs = qs[cost];
+//       if (!is_gimple_assign (gs))
+//     {
+//       // end of ssa chain, end?
+//       break;
+//     }
+//
+//       enum gimple_rhs_class cls = gimple_assign_rhs_class (gs);
+//       switch (cls)
+//     {
+//     case GIMPLE_SINGLE_RHS:
+//       {
+//         tree rhs = gimple_assign_rhs1 (gs);
+//         if (TREE_CODE (rhs) == ADDR_EXPR)
+//           rhs = TREE_OPERAND (rhs, 0);
+//         tree_code code = TREE_CODE (rhs);
+//
+//         bool supported_code = false;
+//
+//         // check if code is supported
+//         switch (code)
+//           {
+//           case VAR_DECL:
+//           case COMPONENT_REF:
+//           case SSA_NAME:
+//             {
+//               supported_code = true;
+//               break;
+//             }
+//           default:
+//             break;
+//           };
+//
+//         if (!supported_code)
+//           break;
+//
+//         if (code == COMPONENT_REF)
+//           {
+//             // potential anceestor jf
+//             tree obj;
+//             HOST_WIDE_INT off;
+//             tree base = get_ancestor_addr_info (gs, &obj, &off);
+//             if (base)
+//               rhs = SSA_NAME_VAR (TREE_OPERAND (base, 0));
+//           }
+//         else if (code == SSA_NAME)
+//           rhs = SSA_NAME_VAR (rhs);
+//
+//         int potential_param = ipa_get_param_decl_index (info, rhs);
+//         if (formal_id != -1 && formal_id != potential_param)
+//           {
+//             // another param used, abort
+//             formal_id = -1;
+//           }
+//         else
+//           {
+//             formal_id = potential_param;
+//           }
+//
+//         break;
+//       }
+//     case GIMPLE_UNARY_RHS:
+//       {
+//         tree rhs = gimple_assign_rhs1 (gs);
+//         if (TREE_CODE (rhs) == SSA_NAME && SSA_NAME_DEF_STMT (rhs))
+//           qs.safe_push (SSA_NAME_DEF_STMT (rhs));
+//         break;
+//       }
+//     case GIMPLE_BINARY_RHS:
+//       {
+//         tree rhs1 = gimple_assign_rhs1 (gs);
+//         tree rhs2 = gimple_assign_rhs2 (gs);
+//
+//         if (TREE_CODE (rhs1) == SSA_NAME)
+//           {
+//             if (gimple_nop_p (SSA_NAME_DEF_STMT (rhs1)))
+//               {
+//                 if (formal_id == -1)
+//                   formal_id
+//                     = ipa_get_param_decl_index (info, SSA_NAME_VAR (rhs1));
+//               }
+//             else
+//               qs.safe_push (SSA_NAME_DEF_STMT (rhs1));
+//           }
+//
+//           if (TREE_CODE (rhs2) == SSA_NAME)
+//           {
+//             if (gimple_nop_p (SSA_NAME_DEF_STMT (rhs2)))
+//               {
+//                 if (formal_id == -1)
+//                   formal_id
+//                     = ipa_get_param_decl_index (info, SSA_NAME_VAR (rhs2));
+//               }
+//             else
+//               qs.safe_push (SSA_NAME_DEF_STMT (rhs2));
+//           }
+//
+//         break;
+//       }
+//     default:
+//       gcc_checking_assert (0 && "currently unhandled");
+//     }
+//     }
+//
+//   if (formal_id == -1)
+//     // dont have formal_id of param, abort?
+//     return;
+//
+//   expr->formal_id = formal_id;
+//   struct ipa_jf_op *op;
+//   unsigned i;
+//   int index;
+//   FOR_EACH_VEC_ELT_REVERSE (qs, i, gs)
+//     {
+//       gcc_checking_assert (is_gimple_assign (gs));
+//       enum gimple_rhs_class cls = gimple_assign_rhs_class (gs);
+//       switch (cls)
+//     {
+//     case GIMPLE_SINGLE_RHS:
+//       {
+//         tree rhs = gimple_assign_rhs1 (gs);
+//         op = ipa_jf_op_pool.allocate ();
+//         op->type = TREE_TYPE (rhs);
+//
+//         tree_code code = TREE_CODE (rhs);
+//
+//         if (code == SSA_NAME)
+//           rhs = SSA_NAME_VAR (rhs);
+//
+//         if (TREE_CODE (rhs) == VAR_DECL || TREE_CODE (rhs) == PARM_DECL)
+//           {
+//             // load from formal parameter
+//             op->code = NOP_EXPR;
+//             op->special_ops = JF_OP_PARAM_LOAD;
+//           }
+//         else
+//           {
+//             op->code = TREE_CODE (rhs);
+//             op->special_ops = JF_OP_INVALID;
+//           }
+//
+//         break;
+//       }
+//     case GIMPLE_UNARY_RHS:
+//       {
+//         op = ipa_jf_op_pool.allocate ();
+//         tree rhs = gimple_assign_rhs1 (gs);
+//         op->type = TREE_TYPE (rhs);
+//         op->code = TREE_CODE (rhs);
+//         op->special_ops = JF_OP_INVALID;
+//         op->index = 0;
+//
+//         break;
+//       }
+//     case GIMPLE_BINARY_RHS:
+//       {
+//         tree rhs1 = gimple_assign_rhs1 (gs);
+//         tree rhs2 = gimple_assign_rhs2 (gs);
+//
+//         bool rhs1_invariant = is_gimple_ip_invariant (rhs1);
+//         bool rhs2_invariant = is_gimple_ip_invariant (rhs2);
+//         if (!rhs1_invariant && !rhs2_invariant)
+//           {
+//             // two non constants, cant handle this case rn
+//             expr->param_ops.release ();
+//             return;
+//           }
+//
+//         // should get folded if both are consts?
+//         gcc_checking_assert (rhs1_invariant ^ rhs2_invariant);
+//
+//         if (rhs1_invariant)
+//           {
+//             op->val[0] = rhs1;
+//             op->index = 1;
+//             op->type = TREE_TYPE (rhs2);
+//           }
+//         else
+//           {
+//             op->val[0] = rhs2;
+//             op->index = 0;
+//             op->type = TREE_TYPE (rhs1);
+//           }
+//
+//         gcc_checking_assert (is_gimple_assign (gs));
+//
+//         op->code = gimple_assign_rhs_code (gs);
+//         op->special_ops = JF_OP_INVALID;
+//
+//         break;
+//       }
+//     }
+//
+//       expr->param_ops.safe_push (op);
+//     }
+// }
+
 /* Given that an actual argument is an SSA_NAME (given in NAME) and is a result
    of an assignment statement STMT, try to determine whether we are actually
    handling any of the following cases and construct an appropriate jump
@@ -1564,6 +2167,7 @@ static void
 compute_complex_assign_jump_func (struct ipa_func_body_info *fbi,
                                  class ipa_node_params *info,
                                  struct ipa_jump_func *jfunc,
+                                 struct ipa_jf *new_jf,
                                  gcall *call, gimple *stmt, tree name,
                                  tree param_type)
 {
@@ -1589,11 +2193,17 @@ compute_complex_assign_jump_func (struct 
ipa_func_body_info *fbi,
       tc_ssa = gimple_assign_lhs (stmt);
     }
 
+  ipa_jf_expr *expr = &new_jf->pass_through;
+  if (index < 0)
+    if (linearize_gimple_stmt (fbi, expr, NULL_TREE, stmt))
+      new_jf->type = NEW_IPA_JF_PASS_THROUGH;
+
   if (index >= 0)
     {
       if (lto_variably_modified_type_p (TREE_TYPE (name)))
        return;
 
+
       switch (gimple_assign_rhs_class (stmt))
        {
        case GIMPLE_BINARY_RHS:
@@ -1609,6 +2219,8 @@ compute_complex_assign_jump_func (struct 
ipa_func_body_info *fbi,
            ipa_set_jf_arith_pass_through (jfunc, index, op2,
                                           gimple_assign_rhs_code (stmt),
                                           TREE_TYPE (name));
+           if (linearize_gimple_stmt (fbi, expr, NULL_TREE, stmt))
+             new_jf->type = NEW_IPA_JF_PASS_THROUGH;
            break;
          }
        case GIMPLE_SINGLE_RHS:
@@ -1616,6 +2228,7 @@ compute_complex_assign_jump_func (struct 
ipa_func_body_info *fbi,
            bool agg_p = parm_ref_data_pass_through_p (fbi, index, call,
                                                       tc_ssa);
            ipa_set_jf_simple_pass_through (jfunc, index, agg_p);
+           ipa_set_new_jf_simple_pass_through (new_jf, index, agg_p);
            break;
          }
        case GIMPLE_UNARY_RHS:
@@ -1623,11 +2236,15 @@ compute_complex_assign_jump_func (struct 
ipa_func_body_info *fbi,
            ipa_set_jf_unary_pass_through (jfunc, index,
                                           gimple_assign_rhs_code (stmt),
                                           TREE_TYPE (name));
+         if (linearize_gimple_stmt (fbi, expr, NULL_TREE, stmt))
+           new_jf->type = NEW_IPA_JF_PASS_THROUGH;
        default:;
        }
       return;
     }
 
+  /* basically get_ancestor_addr_info
+             vvvvvvvvvvvvvvvvvv*/
   if (TREE_CODE (op1) != ADDR_EXPR)
     return;
   op1 = TREE_OPERAND (op1, 0);
@@ -1647,56 +2264,14 @@ compute_complex_assign_jump_func (struct 
ipa_func_body_info *fbi,
   /* Dynamic types are changed in constructors and destructors.  */
   index = ipa_get_param_decl_index (info, SSA_NAME_VAR (ssa));
   if (index >= 0 && param_type && POINTER_TYPE_P (param_type))
-    ipa_set_ancestor_jf (jfunc, offset,  index,
+    ipa_set_ancestor_jf (jfunc, offset, index,
                         parm_ref_data_pass_through_p (fbi, index, call, ssa),
                         false);
-}
 
-/* Extract the base, offset and MEM_REF expression from a statement ASSIGN if
-   it looks like:
-
-   iftmp.1_3 = &obj_2(D)->D.1762;
-
-   The base of the MEM_REF must be a default definition SSA NAME of a
-   parameter.  Return NULL_TREE if it looks otherwise.  If case of success, the
-   whole MEM_REF expression is returned and the offset calculated from any
-   handled components and the MEM_REF itself is stored into *OFFSET.  The whole
-   RHS stripped off the ADDR_EXPR is stored into *OBJ_P.  */
-
-static tree
-get_ancestor_addr_info (gimple *assign, tree *obj_p, HOST_WIDE_INT *offset)
-{
-  HOST_WIDE_INT size;
-  tree expr, parm, obj;
-  bool reverse;
-
-  if (!gimple_assign_single_p (assign))
-    return NULL_TREE;
-  expr = gimple_assign_rhs1 (assign);
-
-  if (TREE_CODE (expr) != ADDR_EXPR)
-    return NULL_TREE;
-  expr = TREE_OPERAND (expr, 0);
-  obj = expr;
-  expr = get_ref_base_and_extent_hwi (expr, offset, &size, &reverse);
-
-  offset_int mem_offset;
-  if (!expr
-      || TREE_CODE (expr) != MEM_REF
-      || !mem_ref_offset (expr).is_constant (&mem_offset))
-    return NULL_TREE;
-  parm = TREE_OPERAND (expr, 0);
-  if (TREE_CODE (parm) != SSA_NAME
-      || !SSA_NAME_IS_DEFAULT_DEF (parm)
-      || TREE_CODE (SSA_NAME_VAR (parm)) != PARM_DECL)
-    return NULL_TREE;
-
-  *offset += mem_offset.to_short_addr () * BITS_PER_UNIT;
-  *obj_p = obj;
-  return expr;
+  if (linearize_gimple_stmt (fbi, expr, NULL_TREE, stmt))
+    new_jf->type = NEW_IPA_JF_PASS_THROUGH;
 }
 
-
 /* Given that an actual argument is an SSA_NAME that is a result of a phi
    statement PHI, try to find out whether NAME is in fact a
    multiple-inheritance typecast from a descendant into an ancestor of a formal
@@ -1722,6 +2297,7 @@ static void
 compute_complex_ancestor_jump_func (struct ipa_func_body_info *fbi,
                                    class ipa_node_params *info,
                                    struct ipa_jump_func *jfunc,
+                                   struct ipa_jf *new_jf,
                                    gcall *call, gphi *phi)
 {
   HOST_WIDE_INT offset;
@@ -1776,6 +2352,9 @@ compute_complex_ancestor_jump_func (struct 
ipa_func_body_info *fbi,
   ipa_set_ancestor_jf (jfunc, offset, index,
                       parm_ref_data_pass_through_p (fbi, index, call, parm),
                       true);
+
+  if (linearize_gimple_stmt (fbi, &new_jf->pass_through, NULL_TREE, assign))
+    new_jf->type = NEW_IPA_JF_PASS_THROUGH;
 }
 
 /* Inspect the given TYPE and return true iff it has the same structure (the
@@ -1966,6 +2545,7 @@ build_agg_jump_func_from_list (struct 
ipa_known_agg_contents_list *list,
 static void
 analyze_agg_content_value (struct ipa_func_body_info *fbi,
                           struct ipa_load_agg_data *agg_value,
+                          struct ipa_jf_expr *expr,
                           gimple *stmt)
 {
   tree lhs = gimple_assign_lhs (stmt);
@@ -1979,6 +2559,9 @@ analyze_agg_content_value (struct ipa_func_body_info *fbi,
   agg_value->pass_through.formal_id = -1;
   agg_value->offset = -1;
 
+  expr->offset = -1;
+  expr->formal_id = -1;
+
   if (AGGREGATE_TYPE_P (TREE_TYPE (lhs))  /* TODO: Support aggregate type.  */
       || TREE_THIS_VOLATILE (lhs)
       || TREE_CODE (lhs) == BIT_FIELD_REF
@@ -1999,6 +2582,7 @@ analyze_agg_content_value (struct ipa_func_body_info *fbi,
       rhs1 = gimple_assign_rhs1 (stmt);
     }
 
+  // TODO: handle phi in expr
   if (gphi *phi = dyn_cast<gphi *> (stmt))
     {
       /* Also special case like the following (a is a formal parameter):
@@ -2045,6 +2629,8 @@ analyze_agg_content_value (struct ipa_func_body_info *fbi,
     }
   else if (is_gimple_assign (stmt))
     {
+      linearize_gimple_stmt(fbi, expr, NULL_TREE, stmt);
+
       code = gimple_assign_rhs_code (stmt);
       switch (gimple_assign_rhs_class (stmt))
        {
@@ -2146,9 +2732,10 @@ analyze_agg_content_value (struct ipa_func_body_info 
*fbi,
    is expected to be in form of MEM_REF expression.  */
 
 static bool
-extract_mem_content (struct ipa_func_body_info *fbi,
-                    gimple *stmt, tree base, bool check_ref,
-                    struct ipa_known_agg_contents_list *content)
+extract_mem_content (struct ipa_func_body_info *fbi, gimple *stmt, tree base,
+                    bool check_ref,
+                    struct ipa_known_agg_contents_list *content,
+                    struct ipa_jf_expr *expr)
 {
   HOST_WIDE_INT lhs_offset, lhs_size;
   bool reverse;
@@ -2177,7 +2764,10 @@ extract_mem_content (struct ipa_func_body_info *fbi,
   content->type = TREE_TYPE (lhs);
   content->next = NULL;
 
-  analyze_agg_content_value (fbi, &content->value, stmt);
+  expr->offset = lhs_offset;
+  expr->type = TREE_TYPE (lhs);
+
+  analyze_agg_content_value (fbi, &content->value, expr, stmt);
   return true;
 }
 
@@ -2189,10 +2779,10 @@ extract_mem_content (struct ipa_func_body_info *fbi,
    the type of the aggregate, JFUNC is the jump function for the aggregate.  */
 
 static void
-determine_known_aggregate_parts (struct ipa_func_body_info *fbi,
-                                gcall *call, tree arg,
-                                tree arg_type,
-                                struct ipa_jump_func *jfunc)
+determine_known_aggregate_parts (struct ipa_func_body_info *fbi, gcall *call,
+                                tree arg, tree arg_type,
+                                struct ipa_jump_func *jfunc,
+                                struct ipa_jf *new_jf)
 {
   struct ipa_known_agg_contents_list *list = NULL, *all_list = NULL;
   bitmap visited = NULL;
@@ -2268,13 +2858,16 @@ determine_known_aggregate_parts (struct 
ipa_func_body_info *fbi,
      of the aggregate is affected by definition of the virtual operand, it
      builds a sorted linked list of ipa_agg_jf_list describing that.  */
 
+  auto_vec<ipa_jf_expr> exprs;
   for (tree dom_vuse = gimple_vuse (call);
        dom_vuse && fbi->aa_walk_budget > 0;)
     {
       gimple *stmt = SSA_NAME_DEF_STMT (dom_vuse);
 
+      struct ipa_jf_expr expr;
       if (gphi *phi = dyn_cast <gphi *> (stmt))
        {
+         // TODO: handle with ipa_jf_expr
          dom_vuse = get_continuation_for_phi (phi, &r, true,
                                               fbi->aa_walk_budget,
                                               &visited, false, NULL, NULL);
@@ -2287,7 +2880,8 @@ determine_known_aggregate_parts (struct 
ipa_func_body_info *fbi,
          struct ipa_known_agg_contents_list *content
                        = XALLOCA (struct ipa_known_agg_contents_list);
 
-         if (!extract_mem_content (fbi, stmt, arg_base, check_ref, content))
+         if (!extract_mem_content (fbi, stmt, arg_base, check_ref, content,
+                                   &expr))
            break;
 
          /* Now we get a dominating virtual operand, and need to check
@@ -2307,6 +2901,7 @@ determine_known_aggregate_parts (struct 
ipa_func_body_info *fbi,
              /* Add to the list consisting of only dominating virtual
                 operands, whose definitions can finally reach the call.  */
              add_to_agg_contents_list (&list, (*copy = *content, copy));
+             exprs.safe_push (expr);
 
              if (++value_count == max_agg_items)
                break;
@@ -2332,6 +2927,11 @@ determine_known_aggregate_parts (struct 
ipa_func_body_info *fbi,
     {
       jfunc->agg.by_ref = by_ref;
       build_agg_jump_func_from_list (list, value_count, arg_offset, jfunc);
+
+      for (auto &expr : exprs)
+       new_jf->aggregate.agg_parts.safe_push (expr);
+      new_jf->type = NEW_IPA_JF_AGGREGATE;
+      new_jf->aggregate.by_ref = by_ref;
     }
 }
 
@@ -2509,6 +3109,10 @@ ipa_compute_jump_functions_for_edge (struct 
ipa_func_body_info *fbi,
   if (arg_num == 0 || args->jump_functions)
     return;
   vec_safe_grow_cleared (args->jump_functions, arg_num, true);
+
+  auto_vec<ipa_jf> new_jfs;
+  new_jfs.safe_grow_cleared (arg_num);
+
   if (flag_devirtualize)
     vec_safe_grow_cleared (args->polymorphic_call_contexts, arg_num, true);
 
@@ -2521,6 +3125,7 @@ ipa_compute_jump_functions_for_edge (struct 
ipa_func_body_info *fbi,
   for (n = 0; n < arg_num; n++)
     {
       struct ipa_jump_func *jfunc = ipa_get_ith_jump_func (args, n);
+      struct ipa_jf *njf = &new_jfs[n];
       tree arg = gimple_call_arg (call, n);
       tree param_type = ipa_get_callee_param_type (cs, n);
       if (flag_devirtualize && POINTER_TYPE_P (TREE_TYPE (arg)))
@@ -2593,11 +3198,14 @@ ipa_compute_jump_functions_for_edge (struct 
ipa_func_body_info *fbi,
            gcc_assert (!jfunc->m_vr);
        }
 
+      njf->vr = jfunc->m_vr;
+
       arg = skip_a_safe_conversion_op (arg);
       if (is_gimple_ip_invariant (arg)
          || (VAR_P (arg) && is_global_var (arg) && TREE_READONLY (arg)))
        {
          ipa_set_jf_constant (jfunc, arg, cs);
+         ipa_set_new_jf_constant (njf, arg, cs);
          if (TREE_CODE (arg) == ADDR_EXPR)
            {
              tree pointee = TREE_OPERAND (arg, 0);
@@ -2646,7 +3254,8 @@ ipa_compute_jump_functions_for_edge (struct 
ipa_func_body_info *fbi,
                }
            }
        }
-      else if (!is_gimple_reg_type (TREE_TYPE (arg))
+      // this is what is_gimple_reg_type means, but it's more readable this way
+      else if (AGGREGATE_TYPE_P (TREE_TYPE (arg))
               && TREE_CODE (arg) == PARM_DECL)
        {
          int index = ipa_get_param_decl_index (info, arg);
@@ -2658,6 +3267,7 @@ ipa_compute_jump_functions_for_edge (struct 
ipa_func_body_info *fbi,
          if (parm_preserved_before_stmt_p (fbi, index, call, arg))
            {
              ipa_set_jf_simple_pass_through (jfunc, index, false);
+             ipa_set_new_jf_simple_pass_through (njf, index, false);
              continue;
            }
        }
@@ -2671,16 +3281,17 @@ ipa_compute_jump_functions_for_edge (struct 
ipa_func_body_info *fbi,
                  bool agg_p;
                  agg_p = parm_ref_data_pass_through_p (fbi, index, call, arg);
                  ipa_set_jf_simple_pass_through (jfunc, index, agg_p);
+                 ipa_set_new_jf_simple_pass_through (njf, index, agg_p);
                }
            }
          else
            {
              gimple *stmt = SSA_NAME_DEF_STMT (arg);
              if (is_gimple_assign (stmt))
-               compute_complex_assign_jump_func (fbi, info, jfunc,
+               compute_complex_assign_jump_func (fbi, info, jfunc, njf,
                                                  call, stmt, arg, param_type);
              else if (gimple_code (stmt) == GIMPLE_PHI)
-               compute_complex_ancestor_jump_func (fbi, info, jfunc,
+               compute_complex_ancestor_jump_func (fbi, info, jfunc, njf,
                                                    call,
                                                    as_a <gphi *> (stmt));
            }
@@ -2696,12 +3307,13 @@ ipa_compute_jump_functions_for_edge (struct 
ipa_func_body_info *fbi,
        param_type = TREE_TYPE (arg);
 
       if ((jfunc->type != IPA_JF_PASS_THROUGH
-             || !ipa_get_jf_pass_through_agg_preserved (jfunc))
+          || !ipa_get_jf_pass_through_agg_preserved (jfunc))
          && (jfunc->type != IPA_JF_ANCESTOR
              || !ipa_get_jf_ancestor_agg_preserved (jfunc))
          && (AGGREGATE_TYPE_P (TREE_TYPE (arg))
              || POINTER_TYPE_P (param_type)))
-       determine_known_aggregate_parts (fbi, call, arg, param_type, jfunc);
+       determine_known_aggregate_parts (fbi, call, arg, param_type, jfunc,
+                                        njf);
     }
 
   if (!callback_edges.is_empty ())
@@ -2731,6 +3343,20 @@ ipa_compute_jump_functions_for_edge (struct 
ipa_func_body_info *fbi,
        }
     }
 
+  if (dump_file && flag_openmp)
+    {
+      fprintf (
+       dump_file,
+       "New jump functions for edge %s -> %s:\n", cs->caller->dump_name (),
+       cs->callee->dump_name ());
+      for (auto &jf : new_jfs)
+       {
+         ipa_dump_new_jump_function (dump_file, &jf);
+         fprintf (dump_file, "--------------------\n");
+       }
+      fprintf (dump_file, "\n");
+    }
+
   if (!useful_context)
     vec_free (args->polymorphic_call_contexts);
 }
diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h
index 9099afaea304..f30de2644125 100644
--- a/gcc/ipa-prop.h
+++ b/gcc/ipa-prop.h
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 /* The following definitions and interfaces are used by
    interprocedural analyses or parameters.  */
 
+#include "system.h"
 #define IPA_UNDESCRIBED_USE -1
 
 /* Index identifying an actualargument or a formal parameter may have only this
@@ -355,6 +356,212 @@ struct GTY (()) ipa_jump_func
   } GTY ((desc ("%1.type"))) value;
 };
 
+enum ipa_jf_bytecode : uint8_t
+{
+  /* Start sentinel.  */
+  IPA_JF_BYTECODE_START = 0x31,
+
+  /* Shared layout:
+     INSTRUCTION RESULT_TYPE ...
+
+     Instruction is a member of this enum.  Result type is the type of the
+     result of the expression.  Other arguments may be specified for a given
+     instruction.  */
+
+  /* Basic operations.  */
+  /* =================  */
+
+  /* INSTRUCTION RESULT_TYPE CONSTANT */
+  /* Pushes CONSTANT onto the stack.  */
+  IPA_JF_CONSTANT_LOAD,
+
+  /* INSTRUCTION RESULT_TYPE INDEX */
+  /* Loads formal parameter at INDEX and pushes it onto the stack.  */
+  IPA_JF_PARAM_LOAD,
+
+  /* Casts the top of the stack to RESULT_TYPE.  */
+  IPA_JF_TYPE_CAST,
+
+  /* Arithmetic operations.  */
+  /* ======================  */
+
+  /* These instructions expect their arguments on the stack.  Arguments for
+     non-unary operations should be on the stack in right-to-left order,
+     meaning RHS will be popped before LHS.  */
+
+  /* Binary operation.  Adds the arguments and pushes the result onto the
+     stack.  */
+  IPA_JF_PLUS_EXPR,
+  /* Binary operation.  Subtracts the arguments and pushes the result onto the
+     stack.  */
+  IPA_JF_SUB_EXPR,
+  /* Binary operation.  Multiplies the arguments and pushes the result onto the
+     stack.  */
+  IPA_JF_MULT_EXPR,
+  /* Binary operation.  Divides the arguments and pushes the result onto the
+     stack.  */
+  IPA_JF_DIV_EXPR,
+  /* Unary operation.  Negates the argument and pushes the results onto
+     the stack.  */
+  IPA_JF_MINUS_EXPR,
+
+  /* Conditionals.  */
+  /* =============  */
+
+  /* Ternary conditional expression.  Pops CONDITION, TRUE_BRANCH and
+     ELSE_BRANCH off the stack.  If CONDITION evaluates to true, pushes
+     TRUE_BRANCH onto the stack.  Pushes ELSE_BRANCH onto the stack
+     otherwise.  */
+  IPA_JF_COND_TERNARY,
+
+  /* End sentinel.  */
+  IPA_JF_BYTECODE_END
+};
+
+inline bool
+ipa_jf_bytecode_valid_p (ipa_jf_bytecode op)
+{
+  return IPA_JF_BYTECODE_START < op && op < IPA_JF_BYTECODE_END;
+}
+
+struct ipa_bytecode_seq
+{
+public:
+  ipa_bytecode_seq () : _data (nullptr), _size (0), _max (0) {}
+  ~ipa_bytecode_seq () = default; // has to be destroyed by calling destroy()
+
+  void destroy ()
+  {
+    ::operator delete (_data);
+    _data = nullptr;
+    _size = _max = 0;
+  }
+
+  template <typename T> void push (const T &elt)
+  {
+    _ensure_fits (sizeof (T));
+
+    __builtin_memcpy (_data + _size, &elt, sizeof (T));
+    _size += sizeof (T);
+  }
+
+  template <typename T> T read_and_advance (unsigned &offset) const
+  {
+    T res;
+    __builtin_memcpy (&res, _data + offset, sizeof (T));
+    offset += sizeof (T);
+    return res;
+  }
+
+  unsigned size () const { return _size; }
+
+  bool is_empty () const { return !_size; }
+
+private:
+  ipa_jf_bytecode *_data;
+  unsigned _size, _max;
+
+  void _ensure_fits (unsigned elt_size)
+  {
+    if (_size + elt_size < _max)
+      return;
+
+    _max = _max ? _max * GROWTH_FACTOR : DEFAULT_SIZE;
+    ipa_jf_bytecode *new_data = (ipa_jf_bytecode *) ::operator new (_max);
+    __builtin_memcpy (new_data, _data, _size);
+    ::operator delete (_data);
+    _data = new_data;
+  }
+
+  static constexpr unsigned DEFAULT_SIZE = 128; // arbitrary number
+  static constexpr unsigned GROWTH_FACTOR = 2; // arbitrary number
+};
+
+inline ipa_jf_bytecode
+tree_code_to_ipa_jf_bytecode (tree_code tc)
+{
+  switch (tc)
+    {
+    case PLUS_EXPR:
+    case POINTER_PLUS_EXPR:
+      return IPA_JF_PLUS_EXPR;
+
+    case MINUS_EXPR:
+      return IPA_JF_SUB_EXPR;
+
+    case MULT_EXPR:
+      return IPA_JF_MULT_EXPR;
+
+    // many more div exprs to go :(
+    case TRUNC_DIV_EXPR:
+    case CEIL_DIV_EXPR:
+    case FLOOR_DIV_EXPR:
+    case ROUND_DIV_EXPR:
+      return IPA_JF_DIV_EXPR;
+
+    default:
+      return IPA_JF_BYTECODE_END;
+    }
+}
+
+struct ipa_jf_expr
+{
+  /* If agg_contents is set, this is the offset from which the used data was
+     loaded.  */
+  HOST_WIDE_INT offset;
+  /* Type of the access reading the data (or the PARM_DECL SSA_NAME).  */
+  tree type;
+
+  /* Index in the list of formals.  */
+  int formal_id;
+
+  unsigned agg_preserved : 1;
+
+  /* Set if the used data were loaded from an aggregate parameter or from
+     data received by reference.  */
+  unsigned agg_contents : 1;
+  /* If agg_contents is set, this differentiates between loads from data
+     passed by reference and by value.  */
+  unsigned by_ref : 1;
+  /* A set of sequential operations on the parameter, which can be seen as
+     a mathematical function on the parameter.  */
+  ipa_bytecode_seq bytecode;
+
+  inline bool scalar_p () const noexcept { return offset == SCALAR_SENTINEL; }
+
+  static constexpr HOST_WIDE_INT SCALAR_SENTINEL = -1;
+};
+
+struct ipa_jf_agg_data
+{
+  vec<ipa_jf_expr> agg_parts;
+  bool by_ref : 1;
+};
+
+enum new_jump_func_type
+{
+  NEW_IPA_JF_UNKNOWN,
+  NEW_IPA_JF_CONST,
+  NEW_IPA_JF_PASS_THROUGH,
+  NEW_IPA_JF_AGGREGATE,
+};
+
+struct ipa_jf
+{
+  ipa_jf () : type (NEW_IPA_JF_UNKNOWN), vr (NULL) {}
+
+  ~ipa_jf () = default;
+
+  enum new_jump_func_type type;
+  ipa_vr *vr = NULL;
+
+  union
+  {
+    ipa_constant_data constant;
+    ipa_jf_expr pass_through;
+    ipa_jf_agg_data aggregate;
+  };
+};
 
 /* Return the constant stored in a constant jump function JFUNC.  */

Reply via email to