Skip to content

Commit f6219d4

Browse files
authored
Merge pull request diffblue#1940 from smowton/smowton/cleanup/remove-virtual-functions
Remove virtual functions: simplify and fix no-fallback-function mode
2 parents 34b2b02 + 126e90a commit f6219d4

11 files changed

+407
-47
lines changed

src/goto-programs/goto_program.h

+9
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,15 @@ class goto_programt
593593
return end_function;
594594
}
595595

596+
const_targett get_end_function() const
597+
{
598+
PRECONDITION(!instructions.empty());
599+
const auto end_function=std::prev(instructions.end());
600+
DATA_INVARIANT(end_function->is_end_function(),
601+
"goto program ends on END_FUNCTION");
602+
return end_function;
603+
}
604+
596605
/// Copy a full goto program, preserving targets
597606
void copy_from(const goto_programt &src);
598607

src/goto-programs/remove_virtual_functions.cpp

+44-45
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,8 @@ void remove_virtual_functionst::remove_virtual_function(
178178

179179
// So long as `this` is already not `void*` typed, the second parameter
180180
// is ignored:
181-
exprt c_id2=get_class_identifier_field(this_expr, symbol_typet(), ns);
181+
exprt this_class_identifier =
182+
get_class_identifier_field(this_expr, symbol_typet(), ns);
182183

183184
// If instructed, add an ASSUME(FALSE) to handle the case where we don't
184185
// match any expected type:
@@ -191,16 +192,12 @@ void remove_virtual_functionst::remove_virtual_function(
191192

192193
// get initial identifier for grouping
193194
INVARIANT(!functions.empty(), "Function dispatch table cannot be empty.");
194-
auto last_id = functions.back().symbol_expr.get_identifier();
195-
// record class_ids for disjunction
196-
std::set<irep_idt> class_ids;
197195

198196
std::map<irep_idt, goto_programt::targett> calls;
199197
// Note backwards iteration, to get the fallback candidate first.
200198
for(auto it=functions.crbegin(), itend=functions.crend(); it!=itend; ++it)
201199
{
202200
const auto &fun=*it;
203-
class_ids.insert(fun.class_id);
204201
auto insertit=calls.insert(
205202
{fun.symbol_expr.get_identifier(), goto_programt::targett()});
206203

@@ -232,49 +229,36 @@ void remove_virtual_functionst::remove_virtual_function(
232229
t3->make_goto(t_final, true_exprt());
233230
}
234231

235-
// Emit target if end of dispatch table is reached or if the next element is
236-
// dispatched to another function call. Assumes entries in the functions
237-
// variable to be sorted for the identifier of the function to be called.
238-
auto l_it = std::next(it);
239-
bool next_emit_target =
240-
(l_it == functions.crend()) ||
241-
l_it->symbol_expr.get_identifier() != fun.symbol_expr.get_identifier();
242-
243-
// The root function call is done via fall-through, so nothing to emit
244-
// explicitly for this.
245-
if(next_emit_target && fun.symbol_expr == last_function_symbol)
232+
// Fall through to the default callee if possible:
233+
if(fallback_action ==
234+
virtual_dispatch_fallback_actiont::CALL_LAST_FUNCTION &&
235+
fun.symbol_expr == last_function_symbol)
246236
{
247-
class_ids.clear();
237+
// Nothing to do
248238
}
249-
250-
// If this calls the fallback function we just fall through.
251-
// Otherwise branch to the right call:
252-
if(fallback_action!=virtual_dispatch_fallback_actiont::CALL_LAST_FUNCTION ||
253-
fun.symbol_expr!=last_function_symbol)
239+
else
254240
{
255-
// create a disjunction of class_ids to test
256-
if(next_emit_target && fun.symbol_expr != last_function_symbol)
241+
const constant_exprt fun_class_identifier(fun.class_id, string_typet());
242+
const equal_exprt class_id_test(
243+
fun_class_identifier, this_class_identifier);
244+
245+
// If the previous GOTO goes to the same callee, join it
246+
// (e.g. turning IF x GOTO y into IF x || z GOTO y)
247+
if(it != functions.crbegin() &&
248+
std::prev(it)->symbol_expr == fun.symbol_expr)
257249
{
258-
exprt::operandst or_ops;
259-
for(const auto &id : class_ids)
260-
{
261-
const constant_exprt c_id1(id, string_typet());
262-
const equal_exprt class_id_test(c_id1, c_id2);
263-
or_ops.push_back(class_id_test);
264-
}
265-
266-
goto_programt::targett t4 = new_code_gotos.add_instruction();
267-
t4->source_location = vcall_source_loc;
268-
t4->make_goto(insertit.first->second, disjunction(or_ops));
269-
270-
last_id = fun.symbol_expr.get_identifier();
271-
class_ids.clear();
250+
INVARIANT(
251+
!new_code_gotos.empty(),
252+
"a dispatch table entry has been processed already, "
253+
"which should have created a GOTO");
254+
new_code_gotos.instructions.back().guard =
255+
or_exprt(new_code_gotos.instructions.back().guard, class_id_test);
272256
}
273-
// record class_id
274-
else if(next_emit_target)
257+
else
275258
{
276-
last_id = fun.symbol_expr.get_identifier();
277-
class_ids.clear();
259+
goto_programt::targett new_goto = new_code_gotos.add_instruction();
260+
new_goto->source_location = vcall_source_loc;
261+
new_goto->make_goto(insertit.first->second, class_id_test);
278262
}
279263
}
280264
}
@@ -555,20 +539,35 @@ void remove_virtual_functions(goto_model_functiont &function)
555539
}
556540

557541
void remove_virtual_function(
558-
goto_modelt &goto_model,
542+
symbol_tablet &symbol_table,
559543
goto_programt &goto_program,
560544
goto_programt::targett instruction,
561545
const dispatch_table_entriest &dispatch_table,
562546
virtual_dispatch_fallback_actiont fallback_action)
563547
{
564548
class_hierarchyt class_hierarchy;
565-
class_hierarchy(goto_model.symbol_table);
566-
remove_virtual_functionst rvf(goto_model.symbol_table, class_hierarchy);
549+
class_hierarchy(symbol_table);
550+
remove_virtual_functionst rvf(symbol_table, class_hierarchy);
567551

568552
rvf.remove_virtual_function(
569553
goto_program, instruction, dispatch_table, fallback_action);
570554
}
571555

556+
void remove_virtual_function(
557+
goto_modelt &goto_model,
558+
goto_programt &goto_program,
559+
goto_programt::targett instruction,
560+
const dispatch_table_entriest &dispatch_table,
561+
virtual_dispatch_fallback_actiont fallback_action)
562+
{
563+
remove_virtual_function(
564+
goto_model.symbol_table,
565+
goto_program,
566+
instruction,
567+
dispatch_table,
568+
fallback_action);
569+
}
570+
572571
void collect_virtual_function_callees(
573572
const exprt &function,
574573
const symbol_table_baset &symbol_table,

src/goto-programs/remove_virtual_functions.h

+7
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ void remove_virtual_function(
6666
const dispatch_table_entriest &dispatch_table,
6767
virtual_dispatch_fallback_actiont fallback_action);
6868

69+
void remove_virtual_function(
70+
symbol_tablet &symbol_table,
71+
goto_programt &goto_program,
72+
goto_programt::targett instruction,
73+
const dispatch_table_entriest &dispatch_table,
74+
virtual_dispatch_fallback_actiont fallback_action);
75+
6976
/// Given a function expression representing a virtual method of a class,
7077
/// the function computes all overridden methods of that virtual method.
7178
/// \param function: The virtual function expression for which the overridden

unit/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ SRC += unit_tests.cpp \
1919
goto-programs/goto_trace_output.cpp \
2020
goto-programs/class_hierarchy_output.cpp \
2121
goto-programs/class_hierarchy_graph.cpp \
22+
goto-programs/remove_virtual_functions_without_fallback.cpp \
2223
java_bytecode/java_bytecode_convert_class/convert_abstract_class.cpp \
2324
java_bytecode/java_bytecode_parse_generics/parse_generic_class.cpp \
2425
java_bytecode/java_object_factory/gen_nondet_string_init.cpp \
279 Bytes
Binary file not shown.
279 Bytes
Binary file not shown.
Binary file not shown.
404 Bytes
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
class VirtualFunctionsTestParent {
3+
4+
// These fields only exist so the classloader will load the children when
5+
// asked to load the parent:
6+
VirtualFunctionsTestChild1 c1;
7+
VirtualFunctionsTestChild2 c2;
8+
VirtualFunctionsTestGrandchild g;
9+
10+
public void f() { }
11+
12+
}
13+
14+
class VirtualFunctionsTestChild1 extends VirtualFunctionsTestParent {
15+
16+
public void f() { }
17+
18+
}
19+
20+
class VirtualFunctionsTestChild2 extends VirtualFunctionsTestParent {
21+
22+
public void f() { }
23+
24+
}
25+
26+
class VirtualFunctionsTestGrandchild extends VirtualFunctionsTestChild1 {
27+
28+
}

0 commit comments

Comments
 (0)