https://gcc.gnu.org/g:684c9811dee6573fdb8b01c9bef16e68054fc87c
commit r17-5633-g684c9811dee6573fdb8b01c9bef16e68054fc87c Author: Josef Melcr <[email protected]> Date: Wed Sep 16 13:54:18 2026 +0200 ipa: Create a callback edge when encountering an internal .ASSUME call This patch adds logic which creates an internal callback edge when it encounters an IFN_ASSUME internal call. It acts as a trigger for most of the patches in this series. ipa_compute_jump_functions_for_edge has to be called separately for the new edge, as all jump functions would have been computed at this point in the code. I squashed the patch adding testcases into this commit. PR ipa/109112 gcc/ChangeLog: * ipa-prop.cc (ipa_analyze_call_uses): Create an internal callback edge when dealing with a .ASSUME internal call. gcc/testsuite/ChangeLog: * g++.dg/ipa/ipa-cp-assume-2.C: New test. * g++.dg/ipa/ipa-cp-assume-3.C: New test. * g++.dg/ipa/ipa-cp-assume.C: New test. * g++.dg/ipa/ipa-sra-assume-2.C: New test. * g++.dg/ipa/ipa-sra-assume-3.C: New test. * g++.dg/ipa/ipa-sra-assume.C: New test. * g++.dg/lto/ipa-cp-assume_0.C: New test. * g++.dg/lto/ipa-cp-assume_1.C: New test. * g++.dg/lto/ipa-sra-assume_0.C: New test. * g++.dg/lto/ipa-sra-assume_1.C: New test. * g++.dg/lto/ipa-cp-assume-1_0.C: New test. * g++.dg/lto/ipa-cp-assume-2_0.C: New test. * g++.dg/lto/ipa-cp-assume-3_0.C: New test. * g++.dg/lto/ipa-sra-assume-1_0.C: New test. * g++.dg/lto/ipa-sra-assume-2_0.C: New test. * g++.dg/lto/ipa-sra-assume-3_0.C: New test. Signed-off-by: Josef Melcr <[email protected]> Diff: --- gcc/ipa-prop.cc | 13 +++++++ gcc/testsuite/g++.dg/ipa/ipa-cp-assume-2.C | 40 +++++++++++++++++++++ gcc/testsuite/g++.dg/ipa/ipa-cp-assume-3.C | 51 +++++++++++++++++++++++++++ gcc/testsuite/g++.dg/ipa/ipa-cp-assume.C | 26 ++++++++++++++ gcc/testsuite/g++.dg/ipa/ipa-sra-assume-2.C | 33 +++++++++++++++++ gcc/testsuite/g++.dg/ipa/ipa-sra-assume-3.C | 34 ++++++++++++++++++ gcc/testsuite/g++.dg/ipa/ipa-sra-assume.C | 37 +++++++++++++++++++ gcc/testsuite/g++.dg/lto/ipa-cp-assume-1_0.C | 16 +++++++++ gcc/testsuite/g++.dg/lto/ipa-cp-assume-2_0.C | 16 +++++++++ gcc/testsuite/g++.dg/lto/ipa-cp-assume-3_0.C | 18 ++++++++++ gcc/testsuite/g++.dg/lto/ipa-cp-assume_0.C | 27 ++++++++++++++ gcc/testsuite/g++.dg/lto/ipa-cp-assume_1.C | 13 +++++++ gcc/testsuite/g++.dg/lto/ipa-sra-assume-1_0.C | 22 ++++++++++++ gcc/testsuite/g++.dg/lto/ipa-sra-assume-2_0.C | 6 ++++ gcc/testsuite/g++.dg/lto/ipa-sra-assume-3_0.C | 6 ++++ gcc/testsuite/g++.dg/lto/ipa-sra-assume_0.C | 28 +++++++++++++++ gcc/testsuite/g++.dg/lto/ipa-sra-assume_1.C | 11 ++++++ 17 files changed, 397 insertions(+) diff --git a/gcc/ipa-prop.cc b/gcc/ipa-prop.cc index 54c40691932b..c614ddccbbb0 100644 --- a/gcc/ipa-prop.cc +++ b/gcc/ipa-prop.cc @@ -3214,6 +3214,19 @@ ipa_analyze_call_uses (struct ipa_func_body_info *fbi, gcall *call) { tree target = gimple_call_fn (call); + /* Internal .ASSUME call. Create a callback edge to its body, calculate its + jump functions and return. This is done here as the main creation route in + ipa_compute_jump_functions_for_edge doesn't analyze call statements. */ + if (callback_ifn_assume_call_p (call)) + { + tree callee_decl = TREE_OPERAND (gimple_call_arg (call, 0), 0); + cgraph_node *target = cgraph_node::get_create (callee_decl); + cgraph_edge *cbei + = cgraph_edge::make_callback_internal (fbi->node, target, call); + ipa_compute_jump_functions_for_edge (fbi, cbei); + return; + } + if (!target || (TREE_CODE (target) != SSA_NAME && !virtual_method_call_p (target))) diff --git a/gcc/testsuite/g++.dg/ipa/ipa-cp-assume-2.C b/gcc/testsuite/g++.dg/ipa/ipa-cp-assume-2.C new file mode 100644 index 000000000000..d9f7af46ec9f --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/ipa-cp-assume-2.C @@ -0,0 +1,40 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fdump-ipa-cp-details -fdump-tree-optimized" } */ + +/* Should propagate in chain baz -> foo -> assume and fold body of foo + accordingly. Works because bar can be inlined (otherwise we can't derive the + value range for x). */ + +void do_something (); + +inline bool +bar (int x, int y) +{ + return x < y; +} + +static bool __attribute__ ((noinline)) +foo (int x, int a) +{ + [[assume (bar (x, a))]]; + bool ret = x < 42; + + if (!__builtin_constant_p (ret)) + __builtin_abort (); + + return ret; +} + +void +baz (int x) +{ + bool should_do_something = false; + for (int i = 0; i < 1000000; i++) + should_do_something = foo (x, 42); + + if (!should_do_something) + do_something (); +} + +/* { dg-final { scan-ipa-dump-times "replacing param #1 a with const 42" 2 "cp" } } */ +/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */ diff --git a/gcc/testsuite/g++.dg/ipa/ipa-cp-assume-3.C b/gcc/testsuite/g++.dg/ipa/ipa-cp-assume-3.C new file mode 100644 index 000000000000..4b79480b4f3b --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/ipa-cp-assume-3.C @@ -0,0 +1,51 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fdump-ipa-cp-details -fdump-ipa-sra-details -fdump-tree-optimized" } */ + +/* Should propagate baz -> foo -> assume, split the parameter, derive the range + for s->x and fold foo accordingly. */ + +void something (); + +struct S +{ + int x, y; +}; + +inline bool +bar (int x, int y) +{ + return x < y; +} + +static bool __attribute__ ((noinline)) +foo (struct S *s) +{ + [[assume (bar (s->x, s->y))]]; + bool ret = s->x < 42; + bool ahoj = s->x < s->y; + + if (!__builtin_constant_p (ret)) + __builtin_abort (); + + if (!__builtin_constant_p (ahoj)) + __builtin_abort (); + + return ret; +} + +void +baz (int x) +{ + struct S s; + s.x = x; + s.y = 42; + bool shouldnt_do_something = foo (&s); + + if (!shouldnt_do_something) + something (); +} + +/* { dg-final { scan-ipa-dump "Aggregate replacements: 0\\\[4]=42" "cp" } } */ +/* { dg-final { scan-ipa-dump ".*assume.*constprop.* Will split parameter 0" "sra" } } */ +/* { dg-final { scan-ipa-dump "assume.*constprop\\.isra" "sra" } } */ +/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */ diff --git a/gcc/testsuite/g++.dg/ipa/ipa-cp-assume.C b/gcc/testsuite/g++.dg/ipa/ipa-cp-assume.C new file mode 100644 index 000000000000..6eeebd4ad5c8 --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/ipa-cp-assume.C @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fdump-ipa-cp-details -fdump-tree-optimized" } */ + +/* Should propagate 'a' into the assume body and fold foo. */ + +inline bool +bar (int x, int y) +{ + return x < y; +} + +bool +foo (int x) +{ + int a = 42; + [[assume (bar (x, a))]]; + bool ret = x < 42; + + if (!__builtin_constant_p (ret)) + __builtin_abort (); + + return ret; +} + +/* { dg-final { scan-ipa-dump "replacing param #1 a with const 42" "cp" } } */ +/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */ diff --git a/gcc/testsuite/g++.dg/ipa/ipa-sra-assume-2.C b/gcc/testsuite/g++.dg/ipa/ipa-sra-assume-2.C new file mode 100644 index 000000000000..009b315ac42b --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/ipa-sra-assume-2.C @@ -0,0 +1,33 @@ +/* Test that the assume can be copied before creating the edges. */ + +/* { dg-do compile } */ +/* { dg-options "-O3" } */ + +struct S +{ + int x, y; +}; + +inline bool +baz (S x) +{ + return x.x < 42; +} + +inline bool +qux (S x, int h) +{ + [[assume (baz (x))]]; + return x.x < 42 && x.y > 52 && h < 50; +} + +void +corge (S x, S y, S z, bool *p) +{ + for (int i = 0; i < x.x + x.y; i++) + { + p[0] = qux (x, 41); + p[1] = qux (y, 60); + p[2] = qux (z, 20); + } +} diff --git a/gcc/testsuite/g++.dg/ipa/ipa-sra-assume-3.C b/gcc/testsuite/g++.dg/ipa/ipa-sra-assume-3.C new file mode 100644 index 000000000000..fd15c957794a --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/ipa-sra-assume-3.C @@ -0,0 +1,34 @@ +/* Test that the assume can be copied after creating the edges. */ + +/* { dg-do compile } */ +/* { dg-options "-O3 -fno-early-inlining" } */ + +struct S +{ + int x, y; +}; + +inline bool +baz (S x) +{ + return x.x < 42; +} + +[[gnu::always_inline]] +inline bool +qux (S x, int h) +{ + [[assume (baz (x))]]; + return x.x < 42 && x.y > 52 && h < 50; +} + +void +corge (S x, S y, S z, bool *p) +{ + for (int i = 0; i < x.x + x.y; i++) + { + p[0] = qux (x, 41); + p[1] = qux (y, 60); + p[2] = qux (z, 20); + } +} diff --git a/gcc/testsuite/g++.dg/ipa/ipa-sra-assume.C b/gcc/testsuite/g++.dg/ipa/ipa-sra-assume.C new file mode 100644 index 000000000000..91b6366eb6af --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/ipa-sra-assume.C @@ -0,0 +1,37 @@ +/* Test that ipa-sra can work with .ASSUME outlined bodies. */ + +/* { dg-do compile } */ +/* { dg-options "-O2 -fipa-sra -fdump-ipa-sra-details -fdump-tree-optimized" } */ + +struct S +{ + bool x; + int y; +}; + +void +split_struct (struct S s) +{ + [[assume (!s.x)]]; + if (s.x) + __builtin_abort (); +} + +void +remove_ptr (bool *x) +{ + [[assume (!*x)]]; + if (*x) + __builtin_abort (); +} + +void +two_values (struct S s) +{ + [[assume (s.y == 3 || s.y == 4)]]; + if (s.y == 5) + __builtin_abort (); +} + +/* { dg-final { scan-ipa-dump-times "Will split parameter 0" 3 "sra" } } */ +/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */ diff --git a/gcc/testsuite/g++.dg/lto/ipa-cp-assume-1_0.C b/gcc/testsuite/g++.dg/lto/ipa-cp-assume-1_0.C new file mode 100644 index 000000000000..1111087d8006 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/ipa-cp-assume-1_0.C @@ -0,0 +1,16 @@ +/* { dg-lto-do link } */ +/* { dg-lto-options {{ -O3 -fdump-ipa-cp-details -fdump-tree-optimized -flto }} } */ + +#include "../ipa/ipa-cp-assume.C" + +int +main () +{ + int x; + __builtin_scanf ("%d", &x); + foo (x); + return 0; +} + +/* { dg-final { scan-wpa-ipa-dump "replacing param #1 int with const 42" "cp" } } */ +/* { dg-final { scan-ltrans-tree-dump-not "abort" "optimized" } } */ diff --git a/gcc/testsuite/g++.dg/lto/ipa-cp-assume-2_0.C b/gcc/testsuite/g++.dg/lto/ipa-cp-assume-2_0.C new file mode 100644 index 000000000000..caafb9c3c93f --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/ipa-cp-assume-2_0.C @@ -0,0 +1,16 @@ +/* { dg-lto-do link } */ +/* { dg-lto-options {{ -O3 -fdump-ipa-cp-details -fdump-tree-optimized -flto }} } */ + +#include "../ipa/ipa-cp-assume-2.C" + +int +main () +{ + int x; + __builtin_scanf ("%d", &x); + baz (x); + return 0; +} + +/* { dg-final { scan-wpa-ipa-dump-times "replacing param #1 int with const 42" 2 "cp" } } */ +/* { dg-final { scan-ltrans-tree-dump-not "abort" "optimized" } } */ diff --git a/gcc/testsuite/g++.dg/lto/ipa-cp-assume-3_0.C b/gcc/testsuite/g++.dg/lto/ipa-cp-assume-3_0.C new file mode 100644 index 000000000000..8ca3c99d8aa2 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/ipa-cp-assume-3_0.C @@ -0,0 +1,18 @@ +/* { dg-lto-do link } */ +/* { dg-lto-options {{ -O3 -fdump-ipa-cp-details -fdump-ipa-sra-details -fdump-tree-optimized -flto }} } */ + +#include "../ipa/ipa-cp-assume-3.C" + +int +main () +{ + int x; + __builtin_scanf ("%d", &x); + baz (x); + return 0; +} + +/* { dg-final { scan-wpa-ipa-dump "Aggregate replacements: 0\\\[4]=42" "cp" } } */ +/* { dg-final { scan-wpa-ipa-dump ".*assume.*constprop.* Will split parameter 0" "sra" } } */ +/* { dg-final { scan-wpa-ipa-dump "assume.*constprop\\.isra" "sra" } } */ +/* { dg-final { scan-ltrans-tree-dump-not "abort" "optimized" } } */ diff --git a/gcc/testsuite/g++.dg/lto/ipa-cp-assume_0.C b/gcc/testsuite/g++.dg/lto/ipa-cp-assume_0.C new file mode 100644 index 000000000000..3f69250f33b3 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/ipa-cp-assume_0.C @@ -0,0 +1,27 @@ +/* { dg-lto-options {{ -O3 -flto -fdump-tree-optimized }} } */ +/* { dg-lto-do link } */ + +/* The point of this test is to propagate the constant into the assume body in + the other file, infer the value range for 'x', evaluate bar to 0, meaning + that the loop will never run and the abort would never be executed. */ + +int bar (int, int); + +void +foo (int x, int y) +{ + int c = bar (x, y); + for (int i = 0; i < c; i++) + __builtin_abort (); +} + +int +main () +{ + int x; + __builtin_scanf ("%d", &x); + foo (x, 67); + return 0; +} + +/* { dg-final { scan-ltrans-tree-dump-not "__builtin_abort" "optimized" } } */ diff --git a/gcc/testsuite/g++.dg/lto/ipa-cp-assume_1.C b/gcc/testsuite/g++.dg/lto/ipa-cp-assume_1.C new file mode 100644 index 000000000000..4ac52422d372 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/ipa-cp-assume_1.C @@ -0,0 +1,13 @@ +inline bool +baz (int x, int y) +{ + return x < y; +} + +int +bar (int x, int y) +{ + [[assume (baz (x, y))]]; + __builtin_printf ("%d %d", x, y); + return x > y; +} diff --git a/gcc/testsuite/g++.dg/lto/ipa-sra-assume-1_0.C b/gcc/testsuite/g++.dg/lto/ipa-sra-assume-1_0.C new file mode 100644 index 000000000000..5d55efa44975 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/ipa-sra-assume-1_0.C @@ -0,0 +1,22 @@ +/* { dg-lto-do link } */ +/* { dg-lto-options {{ -O2 -fno-inline -fipa-sra -fdump-ipa-sra-details -fdump-tree-optimized -flto }} } */ + +#include "../ipa/ipa-sra-assume.C" + +int +main () +{ + int x, y, z; + __builtin_scanf ("%d %d %d", &x, &y, &z); + + struct S s = {.x = (bool) x, .y = y}; + bool a = z; + + split_struct (s); + remove_ptr (&a); + two_values (s); + return 0; +} + +/* { dg-final { scan-wpa-ipa-dump-times "Will split parameter 0" 6 "sra" } } */ +/* { dg-final { scan-ltrans-tree-dump-not "abort" "optimized" } } */ diff --git a/gcc/testsuite/g++.dg/lto/ipa-sra-assume-2_0.C b/gcc/testsuite/g++.dg/lto/ipa-sra-assume-2_0.C new file mode 100644 index 000000000000..d3d0251ce5da --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/ipa-sra-assume-2_0.C @@ -0,0 +1,6 @@ +/* Test that the assume can be copied before creating the edges. */ + +/* { dg-lto-do assemble } */ +/* { dg-lto-options {{ -O3 -flto }} } */ + +#include "../ipa/ipa-sra-assume-2.C" diff --git a/gcc/testsuite/g++.dg/lto/ipa-sra-assume-3_0.C b/gcc/testsuite/g++.dg/lto/ipa-sra-assume-3_0.C new file mode 100644 index 000000000000..432cfcfd5f35 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/ipa-sra-assume-3_0.C @@ -0,0 +1,6 @@ +/* Test that the assume can be copied after creating the edges. */ + +/* { dg-lto-do assemble } */ +/* { dg-lto-options {{ -O3 -fno-early-inlining -flto }} } */ + +#include "../ipa/ipa-sra-assume-3.C" diff --git a/gcc/testsuite/g++.dg/lto/ipa-sra-assume_0.C b/gcc/testsuite/g++.dg/lto/ipa-sra-assume_0.C new file mode 100644 index 000000000000..c4de5f86d3c3 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/ipa-sra-assume_0.C @@ -0,0 +1,28 @@ +/* { dg-lto-options {{ -O3 -flto -fdump-tree-optimized }} } */ +/* { dg-lto-do link } */ + +struct S +{ + int x; +}; + +int bar (struct S); + +void +foo (struct S s) +{ + int c = bar (s); + for (int i = 0; i < c; i++) + __builtin_abort (); +} + +int +main () +{ + int x; + __builtin_scanf ("%d", &x); + foo ({x}); + return 0; +} + +/* { dg-final { scan-ltrans-tree-dump-not "__builtin_abort" "optimized" } } */ diff --git a/gcc/testsuite/g++.dg/lto/ipa-sra-assume_1.C b/gcc/testsuite/g++.dg/lto/ipa-sra-assume_1.C new file mode 100644 index 000000000000..a33ed888a32f --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/ipa-sra-assume_1.C @@ -0,0 +1,11 @@ +struct S +{ + int x; +}; + +int +bar (struct S s) +{ + [[assume (s.x < 0)]]; + return s.x; +}
