diff options
-rw-r--r-- | gcc/analyzer/ChangeLog | 12 | ||||
-rw-r--r-- | gcc/analyzer/region-model.cc | 115 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 6 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/analyzer/torture/pr93438-2.c | 26 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/analyzer/torture/pr93438.c | 13 |
5 files changed, 153 insertions, 19 deletions
diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog index a46ee26..8806a77 100644 --- a/gcc/analyzer/ChangeLog +++ b/gcc/analyzer/ChangeLog @@ -1,5 +1,17 @@ 2020-01-31 David Malcolm <dmalcolm@redhat.com> + PR analyzer/93438 + * region-model.cc (stack_region::can_merge_p): Split into a two + pass approach, creating all stack regions first, then populating + them. + (selftest::test_state_merging): Add test coverage for (a) the case + of self-merging a model in which a local in an older stack frame + points to a local in a more recent stack frame (which previously + would ICE), and (b) the case of self-merging a model in which a + local points to a global (which previously worked OK). + +2020-01-31 David Malcolm <dmalcolm@redhat.com> + * analyzer.cc (is_named_call_p): Replace tests for fndecl being extern at file scope and having a non-NULL DECL_NAME with a call to maybe_special_function_p. diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index b546114..f116c0a 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -2624,27 +2624,45 @@ stack_region::can_merge_p (const stack_region *stack_region_a, stack_region *merged_stack = merged_model->get_region <stack_region> (rid_merged_stack); - for (unsigned i = 0; i < stack_region_a->get_num_frames (); i++) - { - region_id rid_a = stack_region_a->get_frame_rid (i); - frame_region *frame_a = merger->get_region_a <frame_region> (rid_a); + /* First, create all frames in the merged model, without populating them. + The merging code assumes that all frames in the merged model already exist, + so we have to do this first to handle the case in which a local in an + older frame points at a local in a more recent frame. */ + for (unsigned i = 0; i < stack_region_a->get_num_frames (); i++) + { + region_id rid_a = stack_region_a->get_frame_rid (i); + frame_region *frame_a = merger->get_region_a <frame_region> (rid_a); - region_id rid_b = stack_region_b->get_frame_rid (i); - frame_region *frame_b = merger->get_region_b <frame_region> (rid_b); + region_id rid_b = stack_region_b->get_frame_rid (i); + frame_region *frame_b = merger->get_region_b <frame_region> (rid_b); - if (frame_a->get_function () != frame_b->get_function ()) - return false; - frame_region *merged_frame = new frame_region (rid_merged_stack, - frame_a->get_function (), - frame_a->get_depth ()); - region_id rid_merged_frame = merged_model->add_region (merged_frame); - merged_stack->push_frame (rid_merged_frame); - - if (!map_region::can_merge_p (frame_a, frame_b, - merged_frame, rid_merged_frame, - merger)) - return false; - } + if (frame_a->get_function () != frame_b->get_function ()) + return false; + + frame_region *merged_frame = new frame_region (rid_merged_stack, + frame_a->get_function (), + frame_a->get_depth ()); + region_id rid_merged_frame = merged_model->add_region (merged_frame); + merged_stack->push_frame (rid_merged_frame); + } + + /* Now populate the frames we created. */ + for (unsigned i = 0; i < stack_region_a->get_num_frames (); i++) + { + region_id rid_a = stack_region_a->get_frame_rid (i); + frame_region *frame_a = merger->get_region_a <frame_region> (rid_a); + + region_id rid_b = stack_region_b->get_frame_rid (i); + frame_region *frame_b = merger->get_region_b <frame_region> (rid_b); + + region_id rid_merged_frame = merged_stack->get_frame_rid (i); + frame_region *merged_frame + = merged_model->get_region <frame_region> (rid_merged_frame); + if (!map_region::can_merge_p (frame_a, frame_b, + merged_frame, rid_merged_frame, + merger)) + return false; + } return true; } @@ -7721,6 +7739,11 @@ test_state_merging () integer_type_node); tree addr_of_a = build1 (ADDR_EXPR, ptr_type_node, a); + /* Param "q", a pointer. */ + tree q = build_decl (UNKNOWN_LOCATION, PARM_DECL, + get_identifier ("q"), + ptr_type_node); + { region_model model0; region_model model1; @@ -7991,6 +8014,60 @@ test_state_merging () region_model merged; ASSERT_TRUE (model0.can_merge_with_p (model1, &merged)); } + + /* Verify that we can merge a model in which a local in an older stack + frame points to a local in a more recent stack frame. */ + { + region_model model0; + model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL); + region_id q_in_first_frame = model0.get_lvalue (q, NULL); + + /* Push a second frame. */ + region_id rid_2nd_frame + = model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL); + + /* Have a pointer in the older frame point to a local in the + more recent frame. */ + svalue_id sid_ptr = model0.get_rvalue (addr_of_a, NULL); + model0.set_value (q_in_first_frame, sid_ptr, NULL); + + /* Verify that it's pointing at the newer frame. */ + region_id rid_pointee + = model0.get_svalue (sid_ptr)->dyn_cast_region_svalue ()->get_pointee (); + ASSERT_EQ (model0.get_region (rid_pointee)->get_parent (), rid_2nd_frame); + + model0.canonicalize (NULL); + + region_model model1 (model0); + ASSERT_EQ (model0, model1); + + /* They should be mergeable, and the result should be the same + (after canonicalization, at least). */ + region_model merged; + ASSERT_TRUE (model0.can_merge_with_p (model1, &merged)); + merged.canonicalize (NULL); + ASSERT_EQ (model0, merged); + } + + /* Verify that we can merge a model in which a local points to a global. */ + { + region_model model0; + model0.push_frame (DECL_STRUCT_FUNCTION (test_fndecl), NULL, NULL); + model0.set_value (model0.get_lvalue (q, NULL), + model0.get_rvalue (addr_of_y, NULL), NULL); + + model0.canonicalize (NULL); + + region_model model1 (model0); + ASSERT_EQ (model0, model1); + + /* They should be mergeable, and the result should be the same + (after canonicalization, at least). */ + region_model merged; + ASSERT_TRUE (model0.can_merge_with_p (model1, &merged)); + merged.canonicalize (NULL); + ASSERT_EQ (model0, merged); + } } /* Verify that constraints are correctly merged when merging region_model diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index ae14602..f8051b44 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2020-01-31 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/93438 + * gcc.dg/analyzer/torture/pr93438.c: New test. + * gcc.dg/analyzer/torture/pr93438-2.c: New test. + 2020-01-31 Jakub Jelinek <jakub@redhat.com> PR rtl-optimization/91838 diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/pr93438-2.c b/gcc/testsuite/gcc.dg/analyzer/torture/pr93438-2.c new file mode 100644 index 0000000..218dc78 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/torture/pr93438-2.c @@ -0,0 +1,26 @@ +/* A non-recursive example of state-merger of a pointer + from an old stack frame to a local in a newer stack frame. */ + +int newer (int **ptr_to_ow, int flag); + +int +older (int flag) +{ + int *ow; + return newer (&ow, flag); +} + +int +newer (int **ptr_to_ow, int flag) +{ + int pk; + *ptr_to_ow = &pk; + + if (flag) + pk = 3; + else + pk = 4; + /* State merger. */ + + return pk; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/pr93438.c b/gcc/testsuite/gcc.dg/analyzer/torture/pr93438.c new file mode 100644 index 0000000..3a23770 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/torture/pr93438.c @@ -0,0 +1,13 @@ +/* { dg-additional-options "-Wno-analyzer-too-complex" } */ + +void +tw (int **la, int pk) +{ + int *ow = *la; + int jo = !!pk; + + if (jo == 0) + *la = &pk; + + tw (&ow, pk); +} |