Skip to content

Commit 80e66ba

Browse files
committed
Remove virtual functions: expose single-call entry-point
This enables passes that take a different approach to virtual function lowering to re-use its dispatch-table-construction logic.
1 parent ae34e9b commit 80e66ba

File tree

2 files changed

+121
-39
lines changed

2 files changed

+121
-39
lines changed

src/goto-programs/remove_virtual_functions.cpp

+88-39
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ class remove_virtual_functionst
3030

3131
bool remove_virtual_functions(goto_programt &goto_program);
3232

33+
void remove_virtual_function(
34+
goto_programt &goto_program,
35+
goto_programt::targett target,
36+
const dispatch_table_entriest &functions,
37+
virtual_dispatch_fallback_actiont fallback_action);
38+
3339
protected:
3440
const namespacet ns;
3541
const symbol_tablet &symbol_table;
@@ -40,25 +46,12 @@ class remove_virtual_functionst
4046
goto_programt &goto_program,
4147
goto_programt::targett target);
4248

43-
class functiont
44-
{
45-
public:
46-
functiont() {}
47-
explicit functiont(const irep_idt &_class_id) :
48-
class_id(_class_id)
49-
{}
50-
51-
symbol_exprt symbol_expr;
52-
irep_idt class_id;
53-
};
54-
55-
typedef std::vector<functiont> functionst;
56-
void get_functions(const exprt &, functionst &);
49+
void get_functions(const exprt &, dispatch_table_entriest &);
5750
void get_child_functions_rec(
5851
const irep_idt &,
5952
const symbol_exprt &,
6053
const irep_idt &,
61-
functionst &,
54+
dispatch_table_entriest &,
6255
std::set<irep_idt> &visited) const;
6356
exprt get_method(
6457
const irep_idt &class_id,
@@ -81,25 +74,46 @@ void remove_virtual_functionst::remove_virtual_function(
8174
const code_function_callt &code=
8275
to_code_function_call(target->code);
8376

84-
const auto &vcall_source_loc=target->source_location;
85-
8677
const exprt &function=code.function();
87-
assert(function.id()==ID_virtual_function);
88-
assert(!code.arguments().empty());
89-
90-
functionst functions;
78+
INVARIANT(
79+
function.id()==ID_virtual_function,
80+
"remove_virtual_function must take a virtual function call instruction");
81+
INVARIANT(
82+
!code.arguments().empty(),
83+
"virtual function calls must have at least a this-argument");
84+
85+
dispatch_table_entriest functions;
9186
get_functions(function, functions);
9287

88+
remove_virtual_function(
89+
goto_program,
90+
target,
91+
functions,
92+
virtual_dispatch_fallback_actiont::CALL_LAST_FUNCTION);
93+
}
94+
95+
void remove_virtual_functionst::remove_virtual_function(
96+
goto_programt &goto_program,
97+
goto_programt::targett target,
98+
const dispatch_table_entriest &functions,
99+
virtual_dispatch_fallback_actiont fallback_action)
100+
{
101+
INVARIANT(
102+
target->is_function_call(),
103+
"remove_virtual_function must target a FUNCTION_CALL instruction");
104+
const code_function_callt &code=
105+
to_code_function_call(target->code);
106+
93107
if(functions.empty())
94108
{
95109
target->make_skip();
96110
return; // give up
97111
}
98112

99113
// only one option?
100-
if(functions.size()==1)
114+
if(functions.size()==1 &&
115+
fallback_action==virtual_dispatch_fallback_actiont::CALL_LAST_FUNCTION)
101116
{
102-
assert(target->is_function_call());
103117
if(functions.begin()->symbol_expr==symbol_exprt())
104118
target->make_skip();
105119
else
@@ -108,6 +122,8 @@ void remove_virtual_functionst::remove_virtual_function(
108122
return;
109123
}
110124

125+
const auto &vcall_source_loc=target->source_location;
126+
111127
// the final target is a skip
112128
goto_programt final_skip;
113129

@@ -122,16 +138,29 @@ void remove_virtual_functionst::remove_virtual_function(
122138
goto_programt new_code_gotos;
123139

124140
exprt this_expr=code.arguments()[0];
125-
// If necessary, cast to the last candidate function to
126-
// get the object's clsid. By the structure of get_functions,
127-
// this is the parent of all other classes under consideration.
128-
const auto &base_classid=functions.back().class_id;
129-
const auto &base_function_symbol=functions.back().symbol_expr;
130-
symbol_typet suggested_type(base_classid);
131-
exprt c_id2=get_class_identifier_field(this_expr, suggested_type, ns);
141+
const auto &last_function_symbol=functions.back().symbol_expr;
142+
143+
const typet &this_type=this_expr.type();
144+
INVARIANT(this_type.id() == ID_pointer, "this parameter must be a pointer");
145+
INVARIANT(
146+
this_type.subtype() != empty_typet(),
147+
"this parameter must not be a void pointer");
148+
149+
// So long as `this` is already not `void*` typed, the second parameter
150+
// is ignored:
151+
exprt c_id2=get_class_identifier_field(this_expr, symbol_typet(), ns);
152+
153+
// If instructed, add an ASSUME(FALSE) to handle the case where we don't
154+
// match any expected type:
155+
if(fallback_action==virtual_dispatch_fallback_actiont::ASSUME_FALSE)
156+
{
157+
auto newinst=new_code_calls.add_instruction(ASSUME);
158+
newinst->guard=false_exprt();
159+
newinst->source_location=vcall_source_loc;
160+
}
132161

133162
std::map<irep_idt, goto_programt::targett> calls;
134-
// Note backwards iteration, to get the least-derived candidate first.
163+
// Note backwards iteration, to get the fallback candidate first.
135164
for(auto it=functions.crbegin(), itend=functions.crend(); it!=itend; ++it)
136165
{
137166
const auto &fun=*it;
@@ -149,8 +178,13 @@ void remove_virtual_functionst::remove_virtual_function(
149178
t1->make_function_call(code);
150179
auto &newcall=to_code_function_call(t1->code);
151180
newcall.function()=fun.symbol_expr;
152-
typet need_type=
153-
pointer_type(symbol_typet(fun.symbol_expr.get(ID_C_class)));
181+
// Cast the `this` pointer to the correct type for the new callee:
182+
const auto &callee_type=
183+
to_code_type(ns.lookup(fun.symbol_expr.get_identifier()).type);
184+
INVARIANT(
185+
callee_type.has_this(),
186+
"Virtual function callees must have a `this` argument");
187+
typet need_type=callee_type.parameters()[0].type();
154188
if(!type_eq(newcall.arguments()[0].type(), need_type, ns))
155189
newcall.arguments()[0].make_typecast(need_type);
156190
}
@@ -166,9 +200,10 @@ void remove_virtual_functionst::remove_virtual_function(
166200
t3->make_goto(t_final, true_exprt());
167201
}
168202

169-
// If this calls the base function we just fall through.
203+
// If this calls the fallback function we just fall through.
170204
// Otherwise branch to the right call:
171-
if(fun.symbol_expr!=base_function_symbol)
205+
if(fallback_action!=virtual_dispatch_fallback_actiont::CALL_LAST_FUNCTION ||
206+
fun.symbol_expr!=last_function_symbol)
172207
{
173208
exprt c_id1=constant_exprt(fun.class_id, string_typet());
174209
goto_programt::targett t4=new_code_gotos.add_instruction();
@@ -221,7 +256,7 @@ void remove_virtual_functionst::get_child_functions_rec(
221256
const irep_idt &this_id,
222257
const symbol_exprt &last_method_defn,
223258
const irep_idt &component_name,
224-
functionst &functions,
259+
dispatch_table_entriest &functions,
225260
std::set<irep_idt> &visited) const
226261
{
227262
auto findit=class_hierarchy.class_map.find(this_id);
@@ -233,7 +268,7 @@ void remove_virtual_functionst::get_child_functions_rec(
233268
if(!visited.insert(child).second)
234269
continue;
235270
exprt method=get_method(child, component_name);
236-
functiont function(child);
271+
dispatch_table_entryt function(child);
237272
if(method.is_not_nil())
238273
{
239274
function.symbol_expr=to_symbol_expr(method);
@@ -256,7 +291,7 @@ void remove_virtual_functionst::get_child_functions_rec(
256291

257292
void remove_virtual_functionst::get_functions(
258293
const exprt &function,
259-
functionst &functions)
294+
dispatch_table_entriest &functions)
260295
{
261296
const irep_idt class_id=function.get(ID_C_class);
262297
const irep_idt component_name=function.get(ID_component_name);
@@ -266,7 +301,7 @@ void remove_virtual_functionst::get_functions(
266301
symbol_table, class_hierarchy);
267302
const resolve_concrete_function_callt::concrete_function_callt &
268303
resolved_call=get_virtual_call_target(class_id, component_name);
269-
functiont root_function;
304+
dispatch_table_entryt root_function;
270305

271306
if(resolved_call.is_valid())
272307
{
@@ -373,3 +408,17 @@ void remove_virtual_functions(goto_modelt &goto_model)
373408
remove_virtual_functions(
374409
goto_model.symbol_table, goto_model.goto_functions);
375410
}
411+
412+
void remove_virtual_function(
413+
goto_modelt &goto_model,
414+
goto_programt &goto_program,
415+
goto_programt::targett instruction,
416+
const dispatch_table_entriest &dispatch_table,
417+
virtual_dispatch_fallback_actiont fallback_action)
418+
{
419+
remove_virtual_functionst
420+
rvf(goto_model.symbol_table, goto_model.goto_functions);
421+
422+
rvf.remove_virtual_function(
423+
goto_program, instruction, dispatch_table, fallback_action);
424+
}

src/goto-programs/remove_virtual_functions.h

+33
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,37 @@ void remove_virtual_functions(
2525
const symbol_tablet &symbol_table,
2626
goto_functionst &goto_functions);
2727

28+
/// Specifies remove_virtual_function's behaviour when the actual supplied
29+
/// parameter does not match any of the possible callee types
30+
enum class virtual_dispatch_fallback_actiont
31+
{
32+
/// When no callee type matches, call the last passed function, which
33+
/// is expected to be some safe default:
34+
CALL_LAST_FUNCTION,
35+
/// When no callee type matches, ASSUME false, thus preventing any complete
36+
/// trace from using this path.
37+
ASSUME_FALSE
38+
};
39+
40+
class dispatch_table_entryt
41+
{
42+
public:
43+
dispatch_table_entryt() = default;
44+
explicit dispatch_table_entryt(const irep_idt &_class_id) :
45+
class_id(_class_id)
46+
{}
47+
48+
symbol_exprt symbol_expr;
49+
irep_idt class_id;
50+
};
51+
52+
typedef std::vector<dispatch_table_entryt> dispatch_table_entriest;
53+
54+
void remove_virtual_function(
55+
goto_modelt &goto_model,
56+
goto_programt &goto_program,
57+
goto_programt::targett instruction,
58+
const dispatch_table_entriest &dispatch_table,
59+
virtual_dispatch_fallback_actiont fallback_action);
60+
2861
#endif // CPROVER_GOTO_PROGRAMS_REMOVE_VIRTUAL_FUNCTIONS_H

0 commit comments

Comments
 (0)