Skip to content

Commit 6898716

Browse files
committed
Add hopping through declarations on jump into scope
When a goto which jumps into a scope is converted, the declarations for that scope need to be passed through. It needs to be the same declaration in both execution paths, so that symex will correctly merge the two different states when generating the phi functions. The requirement for executing the same declaration whether entering the scope through a regular route vs a goto into the middle of the block is acheived by setting up a chain of hops where - * Original goto hops to first declaration. * After first declaration we hop to the next declaration if entering via a goto. * Afer the final declaration we hop to the originally specified label. The identifiers in the existing `symex_should_filter_value_sets` test need to be incremented, because the instance being checked is now the second time the variable is added to the scope.
1 parent 0713688 commit 6898716

File tree

5 files changed

+184
-27
lines changed

5 files changed

+184
-27
lines changed

Diff for: regression/cbmc/destructors/compound_literal.desc

+9-9
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,21 @@ CORE
22
main.c
33
--unwind 10 --show-goto-functions
44
activate-multi-line-match
5-
(?P<comment_block>\/\/ [0-9]+ file main\.c line [0-9]+ function main)[\s]*DEAD .*newAlloc0[\s]*(?P>comment_block)[\s]*DEAD .*pc[\s]*(?P>comment_block)[\s]*DEAD .*literal[\s]*(?P>comment_block)[\s]*8: END_FUNCTION
5+
(?P<comment_block>\/\/ [0-9]+ file main\.c line [0-9]+ function main)[\s]*DEAD .*newAlloc0[\s]*(?P>comment_block)[\s]*DEAD .*pc[\s]*(?P>comment_block)[\s]*DEAD .*literal[\s]*(?P>comment_block)[\s]*9: END_FUNCTION
66
^EXIT=0$
77
^SIGNAL=0$
88
--
99
--
1010
Checks for:
1111

12-
// 49 file main.c line 44 function main
13-
DEAD main::1::newAlloc0
14-
// 50 file main.c line 44 function main
15-
DEAD main::1::pc
16-
// 51 file main.c line 44 function main
17-
DEAD main::$tmp::literal
18-
// 52 file main.c line 45 function main
19-
8: END_FUNCTION
12+
// 57 file main.c line 44 function main
13+
DEAD main::1::newAlloc0
14+
// 58 file main.c line 44 function main
15+
DEAD main::1::pc
16+
// 59 file main.c line 44 function main
17+
DEAD main::$tmp::literal
18+
// 60 file main.c line 45 function main
19+
9: END_FUNCTION
2020

2121
This asserts that when you've created a compound literal that both temp and real
2222
variable gets killed.

Diff for: regression/cbmc/destructors/enter_lexical_block.desc

+14-12
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,34 @@ CORE
22
main.c
33
--unwind 10 --show-goto-functions
44
activate-multi-line-match
5-
(?P<comment_block>\/\/ [0-9]+ file main\.c line [0-9]+ function main)[\s]*5: .*newAlloc4 : struct tag-test[\s]*(?P>comment_block)[\s]*.*newAlloc4 := \{ 4 \}[\s]*(?P>comment_block)[\s]*.*newAlloc6 : struct tag-test[\s]*(?P>comment_block)[\s]*.*newAlloc6 := \{ 6 \}[\s]*(?P>comment_block)[\s]*.*newAlloc7 : struct tag-test[\s]*(?P>comment_block)[\s]*.*newAlloc7 := \{ 7 \}[\s]*(?P>comment_block)[\s]*.*DEAD main::1::2::2::newAlloc7[\s]*(?P>comment_block)[\s]*.*DEAD main::1::2::2::newAlloc6[\s]*(?P>comment_block)[\s]*.*DEAD main::1::2::2::newAlloc4[\s]*(?P>comment_block)[\s]*.*GOTO 3
5+
(?P<comment_block>\/\/ [0-9]+ file main\.c line [0-9]+ function main)[\s]*6: .*newAlloc4 : struct tag-test[\s]*(?P>comment_block)[\s]*.*newAlloc4 := \{ 4 \}[\s]*(?P>comment_block)[\s]*.*newAlloc6 : struct tag-test[\s]*(?P>comment_block)[\s]*.*newAlloc6 := \{ 6 \}[\s]*(?P>comment_block)[\s]*.*newAlloc7 : struct tag-test[\s]*(?P>comment_block)[\s]*.*newAlloc7 := \{ 7 \}[\s]*(?P>comment_block)[\s]*.*DEAD main::1::2::2::newAlloc7[\s]*(?P>comment_block)[\s]*.*DEAD main::1::2::2::newAlloc6[\s]*(?P>comment_block)[\s]*.*DEAD main::1::2::2::newAlloc4[\s]*(?P>comment_block)[\s]*.*ASSIGN going_to::nested_if := true[\s]*(?P>comment_block)[\s]*.*GOTO 3
66
^EXIT=0$
77
^SIGNAL=0$
88
--
99
--
1010
Checks for:
1111

