@@ -803,6 +803,11 @@ parm_preserved_before_stmt_p (struct ipa_func_body_info *fbi, int index,
bool modified = false;
ao_ref refd;
+ tree base = get_base_address (parm_load);
+ gcc_assert (TREE_CODE (base) == PARM_DECL);
+ if (TREE_READONLY (base))
+ return true;
+
/* FIXME: FBI can be NULL if we are being called from outside
ipa_node_analysis or ipcp_transform_function, which currently happens
during inlining analysis. It would be great to extend fbi's lifetime and
@@ -1395,6 +1400,121 @@ build_agg_jump_func_from_list (struct ipa_known_agg_contents_list *list,
}
}
+/* Return how many interprocedural scalar invariants there are in a static
+ CONSTRUCTOR of a variable. */
+
+static unsigned
+count_constants_in_agg_constructor (tree constructor)
+{
+ unsigned res = 0, max = PARAM_VALUE (PARAM_IPA_MAX_AGG_ITEMS);
+ unsigned ix;
+ tree index, val;
+ FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (constructor), ix, index, val)
+ {
+ if (TREE_CODE (TREE_TYPE (constructor)) == RECORD_TYPE
+ && !index)
+ /* We cannot handle field elements that do not have the field decl as
+ its index. */
+ continue;
+ if (is_gimple_reg_type (TREE_TYPE (val))
+ && is_gimple_ip_invariant (val))
+ res++;
+ else if (TREE_CODE (val) == CONSTRUCTOR)
+ res += count_constants_in_agg_constructor (val);
+
+ if (res > max)
+ return max;
+ }
+ return res;
+}
+
+/* Push invariants from static constructor of a global variable into JFUNC's
+ aggregate jump function. BASE_OFFSET is the offset which should be added to
+ offset of each value. It can be negative to represent that only a part of
+ an aggregate starting at BASE_OFFSET is being passed as an actual
+ argument. */
+
+static void
+build_agg_jump_func_from_constructor (tree constructor,
+ HOST_WIDE_INT base_offset,
+ struct ipa_jump_func *jfunc)
+{
+ tree type = TREE_TYPE (constructor);
+ if (TREE_CODE (type) != ARRAY_TYPE
+ && TREE_CODE (type) != RECORD_TYPE)
+ return;
+
+ unsigned ix;
+ tree index, val;
+ FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (constructor), ix, index, val)
+ {
+ if (!jfunc->agg.items->space (1))
+ return;
+
+ HOST_WIDE_INT elt_offset;
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ offset_int off;
+ tree unit_size = TYPE_SIZE_UNIT (TREE_TYPE (type));
+ gcc_assert (TREE_CODE (unit_size) == INTEGER_CST);
+
+ if (index)
+ {
+ off = wi::to_offset (index);
+ if (TYPE_DOMAIN (type) && TYPE_MIN_VALUE (TYPE_DOMAIN (type)))
+ {
+ tree low_bound = TYPE_MIN_VALUE (TYPE_DOMAIN (type));
+ gcc_assert (TREE_CODE (unit_size) == INTEGER_CST);
+ off = wi::sext (off - wi::to_offset (low_bound),
+ TYPE_PRECISION (TREE_TYPE (index)));
+ }
+ off *= wi::to_offset (unit_size);
+ }
+ else
+ off = wi::to_offset (unit_size) * ix;
+
+ off = wi::lshift (off, LOG2_BITS_PER_UNIT);
+ if (!wi::fits_shwi_p (off) || wi::neg_p (off))
+ continue;
+ elt_offset = off.to_shwi ();
+ }
+ else if (TREE_CODE (type) == RECORD_TYPE)
+ {
+ gcc_checking_assert (index && TREE_CODE (index) == FIELD_DECL);
+ if (DECL_BIT_FIELD (index))
+ continue;
+ elt_offset = int_bit_position (index);
+ }
+ else
+ gcc_unreachable ();
+
+ if (is_gimple_reg_type (TREE_TYPE (val))
+ && is_gimple_ip_invariant (val))
+ {
+ HOST_WIDE_INT offset = elt_offset + base_offset;
+ if (offset >= 0
+ && (offset % BITS_PER_UNIT) == 0)
+ {
+ /* FIXME: The following check is just meant for developement, to
+ double check my assumption about constructor elements
+ ordering, either remove it or change it to a checking
+ assert. */
+ if (!jfunc->agg.items->is_empty ())
+ gcc_assert ((*jfunc->agg.items)[jfunc->agg.items->length () - 1]
+ .offset < offset);
+
+ struct ipa_agg_jf_item item;
+ item.offset = offset;
+ item.value = unshare_expr_without_location (val);
+ jfunc->agg.items->quick_push (item);
+ }
+ }
+ else if (TREE_CODE (val) == CONSTRUCTOR)
+ build_agg_jump_func_from_constructor (val, base_offset + elt_offset,
+ jfunc);
+ }
+}
+
/* Traverse statements from CALL backwards, scanning whether an aggregate given
in ARG is filled in with constant values. ARG can either be an aggregate
expression or a pointer to an aggregate. ARG_TYPE is the type of the
@@ -1475,6 +1595,27 @@ determine_locally_known_aggregate_parts (gcall *call, tree arg,
ao_ref_init (&r, arg);
}
+ /* If we happen to be using a constant global variable or a reference to it,
+ construct the jump function from the static initializer. */
+
+ if ((TREE_CODE (arg) == VAR_DECL)
+ && is_global_var (arg)
+ && DECL_INITIAL (arg)
+ && TREE_READONLY (arg)
+ && TREE_CODE (DECL_INITIAL (arg)) == CONSTRUCTOR)
+ {
+ /* PR69708: Figure out aggregate jump-function with constant init
+ value. */
+ tree constructor = DECL_INITIAL (arg);
+ const_count = count_constants_in_agg_constructor (constructor);
+ if (!const_count)
+ return;
+ vec_alloc (jfunc->agg.items, const_count);
+ jfunc->agg.by_ref = by_ref;
+ build_agg_jump_func_from_constructor (constructor, -arg_offset, jfunc);
+ return;
+ }
+
/* Second stage walks back the BB, looks at individual statements and as long
as it is confident of how the statements affect contents of the
aggregates, it builds a sorted linked list of ipa_agg_jf_list structures
new file mode 100644
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-ipa-cp-details" } */
+
+typedef struct S
+{
+ int (*call)(int);
+} S;
+
+static int __attribute__((noinline))
+foo (S f, int x)
+{
+ x = f.call(x);
+ x = f.call(x);
+ x = f.call(x);
+ return x;
+}
+
+static int
+sq (int x)
+{
+ return x * x;
+}
+
+static const S s = {sq};
+
+int
+h (int x)
+{
+ return foo (s, x);
+}
+
+/* { dg-final { scan-ipa-dump-times "Discovered an indirect call to a known target" 3 "cp" } } */
new file mode 100644
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-ipa-cp-details" } */
+
+typedef struct S
+{
+ int (*call)(int);
+} S;
+
+static int __attribute__((noinline))
+bar (const S *f, int x)
+{
+ x = f->call(x);
+ x = f->call(x);
+ x = f->call(x);
+ return x;
+}
+
+static int
+sq (int x)
+{
+ return x * x;
+}
+
+static const S s = {sq};
+
+int
+g (int x)
+{
+ return bar (&s, x);
+}
+
+int
+obfuscate (int x)
+{
+ return bar ((S *) 0, x);
+}
+
+/* { dg-final { scan-ipa-dump "Discovered an indirect call to a known target" "cp" } } */
+/* { dg-final { scan-ipa-dump-times "Discovered an indirect call to a known target" 3 "cp" { xfail *-*-* } } } */
new file mode 100644
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-ipa-cp-details" } */
+
+typedef struct S
+{
+ int confuse;
+ int (*call)(int);
+} S;
+
+extern const S *es;
+
+static int __attribute__((noinline))
+foo (const S f, int x)
+{
+ es = &f; /* This disables IPA-SRA */
+ x = f.call(x+f.confuse);
+ x = f.call(x);
+ x = f.call(x);
+ return x;
+}
+
+static int
+sq (int x)
+{
+ return x * x;
+}
+
+static const S s = {16, sq};
+
+int
+h (int x)
+{
+ return foo (s, x);
+}
+
+/* { dg-final { scan-ipa-dump "Discovered an indirect call to a known target" "cp" } } */
+/* { dg-final { scan-ipa-dump-times "Discovered an indirect call to a known target" 3 "cp" } } */
new file mode 100644
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-ipa-cp-details" } */
+
+typedef struct S
+{
+ int (*call)(int);
+} S;
+
+static int __attribute__((noinline))
+bar (const S *f, int x)
+{
+ x = f->call(x);
+ x = f->call(x);
+ x = f->call(x);
+ return x;
+}
+
+static int
+sq (int x)
+{
+ return x * x;
+}
+
+static const S s = {sq};
+
+int
+g (int x)
+{
+ return bar (&s, x);
+}
+
+int
+obfuscate (int x)
+{
+ return bar ((S *) 0, x);
+}
+
+/* { dg-final { scan-ipa-dump "Discovered an indirect call to a known target" "cp" } } */
+/* { dg-final { scan-ipa-dump-times "Discovered an indirect call to a known target" 3 "cp" { xfail *-*-* } } } */
new file mode 100644
@@ -0,0 +1,59 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-ipa-cp-details" } */
+
+#define N 4
+
+typedef int (* const A[N])(int);
+
+extern const A *ga;
+
+static int __attribute__((noinline))
+bar (const A *f, int x)
+{
+ x = (*f)[2](x);
+ x = (*f)[2](x);
+ x = (*f)[2](x);
+ ga = f;
+ return x;
+}
+
+static int
+zero (int x)
+{
+ return 0;
+}
+
+static int
+addone (int x)
+{
+ return x + 1;
+}
+
+static int
+sq (int x)
+{
+ return x * x;
+}
+
+static int
+cube (int x)
+{
+ return x * x * x;
+}
+
+static const A a = {zero, addone, sq, cube};
+
+int
+g (int x)
+{
+ return bar (&a, x);
+}
+
+int
+obfuscate (int x)
+{
+ return bar ((A *) 0, x);
+}
+
+/* { dg-final { scan-ipa-dump "Discovered an indirect call to a known target" "cp" } } */
+/* { dg-final { scan-ipa-dump-times "Discovered an indirect call to a known target" 3 "cp" { xfail *-*-* } } } */
new file mode 100644
@@ -0,0 +1,81 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-ipa-cp-details" } */
+
+#define N 4
+
+typedef int (* const A[N])(int);
+
+typedef struct S
+{
+ int obfuscate;
+ A a;
+} S;
+
+extern const S *gs;
+
+static int __attribute__((noinline))
+foo (const S f, int x)
+{
+ gs = &f;
+ x = f.a[2](x);
+ x = f.a[2](x);
+ x = f.a[2](x);
+ return x;
+}
+
+static int __attribute__((noinline))
+bar (const S *f, int x)
+{
+ gs = f;
+ x = f->a[2](x);
+ x = f->a[2](x);
+ x = f->a[2](x);
+ return x;
+}
+
+static int
+zero (int x)
+{
+ return 0;
+}
+
+static int
+addone (int x)
+{
+ return x + 1;
+}
+
+static int
+sq (int x)
+{
+ return x * x;
+}
+
+static int
+cube (int x)
+{
+ return x * x * x;
+}
+
+static const S s = {64, {zero, addone, sq, cube}};
+
+int
+h (int x)
+{
+ return foo (s, x);
+}
+
+int
+g (int x)
+{
+ return bar (&s, x);
+}
+
+int
+obfuscate (int x)
+{
+ return bar ((S *) 0, x);
+}
+
+/* { dg-final { scan-ipa-dump "Discovered an indirect call to a known target" "cp" } } */
+/* { dg-final { scan-ipa-dump-times "Discovered an indirect call to a known target" 6 "cp" { xfail *-*-* } } } */
new file mode 100644
@@ -0,0 +1,46 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-ipa-cp-details" } */
+
+typedef struct S
+{
+ int (*call)(int);
+} S;
+
+static int __attribute__((noinline))
+bar (const S *f, int x)
+{
+ x = f->call(x);
+ return x;
+}
+
+extern void impossible_aa (void);
+
+static int __attribute__((noinline))
+baz (const S *f, int x)
+{
+ impossible_aa ();
+ return bar (f, x);
+}
+
+static int
+sq (int x)
+{
+ return x * x;
+}
+
+static const S s = {sq};
+
+int
+g (int x)
+{
+ return baz (&s, x);
+}
+
+int
+obfuscate (int x)
+{
+ return baz ((S *) 0, x);
+}
+
+/* { dg-final { scan-ipa-dump "Discovered an indirect call to a known target" "cp" { xfail *-*-* } } } */
+