Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate foreign function, compiler-interface and kani-middle modules to use StableMIR #2959

Merged
merged 9 commits into from
Dec 20, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ use crate::kani_middle;
use cbmc::goto_program::{Expr, Location, Stmt, Symbol, Type};
use cbmc::{InternString, InternedString};
use lazy_static::lazy_static;
use rustc_middle::ty::Instance;
use rustc_smir::rustc_internal;
use rustc_target::abi::call::Conv;
use stable_mir::mir::mono::Instance as InstanceStable;
use stable_mir::mir::mono::Instance;
use stable_mir::CrateDef;
use tracing::{debug, trace};

lazy_static! {
Expand Down Expand Up @@ -46,15 +46,16 @@ impl<'tcx> GotocCtx<'tcx> {
///
/// For other foreign items, we declare a shim and add to the list of foreign shims to be
/// handled later.
pub fn codegen_foreign_fn(&mut self, instance: InstanceStable) -> &Symbol {
pub fn codegen_foreign_fn(&mut self, instance: Instance) -> &Symbol {
debug!(?instance, "codegen_foreign_function");
let instance = rustc_internal::internal(instance);
let fn_name = self.symbol_name(instance).intern();
let instance_internal = rustc_internal::internal(instance);
let fn_name = self.symbol_name_stable(instance).intern();
if self.symbol_table.contains(fn_name) {
// Symbol has been added (either a built-in CBMC function or a Rust allocation function).
self.symbol_table.lookup(fn_name).unwrap()
} else if RUST_ALLOC_FNS.contains(&fn_name)
|| (self.is_cffi_enabled() && kani_middle::fn_abi(self.tcx, instance).conv == Conv::C)
|| (self.is_cffi_enabled()
&& kani_middle::fn_abi(self.tcx, instance_internal).conv == Conv::C)
{
// Add a Rust alloc lib function as is declared by core.
// When C-FFI feature is enabled, we just trust the rust declaration.
Expand All @@ -63,14 +64,8 @@ impl<'tcx> GotocCtx<'tcx> {
// https://github.com/model-checking/kani/issues/2426
self.ensure(fn_name, |gcx, _| {
let typ = gcx.codegen_ffi_type(instance);
Symbol::function(
fn_name,
typ,
None,
gcx.readable_instance_name(instance),
Location::none(),
)
.with_is_extern(true)
Symbol::function(fn_name, typ, None, instance.name(), Location::none())
.with_is_extern(true)
})
} else {
let shim_name = format!("{fn_name}_ffi_shim");
Expand All @@ -82,7 +77,7 @@ impl<'tcx> GotocCtx<'tcx> {
&shim_name,
typ,
Some(gcx.codegen_ffi_shim(shim_name.as_str().into(), instance)),
gcx.readable_instance_name(instance),
instance.name(),
Location::none(),
)
})
Expand All @@ -96,19 +91,19 @@ impl<'tcx> GotocCtx<'tcx> {
}

/// Generate code for a foreign function shim.
fn codegen_ffi_shim(&mut self, shim_name: InternedString, instance: Instance<'tcx>) -> Stmt {
fn codegen_ffi_shim(&mut self, shim_name: InternedString, instance: Instance) -> Stmt {
debug!(?shim_name, ?instance, sym=?self.symbol_table.lookup(shim_name), "generate_foreign_shim");

let loc = self.codegen_span(&self.tcx.def_span(instance.def_id()));
let loc = self.codegen_span_stable(instance.def.span());
let unsupported_check = self.codegen_ffi_unsupported(instance, loc);
Stmt::block(vec![unsupported_check], loc)
}

/// Generate type for the given foreign instance.
fn codegen_ffi_type(&mut self, instance: Instance<'tcx>) -> Type {
let fn_name = self.symbol_name(instance);
let fn_abi = kani_middle::fn_abi(self.tcx, instance);
let loc = self.codegen_span(&self.tcx.def_span(instance.def_id()));
fn codegen_ffi_type(&mut self, instance: Instance) -> Type {
let fn_name = self.symbol_name_stable(instance);
let fn_abi = kani_middle::fn_abi(self.tcx, rustc_internal::internal(instance));
let loc = self.codegen_span_stable(instance.def.span());
let params = fn_abi
.args
.iter()
Expand Down Expand Up @@ -137,15 +132,15 @@ impl<'tcx> GotocCtx<'tcx> {
///
/// This will behave like `codegen_unimplemented_stmt` but print a message that includes
/// the name of the function not supported and the calling convention.
fn codegen_ffi_unsupported(&mut self, instance: Instance<'tcx>, loc: Location) -> Stmt {
let fn_name = &self.symbol_name(instance);
fn codegen_ffi_unsupported(&mut self, instance: Instance, loc: Location) -> Stmt {
let fn_name = &self.symbol_name_stable(instance);
debug!(?fn_name, ?loc, "codegen_ffi_unsupported");

// Save this occurrence so we can emit a warning in the compilation report.
let entry = self.unsupported_constructs.entry("foreign function".into()).or_default();
entry.push(loc);

let call_conv = kani_middle::fn_abi(self.tcx, instance).conv;
let call_conv = kani_middle::fn_abi(self.tcx, rustc_internal::internal(instance)).conv;
let msg = format!("call to foreign \"{call_conv:?}\" function `{fn_name}`");
let url = if call_conv == Conv::C {
"https://github.com/model-checking/kani/issues/2423"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1331,7 +1331,7 @@ impl<'tcx> GotocCtx<'tcx> {
TyKind::RigidTy(RigidTy::Slice(_)) | TyKind::RigidTy(RigidTy::Str) => {
let unit_t = match ty.kind() {
TyKind::RigidTy(RigidTy::Slice(et)) => et,
TyKind::RigidTy(RigidTy::Str) => rustc_internal::stable(self.tcx.types.u8),
TyKind::RigidTy(RigidTy::Str) => Ty::unsigned_ty(UintTy::U8),
_ => unreachable!(),
};
let unit = self.layout_of_stable(unit_t);
Expand Down
80 changes: 36 additions & 44 deletions kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::kani_middle::{check_reachable_items, dump_mir_items};
use crate::kani_queries::QueryDb;
use cbmc::goto_program::Location;
use cbmc::irep::goto_binary_serde::write_goto_binary_file;
use cbmc::RoundingMode;
use cbmc::{InternString, RoundingMode};
use cbmc::{InternedString, MachineModel};
use kani_metadata::artifact::convert_type;
use kani_metadata::CompilerArtifactStub;
Expand All @@ -32,12 +32,10 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_errors::{ErrorGuaranteed, DEFAULT_LOCALE_RESOURCE};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_hir::definitions::DefPathHash;
use rustc_metadata::creader::MetadataLoaderDyn;
use rustc_metadata::fs::{emit_wrapper_file, METADATA_FILENAME};
use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
use rustc_middle::mir::mono::MonoItem;
use rustc_middle::ty::TyCtxt;
use rustc_middle::util::Providers;
use rustc_session::config::{CrateType, OutputFilenames, OutputType};
Expand All @@ -46,7 +44,7 @@ use rustc_session::Session;
use rustc_smir::rustc_internal;
use rustc_target::abi::Endian;
use rustc_target::spec::PanicStrategy;
use stable_mir::mir::mono::MonoItem as MonoItemStable;
use stable_mir::mir::mono::{Instance, MonoItem};
use stable_mir::CrateDef;
use std::any::Any;
use std::collections::BTreeMap;
Expand Down Expand Up @@ -82,10 +80,10 @@ impl GotocCodegenBackend {
fn codegen_items<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
starting_items: &[MonoItem<'tcx>],
starting_items: &[MonoItem],
symtab_goto: &Path,
machine_model: &MachineModel,
) -> (GotocCtx<'tcx>, Vec<MonoItem<'tcx>>) {
) -> (GotocCtx<'tcx>, Vec<MonoItem>) {
let items = with_timer(
|| collect_reachable_items(tcx, starting_items),
"codegen reachability analysis",
Expand All @@ -103,17 +101,13 @@ impl GotocCodegenBackend {
for item in &items {
match *item {
MonoItem::Fn(instance) => {
let instance = rustc_internal::stable(instance);
gcx.call_with_panic_debug_info(
|ctx| ctx.declare_function(instance),
format!("declare_function: {}", instance.name()),
instance.def,
);
}
MonoItem::Static(_) => {
let MonoItemStable::Static(def) = rustc_internal::stable(item) else {
unreachable!()
};
MonoItem::Static(def) => {
gcx.call_with_panic_debug_info(
|ctx| ctx.declare_static(def),
format!("declare_static: {}", def.name()),
Expand All @@ -128,7 +122,6 @@ impl GotocCodegenBackend {
for item in &items {
match *item {
MonoItem::Fn(instance) => {
let instance = rustc_internal::stable(instance);
gcx.call_with_panic_debug_info(
|ctx| ctx.codegen_function(instance),
format!(
Expand All @@ -139,10 +132,7 @@ impl GotocCodegenBackend {
instance.def,
);
}
MonoItem::Static(_) => {
let MonoItemStable::Static(def) = rustc_internal::stable(item) else {
unreachable!()
};
MonoItem::Static(def) => {
gcx.call_with_panic_debug_info(
|ctx| ctx.codegen_static(def),
format!("codegen_static: {}", def.name()),
Expand Down Expand Up @@ -237,28 +227,31 @@ impl CodegenBackend for GotocCodegenBackend {
ReachabilityType::Harnesses => {
// Cross-crate collecting of all items that are reachable from the crate harnesses.
let harnesses = queries.target_harnesses();
let mut items: HashSet<DefPathHash> = HashSet::with_capacity(harnesses.len());
let mut items: HashSet<_> = HashSet::with_capacity(harnesses.len());
items.extend(harnesses);
let harnesses = filter_crate_items(tcx, |_, def_id| {
items.contains(&tcx.def_path_hash(def_id))
let harnesses = filter_crate_items(tcx, |_, instance| {
items.contains(&instance.mangled_name().intern())
});
for harness in harnesses {
let model_path = queries
.harness_model_path(&tcx.def_path_hash(harness.def_id()))
.unwrap();
let (gcx, items) =
self.codegen_items(tcx, &[harness], model_path, &results.machine_model);
results.extend(gcx, items, None);
let model_path =
queries.harness_model_path(&harness.mangled_name()).unwrap();
let (gcx, mono_items) = self.codegen_items(
tcx,
&[MonoItem::Fn(harness)],
adpaco-aws marked this conversation as resolved.
Show resolved Hide resolved
model_path,
&results.machine_model,
);
results.extend(gcx, mono_items, None);
}
}
ReachabilityType::Tests => {
// We're iterating over crate items here, so what we have to codegen is the "test description" containing the
// test closure that we want to execute
// TODO: Refactor this code so we can guarantee that the pair (test_fn, test_desc) actually match.
let mut descriptions = vec![];
let harnesses = filter_const_crate_items(tcx, |_, def_id| {
if is_test_harness_description(tcx, def_id) {
descriptions.push(def_id);
let harnesses = filter_const_crate_items(tcx, |_, item| {
if is_test_harness_description(tcx, item.def) {
descriptions.push(item.def);
true
} else {
false
Expand Down Expand Up @@ -289,11 +282,15 @@ impl CodegenBackend for GotocCodegenBackend {
}
ReachabilityType::None => {}
ReachabilityType::PubFns => {
let entry_fn = tcx.entry_fn(()).map(|(id, _)| id);
let local_reachable = filter_crate_items(tcx, |_, def_id| {
(tcx.is_reachable_non_generic(def_id) && tcx.def_kind(def_id).is_fn_like())
|| entry_fn == Some(def_id)
});
let main_instance =
stable_mir::entry_fn().map(|main_fn| Instance::try_from(main_fn).unwrap());
let local_reachable = filter_crate_items(tcx, |_, instance| {
let def_id = rustc_internal::internal(instance.def.def_id());
Some(instance) == main_instance || tcx.is_reachable_non_generic(def_id)
})
.into_iter()
.map(MonoItem::Fn)
adpaco-aws marked this conversation as resolved.
Show resolved Hide resolved
.collect::<Vec<_>>();
let model_path = base_filename.with_extension(ArtifactType::SymTabGoto);
let (gcx, items) = self.codegen_items(
tcx,
Expand Down Expand Up @@ -527,17 +524,17 @@ where
}
}

struct GotoCodegenResults<'tcx> {
struct GotoCodegenResults {
reachability: ReachabilityType,
harnesses: Vec<HarnessMetadata>,
unsupported_constructs: UnsupportedConstructs,
concurrent_constructs: UnsupportedConstructs,
items: Vec<MonoItem<'tcx>>,
items: Vec<MonoItem>,
crate_name: InternedString,
machine_model: MachineModel,
}

impl<'tcx> GotoCodegenResults<'tcx> {
impl GotoCodegenResults {
pub fn new(tcx: TyCtxt, reachability: ReachabilityType) -> Self {
GotoCodegenResults {
reachability,
Expand Down Expand Up @@ -585,12 +582,7 @@ impl<'tcx> GotoCodegenResults<'tcx> {
}
}

fn extend(
&mut self,
gcx: GotocCtx,
items: Vec<MonoItem<'tcx>>,
metadata: Option<HarnessMetadata>,
) {
fn extend(&mut self, gcx: GotocCtx, items: Vec<MonoItem>, metadata: Option<HarnessMetadata>) {
let mut items = items;
self.harnesses.extend(metadata);
self.concurrent_constructs.extend(gcx.concurrent_constructs);
Expand All @@ -599,7 +591,7 @@ impl<'tcx> GotoCodegenResults<'tcx> {
}

/// Prints a report at the end of the compilation.
fn print_report(&self, tcx: TyCtxt<'tcx>) {
fn print_report(&self, tcx: TyCtxt) {
// Print all unsupported constructs.
if !self.unsupported_constructs.is_empty() {
// Sort alphabetically.
Expand Down Expand Up @@ -631,7 +623,7 @@ impl<'tcx> GotoCodegenResults<'tcx> {

// Print some compilation stats.
if tracing::enabled!(tracing::Level::INFO) {
analysis::print_stats(tcx, &self.items);
analysis::print_stats(&self.items);
}
}
}
Expand Down
31 changes: 3 additions & 28 deletions kani-compiler/src/codegen_cprover_gotoc/utils/names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ use crate::codegen_cprover_gotoc::GotocCtx;
use cbmc::InternedString;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::mir::mono::CodegenUnitNameBuilder;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{Instance, TyCtxt};
use stable_mir::mir::mono::Instance as InstanceStable;
use rustc_middle::ty::TyCtxt;
use stable_mir::mir::mono::Instance;
use stable_mir::mir::Local;
use tracing::debug;

impl<'tcx> GotocCtx<'tcx> {
/// The full crate name including versioning info
Expand Down Expand Up @@ -52,34 +50,11 @@ impl<'tcx> GotocCtx<'tcx> {
format!("{var_name}_init")
}

/// A human readable name in Rust for reference, should not be used as a key.
pub fn readable_instance_name(&self, instance: Instance<'tcx>) -> String {
with_no_trimmed_paths!(self.tcx.def_path_str_with_args(instance.def_id(), instance.args))
}

/// The actual function name used in the symbol table
pub fn symbol_name(&self, instance: Instance<'tcx>) -> String {
let llvm_mangled = self.tcx.symbol_name(instance).name.to_string();
debug!(
"finding function name for instance: {}, debug: {:?}, name: {}, symbol: {}",
instance,
instance,
self.readable_instance_name(instance),
llvm_mangled,
);

let pretty = self.readable_instance_name(instance);

// Make main function a special case in order to support `--function main`
// TODO: Get rid of this: https://github.com/model-checking/kani/issues/2129
adpaco-aws marked this conversation as resolved.
Show resolved Hide resolved
if pretty == "main" { pretty } else { llvm_mangled }
}

/// Return the mangled name to be used in the symbol table.
///
/// We special case main function in order to support `--function main`.
// TODO: Get rid of this: https://github.com/model-checking/kani/issues/2129
pub fn symbol_name_stable(&self, instance: InstanceStable) -> String {
pub fn symbol_name_stable(&self, instance: Instance) -> String {
let pretty = instance.name();
if pretty == "main" { pretty } else { instance.mangled_name() }
}
Expand Down
Loading