Skip to content

Commit d4605b3

Browse files
celinvalCarolyn Zech
andauthored
Improve Kani handling of function markers (#3718)
Change our library to use `kanitool::fn_marker` instead of rustc's internal `rustc_diagnostic_item` to mark functions in the Kani library that are required for Kani compiler. Also, validate and cache Kani's functions upfront to detect any incoherence between our libraries and our compiler. Note that I am moving the code from #3687 that started this migration to this new PR, and I decided to move almost our entire code base out of the diagnostic item with the exception of our SIMD bitmask model and the shadow memory for uninit checks. Finally, I changed our standard library codegen to be a regular test in our script based suite. I also changed it to inject `kani_core` so we can remove hacks in our goto-c codegen. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Carolyn Zech <cmzech@amazon.com>
1 parent 9c2318b commit d4605b3

File tree

26 files changed

+691
-446
lines changed

26 files changed

+691
-446
lines changed

kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,21 @@ impl CodegenBackend for GotocCodegenBackend {
237237
let ret_val = rustc_internal::run(tcx, || {
238238
super::utils::init();
239239

240-
// Queries shouldn't change today once codegen starts.
240+
// Any changes to queries from this point on is just related to caching information
241+
// needed for generating code to the given crate.
242+
// The cached information must not outlive the stable-mir `run` scope.
243+
// See [QueryDb::kani_functions] for more information.
241244
let queries = self.queries.lock().unwrap().clone();
245+
242246
check_target(tcx.sess);
243247
check_options(tcx.sess);
248+
if queries.args().reachability_analysis != ReachabilityType::None
249+
&& queries.kani_functions().is_empty()
250+
{
251+
tcx.sess.dcx().err(
252+
"Failed to detect Kani functions. Please check your installation is correct.",
253+
);
254+
}
244255

245256
// Codegen all items that need to be processed according to the selected reachability mode:
246257
//

kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs

Lines changed: 59 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,23 @@
1010
1111
use crate::codegen_cprover_gotoc::GotocCtx;
1212
use crate::codegen_cprover_gotoc::codegen::{PropertyClass, bb_label};
13-
use crate::kani_middle::attributes::KaniAttributes;
14-
use crate::kani_middle::attributes::matches_diagnostic as matches_function;
13+
use crate::kani_middle::attributes;
14+
use crate::kani_middle::kani_functions::{KaniFunction, KaniHook};
1515
use crate::unwrap_or_return_codegen_unimplemented_stmt;
1616
use cbmc::goto_program::CIntType;
1717
use cbmc::goto_program::{BuiltinFn, Expr, Stmt, Type};
1818
use rustc_middle::ty::TyCtxt;
1919
use rustc_smir::rustc_internal;
20-
use rustc_span::Symbol;
2120
use stable_mir::mir::mono::Instance;
2221
use stable_mir::mir::{BasicBlockIdx, Place};
2322
use stable_mir::{CrateDef, ty::Span};
23+
use std::collections::HashMap;
2424
use std::rc::Rc;
2525
use tracing::debug;
2626

2727
pub trait GotocHook {
2828
/// if the hook applies, it means the codegen would do something special to it
29-
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool;
29+
fn hook_applies(&self, _tcx: TyCtxt, _instance: Instance) -> bool;
3030
/// the handler for codegen
3131
fn handle(
3232
&self,
@@ -48,9 +48,12 @@ pub trait GotocHook {
4848
/// <https://github.com/model-checking/kani/blob/main/rfc/src/rfcs/0003-cover-statement.md>
4949
/// for more details.
5050
struct Cover;
51+
52+
const UNEXPECTED_CALL: &str = "Hooks from kani library handled as a map";
53+
5154
impl GotocHook for Cover {
52-
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
53-
matches_function(tcx, instance.def, "KaniCover")
55+
fn hook_applies(&self, _tcx: TyCtxt, _instance: Instance) -> bool {
56+
unreachable!("{UNEXPECTED_CALL}")
5457
}
5558

5659
fn handle(
@@ -84,8 +87,8 @@ impl GotocHook for Cover {
8487

8588
struct Assume;
8689
impl GotocHook for Assume {
87-
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
88-
matches_function(tcx, instance.def, "KaniAssume")
90+
fn hook_applies(&self, _tcx: TyCtxt, _instance: Instance) -> bool {
91+
unreachable!("{UNEXPECTED_CALL}")
8992
}
9093

9194
fn handle(
@@ -108,8 +111,8 @@ impl GotocHook for Assume {
108111

109112
struct Assert;
110113
impl GotocHook for Assert {
111-
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
112-
matches_function(tcx, instance.def, "KaniAssert")
114+
fn hook_applies(&self, _tcx: TyCtxt, _instance: Instance) -> bool {
115+
unreachable!("{UNEXPECTED_CALL}")
113116
}
114117

115118
fn handle(
@@ -148,8 +151,8 @@ impl GotocHook for Assert {
148151

149152
struct Check;
150153
impl GotocHook for Check {
151-
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
152-
matches_function(tcx, instance.def, "KaniCheck")
154+
fn hook_applies(&self, _tcx: TyCtxt, _instance: Instance) -> bool {
155+
unreachable!("{UNEXPECTED_CALL}")
153156
}
154157

155158
fn handle(
@@ -184,8 +187,8 @@ impl GotocHook for Check {
184187
struct Nondet;
185188

186189
impl GotocHook for Nondet {
187-
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
188-
matches_function(tcx, instance.def, "KaniAnyRaw")
190+
fn hook_applies(&self, _tcx: TyCtxt, _instance: Instance) -> bool {
191+
unreachable!("{UNEXPECTED_CALL}")
189192
}
190193

191194
fn handle(
@@ -229,7 +232,6 @@ impl GotocHook for Panic {
229232
|| tcx.has_attr(def_id, rustc_span::sym::rustc_const_panic_str)
230233
|| Some(def_id) == tcx.lang_items().panic_fmt()
231234
|| Some(def_id) == tcx.lang_items().begin_panic_fn()
232-
|| matches_function(tcx, instance.def, "KaniPanic")
233235
}
234236

235237
fn handle(
@@ -248,8 +250,8 @@ impl GotocHook for Panic {
248250
/// Encodes __CPROVER_r_ok(ptr, size)
249251
struct IsAllocated;
250252
impl GotocHook for IsAllocated {
251-
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
252-
matches_function(tcx, instance.def, "KaniIsAllocated")
253+
fn hook_applies(&self, _tcx: TyCtxt, _instance: Instance) -> bool {
254+
unreachable!("{UNEXPECTED_CALL}")
253255
}
254256

255257
fn handle(
@@ -285,8 +287,8 @@ impl GotocHook for IsAllocated {
285287
/// Encodes __CPROVER_pointer_object(ptr)
286288
struct PointerObject;
287289
impl GotocHook for PointerObject {
288-
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
289-
matches_function(tcx, instance.def, "KaniPointerObject")
290+
fn hook_applies(&self, _tcx: TyCtxt, _instance: Instance) -> bool {
291+
unreachable!("{UNEXPECTED_CALL}")
290292
}
291293

292294
fn handle(
@@ -321,8 +323,8 @@ impl GotocHook for PointerObject {
321323
/// Encodes __CPROVER_pointer_offset(ptr)
322324
struct PointerOffset;
323325
impl GotocHook for PointerOffset {
324-
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
325-
matches_function(tcx, instance.def, "KaniPointerOffset")
326+
fn hook_applies(&self, _tcx: TyCtxt, _instance: Instance) -> bool {
327+
unreachable!("{UNEXPECTED_CALL}")
326328
}
327329

328330
fn handle(
@@ -467,8 +469,8 @@ impl GotocHook for MemCmp {
467469
struct UntrackedDeref;
468470

469471
impl GotocHook for UntrackedDeref {
470-
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
471-
matches_function(tcx, instance.def, "KaniUntrackedDeref")
472+
fn hook_applies(&self, _tcx: TyCtxt, _instance: Instance) -> bool {
473+
unreachable!("{UNEXPECTED_CALL}")
472474
}
473475

474476
fn handle(
@@ -515,8 +517,8 @@ struct InitContracts;
515517
/// free(NULL);
516518
/// ```
517519
impl GotocHook for InitContracts {
518-
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
519-
matches_function(tcx, instance.def, "KaniInitContracts")
520+
fn hook_applies(&self, _tcx: TyCtxt, _instance: Instance) -> bool {
521+
unreachable!("{UNEXPECTED_CALL}")
520522
}
521523

522524
fn handle(
@@ -557,9 +559,9 @@ impl GotocHook for InitContracts {
557559
pub struct LoopInvariantRegister;
558560

559561
impl GotocHook for LoopInvariantRegister {
560-
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
561-
KaniAttributes::for_instance(tcx, instance).fn_marker()
562-
== Some(Symbol::intern("kani_register_loop_contract"))
562+
fn hook_applies(&self, _tcx: TyCtxt, instance: Instance) -> bool {
563+
attributes::fn_marker(instance.def)
564+
.map_or(false, |marker| marker == "kani_register_loop_contract")
563565
}
564566

565567
fn handle(
@@ -617,37 +619,50 @@ impl GotocHook for LoopInvariantRegister {
617619
}
618620

619621
pub fn fn_hooks() -> GotocHooks {
622+
let kani_lib_hooks: [(KaniHook, Rc<dyn GotocHook>); 11] = [
623+
(KaniHook::Assert, Rc::new(Assert)),
624+
(KaniHook::Assume, Rc::new(Assume)),
625+
(KaniHook::Panic, Rc::new(Panic)),
626+
(KaniHook::Check, Rc::new(Check)),
627+
(KaniHook::Cover, Rc::new(Cover)),
628+
(KaniHook::AnyRaw, Rc::new(Nondet)),
629+
(KaniHook::IsAllocated, Rc::new(IsAllocated)),
630+
(KaniHook::PointerObject, Rc::new(PointerObject)),
631+
(KaniHook::PointerOffset, Rc::new(PointerOffset)),
632+
(KaniHook::UntrackedDeref, Rc::new(UntrackedDeref)),
633+
(KaniHook::InitContracts, Rc::new(InitContracts)),
634+
];
620635
GotocHooks {
621-
hooks: vec![
636+
kani_lib_hooks: HashMap::from(kani_lib_hooks),
637+
other_hooks: vec![
622638
Rc::new(Panic),
623-
Rc::new(Assume),
624-
Rc::new(Assert),
625-
Rc::new(Check),
626-
Rc::new(Cover),
627-
Rc::new(Nondet),
628-
Rc::new(IsAllocated),
629-
Rc::new(PointerObject),
630-
Rc::new(PointerOffset),
631639
Rc::new(RustAlloc),
632640
Rc::new(MemCmp),
633-
Rc::new(UntrackedDeref),
634-
Rc::new(InitContracts),
635641
Rc::new(LoopInvariantRegister),
636642
],
637643
}
638644
}
639645

640646
pub struct GotocHooks {
641-
hooks: Vec<Rc<dyn GotocHook>>,
647+
/// Match functions that are unique and defined in the Kani library, which we can prefetch
648+
/// using `KaniFunctions`.
649+
kani_lib_hooks: HashMap<KaniHook, Rc<dyn GotocHook>>,
650+
/// Match functions that are not defined in the Kani library, which we cannot prefetch
651+
/// beforehand.
652+
other_hooks: Vec<Rc<dyn GotocHook>>,
642653
}
643654

644655
impl GotocHooks {
645656
pub fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> Option<Rc<dyn GotocHook>> {
646-
for h in &self.hooks {
647-
if h.hook_applies(tcx, instance) {
648-
return Some(h.clone());
657+
if let Ok(KaniFunction::Hook(hook)) = KaniFunction::try_from(instance) {
658+
Some(self.kani_lib_hooks[&hook].clone())
659+
} else {
660+
for h in &self.other_hooks {
661+
if h.hook_applies(tcx, instance) {
662+
return Some(h.clone());
663+
}
649664
}
665+
None
650666
}
651-
None
652667
}
653668
}

kani-compiler/src/kani_middle/attributes.rs

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,17 @@ use rustc_middle::ty::{Instance, TyCtxt, TyKind};
1515
use rustc_session::Session;
1616
use rustc_smir::rustc_internal;
1717
use rustc_span::{Span, Symbol};
18+
use stable_mir::crate_def::Attribute as AttributeStable;
1819
use stable_mir::mir::mono::Instance as InstanceStable;
19-
use stable_mir::{CrateDef, DefId as StableDefId};
20+
use stable_mir::{CrateDef, DefId as StableDefId, Symbol as SymbolStable};
2021
use std::str::FromStr;
2122
use strum_macros::{AsRefStr, EnumString};
2223
use syn::parse::Parser;
2324
use syn::punctuated::Punctuated;
24-
use syn::{PathSegment, TypePath};
25-
26-
use tracing::{debug, trace};
25+
use syn::{Expr, ExprLit, Lit, PathSegment, TypePath};
2726

2827
use super::resolve::{FnResolution, ResolveError, resolve_fn, resolve_fn_path};
28+
use tracing::{debug, trace};
2929

3030
#[derive(Debug, Clone, Copy, AsRefStr, EnumString, PartialEq, Eq, PartialOrd, Ord)]
3131
#[strum(serialize_all = "snake_case")]
@@ -1010,17 +1010,6 @@ fn attr_kind(tcx: TyCtxt, attr: &Attribute) -> Option<KaniAttributeKind> {
10101010
}
10111011
}
10121012

1013-
pub fn matches_diagnostic<T: CrateDef>(tcx: TyCtxt, def: T, attr_name: &str) -> bool {
1014-
let attr_sym = rustc_span::symbol::Symbol::intern(attr_name);
1015-
if let Some(attr_id) = tcx.all_diagnostic_items(()).name_to_id.get(&attr_sym) {
1016-
if rustc_internal::internal(tcx, def.def_id()) == *attr_id {
1017-
debug!("matched: {:?} {:?}", attr_id, attr_sym);
1018-
return true;
1019-
}
1020-
}
1021-
false
1022-
}
1023-
10241013
/// Parse an attribute using `syn`.
10251014
///
10261015
/// This provides a user-friendly interface to manipulate than the internal compiler AST.
@@ -1030,6 +1019,12 @@ fn syn_attr(attr: &Attribute) -> syn::Attribute {
10301019
parser.parse_str(&attr_str).unwrap().pop().unwrap()
10311020
}
10321021

1022+
/// Parse a stable attribute using `syn`.
1023+
fn syn_attr_stable(attr: &AttributeStable) -> syn::Attribute {
1024+
let parser = syn::Attribute::parse_outer;
1025+
parser.parse_str(&attr.as_str()).unwrap().pop().unwrap()
1026+
}
1027+
10331028
/// Return a more user-friendly string for path by trying to remove unneeded whitespace.
10341029
///
10351030
/// `quote!()` and `TokenString::to_string()` introduce unnecessary space around separators.
@@ -1071,3 +1066,20 @@ fn pretty_type_path(path: &TypePath) -> String {
10711066
format!("{leading}{}", segments_str(&path.path.segments))
10721067
}
10731068
}
1069+
1070+
/// Retrieve the value of the `fn_marker` attribute for the given definition if it has one.
1071+
pub(crate) fn fn_marker<T: CrateDef>(def: T) -> Option<String> {
1072+
let fn_marker: [SymbolStable; 2] = ["kanitool".into(), "fn_marker".into()];
1073+
let marker = def.attrs_by_path(&fn_marker).pop()?;
1074+
let attribute = syn_attr_stable(&marker);
1075+
let meta_name = attribute.meta.require_name_value().unwrap_or_else(|_| {
1076+
panic!("Expected name value attribute for `kanitool::fn_marker`, but found: `{:?}`", marker)
1077+
});
1078+
let Expr::Lit(ExprLit { lit: Lit::Str(lit_str), .. }) = &meta_name.value else {
1079+
panic!(
1080+
"Expected string literal for `kanitool::fn_marker`, but found: `{:?}`",
1081+
meta_name.value
1082+
);
1083+
};
1084+
Some(lit_str.value())
1085+
}

0 commit comments

Comments
 (0)