Skip to content

Commit c1e9ba5

Browse files
avanhatttedinski
authored andcommitted
Remove slow find_by_pretty_name vtable search (rust-lang#193)
* Replace slower lookup by pretty name with symbol name * Add nonnull fn call assertion (with new utility fn) * Kill pretty_name_from_instance * Remove fixme from boxmuttrait test that now works * Pipe pretty_name through Symbol::function constructor * Don't pass non-specific pretty names
1 parent a0f051d commit c1e9ba5

File tree

11 files changed

+97
-84
lines changed

11 files changed

+97
-84
lines changed

compiler/rustc_codegen_llvm/src/gotoc/assumptions.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ impl<'tcx> GotocCtx<'tcx> {
215215
let len = sl.member("len", &tcx.symbol_table);
216216
let mut invariants = vec![];
217217
if is_ref {
218-
invariants.push(data.clone().neq(data.typ().null()));
218+
invariants.push(data.clone().is_nonnull());
219219
}
220220
if let Some(f) = f {
221221
//CHECKME: why is this 2?
@@ -224,7 +224,7 @@ impl<'tcx> GotocCtx<'tcx> {
224224
let lbody = Stmt::block(
225225
vec![
226226
data.clone()
227-
.neq(data.typ().null())
227+
.is_nonnull()
228228
.implies(f.call(vec![data.plus(idx.clone())]))
229229
.not()
230230
.if_then_else(
@@ -262,10 +262,10 @@ impl<'tcx> GotocCtx<'tcx> {
262262
let x = ptr.dereference();
263263
let mut invarints = vec![];
264264
if is_ref {
265-
invarints.push(x.clone().neq(x.typ().null()));
265+
invarints.push(x.clone().is_nonnull());
266266
}
267267
if let Some(f) = f {
268-
invarints.push(x.clone().neq(x.typ().null()).implies(f.call(vec![x])));
268+
invarints.push(x.clone().is_nonnull().implies(f.call(vec![x])));
269269
}
270270
body.push(fold_invariants(invarints).ret(Location::none()));
271271
})
@@ -586,6 +586,7 @@ impl<'tcx> GotocCtx<'tcx> {
586586
fname,
587587
Type::code(vec![var.to_function_parameter()], Type::bool()),
588588
Some(body),
589+
None,
589590
Location::none(),
590591
)
591592
}

compiler/rustc_codegen_llvm/src/gotoc/cbmc/goto_program/expr.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,13 @@ impl Expr {
11051105
self.lt(typ.zero())
11061106
}
11071107

1108+
/// `self != NULL`
1109+
pub fn is_nonnull(self) -> Self {
1110+
assert!(self.typ.is_pointer());
1111+
let nullptr = self.typ().null();
1112+
self.neq(nullptr)
1113+
}
1114+
11081115
/// `ArithmeticOverflowResult r; >>>r.overflowed = builtin_add_overflow(self, e, &r.result)<<<`
11091116
pub fn add_overflow(self, e: Expr) -> ArithmeticOverflowResult {
11101117
let result = self.clone().plus(e.clone());

compiler/rustc_codegen_llvm/src/gotoc/cbmc/goto_program/symbol.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ impl Symbol {
113113
name,
114114
Type::code_with_unnamed_parameters(param_types, return_type),
115115
None,
116+
None,
116117
Location::builtin_function(name, None),
117118
)
118119
}
@@ -135,15 +136,20 @@ impl Symbol {
135136
.with_is_static_lifetime(true) //TODO with thread local was also true??
136137
}
137138

138-
pub fn function(name: &str, typ: Type, body: Option<Stmt>, loc: Location) -> Symbol {
139-
// TODO should take pretty name
139+
pub fn function(
140+
name: &str,
141+
typ: Type,
142+
body: Option<Stmt>,
143+
pretty_name: Option<String>,
144+
loc: Location,
145+
) -> Symbol {
140146
Symbol::new(
141147
name.to_string(),
142148
loc,
143149
typ,
144150
body.map_or(SymbolValues::None, |x| SymbolValues::Stmt(x)),
145151
Some(name.to_string()),
146-
None,
152+
pretty_name,
147153
)
148154
.with_is_lvalue(true)
149155
}

compiler/rustc_codegen_llvm/src/gotoc/cbmc/goto_program/symbol_table.rs

-13
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,6 @@ impl SymbolTable {
7676
self.symbol_table.contains_key(name)
7777
}
7878

79-
//TODO DSN
80-
// https://github.com/model-checking/rmc/issues/4
81-
// This is SUPER DUMB AND SLOW. We should just keep a second map. But for now, this is good enough
82-
pub fn find_by_pretty_name(&self, pretty_name: &str) -> Vec<&Symbol> {
83-
let mut rval = Vec::new();
84-
for (_name, symbol) in &self.symbol_table {
85-
if symbol.pretty_name.as_ref().map_or(false, |x| x == pretty_name) {
86-
rval.push(symbol);
87-
}
88-
}
89-
rval
90-
}
91-
9279
pub fn iter(&self) -> std::collections::btree_map::Iter<'_, String, Symbol> {
9380
self.symbol_table.iter()
9481
}

compiler/rustc_codegen_llvm/src/gotoc/hooks.rs

+1
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ impl<'tcx> GotocHook<'tcx> for MemSwap {
328328
Type::empty(),
329329
),
330330
Some(Stmt::block(block, loc.clone())),
331+
None,
331332
Location::none(),
332333
)
333334
});

compiler/rustc_codegen_llvm/src/gotoc/metadata.rs

+10-21
Original file line numberDiff line numberDiff line change
@@ -182,52 +182,40 @@ impl<'tcx> GotocCtx<'tcx> {
182182
self.current_fn.as_ref().map(|x| self.symbol_name(x.instance))
183183
}
184184

185-
/// Pretty name including crate path and trait information. For example:
186-
/// boxtrait_fail::<Concrete as Trait>::increment
187-
/// Generated from the fn instance to insert into/lookup in the symbol table.
188-
/// TODO: internal unit tests https://github.com/model-checking/rmc/issues/172
189-
pub fn pretty_name_from_instance(&self, instance: Instance<'tcx>) -> String {
190-
format!(
191-
"{}::{}",
192-
self.tcx.crate_name(instance.def_id().krate),
193-
with_no_trimmed_paths(|| instance.to_string())
194-
)
195-
}
196-
197185
/// For the vtable field name, we need exactly the dyn trait name and the function
198186
/// name. The table itself already is scoped by the object type.
199187
/// Example: ::Shape::vol
200188
/// Note: this is _not_ the same name for top-level entry into the symbol table,
201-
/// which does need more crate and type information. For now, the symbol table
202-
/// name is from the pretty_name_from_instance function above.
189+
/// which does need more crate/type information and uses the full symbol_name(...)
203190
pub fn vtable_field_name(&self, def_id: DefId) -> String {
204191
// `to_string_no_crate_verbose` is from Rust proper, we use it here because it
205192
// always includes the dyn trait name and function name.
206193
// Tracking a less brittle solution here: https://github.com/model-checking/rmc/issues/187
207194
self.tcx.def_path(def_id).to_string_no_crate_verbose()
208195
}
209196

210-
/// a human readable name in rust for reference
211-
pub fn instance_name(&self, instance: Instance<'tcx>) -> String {
197+
/// A human readable name in Rust for reference, should not be used as a key.
198+
pub fn readable_instance_name(&self, instance: Instance<'tcx>) -> String {
212199
with_no_trimmed_paths(|| self.tcx.def_path_str(instance.def_id()))
213200
}
214201

215-
/// the actual function name used in the symbol table
202+
/// The actual function name used in the symbol table
216203
pub fn symbol_name(&self, instance: Instance<'tcx>) -> String {
217204
let llvm_mangled = self.tcx.symbol_name(instance).name.to_string();
218205
debug!(
219206
"finding function name for instance: {}, debug: {:?}, name: {}, symbol: {}, demangle: {}",
220207
instance,
221208
instance,
222-
self.instance_name(instance),
209+
self.readable_instance_name(instance),
223210
llvm_mangled,
224211
rustc_demangle::demangle(&llvm_mangled).to_string()
225212
);
226213

227-
let pretty = self.pretty_name_from_instance(instance);
214+
let pretty = self.readable_instance_name(instance);
228215

229-
// make main function a special case for easy CBMC entry
230-
if pretty.ends_with("::main") {
216+
// Make main function a special case for easy CBMC entry
217+
// TODO: probably need to edit for https://github.com/model-checking/rmc/issues/169
218+
if pretty == "main" {
231219
"main".to_string()
232220
} else {
233221
// TODO: llvm mangled string is not very readable. one way to tackle this is to
@@ -460,6 +448,7 @@ impl<'tcx> GotocCtx<'tcx> {
460448
&fn_name,
461449
Type::code(vec![], Type::constructor()),
462450
Some(Stmt::block(vec![body], Location::none())), //TODO is this block needed?
451+
None,
463452
Location::none(),
464453
)
465454
.with_is_file_local(true)

compiler/rustc_codegen_llvm/src/gotoc/mod.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,13 @@ impl<'tcx> GotocCtx<'tcx> {
165165
self.set_current_fn(instance);
166166
self.ensure(&self.fname(), |ctx, fname| {
167167
let mir = ctx.mir();
168-
Symbol::function(fname, ctx.fn_typ(), None, ctx.codegen_span2(&mir.span))
169-
.with_pretty_name(&ctx.pretty_name_from_instance(instance))
168+
Symbol::function(
169+
fname,
170+
ctx.fn_typ(),
171+
None,
172+
Some(ctx.readable_instance_name(instance)),
173+
ctx.codegen_span2(&mir.span),
174+
)
170175
});
171176
self.reset_current_fn();
172177
}

compiler/rustc_codegen_llvm/src/gotoc/operand.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ impl<'tcx> GotocCtx<'tcx> {
282282
&func_name,
283283
Type::code(vec![param.to_function_parameter()], cgt),
284284
Some(Stmt::block(body, Location::none())),
285+
None,
285286
Location::none(),
286287
)
287288
});
@@ -538,6 +539,7 @@ impl<'tcx> GotocCtx<'tcx> {
538539
&fname,
539540
Type::code(vec![param.to_function_parameter()], cgt),
540541
Some(Stmt::block(body, Location::none())),
542+
None,
541543
Location::none(),
542544
)
543545
});
@@ -548,8 +550,15 @@ impl<'tcx> GotocCtx<'tcx> {
548550
let func = self.symbol_name(instance);
549551
let funct = self.codegen_function_sig(self.fn_sig_of_instance(instance));
550552
// make sure the functions imported from other modules are in the symbol table
551-
self.ensure(&func, |_, _| {
552-
Symbol::function(&func, funct.clone(), None, Location::none()).with_is_extern(true)
553+
self.ensure(&func, |ctx, _| {
554+
Symbol::function(
555+
&func,
556+
funct.clone(),
557+
None,
558+
Some(ctx.readable_instance_name(instance)),
559+
Location::none(),
560+
)
561+
.with_is_extern(true)
553562
});
554563
Expr::symbol_expression(func, funct).with_location(self.codegen_span_option2(span.cloned()))
555564
}

compiler/rustc_codegen_llvm/src/gotoc/rvalue.rs

+17-25
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ impl<'tcx> GotocCtx<'tcx> {
203203
&func_name,
204204
Type::code(vec![inp.to_function_parameter()], res_t),
205205
Some(Stmt::block(body, Location::none())),
206+
None,
206207
Location::none(),
207208
)
208209
});
@@ -691,31 +692,22 @@ impl<'tcx> GotocCtx<'tcx> {
691692
.unwrap()
692693
.unwrap();
693694

694-
// TODO: stop using this pretty name here
695-
// https://github.com/model-checking/rmc/issues/187
696-
let pretty_function_name = self.pretty_name_from_instance(instance);
697-
let matching_symbols = self.symbol_table.find_by_pretty_name(&pretty_function_name); //("<path>::<Rectangle as Vol>::vol");
698-
match matching_symbols.len() {
699-
0 => {
700-
warn!(
701-
"Unable to find vtable symbol for {}. Using NULL instead",
702-
&pretty_function_name
703-
);
704-
field_type.null()
705-
}
706-
1 => {
707-
let fn_symbol = matching_symbols[0];
708-
// create a pointer to the method
709-
// Note that the method takes a self* as the first argument, but the vtable field type has a void* as the first arg.
710-
// So we need to cast it at the end.
711-
Expr::symbol_expression(fn_symbol.name.clone(), fn_symbol.typ.clone())
712-
.address_of()
713-
.cast_to(field_type)
714-
}
715-
_ => unreachable!(
716-
"Too many options when trying to build vtable for {} {:?}",
717-
pretty_function_name, matching_symbols
718-
),
695+
// Lookup in the symbol table using the full symbol table name/key
696+
let fn_name = self.symbol_name(instance);
697+
if let Some(fn_symbol) = self.symbol_table.lookup(&fn_name) {
698+
// Create a pointer to the method
699+
// Note that the method takes a self* as the first argument, but the vtable field type has a void* as the first arg.
700+
// So we need to cast it at the end.
701+
Expr::symbol_expression(fn_symbol.name.clone(), fn_symbol.typ.clone())
702+
.address_of()
703+
.cast_to(field_type)
704+
} else {
705+
warn!(
706+
"Unable to find vtable symbol for virtual function {}, attempted lookup for symbol name: {}",
707+
self.readable_instance_name(instance),
708+
fn_name,
709+
);
710+
field_type.null()
719711
}
720712
}
721713

compiler/rustc_codegen_llvm/src/gotoc/statement.rs

+30-14
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,8 @@ impl<'tcx> GotocCtx<'tcx> {
278278
return Stmt::goto(self.find_label(&target), Location::none());
279279
}
280280

281-
// Mutable so that we can override it in case of a virtual function call
282-
// TODO is there a better way to do this without needing the mut?
283-
let mut funce = self.codegen_operand(func);
284-
if let InstanceDef::Virtual(def_id, size) = instance.def {
281+
// Handle the case of a virtual function call via vtable lookup.
282+
let mut stmts = if let InstanceDef::Virtual(def_id, size) = instance.def {
285283
debug!(
286284
"Codegen a call through a virtual function. def_id: {:?} size: {:?}",
287285
def_id, size
@@ -299,21 +297,39 @@ impl<'tcx> GotocCtx<'tcx> {
299297
let vtable_ref = trait_fat_ptr.to_owned().member("vtable", &self.symbol_table);
300298
let vtable = vtable_ref.dereference();
301299
let fn_ptr = vtable.member(&vtable_field_name, &self.symbol_table);
302-
funce = fn_ptr.dereference();
303300

304-
//Update the argument from arg0 to arg0.data
301+
// Update the argument from arg0 to arg0.data
305302
fargs[0] = trait_fat_ptr.to_owned().member("data", &self.symbol_table);
306-
}
307303

308-
// Actually generate the function call, and store the return value, if any.
309-
Stmt::block(
304+
// For soundness, add an assertion that the vtable function call is not null.
305+
// Otherwise, CBMC might treat this as an assert(0) and later user-added assertions
306+
// could be vacuously true.
307+
let call_is_nonnull = fn_ptr.clone().is_nonnull();
308+
let assert_msg =
309+
format!("Non-null virtual function call for {:?}", vtable_field_name);
310+
let assert_nonnull = Stmt::assert(call_is_nonnull, &assert_msg, loc.clone());
311+
312+
// Virtual function call and corresponding nonnull assertion.
313+
let func_exp: Expr = fn_ptr.dereference();
310314
vec![
311-
self.codegen_expr_to_place(&p, funce.call(fargs))
315+
assert_nonnull,
316+
self.codegen_expr_to_place(&p, func_exp.call(fargs))
312317
.with_location(loc.clone()),
313-
Stmt::goto(self.find_label(&target), loc.clone()),
314-
],
315-
loc,
316-
)
318+
]
319+
} else {
320+
// Non-virtual function call.
321+
let func_exp = self.codegen_operand(func);
322+
vec![
323+
self.codegen_expr_to_place(&p, func_exp.call(fargs))
324+
.with_location(loc.clone()),
325+
]
326+
};
327+
328+
// Add return jump.
329+
stmts.push(Stmt::goto(self.find_label(&target), loc.clone()));
330+
331+
// Produce the full function call block.
332+
Stmt::block(stmts, loc)
317333
}
318334
ty::FnPtr(_) => {
319335
let (p, target) = destination.unwrap();

0 commit comments

Comments
 (0)