diff --git a/install.sh b/install.sh index c40de89..93ccf56 100755 --- a/install.sh +++ b/install.sh @@ -24,7 +24,7 @@ esac # Set the library path to be added #LIBRARY_PATH="$HOME/.rustup/toolchains/nightly-2023-10-05-x86_64-unknown-linux-gnu/lib" -toolchain_date="nightly-2024-06-30" +toolchain_date="nightly-2024-10-12" toolchain_file="rust-toolchain.toml" if [ ! -f "$toolchain_file" ]; then printf "%bError: %s does not exist.%b\n" "${RED}" "$toolchain_file" "${NC}" diff --git a/rap/Cargo.lock b/rap/Cargo.lock index 6c247e3..ab0aaa6 100644 --- a/rap/Cargo.lock +++ b/rap/Cargo.lock @@ -372,9 +372,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "peeking_take_while" @@ -410,6 +410,7 @@ dependencies = [ "fern", "lazy_static", "log", + "once_cell", "regex", "rustc-demangle", "serde_json", diff --git a/rap/Cargo.toml b/rap/Cargo.toml index 4500112..b1c0981 100644 --- a/rap/Cargo.toml +++ b/rap/Cargo.toml @@ -28,6 +28,7 @@ wait-timeout = "0.2.0" rustc-demangle = "0.1.21" colorful = "0.2.1" regex = "1.11.1" +once_cell = "1.20.1" walkdir = "2" cargo_metadata = "0.18" diff --git a/rap/rust-toolchain.toml b/rap/rust-toolchain.toml index 46d8578..896b098 100644 --- a/rap/rust-toolchain.toml +++ b/rap/rust-toolchain.toml @@ -1,5 +1,4 @@ [toolchain] # The default version of the rustc compiler -#channel = "nightly-2023-10-05-x86_64-unknown-linux-gnu" -channel = "nightly-2024-06-30" +channel = "nightly-2024-10-12" components = ["rustc-dev", "rust-src", "llvm-tools-preview"] diff --git a/rap/rust-toolchain.toml.bak b/rap/rust-toolchain.toml.bak deleted file mode 100644 index e3337a7..0000000 --- a/rap/rust-toolchain.toml.bak +++ /dev/null @@ -1,5 +0,0 @@ -[toolchain] -# The default version of the rustc compiler -#channel = "nightly-2023-10-05-x86_64-unknown-linux-gnu" -channel = "nightly-2024-06-30-x86_64-unknown-linux-gnu" -components = ["rustc-dev", "rust-src", "llvm-tools-preview"] diff --git a/rap/src/analysis.rs b/rap/src/analysis.rs index 740693d..f260460 100644 --- a/rap/src/analysis.rs +++ b/rap/src/analysis.rs @@ -1,4 +1,5 @@ pub mod core; +pub mod opt; pub mod rcanary; pub mod safedrop; pub mod senryx; diff --git a/rap/src/analysis/core.rs b/rap/src/analysis/core.rs index 36ec480..db05500 100644 --- a/rap/src/analysis/core.rs +++ b/rap/src/analysis/core.rs @@ -1,5 +1,4 @@ pub mod alias; pub mod call_graph; -pub mod control_flow; pub mod dataflow; pub mod heap_item; diff --git a/rap/src/analysis/core/alias/mop/graph.rs b/rap/src/analysis/core/alias/mop/graph.rs index cbf3c51..ca7bf16 100644 --- a/rap/src/analysis/core/alias/mop/graph.rs +++ b/rap/src/analysis/core/alias/mop/graph.rs @@ -228,7 +228,7 @@ impl<'tcx> MopGraph<'tcx> { } } } - Rvalue::Ref(_, _, ref p) | Rvalue::AddressOf(_, ref p) => { + Rvalue::Ref(_, _, ref p) => { let rv_local = p.local.as_usize(); if values[lv_local].may_drop && values[rv_local].may_drop { let rv = *p; @@ -354,6 +354,7 @@ impl<'tcx> MopGraph<'tcx> { } cur_bb.calls.push(terminator.clone()); } + TerminatorKind::TailCall { .. } => todo!(), TerminatorKind::Assert { cond: _, expected: _, @@ -396,6 +397,7 @@ impl<'tcx> MopGraph<'tcx> { line_spans: _, ref unwind, targets, + asm_macro: _, } => { for target in targets { cur_bb.add_next(target.as_usize()); diff --git a/rap/src/analysis/core/call_graph/call_graph_visitor.rs b/rap/src/analysis/core/call_graph/call_graph_visitor.rs index 3a436b3..b10795c 100644 --- a/rap/src/analysis/core/call_graph/call_graph_visitor.rs +++ b/rap/src/analysis/core/call_graph/call_graph_visitor.rs @@ -83,7 +83,7 @@ impl<'b, 'tcx> CallGraphVisitor<'b, 'tcx> { if let FnDef(callee_def_id, callee_substs) = constant.const_.ty().kind() { let param_env = self.tcx.param_env(self.def_id); if let Ok(Some(instance)) = - Instance::resolve(self.tcx, param_env, *callee_def_id, callee_substs) + Instance::try_resolve(self.tcx, param_env, *callee_def_id, callee_substs) { let mut is_virtual = false; // Try to analysis the specific type of callee. @@ -114,9 +114,6 @@ impl<'b, 'tcx> CallGraphVisitor<'b, 'tcx> { None } } - InstanceKind::CoroutineKindShim { - coroutine_def_id, .. - } => Some(coroutine_def_id), }; if let Some(instance_def_id) = instance_def_id { self.add_to_call_graph(instance_def_id, Some(is_virtual)); diff --git a/rap/src/analysis/core/control_flow.rs b/rap/src/analysis/core/control_flow.rs deleted file mode 100644 index 8b13789..0000000 --- a/rap/src/analysis/core/control_flow.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/rap/src/analysis/core/dataflow.rs b/rap/src/analysis/core/dataflow.rs index e605fb8..1556a58 100644 --- a/rap/src/analysis/core/dataflow.rs +++ b/rap/src/analysis/core/dataflow.rs @@ -28,6 +28,13 @@ impl<'tcx> DataFlow<'tcx> { } pub fn start(&mut self) { + self.build_graphs(); + if self.debug { + self.draw_graphs(); + } + } + + pub fn build_graphs(&mut self) { for local_def_id in self.tcx.iter_local_def_id() { let hir_map = self.tcx.hir(); if hir_map.maybe_body_owned_by(local_def_id).is_some() { @@ -36,9 +43,6 @@ impl<'tcx> DataFlow<'tcx> { self.graphs.insert(def_id, graph); } } - if self.debug { - self.draw_graphs(); - } } fn build_graph(&self, def_id: DefId) -> Graph { @@ -47,10 +51,10 @@ impl<'tcx> DataFlow<'tcx> { let basic_blocks = &body.basic_blocks; for basic_block_data in basic_blocks.iter() { for statement in basic_block_data.statements.iter() { - graph.add_statm_to_graph(&statement.kind); + graph.add_statm_to_graph(&statement); } if let Some(terminator) = &basic_block_data.terminator { - graph.add_terminator_to_graph(&terminator.kind); + graph.add_terminator_to_graph(&terminator); } } graph diff --git a/rap/src/analysis/core/dataflow/graph.rs b/rap/src/analysis/core/dataflow/graph.rs index c4967e7..b96ad31 100644 --- a/rap/src/analysis/core/dataflow/graph.rs +++ b/rap/src/analysis/core/dataflow/graph.rs @@ -4,10 +4,11 @@ use std::collections::HashSet; use rustc_hir::def_id::DefId; use rustc_index::IndexVec; use rustc_middle::mir::{ - AggregateKind, BorrowKind, Const, Local, Mutability, Operand, Place, PlaceElem, Rvalue, - StatementKind, TerminatorKind, + AggregateKind, BorrowKind, Const, Local, Operand, Place, PlaceElem, Rvalue, Statement, + StatementKind, Terminator, TerminatorKind, }; use rustc_middle::ty::TyKind; +use rustc_span::{Span, DUMMY_SP}; #[derive(Clone, Debug)] pub enum NodeOp { @@ -59,6 +60,7 @@ pub enum GraphEdge { #[derive(Clone)] pub struct GraphNode { pub op: NodeOp, + pub span: Span, pub out_edges: Vec, pub in_edges: Vec, } @@ -67,6 +69,7 @@ impl GraphNode { pub fn new() -> Self { Self { op: NodeOp::Nop, + span: DUMMY_SP, out_edges: vec![], in_edges: vec![], } @@ -150,10 +153,11 @@ impl Graph { ret } - pub fn add_statm_to_graph(&mut self, kind: &StatementKind) { - if let StatementKind::Assign(boxed_statm) = &kind { + pub fn add_statm_to_graph(&mut self, statement: &Statement) { + if let StatementKind::Assign(boxed_statm) = &statement.kind { let place = boxed_statm.0; let dst = self.parse_place(&place); + self.nodes[dst].span = statement.source_info.span; let rvalue = &boxed_statm.1; match rvalue { Rvalue::Use(op) => { @@ -174,15 +178,6 @@ impl Graph { self.add_node_edge(src, dst, op); self.nodes[dst].op = NodeOp::Ref; } - Rvalue::AddressOf(mutability, place) => { - let op = match mutability { - Mutability::Not => EdgeOp::Immut, - Mutability::Mut => EdgeOp::Mut, - }; - let src = self.parse_place(place); - self.add_node_edge(src, dst, op); - self.nodes[dst].op = NodeOp::AddressOf; - } Rvalue::Len(place) => { let src = self.parse_place(place); self.add_node_edge(src, dst, EdgeOp::Nop); @@ -239,17 +234,18 @@ impl Graph { self.add_node_edge(src, dst, EdgeOp::Nop); self.nodes[dst].op = NodeOp::CopyForDeref; } + Rvalue::RawPtr(_, _) => todo!(), }; } } - pub fn add_terminator_to_graph(&mut self, kind: &TerminatorKind) { + pub fn add_terminator_to_graph(&mut self, terminator: &Terminator) { if let TerminatorKind::Call { func, args, destination, .. - } = &kind + } = &terminator.kind { if let Operand::Constant(boxed_cnst) = func { if let Const::Val(_, ty) = boxed_cnst.const_ { @@ -260,6 +256,7 @@ impl Graph { self.add_operand(&op.node, dst); } self.nodes[dst].op = NodeOp::Call(*def_id); + self.nodes[dst].span = terminator.source_info.span; return; } } @@ -270,38 +267,45 @@ impl Graph { pub fn collect_equivalent_locals(&self, local: Local) -> HashSet { let mut set = HashSet::new(); - let mut node_operator = |idx: Local| -> bool { + let mut root = local; + let mut find_root_operator = |idx: Local| -> DFSStatus { let node = &self.nodes[idx]; match node.op { NodeOp::Nop | NodeOp::Use | NodeOp::Ref => { - //Nop means a orphan node or a parameter - set.insert(idx); - true + //Nop means an orphan node or a parameter + root = idx; + DFSStatus::Continue } - _ => false, + _ => DFSStatus::Stop, } }; - let mut edge_validator = |op: &EdgeOp| -> bool { - match op { - EdgeOp::Copy | EdgeOp::Move | EdgeOp::Mut | EdgeOp::Immut => true, - EdgeOp::Nop - | EdgeOp::Const - | EdgeOp::Deref - | EdgeOp::Downcast(_) - | EdgeOp::Field(_) => false, + let mut find_equivalent_operator = |idx: Local| -> DFSStatus { + let node = &self.nodes[idx]; + if set.contains(&idx) { + return DFSStatus::Stop; + } + match node.op { + NodeOp::Nop | NodeOp::Use | NodeOp::Ref => { + set.insert(idx); + DFSStatus::Continue + } + _ => DFSStatus::Stop, } }; + // Algorithm: dfs along upside to find the root node, and then dfs along downside to collect equivalent locals self.dfs( local, Direction::Upside, - &mut node_operator, - &mut edge_validator, + &mut find_root_operator, + &mut Self::equivalent_edge_validator, + true, ); self.dfs( - local, + root, Direction::Downside, - &mut node_operator, - &mut edge_validator, + &mut find_equivalent_operator, + &mut Self::equivalent_edge_validator, + true, ); set } @@ -309,23 +313,29 @@ impl Graph { pub fn is_connected(&self, idx_1: Local, idx_2: Local) -> bool { let target = idx_2; let find = Cell::new(false); - let mut node_operator = |idx: Local| -> bool { + let mut node_operator = |idx: Local| -> DFSStatus { find.set(idx == target); - !find.get() // if not found, move on + if find.get() { + DFSStatus::Stop + } else { + // if not found, move on + DFSStatus::Continue + } }; - let mut edge_validator = |_: &EdgeOp| -> bool { true }; self.dfs( idx_1, Direction::Downside, &mut node_operator, - &mut edge_validator, + &mut Self::always_true_edge_validator, + false, ); if !find.get() { self.dfs( idx_1, Direction::Upside, &mut node_operator, - &mut edge_validator, + &mut Self::always_true_edge_validator, + false, ); } find.get() @@ -343,47 +353,114 @@ impl Graph { deps } + // This function uses precedence traversal. + // The node operator and edge validator decide how far the traversal can reach. + // `traverse_all` decides if a branch finds the target successfully, whether the traversal will continue or not. + // For example, if you need to instantly stop the traversal once finding a certain node, then set `traverse_all` to false. + // If you want to traverse all the reachable nodes which are decided by the operator and validator, then set `traverse_all` to true. pub fn dfs( &self, now: Local, direction: Direction, node_operator: &mut F, edge_validator: &mut G, - ) where - F: FnMut(Local) -> bool, - G: FnMut(&EdgeOp) -> bool, + traverse_all: bool, + ) -> DFSStatus + where + F: FnMut(Local) -> DFSStatus, + G: FnMut(&EdgeOp) -> DFSStatus, { - if node_operator(now) { - match direction { - Direction::Upside => { - for edge_idx in self.nodes[now].in_edges.iter() { - let edge = &self.edges[*edge_idx]; - if let GraphEdge::NodeEdge { src, op, .. } = edge { - if edge_validator(op) { - self.dfs(*src, direction, node_operator, edge_validator); + macro_rules! traverse { + ($edges: ident, $field: ident) => { + for edge_idx in self.nodes[now].$edges.iter() { + let edge = &self.edges[*edge_idx]; + if let GraphEdge::NodeEdge { $field, op, .. } = edge { + if matches!(edge_validator(op), DFSStatus::Continue) { + let result = self.dfs( + *$field, + direction, + node_operator, + edge_validator, + traverse_all, + ); + if matches!(result, DFSStatus::Stop) && !traverse_all { + return DFSStatus::Stop; } } } } + }; + } + + if matches!(node_operator(now), DFSStatus::Continue) { + match direction { + Direction::Upside => { + traverse!(in_edges, src); + } Direction::Downside => { - for edge_idx in self.nodes[now].out_edges.iter() { - let edge = &self.edges[*edge_idx]; - if let GraphEdge::NodeEdge { op, dst, .. } = edge { - if edge_validator(op) { - self.dfs(*dst, direction, node_operator, edge_validator); - } - } - } + traverse!(out_edges, dst); + } + Direction::Both => { + traverse!(in_edges, src); + traverse!(out_edges, dst); } }; + DFSStatus::Continue + } else { + DFSStatus::Stop + } + } + + pub fn get_upside_idx(&self, node_idx: Local, order: usize) -> Option { + if let Some(edge_idx) = self.nodes[node_idx].in_edges.get(order) { + match self.edges[*edge_idx] { + GraphEdge::NodeEdge { src, .. } => Some(src), + GraphEdge::ConstEdge { .. } => None, + } + } else { + None + } + } + + pub fn get_downside_idx(&self, node_idx: Local, order: usize) -> Option { + if let Some(edge_idx) = self.nodes[node_idx].out_edges.get(order) { + match self.edges[*edge_idx] { + GraphEdge::NodeEdge { dst, .. } => Some(dst), + GraphEdge::ConstEdge { .. } => None, + } + } else { + None } } } +impl Graph { + pub fn equivalent_edge_validator(op: &EdgeOp) -> DFSStatus { + match op { + EdgeOp::Copy | EdgeOp::Move | EdgeOp::Mut | EdgeOp::Immut => DFSStatus::Continue, + EdgeOp::Nop + | EdgeOp::Const + | EdgeOp::Deref + | EdgeOp::Downcast(_) + | EdgeOp::Field(_) => DFSStatus::Stop, + } + } + + pub fn always_true_edge_validator(_: &EdgeOp) -> DFSStatus { + DFSStatus::Continue + } +} + #[derive(Clone, Copy)] pub enum Direction { Upside, Downside, + Both, +} + +pub enum DFSStatus { + Continue, + Stop, } #[derive(Clone, Copy, Debug)] diff --git a/rap/src/analysis/opt.rs b/rap/src/analysis/opt.rs new file mode 100644 index 0000000..fdb4301 --- /dev/null +++ b/rap/src/analysis/opt.rs @@ -0,0 +1,24 @@ +pub mod checking; + +use rustc_middle::ty::TyCtxt; + +use super::core::dataflow::DataFlow; +use checking::bounds_checking::check; + +pub struct Opt<'tcx> { + pub tcx: TyCtxt<'tcx>, +} + +impl<'tcx> Opt<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>) -> Self { + Self { tcx } + } + + pub fn start(&mut self) { + let mut dataflow = DataFlow::new(self.tcx, false); + dataflow.build_graphs(); + for (_, graph) in dataflow.graphs.iter() { + check(graph, &self.tcx); + } + } +} diff --git a/rap/src/analysis/opt/checking.rs b/rap/src/analysis/opt/checking.rs new file mode 100644 index 0000000..5db75d9 --- /dev/null +++ b/rap/src/analysis/opt/checking.rs @@ -0,0 +1 @@ +pub mod bounds_checking; diff --git a/rap/src/analysis/opt/checking/bounds_checking.rs b/rap/src/analysis/opt/checking/bounds_checking.rs new file mode 100644 index 0000000..35bec4b --- /dev/null +++ b/rap/src/analysis/opt/checking/bounds_checking.rs @@ -0,0 +1,137 @@ +use once_cell::sync::OnceCell; + +use rustc_middle::mir::Local; +use rustc_middle::ty::TyCtxt; + +use crate::analysis::core::dataflow::graph::{ + AggKind, DFSStatus, Direction, Graph, GraphEdge, GraphNode, NodeOp, +}; +use crate::analysis::utils::def_path::DefPath; +use crate::rap_warn; +use crate::utils::log::underline_span_in_the_line; + +static DEFPATHS: OnceCell = OnceCell::new(); + +struct DefPaths { + ops_range: DefPath, + vec_len: DefPath, + ops_index: DefPath, + ops_index_mut: DefPath, +} + +impl DefPaths { + pub fn new(tcx: &TyCtxt<'_>) -> Self { + Self { + ops_range: DefPath::new("std::ops::Range", tcx), + vec_len: DefPath::new("std::vec::Vec::len", tcx), + ops_index: DefPath::new("std::ops::Index::index", tcx), + ops_index_mut: DefPath::new("std::ops::IndexMut::index_mut", tcx), + } + } +} + +pub fn check(graph: &Graph, tcx: &TyCtxt) { + fn extract_upperbound_node_if_ops_range(graph: &Graph, node: &GraphNode) -> Option { + let def_paths = &DEFPATHS.get().unwrap(); + let target_def_id = def_paths.ops_range.last_def_id(); + if let NodeOp::Aggregate(AggKind::Adt(def_id)) = node.op { + if def_id == target_def_id { + let upperbound_edge = &graph.edges[node.in_edges[1]]; // the second field + if let GraphEdge::NodeEdge { src, .. } = upperbound_edge { + return Some(*src); + } else { + panic!("The upperbound edge of Agg node is not a NodeEdge"); + } + } + } + None + } + + fn find_upside_vec_len_node(graph: &Graph, node_idx: Local) -> Option { + let mut vec_len_node_idx = None; + let def_paths = &DEFPATHS.get().unwrap(); + let target_def_id = def_paths.vec_len.last_def_id(); + // Warning: may traverse all upside nodes and the new result will overwrite on the previous result + let mut node_operator = |idx: Local| -> DFSStatus { + let node = &graph.nodes[idx]; + if let NodeOp::Call(def_id) = node.op { + if def_id == target_def_id { + vec_len_node_idx = Some(idx); + return DFSStatus::Stop; + } + } + DFSStatus::Continue + }; + graph.dfs( + node_idx, + Direction::Upside, + &mut node_operator, + &mut Graph::equivalent_edge_validator, + false, + ); + vec_len_node_idx + } + + fn find_downside_index_node(graph: &Graph, node_idx: Local) -> Vec { + let mut index_node_idxs: Vec = Vec::new(); + let def_paths = &DEFPATHS.get().unwrap(); + // Warning: traverse all downside nodes + let mut node_operator = |idx: Local| -> DFSStatus { + let node = &graph.nodes[idx]; + if let NodeOp::Call(def_id) = node.op { + if def_id == def_paths.ops_index.last_def_id() + || def_id == def_paths.ops_index_mut.last_def_id() + { + index_node_idxs.push(idx); + } + } + DFSStatus::Continue + }; + graph.dfs( + node_idx, + Direction::Downside, + &mut node_operator, + &mut Graph::always_true_edge_validator, + true, + ); + index_node_idxs + } + + let _ = &DEFPATHS.get_or_init(|| DefPaths::new(tcx)); + for (node_idx, node) in graph.nodes.iter_enumerated() { + if let Some(upperbound_node_idx) = extract_upperbound_node_if_ops_range(graph, node) { + if let Some(vec_len_node_idx) = find_upside_vec_len_node(graph, upperbound_node_idx) { + let maybe_vec_node_idx = graph.get_upside_idx(vec_len_node_idx, 0).unwrap(); + let maybe_vec_node_idxs = graph.collect_equivalent_locals(maybe_vec_node_idx); + let mut index_record = Vec::new(); + for index_node_idx in find_downside_index_node(graph, node_idx).into_iter() { + let maybe_vec_node_idx = graph.get_upside_idx(index_node_idx, 0).unwrap(); + if maybe_vec_node_idxs.contains(&maybe_vec_node_idx) { + index_record.push(index_node_idx); + } + } + report_bug(graph, upperbound_node_idx, &index_record); + } + } + } +} + +fn report_bug(graph: &Graph, upperbound_node_idx: Local, index_record: &Vec) { + rap_warn!( + "Unnecessary bounds checkings detected in function {:?}. +Index is upperbounded at: +{} +Checked at: +{} +", + graph.def_id, + underline_span_in_the_line(graph.nodes[upperbound_node_idx].span), + index_record + .iter() + .map(|node_idx| { + underline_span_in_the_line(graph.nodes[*node_idx].span) // buggy, what about multiple index in the same line? + }) + .collect::>() + .join("\n") + ); +} diff --git a/rap/src/analysis/rcanary/ranalyzer/intra_visitor.rs b/rap/src/analysis/rcanary/ranalyzer/intra_visitor.rs index 3d55179..a4d106e 100644 --- a/rap/src/analysis/rcanary/ranalyzer/intra_visitor.rs +++ b/rap/src/analysis/rcanary/ranalyzer/intra_visitor.rs @@ -433,30 +433,6 @@ impl<'tcx, 'ctx, 'a> IntraFlowAnalysis<'tcx, 'ctx, 'a> { } } } - Rvalue::AddressOf(.., rplace) => { - let kind = AsgnKind::Reference; - let rvalue_has_projection = has_projection(rplace); - match (lvalue_has_projection, rvalue_has_projection) { - (true, true) => { - self.handle_copy_field_to_field( - ctx, goal, solver, kind, lplace, rplace, disc, bidx, sidx, - ); - } - (true, false) => { - self.handle_copy_to_field( - ctx, goal, solver, kind, lplace, rplace, disc, bidx, sidx, - ); - } - (false, true) => { - self.handle_copy_from_field( - ctx, goal, solver, kind, lplace, rplace, bidx, sidx, - ); - } - (false, false) => { - self.handle_copy(ctx, goal, solver, kind, lplace, rplace, bidx, sidx); - } - } - } Rvalue::Cast(_cast_kind, op, ..) => { let kind = AsgnKind::Cast; match op { diff --git a/rap/src/analysis/rcanary/ranalyzer/order.rs b/rap/src/analysis/rcanary/ranalyzer/order.rs index e5f9580..a9672dd 100644 --- a/rap/src/analysis/rcanary/ranalyzer/order.rs +++ b/rap/src/analysis/rcanary/ranalyzer/order.rs @@ -71,6 +71,7 @@ impl<'tcx> NodeOrder<'tcx> { None => (), } } + TerminatorKind::TailCall { .. } => todo!(), } // Update the lev for generating topo order. for index in result.iter() { diff --git a/rap/src/analysis/safedrop/graph.rs b/rap/src/analysis/safedrop/graph.rs index 3b7e9ef..a572992 100644 --- a/rap/src/analysis/safedrop/graph.rs +++ b/rap/src/analysis/safedrop/graph.rs @@ -246,7 +246,7 @@ impl<'tcx> SafeDropGraph<'tcx> { } } } - Rvalue::Ref(_, _, ref p) | Rvalue::AddressOf(_, ref p) => { + Rvalue::Ref(_, _, ref p) => { let rv_local = p.local.as_usize(); if values[lv_local].may_drop && values[rv_local].may_drop { let rv = p.clone(); @@ -391,6 +391,7 @@ impl<'tcx> SafeDropGraph<'tcx> { } cur_bb.calls.push(terminator.clone()); } + TerminatorKind::TailCall { .. } => todo!(), TerminatorKind::Assert { cond: _, expected: _, @@ -436,6 +437,7 @@ impl<'tcx> SafeDropGraph<'tcx> { line_spans: _, ref unwind, targets, + asm_macro: _, } => { for target in targets { cur_bb.add_next(target.as_usize()); diff --git a/rap/src/analysis/senryx/visitor.rs b/rap/src/analysis/senryx/visitor.rs index 54a293f..6a66887 100644 --- a/rap/src/analysis/senryx/visitor.rs +++ b/rap/src/analysis/senryx/visitor.rs @@ -178,7 +178,7 @@ impl<'tcx> BodyVisitor<'tcx> { } _ => {} }, - Rvalue::Ref(_, _, rplace) | Rvalue::AddressOf(_, rplace) => { + Rvalue::Ref(_, _, rplace) => { let rpjc_local = self .safedrop_graph .projection(self.tcx, true, rplace.clone()); diff --git a/rap/src/analysis/utils/def_path.rs b/rap/src/analysis/utils/def_path.rs index 3f70027..986b97a 100644 --- a/rap/src/analysis/utils/def_path.rs +++ b/rap/src/analysis/utils/def_path.rs @@ -1,4 +1,3 @@ -use rustc_errors; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::PrimTy; @@ -45,19 +44,11 @@ pub fn def_path_def_ids(tcx: &TyCtxt<'_>, path: &[&str]) -> impl Iterator, path: &[&str]) -> Vec { - fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator + '_ { - tcx.crates(()) - .iter() - .copied() - .filter(move |&num| tcx.crate_name(num) == name) - .map(CrateNum::as_def_id) - } - - let (base, mut path) = match *path { + let (base, path) = match path { [primitive] => { return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)]; } - [base, ref path @ ..] => (base, path), + [base, path @ ..] => (base, path), _ => return Vec::new(), }; @@ -69,18 +60,21 @@ pub fn def_path_res(tcx: &TyCtxt<'_>, path: &[&str]) -> Vec { None }; - let starts = find_primitive_impls(tcx, base) - .chain(find_crates(*tcx, base_sym)) + let crates = find_primitive_impls(tcx, base) .chain(local_crate) - .map(|id| Res::Def(tcx.def_kind(id), id)); + .map(|id| Res::Def(tcx.def_kind(id), id)) + .chain(find_crates(tcx, base_sym)) + .collect(); - let mut resolutions: Vec = starts.collect(); + def_path_res_with_base(tcx, crates, path) +} +pub fn def_path_res_with_base(tcx: &TyCtxt<'_>, mut base: Vec, mut path: &[&str]) -> Vec { while let [segment, rest @ ..] = path { path = rest; let segment = Symbol::intern(segment); - resolutions = resolutions + base = base .into_iter() .filter_map(|res| res.opt_def_id()) .flat_map(|def_id| { @@ -88,8 +82,7 @@ pub fn def_path_res(tcx: &TyCtxt<'_>, path: &[&str]) -> Vec { // `impl S { ... }` let inherent_impl_children = tcx .inherent_impls(def_id) - .into_iter() - .flatten() + .iter() .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment)); let direct_children = item_children_by_name(tcx, def_id, segment); @@ -99,7 +92,7 @@ pub fn def_path_res(tcx: &TyCtxt<'_>, path: &[&str]) -> Vec { .collect(); } - resolutions + base } fn find_primitive_impls<'tcx>( @@ -132,14 +125,11 @@ fn find_primitive_impls<'tcx>( "f32" => SimplifiedType::Float(FloatTy::F32), "f64" => SimplifiedType::Float(FloatTy::F64), _ => { - return Result::<_, rustc_errors::ErrorGuaranteed>::Ok(&[] as &[_]) - .into_iter() - .flatten() - .copied(); + return [].iter().copied(); } }; - tcx.incoherent_impls(ty).into_iter().flatten().copied() + tcx.incoherent_impls(ty).iter().copied() } fn non_local_item_children_by_name(tcx: &TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec { @@ -209,3 +199,13 @@ fn item_children_by_name(tcx: &TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec, name: Symbol) -> Vec { + tcx.crates(()) + .iter() + .copied() + .filter(move |&num| tcx.crate_name(num) == name) + .map(CrateNum::as_def_id) + .map(|id| Res::Def(tcx.def_kind(id), id)) + .collect() +} diff --git a/rap/src/analysis/utils/show_mir.rs b/rap/src/analysis/utils/show_mir.rs index d5d82d4..9c0442a 100644 --- a/rap/src/analysis/utils/show_mir.rs +++ b/rap/src/analysis/utils/show_mir.rs @@ -51,6 +51,7 @@ impl<'tcx> Display for TerminatorKind<'tcx> { }, _ => (), }, + TerminatorKind::TailCall { .. } => todo!(), }; s } @@ -98,7 +99,6 @@ impl<'tcx> Display for Rvalue<'tcx> { Rvalue::Repeat(..) => s += "Repeat", Rvalue::Ref(..) => s += "Ref", Rvalue::ThreadLocalRef(..) => s += "ThreadLocalRef", - Rvalue::AddressOf(..) => s += "AddressOf", Rvalue::Len(..) => s += "Len", Rvalue::Cast(..) => s += "Cast", Rvalue::BinaryOp(..) => s += "BinaryOp", @@ -108,6 +108,7 @@ impl<'tcx> Display for Rvalue<'tcx> { Rvalue::Aggregate(..) => s += "Aggregate", Rvalue::ShallowInitBox(..) => s += "ShallowInitBox", Rvalue::CopyForDeref(..) => s += "CopyForDeref", + Rvalue::RawPtr(_, _) => s += "RawPtr", } s } diff --git a/rap/src/bin/cargo-rap/help.rs b/rap/src/bin/cargo-rap/help.rs index 5ecb423..ef8a092 100644 --- a/rap/src/bin/cargo-rap/help.rs +++ b/rap/src/bin/cargo-rap/help.rs @@ -19,6 +19,9 @@ Controlflow tracing Dataflow tracing -dataflow generate dataflow graphs +Automatic optimization + -O or -opt automatically detect code optimization chances + General command: -H or -help: show help information -V or -version: show the version of RAP diff --git a/rap/src/bin/cargo-rap/main.rs b/rap/src/bin/cargo-rap/main.rs index a405ebe..6148092 100644 --- a/rap/src/bin/cargo-rap/main.rs +++ b/rap/src/bin/cargo-rap/main.rs @@ -2,6 +2,7 @@ This is a cargo program to start RAP. The file references the cargo file for Miri: https://github.com/rust-lang/miri/blob/master/cargo-miri/src/main.rs */ +#![feature(rustc_private)] #[macro_use] extern crate rap; diff --git a/rap/src/bin/rap.rs b/rap/src/bin/rap.rs index a12b0e5..934f000 100644 --- a/rap/src/bin/rap.rs +++ b/rap/src/bin/rap.rs @@ -48,6 +48,7 @@ fn main() { "-ucons" => compiler.enable_unsafety_isolation(4), "-senryx" => compiler.enable_senryx(), "-callgraph" => compiler.enable_callgraph(), + "-O" | "-opt" => compiler.enable_opt(), "-mir" => compiler.enable_show_mir(), "-adt" => {} "-z3" => {} diff --git a/rap/src/lib.rs b/rap/src/lib.rs index 4c37a32..c878e95 100644 --- a/rap/src/lib.rs +++ b/rap/src/lib.rs @@ -1,10 +1,8 @@ #![feature(rustc_private)] -#![feature(control_flow_enum)] #![feature(box_patterns)] #[macro_use] pub mod utils; - pub mod analysis; extern crate rustc_data_structures; @@ -22,6 +20,7 @@ extern crate rustc_target; use analysis::core::alias::mop::MopAlias; use analysis::core::call_graph::CallGraph; use analysis::core::dataflow::DataFlow; +use analysis::opt::Opt; use analysis::rcanary::rCanary; use analysis::safedrop::SafeDrop; use analysis::senryx::SenryxCheck; @@ -52,6 +51,7 @@ pub struct RapCallback { callgraph: bool, show_mir: bool, dataflow: usize, + opt: bool, } impl Default for RapCallback { @@ -65,6 +65,7 @@ impl Default for RapCallback { callgraph: false, show_mir: false, dataflow: 0, + opt: false, } } } @@ -164,6 +165,14 @@ impl RapCallback { pub fn is_dataflow_enabled(self) -> usize { self.dataflow } + + pub fn enable_opt(&mut self) { + self.opt = true; + } + + pub fn is_opt_enabled(self) -> bool { + self.opt + } } #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] @@ -242,4 +251,8 @@ pub fn start_analyzer(tcx: TyCtxt, callback: RapCallback) { if callback.is_callgraph_enabled() { CallGraph::new(tcx).start(); } + + if callback.is_opt_enabled() { + Opt::new(tcx).start(); + } } diff --git a/rap/src/utils/log.rs b/rap/src/utils/log.rs index 84c419c..1aedc63 100644 --- a/rap/src/utils/log.rs +++ b/rap/src/utils/log.rs @@ -2,6 +2,8 @@ use chrono::Local; use fern::colors::{Color, ColoredLevelConfig}; use fern::{self, Dispatch}; use log::LevelFilter; +use rustc_span::source_map::get_source_map; +use rustc_span::{Pos, Span}; fn log_level() -> LevelFilter { if let Ok(s) = std::env::var("RAP_LOG") { @@ -83,3 +85,26 @@ pub fn rap_error_and_exit(msg: impl AsRef) -> ! { rap_error!("{}", msg.as_ref()); std::process::exit(1) } + +pub fn underline_span_in_the_line(span: Span) -> String { + fn compose_underline(line_span: Span, span: Span) -> String { + let line_len = (line_span.hi() - line_span.lo()).to_u32(); + let line_start_pos = line_span.lo(); + let lo = (span.lo() - line_start_pos).to_u32(); + let hi = (span.hi() - line_start_pos).to_u32(); + (0..line_len) + .map(|i| if i >= lo && i < hi { '^' } else { ' ' }) + .collect() + } + + let source_map = get_source_map().unwrap(); + let line_span = source_map.span_extend_to_line(span); + let line = source_map.span_to_snippet(line_span).unwrap(); + let underline = compose_underline(line_span, span); + format!( + "{}\n{}\n{}", + source_map.span_to_diagnostic_string(span), + line, + underline + ) +} diff --git a/tests/bounds_len/Cargo.toml b/tests/bounds_len/Cargo.toml new file mode 100644 index 0000000..e8c4e17 --- /dev/null +++ b/tests/bounds_len/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "bounds_len" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/tests/bounds_len/src/lib.rs b/tests/bounds_len/src/lib.rs new file mode 100644 index 0000000..c1384e0 --- /dev/null +++ b/tests/bounds_len/src/lib.rs @@ -0,0 +1,5 @@ +fn foo(mut a: Vec) { + for i in 0..a.len() { + a[i] = a[i] + 1; + } +} \ No newline at end of file