Skip to content

Commit 5e426e1

Browse files
committed
optionally only emit basic validation for functions containing unsafe block / unsafe function
1 parent 6135461 commit 5e426e1

File tree

7 files changed

+155
-58
lines changed

7 files changed

+155
-58
lines changed

src/librustc/hir/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ use rustc_data_structures::indexed_vec;
4949
use std::collections::BTreeMap;
5050
use std::fmt;
5151

52-
/// HIR doesn't commit to a concrete storage type and have its own alias for a vector.
52+
/// HIR doesn't commit to a concrete storage type and has its own alias for a vector.
5353
/// It can be `Vec`, `P<[T]>` or potentially `Box<[T]>`, or some other container with similar
5454
/// behavior. Unlike AST, HIR is mostly a static structure, so we can use an owned slice instead
5555
/// of `Vec` to avoid keeping extra capacity.
@@ -76,14 +76,14 @@ pub mod pat_util;
7676
pub mod print;
7777
pub mod svh;
7878

79-
/// A HirId uniquely identifies a node in the HIR of then current crate. It is
79+
/// A HirId uniquely identifies a node in the HIR of the current crate. It is
8080
/// composed of the `owner`, which is the DefIndex of the directly enclosing
8181
/// hir::Item, hir::TraitItem, or hir::ImplItem (i.e. the closest "item-like"),
8282
/// and the `local_id` which is unique within the given owner.
8383
///
8484
/// This two-level structure makes for more stable values: One can move an item
8585
/// around within the source code, or add or remove stuff before it, without
86-
/// the local_id part of the HirId changing, which is a very useful property
86+
/// the local_id part of the HirId changing, which is a very useful property in
8787
/// incremental compilation where we have to persist things through changes to
8888
/// the code base.
8989
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug,

src/librustc/session/config.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1025,8 +1025,9 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
10251025
"the directory the MIR is dumped into"),
10261026
dump_mir_exclude_pass_number: bool = (false, parse_bool, [UNTRACKED],
10271027
"if set, exclude the pass number when dumping MIR (used in tests)"),
1028-
mir_emit_validate: bool = (false, parse_bool, [TRACKED],
1029-
"emit Validate MIR statements, interpreted e.g. by miri"),
1028+
mir_emit_validate: usize = (0, parse_uint, [TRACKED],
1029+
"emit Validate MIR statements, interpreted e.g. by miri (0: do not emit; 1: if function \
1030+
contains unsafe block, only validate arguments; 2: always emit full validation)"),
10301031
perf_stats: bool = (false, parse_bool, [UNTRACKED],
10311032
"print some performance-related statistics"),
10321033
hir_stats: bool = (false, parse_bool, [UNTRACKED],

src/librustc_mir/transform/add_validation.rs

+132-42
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
//! of MIR building, and only after this pass we think of the program has having the
1515
//! normal MIR semantics.
1616
17+
use syntax_pos::Span;
18+
use syntax::ast::NodeId;
1719
use rustc::ty::{self, TyCtxt, RegionKind};
1820
use rustc::hir;
1921
use rustc::mir::*;
@@ -80,15 +82,78 @@ fn lval_context<'a, 'tcx, D>(
8082
}
8183
}
8284

