Skip to content

Commit 21f2e80

Browse files
authored
Merge pull request #4 from os-checker/stable_mir
Switch from HIR to StableMIR
2 parents 4a17b5d + aaf8db5 commit 21f2e80

File tree

8 files changed

+264
-184
lines changed

8 files changed

+264
-184
lines changed

src/cli/mod.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use clap::Parser;
2-
use std::path::PathBuf;
32

43
/// Parse cli arguments.
54
pub fn parse() -> Args {
@@ -10,9 +9,12 @@ pub fn parse() -> Args {
109
#[derive(Parser, Debug)]
1110
#[command(version, about, long_about = None)]
1211
pub struct Args {
13-
/// Json output file path. Print to stdout if not set.
12+
/// Possible one of these values:
13+
/// * `--json false`: skip serializing to json
14+
/// * `--json path/to/file.json`
15+
/// * print to stdout if not set
1416
#[arg(long)]
15-
pub json: Option<PathBuf>,
17+
pub json: Option<String>,
1618

1719
/// Args for rustc.
1820
pub rustc_args: Vec<String>,

src/functions/call_graph.rs

Lines changed: 0 additions & 107 deletions
This file was deleted.

src/functions/callees.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use super::ty_to_fndef;
2+
use indexmap::IndexSet;
3+
use stable_mir::{CrateDef, mir::*, ty::FnDef};
4+
5+
pub fn calls_in_body(body: &Body) -> Vec<FnDef> {
6+
let locals = body.locals();
7+
let mut direct = Vec::new();
8+
for basic_block in &body.blocks {
9+
terminator_to_fn_def(&basic_block.terminator, locals, &mut direct);
10+
}
11+
// all direct calls in a body
12+
direct
13+
}
14+
15+
/// Extract fn call from the terminator.
16+
fn terminator_to_fn_def(terminator: &Terminator, locals: &[LocalDecl], direct: &mut Vec<FnDef>) {
17+
let TerminatorKind::Call { func, args, .. } = &terminator.kind else { return };
18+
19+
push_fn_def(func, locals, direct);
20+
21+
// FIXME: Will there be a FnDef in an arg in MIR? Probably no?
22+
// Anyway, need find a test for this. But now assume yes.
23+
// Maybe a function pointer or raw pointer cast to fn pointer suit?
24+
for arg in args {
25+
push_fn_def(arg, locals, direct);
26+
}
27+
28+
// Will there ever be a FnDef in destination? Skip destination for now.
29+
}
30+
31+
/// Push a FnDef Operand if any.
32+
fn push_fn_def(func: &Operand, locals: &[LocalDecl], direct: &mut Vec<FnDef>) {
33+
let ty = func.ty(locals).expect("malformed operand or incompatible locals list");
34+
if let Some(fn_def) = ty_to_fndef(ty) {
35+
direct.push(fn_def);
36+
}
37+
}
38+
39+
/// Recursively retrieve calls for a call.
40+
pub fn recursive_callees(fn_def: FnDef, visited: &mut IndexSet<FnDef>) {
41+
// print!("[{}] ", fn_def.name());
42+
43+
let Some(body) = fn_def.body() else { return };
44+
45+
// let mut buf = Vec::with_capacity(1024);
46+
// body.dump(&mut buf, &format!("{fn_def:?}")).unwrap();
47+
// println!("{}", String::from_utf8(buf).unwrap());
48+
49+
let mut direct = calls_in_body(&body);
50+
while let Some(call) = direct.pop() {
51+
// the call hasn't been reached, traverse
52+
if visited.insert(call) {
53+
recursive_callees(call, visited);
54+
}
55+
}
56+
}

src/functions/mod.rs

Lines changed: 81 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1-
use rustc_hir::{ExprKind, Item, ItemKind};
1+
use indexmap::IndexSet;
22
use rustc_middle::ty::TyCtxt;
3-
use rustc_span::source_map::SourceMap;
3+
use rustc_smir::rustc_internal::internal;
4+
use rustc_span::{Span, source_map::SourceMap};
45
use serde::Serialize;
6+
use stable_mir::{
7+
CrateDef, CrateItem, DefId, ItemKind,
8+
mir::mono::Instance,
9+
ty::{FnDef, RigidTy, Ty, TyKind},
10+
};
511

6-
mod call_graph;
12+
mod callees;
713

814
/// A Rust funtion with its file source, attributes, and raw function content.
915
#[derive(Debug, Default, Serialize)]
@@ -16,54 +22,84 @@ pub struct Function {
1622
attrs: Vec<String>,
1723
/// Raw function string, including name, signature, and body.
1824
func: String,
25+
/// Recursive fnction calls inside the body.
26+
callees: Vec<String>,
1927
}
2028

2129
impl Function {
22-
pub fn new(item: &Item, src_map: &SourceMap, tcx: TyCtxt) -> Option<Self> {
23-
if let ItemKind::Fn { has_body, body, .. } = &item.kind
24-
&& *has_body
25-
{
26-
let mut func = Function {
27-
file: src_map
28-
.span_to_filename(item.span)
29-
.prefer_remapped_unconditionaly()
30-
.to_string(),
31-
..Default::default()
32-
};
30+
pub fn new(item: CrateItem, tcx: TyCtxt, src_map: &SourceMap) -> Option<Self> {
31+
if !matches!(item.kind(), ItemKind::Fn) {
32+
// skip non fn items
33+
return None;
34+
}
35+
// item.emit_mir(&mut std::io::stdout()).unwrap(); // MIR body
36+
let inst = Instance::try_from(item).inspect_err(|err| error!(?err)).ok()?;
37+
let fn_def = ty_to_fndef(inst.ty())?;
38+
let file = item.span().get_filename();
39+
let body = fn_def.body()?;
3340

34-
// add attributes
35-
for (i, attr) in tcx.get_all_attrs(item.owner_id).enumerate() {
36-
// FIXME: kani rewrites attributes from source to `#[allow(dead_code)]`
37-
// and `#[kanitool::...]`, but share the same span with the source span.
38-
// As a result, repeated `#[kani::proof]` are obtained from the source text,
39-
// but `attr: &AttrItem` contains real expanded attributes.
40-
let src_attr = src_map
41-
.span_to_source(attr.span(), |text, x, y| {
42-
let src = &text[x..y];
43-
debug!("[attr {i}] [{x}:{y}]\n{src}");
44-
Ok(src.to_owned())
45-
})
46-
.unwrap();
47-
func.attrs.push(src_attr);
48-
}
41+
let mut callees = IndexSet::new();
42+
// retrieve direct calls
43+
callees.extend(callees::calls_in_body(&body));
44+
// recursive calls
45+
let direct_calls: Vec<_> = callees.iter().copied().collect();
46+
for call in direct_calls {
47+
callees::recursive_callees(call, &mut callees);
48+
}
49+
let callees = callees.into_iter().map(|x| format!("{x:?}")).collect();
4950

50-
// add function
51-
func.func = src_map
52-
.span_to_source(item.span, |text, x, y| {
53-
let src = &text[x..y];
54-
debug!("[{x}:{y}]\n{src}");
55-
Ok(src.to_owned())
56-
})
57-
.unwrap();
51+
let func = source_code_with(body.span, tcx, src_map);
52+
info!(" - {:?} ({:?}): {func}", item.name(), item.span());
5853

59-
// dbg!(tcx.hir_body(*body));
60-
let fn_body = tcx.hir_body(*body);
61-
if let ExprKind::Block(block, _) = fn_body.value.kind {
62-
dbg!(block);
63-
}
54+
// FIXME: kanitool and some other proc-macors attributes are generated by parsing,
55+
// and these generated attrs share with span of hand-written attrs in source code.
56+
// As a result, get_all_attributes through source span will emit duplicated attrs.
57+
// Need to fix this in the future, by analyzing Symbols of these attrs.
58+
let attrs = get_all_attributes(item.def_id(), tcx, src_map);
6459

65-
return Some(func);
66-
}
67-
None
60+
// TODO: kanitool kind: proof, proof_for_contract, contract, ...
61+
62+
Some(Function { file, attrs, func, callees })
6863
}
6964
}
65+
66+
/// Extract FnDef from Ty.
67+
fn ty_to_fndef(ty: Ty) -> Option<FnDef> {
68+
let TyKind::RigidTy(RigidTy::FnDef(fn_def, _)) = ty.kind() else {
69+
return None;
70+
};
71+
Some(fn_def)
72+
}
73+
74+
/// Source code for a span.
75+
fn source_code(span: Span, src_map: &SourceMap) -> String {
76+
src_map
77+
.span_to_source(span, |text, x, y| {
78+
let src = &text[x..y];
79+
debug!("[{x}:{y}]\n{src}");
80+
Ok(src.to_owned())
81+
})
82+
.unwrap()
83+
}
84+
85+
/// Source code for a stable_mir span.
86+
fn source_code_with(
87+
stable_mir_span: stable_mir::ty::Span,
88+
tcx: TyCtxt,
89+
src_map: &SourceMap,
90+
) -> String {
91+
let span = internal(tcx, stable_mir_span);
92+
source_code(span, src_map)
93+
}
94+
95+
/// Get all attributes for the item.
96+
///
97+
/// We don't call [`all_tool_attrs`], because it only gives tool attributes,
98+
/// we want as raw attributes as possible.
99+
///
100+
/// [`all_tool_attrs`]: https://doc.rust-lang.org/nightly/nightly-rustc/stable_mir/struct.CrateItem.html#impl-CrateDef-for-CrateItem
101+
fn get_all_attributes(stable_mir_def_id: DefId, tcx: TyCtxt, src_map: &SourceMap) -> Vec<String> {
102+
let def_id = internal(tcx, stable_mir_def_id);
103+
// dbg!(tcx.hir_body_owned_by(def_id.expect_local())); // HIR body
104+
tcx.get_all_attrs(def_id).map(|attr| source_code(attr.span(), src_map)).collect()
105+
}

0 commit comments

Comments
 (0)