12-
// 37 file main.c line 36 function main
13-
5: DECL main::1::2::2::newAlloc4 : struct tag-test
14-
// 38 file main.c line 36 function main
12+
// 41 file main.c line 36 function main
13+
6: DECL main::1::2::2::newAlloc4 : struct tag-test
14+
// 42 file main.c line 36 function main
1515
ASSIGN main::1::2::2::newAlloc4 := { 4 }
16-
// 39 file main.c line 37 function main
16+
// 43 file main.c line 37 function main
1717
DECL main::1::2::2::newAlloc6 : struct tag-test
18-
// 40 file main.c line 37 function main
18+
// 44 file main.c line 37 function main
1919
ASSIGN main::1::2::2::newAlloc6 := { 6 }
20-
// 41 file main.c line 38 function main
20+
// 45 file main.c line 38 function main
2121
DECL main::1::2::2::newAlloc7 : struct tag-test
22-
// 42 file main.c line 38 function main
22+
// 46 file main.c line 38 function main
2323
ASSIGN main::1::2::2::newAlloc7 := { 7 }
24-
// 43 file main.c line 39 function main
24+
// 47 file main.c line 39 function main
2525
DEAD main::1::2::2::newAlloc7
26-
// 44 file main.c line 39 function main
26+
// 48 file main.c line 39 function main
2727
DEAD main::1::2::2::newAlloc6
28-
// 45 file main.c line 39 function main
28+
// 49 file main.c line 39 function main
2929
DEAD main::1::2::2::newAlloc4
30-
// 46 file main.c line 39 function main
30+
// 50 file main.c line 39 function main
31+
ASSIGN going_to::nested_if := true
32+
// 51 file main.c line 39 function main
3133
GOTO 3
3234

3335
This asserts that when the GOTO is going into a lexical block that destructors

Diff for: regression/cbmc/symex_should_filter_value_sets/test.desc

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ main::1::c3!0@1#. = 1
99
main::1::c4!0@1#. = 1
1010
main::1::c5!0@1#. = 1
1111
main::1::c6!0@1#. = 1
12-
main::1::c7!0@1#. = 1
13-
main::1::c8!0@1#. = 1
12+
main::1::c7!0@2#. = 1
13+
main::1::c8!0@2#. = 1
1414
main::1::c9!0@1#. = 1
1515
main::1::c10!0@1#. = 1
1616
main::1::c11!0@1#. = 1

Diff for: src/goto-programs/goto_convert.cpp

+153-4
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,141 @@ static void finish_catch_push_targets(goto_programt &dest)
9393
}
9494
}
9595