85+
/// Check if this function contains an unsafe block or is an unsafe function.
86+
fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) -> bool {
87+
use rustc::hir::intravisit::{self, Visitor};
88+
89+
let fn_node_id = match src {
90+
MirSource::Fn(node_id) => node_id,
91+
_ => return false, // only functions can have unsafe
92+
};
93+
let fn_item = tcx.hir.expect_item(fn_node_id);
94+
95+
struct FindUnsafe<'b, 'tcx> where 'tcx : 'b {
96+
map: &'b hir::map::Map<'tcx>,
97+
found_unsafe: bool,
98+
}
99+
let mut finder = FindUnsafe { map: &tcx.hir, found_unsafe: false };
100+
finder.visit_item(fn_item);
101+
102+
impl<'b, 'tcx> Visitor<'tcx> for FindUnsafe<'b, 'tcx> {
103+
fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
104+
intravisit::NestedVisitorMap::OnlyBodies(self.map)
105+
}
106+
107+
fn visit_fn(&mut self, fk: intravisit::FnKind<'tcx>, fd: &'tcx hir::FnDecl,
108+
b: hir::BodyId, s: Span, id: NodeId)
109+
{
110+
assert!(!self.found_unsafe, "We should never see more than one fn");
111+
let is_unsafe = match fk {
112+
intravisit::FnKind::ItemFn(_, _, unsafety, ..) => unsafety == hir::Unsafety::Unsafe,
113+
intravisit::FnKind::Method(_, sig, ..) => sig.unsafety == hir::Unsafety::Unsafe,
114+
intravisit::FnKind::Closure(_) => false,
115+
};
116+
if is_unsafe {
117+
// This is unsafe, and we are done.
118+
self.found_unsafe = true;
119+
} else {
120+
// Go on searching.
121+
intravisit::walk_fn(self, fk, fd, b, s, id)
122+
}
123+
}
124+
125+
fn visit_block(&mut self, b: &'tcx hir::Block) {
126+
use rustc::hir::BlockCheckMode::*;
127+
128+
if self.found_unsafe { return; } // short-circuit
129+
130+
match b.rules {
131+
UnsafeBlock(_) | PushUnsafeBlock(_) => {
132+
// We found an unsafe block.
133+
self.found_unsafe = true;
134+
}
135+
DefaultBlock | PopUnsafeBlock(_) => {
136+
// No unsafe block here, go on searching.
137+
intravisit::walk_block(self, b);
138+
}
139+
};
140+
}
141+
}
142+
143+
finder.found_unsafe
144+
}
145+
83146
impl MirPass for AddValidation {
84147
fn run_pass<'a, 'tcx>(&self,
85148
tcx: TyCtxt<'a, 'tcx, 'tcx>,
86-
_: MirSource,
87-
mir: &mut Mir<'tcx>) {
88-
if !tcx.sess.opts.debugging_opts.mir_emit_validate {
149+
src: MirSource,
150+
mir: &mut Mir<'tcx>)
151+
{
152+
let emit_validate = tcx.sess.opts.debugging_opts.mir_emit_validate;
153+
if emit_validate == 0 {
89154
return;
90155
}
91-
156+
let restricted_validation = emit_validate == 1 && fn_contains_unsafe(tcx, src);
92157
let local_decls = mir.local_decls.clone(); // FIXME: Find a way to get rid of this clone.
93158

94159
// Convert an lvalue to a validation operand.
@@ -98,22 +163,40 @@ impl MirPass for AddValidation {
98163
ValidationOperand { lval, ty, re, mutbl }
99164
};
100165

166+
// Emit an Acquire at the beginning of the given block. If we are in restricted emission mode
167+
// (mir_emit_validate=1), also emit a Release immediately after the Acquire.
168+
let emit_acquire = |block: &mut BasicBlockData<'tcx>, source_info, operands: Vec<_>| {
169+
if operands.len() == 0 {
170+
return; // Nothing to do
171+
}
172+
// Emit the release first, to avoid cloning if we do not emit it
173+
if restricted_validation {
174+
let release_stmt = Statement {
175+
source_info,
176+
kind: StatementKind::Validate(ValidationOp::Release, operands.clone()),
177+
};
178+
block.statements.insert(0, release_stmt);
179+
}
180+
// Now, the acquire
181+
let acquire_stmt = Statement {
182+
source_info,
183+
kind: StatementKind::Validate(ValidationOp::Acquire, operands),
184+
};
185+
block.statements.insert(0, acquire_stmt);
186+
};
187+
101188
// PART 1
102189
// Add an AcquireValid at the beginning of the start block.
103-
if mir.arg_count > 0 {
104-
let acquire_stmt = Statement {
105-
source_info: SourceInfo {
106-
scope: ARGUMENT_VISIBILITY_SCOPE,
107-
span: mir.span, // FIXME: Consider using just the span covering the function
108-
// argument declaration.
109-
},
110-
kind: StatementKind::Validate(ValidationOp::Acquire,
111-
// Skip return value, go over all the arguments
112-
mir.local_decls.iter_enumerated().skip(1).take(mir.arg_count)
113-
.map(|(local, _)| lval_to_operand(Lvalue::Local(local))).collect()
114-
)
190+
{
191+
let source_info = SourceInfo {
192+
scope: ARGUMENT_VISIBILITY_SCOPE,
193+
span: mir.span, // FIXME: Consider using just the span covering the function
194+
// argument declaration.
115195
};
116-
mir.basic_blocks_mut()[START_BLOCK].statements.insert(0, acquire_stmt);
196+
// Gather all arguments, skip return value.
197+
let operands = mir.local_decls.iter_enumerated().skip(1).take(mir.arg_count)
198+
.map(|(local, _)| lval_to_operand(Lvalue::Local(local))).collect();
199+
emit_acquire(&mut mir.basic_blocks_mut()[START_BLOCK], source_info, operands);
117200
}
118201

119202
// PART 2
@@ -125,18 +208,20 @@ impl MirPass for AddValidation {
125208
Some(Terminator { kind: TerminatorKind::Call { ref args, ref destination, .. },
126209
source_info }) => {
127210
// Before the call: Release all arguments
128-
let release_stmt = Statement {
129-
source_info,
130-
kind: StatementKind::Validate(ValidationOp::Release,
131-
args.iter().filter_map(|op| {
132-
match op {
133-
&Operand::Consume(ref lval) =>
134-
Some(lval_to_operand(lval.clone())),
135-
&Operand::Constant(..) => { None },
136-
}
137-
}).collect())
138-
};
139-
block_data.statements.push(release_stmt);
211+
if !restricted_validation {
212+
let release_stmt = Statement {
213+
source_info,
214+
kind: StatementKind::Validate(ValidationOp::Release,
215+
args.iter().filter_map(|op| {
216+
match op {
217+
&Operand::Consume(ref lval) =>
218+
Some(lval_to_operand(lval.clone())),
219+
&Operand::Constant(..) => { None },
220+
}
221+
}).collect())
222+
};
223+
block_data.statements.push(release_stmt);
224+
}
140225
// Remember the return destination for later
141226
if let &Some(ref destination) = destination {
142227
returns.push((source_info, destination.0.clone(), destination.1));
@@ -147,12 +232,14 @@ impl MirPass for AddValidation {
147232
Some(Terminator { kind: TerminatorKind::DropAndReplace { location: ref lval, .. },
148233
source_info }) => {
149234
// Before the call: Release all arguments
150-
let release_stmt = Statement {
151-
source_info,
152-
kind: StatementKind::Validate(ValidationOp::Release,
153-
vec![lval_to_operand(lval.clone())]),
154-
};
155-
block_data.statements.push(release_stmt);
235+
if !restricted_validation {
236+
let release_stmt = Statement {
237+
source_info,
238+
kind: StatementKind::Validate(ValidationOp::Release,
239+
vec![lval_to_operand(lval.clone())]),
240+
};
241+
block_data.statements.push(release_stmt);
242+
}
156243
// drop doesn't return anything, so we need no acquire.
157244
}
158245
_ => {
@@ -162,18 +249,21 @@ impl MirPass for AddValidation {
162249
}
163250
// Now we go over the returns we collected to acquire the return values.
164251
for (source_info, dest_lval, dest_block) in returns {
165-
let acquire_stmt = Statement {
252+
emit_acquire(
253+
&mut mir.basic_blocks_mut()[dest_block],
166254
source_info,
167-
kind: StatementKind::Validate(ValidationOp::Acquire,
168-
vec![lval_to_operand(dest_lval)]),
169-
};
170-
mir.basic_blocks_mut()[dest_block].statements.insert(0, acquire_stmt);
255+
vec![lval_to_operand(dest_lval)]
256+
);
257+
}
258+
259+
if restricted_validation {
260+
// No part 3 for us.
261+
return;
171262
}
172263

173264
// PART 3
174265
// Add ReleaseValid/AcquireValid around Ref and Cast. Again an iterator does not seem very
175-
// suited
176-
// as we need to add new statements before and after each Ref.
266+
// suited as we need to add new statements before and after each Ref.
177267
for block_data in mir.basic_blocks_mut() {
178268
// We want to insert statements around Ref commands as we iterate. To this end, we
179269
// iterate backwards using indices.

src/librustc_mir/transform/erase_regions.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> {
7777
block: BasicBlock,
7878
statement: &mut Statement<'tcx>,
7979
location: Location) {
80-
if !self.tcx.sess.opts.debugging_opts.mir_emit_validate {
80+
// Do NOT delete EndRegion if validation statements are emitted.
81+
// Validation needs EndRegion.
82+
if self.tcx.sess.opts.debugging_opts.mir_emit_validate == 0 {
8183
if let StatementKind::EndRegion(_) = statement.kind {
8284
statement.kind = StatementKind::Nop;
8385
}

src/test/mir-opt/validate_1.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
// ignore-tidy-linelength
12-
// compile-flags: -Z verbose -Z mir-emit-validate
12+
// compile-flags: -Z verbose -Z mir-emit-validate=1
1313

1414
fn foo(_x: &mut i32) {}
1515

src/test/mir-opt/validate_2.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
// ignore-tidy-linelength
12-
// compile-flags: -Z verbose -Z mir-emit-validate
12+
// compile-flags: -Z verbose -Z mir-emit-validate=1
1313

1414
fn main() {
1515
let _x : Box<[i32]> = Box::new([1, 2, 3]);

src/test/mir-opt/validate_3.rs

+12-8
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
// ignore-tidy-linelength
12-
// compile-flags: -Z verbose -Z mir-emit-validate
12+
// compile-flags: -Z verbose -Z mir-emit-validate=1
1313

1414
struct Test {
1515
x: i32
@@ -18,6 +18,10 @@ struct Test {
1818
fn foo(_x: &i32) {}
1919

2020
fn main() {
21+
// These internal unsafe functions should have no effect on the code generation.
22+
unsafe fn _unused1() {}
23+
fn _unused2(x: *const i32) -> i32 { unsafe { *x }}
24+
2125
let t = Test { x: 0 };
2226
let t = &t;
2327
foo(&t.x);
@@ -28,18 +32,18 @@ fn main() {
2832
// fn main() -> () {
2933
// let mut _5: &ReErased i32;
3034
// bb0: {
31-
// Validate(Suspend(ReScope(Misc(NodeId(31)))), [((*_2).0: i32)@i32/ReScope(Remainder(BlockRemainder { block: NodeId(18), first_statement_index: 1 })) (imm)]);
35+
// Validate(Suspend(ReScope(Misc(NodeId(46)))), [((*_2).0: i32)@i32/ReScope(Remainder(BlockRemainder { block: NodeId(18), first_statement_index: 3 })) (imm)]);
3236
// _5 = &ReErased ((*_2).0: i32);
33-
// Validate(Acquire, [(*_5)@i32/ReScope(Misc(NodeId(31))) (imm)]);
34-
// Validate(Suspend(ReScope(Misc(NodeId(31)))), [(*_5)@i32/ReScope(Misc(NodeId(31))) (imm)]);
37+
// Validate(Acquire, [(*_5)@i32/ReScope(Misc(NodeId(46))) (imm)]);
38+
// Validate(Suspend(ReScope(Misc(NodeId(46)))), [(*_5)@i32/ReScope(Misc(NodeId(46))) (imm)]);
3539
// _4 = &ReErased (*_5);
36-
// Validate(Acquire, [(*_4)@i32/ReScope(Misc(NodeId(31))) (imm)]);
37-
// Validate(Release, [_4@&ReScope(Misc(NodeId(31))) i32]);
40+
// Validate(Acquire, [(*_4)@i32/ReScope(Misc(NodeId(46))) (imm)]);
41+
// Validate(Release, [_4@&ReScope(Misc(NodeId(46))) i32]);
3842
// _3 = const foo(_4) -> bb1;
3943
// }
4044
// bb1: {
41-
// EndRegion(ReScope(Misc(NodeId(31))));
42-
// EndRegion(ReScope(Remainder(BlockRemainder { block: NodeId(18), first_statement_index: 1 })));
45+
// EndRegion(ReScope(Misc(NodeId(46))));
46+
// EndRegion(ReScope(Remainder(BlockRemainder { block: NodeId(18), first_statement_index: 3 })));
4347
// return;
4448
// }
4549
// }

0 commit comments

Comments
 (0)