+/* Analyze EXPR if it represents a series of simple operations performed on
+ a function parameter and return true if so. FBI, STMT, EXPR, INDEX_P and
+ AGGPOS have the same meaning like in unmodified_parm_or_parm_agg_item.
+ Type of the parameter or load from an aggregate via the parameter is
+ stored in *TYPE_P. Operations on the parameter are recorded to
+ PARAM_OPS_P if it is not NULL. */
+
+static bool
+decompose_param_expr (struct ipa_func_body_info *fbi,
+ gimple *stmt, tree expr,
+ int *index_p, tree *type_p,
+ struct agg_position_info *aggpos,
+ expr_eval_ops *param_ops_p = NULL)
+{
+ int op_limit = PARAM_VALUE (PARAM_IPA_MAX_PARAM_EXPR_OPS);
+ int op_count = 0;
+
+ if (param_ops_p)
+ *param_ops_p = NULL;
+
+ while (true)
+ {
+ expr_eval_op eval_op;
+ unsigned rhs_count;
+ unsigned cst_count = 0;
+
+ if (unmodified_parm_or_parm_agg_item (fbi, stmt, expr, index_p, NULL,
+ aggpos))
+ {
+ tree type = TREE_TYPE (expr);
+
+ if (aggpos->agg_contents)
+ {
+ /* Stop if containing bit-field. */
+ if (TREE_CODE (expr) == BIT_FIELD_REF
+ || contains_bitfld_component_ref_p (expr))
+ break;
+ }
+
+ *type_p = type;
+ return true;
+ }
+
+ if (TREE_CODE (expr) != SSA_NAME || SSA_NAME_IS_DEFAULT_DEF (expr))
+ break;
+
+ if (!is_gimple_assign (stmt = SSA_NAME_DEF_STMT (expr)))
+ break;
+
+ switch (gimple_assign_rhs_class (stmt))
+ {
+ case GIMPLE_SINGLE_RHS:
+ expr = gimple_assign_rhs1 (stmt);
+ continue;
+
+ case GIMPLE_UNARY_RHS:
+ rhs_count = 1;
+ break;
+
+ case GIMPLE_BINARY_RHS:
+ rhs_count = 2;
+ break;
+
+ case GIMPLE_TERNARY_RHS:
+ rhs_count = 3;
+ break;
+
+ default:
+ goto fail;
+ }
+
+ /* Stop if expression is too complex. */
+ if (op_count++ == op_limit)
+ break;
+
+ if (param_ops_p)
+ {
+ eval_op.code = gimple_assign_rhs_code (stmt);
+ eval_op.type = TREE_TYPE (gimple_assign_lhs (stmt));
+ eval_op.val[0] = NULL_TREE;
+ eval_op.val[1] = NULL_TREE;
+ }
+
+ expr = NULL_TREE;
+ for (unsigned i = 0; i < rhs_count; i++)
+ {
+ tree op = gimple_op (stmt, i + 1);
+
+ gcc_assert (op && !TYPE_P (op));
+ if (is_gimple_ip_invariant (op))
+ {
+ if (++cst_count == rhs_count)
+ goto fail;
+
+ eval_op.val[cst_count - 1] = op;
+ }
+ else if (!expr)
+ {
+ /* Found a non-constant operand, and record its index in rhs
+ operands. */
+ eval_op.index = i;
+ expr = op;
+ }
+ else
+ {
+ /* Found more than one non-constant operands. */
+ goto fail;
+ }
+ }
+
+ if (param_ops_p)
+ vec_safe_insert (*param_ops_p, 0, eval_op);
+ }
+
+ /* Failed to decompose, free resource and return. */
+fail:
+ if (param_ops_p)
+ vec_free (*param_ops_p);
+
+ return false;
+}