96+
struct build_declaration_hops_inputst
97+
{
98+
irep_idt mode;
99+
irep_idt label;
100+
goto_programt::targett goto_instruction;
101+
goto_programt::targett label_instruction;
102+
node_indext label_scope_index = 0;
103+
node_indext end_scope_index = 0;
104+
};
105+
106+
void goto_convertt::build_declaration_hops(
107+
goto_programt &program,
108+
std::unordered_map<irep_idt, symbolt, irep_id_hash> &label_flags,
109+
const build_declaration_hops_inputst &inputs)
110+
{
111+
// In the case of a goto jumping into a scope, the declarations (but not the
112+
// initialisations) need to be executed. This function performs a
113+
// transformation from code that looks like -
114+
// {
115+
// statement_block_a();
116+
// if(...)
117+
// goto user_label;
118+
// statement_block_b();
119+
// int x;
120+
// x = 0;
121+
// statement_block_c();
122+
// int y;
123+
// y = 0;
124+
// statement_block_d();
125+
// user_label:
126+
// statement_block_e();
127+
// }
128+
// to code which looks like -
129+
// {
130+
// __CPROVER_bool going_to::user_label;
131+
// going_to::user_label = false;
132+
// statement_block_a();
133+
// if(...)
134+
// {
135+
// going_to::user_label = true;
136+
// goto scope_x_label;
137+
// }
138+
// statement_block_b();
139+
// scope_x_label:
140+
// int x;
141+
// if going_to::user_label goto scope_y_label:
142+
// x = 0;
143+
// statement_block_c();
144+
// scope_y_label:
145+
// int y;
146+
// if going_to::user_label goto user_label:
147+
// y = 0;
148+
// statement_block_d();
149+
// user_label:
150+
// going_to::user_label = false;
151+
// statement_block_e();
152+
// }
153+
154+
PRECONDITION(inputs.label_scope_index != inputs.end_scope_index);
155+
156+
const auto flag = [&]() -> symbolt {
157+
const auto existing_flag = label_flags.find(inputs.label);
158+
if(existing_flag != label_flags.end())
159+
return existing_flag->second;
160+
source_locationt label_location =
161+
inputs.label_instruction->source_location();
162+
label_location.set_hide();
163+
const symbolt &new_flag = get_fresh_aux_symbol(
164+
bool_typet{},
165+
"going_to",
166+
id2string(inputs.label),
167+
label_location,
168+
inputs.mode,
169+
symbol_table);
170+
label_flags.emplace(inputs.label, new_flag);
171+
172+
// Create and initialise flag.
173+
goto_programt flag_creation;
174+
flag_creation.instructions.push_back(
175+
goto_programt::make_decl(new_flag.symbol_expr(), label_location));
176+
const auto make_clear_flag = [&]() -> goto_programt::instructiont {
177+
return goto_programt::make_assignment(
178+
new_flag.symbol_expr(), false_exprt{}, label_location);
179+
};
180+
flag_creation.instructions.push_back(make_clear_flag());
181+
program.destructive_insert(program.instructions.begin(), flag_creation);
182+
183+
// Clear flag on arrival at label.
184+
auto clear_on_arrival = make_clear_flag();
185+
program.insert_before_swap(inputs.label_instruction, clear_on_arrival);
186+
return new_flag;
187+
}();
188+
189+
auto goto_instruction = inputs.goto_instruction;
190+
{
191+
// Set flag before the goto.
192+
auto goto_location = goto_instruction->source_location();
193+
goto_location.set_hide();
194+
auto set_flag = goto_programt::make_assignment(
195+
flag.symbol_expr(), true_exprt{}, goto_location);
196+
program.insert_before_swap(goto_instruction, set_flag);
197+
// Keep this iterator referring to the goto instruction, not the assignment.
198+
++goto_instruction;
199+
}
200+
201+
auto target = inputs.label_instruction;
202+
targets.scope_stack.set_current_node(inputs.label_scope_index);
203+
while(targets.scope_stack.get_current_node() > inputs.end_scope_index)
204+
{
205+
node_indext current_node = targets.scope_stack.get_current_node();
206+
auto &declaration = targets.scope_stack.get_declaration(current_node);
207+
targets.scope_stack.descend_tree();
208+
if(!declaration)
209+
continue;
210+
211+
bool add_if = declaration->if_conditions_added.find(flag.name) ==
212+
declaration->if_conditions_added.end();
213+
if(add_if)
214+
{
215+
auto declaration_location = declaration->instruction->source_location();
216+
declaration_location.set_hide();
217+
auto if_goto = goto_programt::make_goto(
218+
target, flag.symbol_expr(), declaration_location);
219+
program.instructions.insert(
220+
std::next(declaration->instruction), std::move(if_goto));
221+
declaration->if_conditions_added.insert(flag.name);
222+
}
223+
target = declaration->instruction;
224+
}
225+
226+
// Update the goto so that it goes to the first declaration rather than its
227+
// original/final destination.
228+
goto_instruction->set_target(target);
229+
}
230+
96231
/******************************************************************* \
97232
98233
Function: goto_convertt::finish_gotos
@@ -107,6 +242,8 @@ Function: goto_convertt::finish_gotos
107242

108243
void goto_convertt::finish_gotos(goto_programt &dest, const irep_idt &mode)
109244
{
245+
std::unordered_map<irep_idt, symbolt, irep_id_hash> label_flags;
246+
110247
for(const auto &g_it : targets.gotos)
111248
{
112249
goto_programt::instructiont &i=*(g_it.first);
@@ -174,17 +311,29 @@ void goto_convertt::finish_gotos(goto_programt &dest, const irep_idt &mode)
174311
// goto_programt::instructionst is std::list.
175312
}
176313

177-
// We don't currently handle variables *entering* scope, which
178-
// is illegal for C++ non-pod types and impossible in Java in any case.
179-
// This is however valid C.
314+
// Variables *entering* scope on goto, is illegal for C++ non-pod types
315+
// and impossible in Java. This is however valid C and should be taken
316+
// into account.
180317
const bool variables_added_to_scope =
181318
intersection_result.right_depth_below_common_ancestor > 0;
182319
if(variables_added_to_scope)
183320
{
321+
// If the goto recorded a destructor stack, execute as much as is
322+
// appropriate for however many automatic variables leave scope.
184323
debug().source_location = i.source_location();
185324
debug() << "encountered goto '" << goto_label
186325
<< "' that enters one or more lexical blocks; "
187-
<< "omitting constructors." << eom;
326+
<< "adding declaration code on jump to '" << goto_label << "'"
327+
<< eom;
328+
329+
build_declaration_hops_inputst inputs;
330+
inputs.mode = mode;
331+
inputs.label = l_it->first;
332+
inputs.goto_instruction = g_it.first;
333+
inputs.label_instruction = l_it->second.first;
334+
inputs.label_scope_index = label_target;
335+
inputs.end_scope_index = intersection_result.common_ancestor;
336+
build_declaration_hops(dest, label_flags, inputs);
188337
}
189338
}
190339
else

Diff for: src/goto-programs/goto_convert_class.h

+6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Author: Daniel Kroening, [email protected]
2626
#include <vector>
2727

2828
class side_effect_expr_overflowt;
29+
struct build_declaration_hops_inputst;
2930

3031
class goto_convertt:public messaget
3132
{
@@ -362,6 +363,11 @@ class goto_convertt:public messaget
362363
optionalt<node_indext> destructor_start_point = {},
363364
optionalt<node_indext> destructor_end_point = {});
364365

366+
void build_declaration_hops(
367+
goto_programt &dest,
368+
std::unordered_map<irep_idt, symbolt, irep_id_hash> &label_flags,
369+
const build_declaration_hops_inputst &inputs);
370+
365371
//
366372
// gotos
367373
//

0 commit comments

Comments
 (0)