Skip to content

Commit 06eb5a6

Browse files
arielb1Ariel Ben-Yehuda
authored and
Ariel Ben-Yehuda
committed
fix codegen of drops of fields of packed structs
1 parent 3801c05 commit 06eb5a6

File tree

8 files changed

+341
-26
lines changed

8 files changed

+341
-26
lines changed

src/librustc_mir/shim.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ use syntax_pos::Span;
2727
use std::fmt;
2828
use std::iter;
2929

30-
use transform::{add_call_guards, no_landing_pads, simplify};
30+
use transform::{add_moves_for_packed_drops, add_call_guards};
31+
use transform::{no_landing_pads, simplify};
3132
use util::elaborate_drops::{self, DropElaborator, DropStyle, DropFlagMode};
3233
use util::patch::MirPatch;
3334

@@ -114,6 +115,8 @@ fn make_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
114115
}
115116
};
116117
debug!("make_shim({:?}) = untransformed {:?}", instance, result);
118+
add_moves_for_packed_drops::add_moves_for_packed_drops(
119+
tcx, &mut result, instance.def_id());
117120
no_landing_pads::no_landing_pads(tcx, &mut result);
118121
simplify::simplify_cfg(&mut result);
119122
add_call_guards::CriticalCallEdges.add_call_guards(&mut result);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use rustc::hir::def_id::DefId;
12+
use rustc::mir::*;
13+
use rustc::ty::TyCtxt;
14+
15+
use transform::{MirPass, MirSource};
16+
use util::patch::MirPatch;
17+
use util;
18+
19+
// This pass moves values being dropped that are within a packed
20+
// struct to a separate local before dropping them, to ensure that
21+
// they are dropped from an aligned address.
22+
//
23+
// For example, if we have something like
24+
// ```Rust
25+
// #[repr(packed)]
26+
// struct Foo {
27+
// dealign: u8,
28+
// data: Vec<u8>
29+
// }
30+
//
31+
// let foo = ...;
32+
// ```
33+
//
34+
// We want to call `drop_in_place::<Vec<u8>>` on `data` from an aligned
35+
// address. This means we can't simply drop `foo.data` directly, because
36+
// its address is not aligned.
37+
//
38+
// Instead, we move `foo.data` to a local and drop that:
39+
// ```
40+
// storage.live(drop_temp)
41+
// drop_temp = foo.data;
42+
// drop(drop_temp) -> next
43+
// next:
44+
// storage.dead(drop_temp)
45+
// ```
46+
//
47+
// The storage instructions are required to avoid stack space
48+
// blowup.
49+
50+
pub struct AddMovesForPackedDrops;
51+
52+
impl MirPass for AddMovesForPackedDrops {
53+
fn run_pass<'a, 'tcx>(&self,
54+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
55+
src: MirSource,
56+
mir: &mut Mir<'tcx>)
57+
{
58+
debug!("add_moves_for_packed_drops({:?} @ {:?})", src, mir.span);
59+
add_moves_for_packed_drops(tcx, mir, src.def_id);
60+
}
61+
}
62+
63+
pub fn add_moves_for_packed_drops<'a, 'tcx>(
64+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
65+
mir: &mut Mir<'tcx>,
66+
def_id: DefId)
67+
{
68+
let patch = add_moves_for_packed_drops_patch(tcx, mir, def_id);
69+
patch.apply(mir);
70+
}
71+
72+
fn add_moves_for_packed_drops_patch<'a, 'tcx>(
73+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
74+
mir: &Mir<'tcx>,
75+
def_id: DefId)
76+
-> MirPatch<'tcx>
77+
{
78+
let mut patch = MirPatch::new(mir);
79+
let param_env = tcx.param_env(def_id);
80+
81+
for (bb, data) in mir.basic_blocks().iter_enumerated() {
82+
let loc = Location { block: bb, statement_index: data.statements.len() };
83+
let terminator = data.terminator();
84+
85+
match terminator.kind {
86+
TerminatorKind::Drop { ref location, .. }
87+
if util::is_disaligned(tcx, mir, param_env, location) =>
88+
{
89+
add_move_for_packed_drop(tcx, mir, &mut patch, terminator,
90+
loc, data.is_cleanup);
91+
}
92+
TerminatorKind::DropAndReplace { .. } => {
93+
span_bug!(terminator.source_info.span,
94+
"replace in AddMovesForPackedDrops");
95+
}
96+
_ => {}
97+
}
98+
}
99+
100+
patch
101+
}
102+
103+
fn add_move_for_packed_drop<'a, 'tcx>(
104+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
105+
mir: &Mir<'tcx>,
106+
patch: &mut MirPatch<'tcx>,
107+
terminator: &Terminator<'tcx>,
108+
loc: Location,
109+
is_cleanup: bool)
110+
{
111+
debug!("add_move_for_packed_drop({:?} @ {:?})", terminator, loc);
112+
let (location, target, unwind) = match terminator.kind {
113+
TerminatorKind::Drop { ref location, target, unwind } =>
114+
(location, target, unwind),
115+
_ => unreachable!()
116+
};
117+
118+
let source_info = terminator.source_info;
119+
let ty = location.ty(mir, tcx).to_ty(tcx);
120+
let temp = patch.new_temp(ty, terminator.source_info.span);
121+
122+
let storage_dead_block = patch.new_block(BasicBlockData {
123+
statements: vec![Statement {
124+
source_info, kind: StatementKind::StorageDead(temp)
125+
}],
126+
terminator: Some(Terminator {
127+
source_info, kind: TerminatorKind::Goto { target }
128+
}),
129+
is_cleanup
130+
});
131+
132+
patch.add_statement(
133+
loc, StatementKind::StorageLive(temp));
134+
patch.add_assign(loc, Lvalue::Local(temp),
135+
Rvalue::Use(Operand::Consume(location.clone())));
136+
patch.patch_terminator(loc.block, TerminatorKind::Drop {
137+
location: Lvalue::Local(temp),
138+
target: storage_dead_block,
139+
unwind
140+
});
141+
}

src/librustc_mir/transform/check_unsafety.rs

+4-25
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_data_structures::fx::FxHashSet;
1212
use rustc_data_structures::indexed_vec::IndexVec;
1313

1414
use rustc::ty::maps::Providers;
15-
use rustc::ty::{self, Ty, TyCtxt};
15+
use rustc::ty::{self, TyCtxt};
1616
use rustc::hir;
1717
use rustc::hir::def_id::DefId;
1818
use rustc::lint::builtin::{SAFE_EXTERN_STATICS, UNUSED_UNSAFE};
@@ -22,16 +22,13 @@ use rustc::mir::visit::{LvalueContext, Visitor};
2222
use syntax::ast;
2323

2424
use std::rc::Rc;
25-
25+
use util;
2626

2727
pub struct UnsafetyChecker<'a, 'tcx: 'a> {
2828
mir: &'a Mir<'tcx>,
2929
visibility_scope_info: &'a IndexVec<VisibilityScope, VisibilityScopeInfo>,
3030
violations: Vec<UnsafetyViolation>,
3131
source_info: SourceInfo,
32-
// true if an a part of this *memory block* of this expression
33-
// is being borrowed, used for repr(packed) checking.
34-
need_check_packed: bool,
3532
tcx: TyCtxt<'a, 'tcx, 'tcx>,
3633
param_env: ty::ParamEnv<'tcx>,
3734
used_unsafe: FxHashSet<ast::NodeId>,
@@ -53,7 +50,6 @@ impl<'a, 'gcx, 'tcx> UnsafetyChecker<'a, 'tcx> {
5350
},
5451
tcx,
5552
param_env,
56-
need_check_packed: false,
5753
used_unsafe: FxHashSet(),
5854
inherited_blocks: vec![],
5955
}
@@ -142,11 +138,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
142138
lvalue: &Lvalue<'tcx>,
143139
context: LvalueContext<'tcx>,
144140
location: Location) {
145-
let old_need_check_packed = self.need_check_packed;
146141
if let LvalueContext::Borrow { .. } = context {
147-
let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
148-
if !self.has_align_1(ty) {
149-
self.need_check_packed = true;
142+
if util::is_disaligned(self.tcx, self.mir, self.param_env, lvalue) {
143+
self.require_unsafe("borrow of packed field")
150144
}
151145
}
152146

@@ -163,9 +157,6 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
163157
self.source_info = self.mir.local_decls[local].source_info;
164158
}
165159
}
166-
if let &ProjectionElem::Deref = elem {
167-
self.need_check_packed = false;
168-
}
169160
let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx);
170161
match base_ty.sty {
171162
ty::TyRawPtr(..) => {
@@ -194,9 +185,6 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
194185
self.require_unsafe("access to union field")
195186
}
196187
}
197-
if adt.repr.packed() && self.need_check_packed {
198-
self.require_unsafe("borrow of packed field")
199-
}
200188
}
201189
_ => {}
202190
}
@@ -221,19 +209,10 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
221209
}
222210
};
223211
self.super_lvalue(lvalue, context, location);
224-
self.need_check_packed = old_need_check_packed;
225212
}
226213
}
227214

228215
impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
229-
230-
fn has_align_1(&self, ty: Ty<'tcx>) -> bool {
231-
self.tcx.at(self.source_info.span)
232-
.layout_raw(self.param_env.and(ty))
233-
.map(|layout| layout.align.abi() == 1)
234-
.unwrap_or(false)
235-
}
236-
237216
fn require_unsafe(&mut self,
238217
description: &'static str)
239218
{

src/librustc_mir/transform/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use syntax::ast;
2323
use syntax_pos::Span;
2424

2525
pub mod add_validation;
26+
pub mod add_moves_for_packed_drops;
2627
pub mod clean_end_regions;
2728
pub mod check_unsafety;
2829
pub mod simplify_branches;
@@ -236,7 +237,12 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx
236237
// an AllCallEdges pass right before it.
237238
add_call_guards::AllCallEdges,
238239
add_validation::AddValidation,
240+
// AddMovesForPackedDrops needs to run after drop
241+
// elaboration.
242+
add_moves_for_packed_drops::AddMovesForPackedDrops,
243+
239244
simplify::SimplifyCfg::new("elaborate-drops"),
245+
240246
// No lifetime analysis based on borrowing can be done from here on out.
241247

242248
// From here on out, regions are gone.

src/librustc_mir/util/alignment.rs

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
12+
use rustc::ty::{self, TyCtxt};
13+
use rustc::mir::*;
14+
15+
/// Return `true` if this lvalue is allowed to be less aligned
16+
/// than its containing struct (because it is within a packed
17+
/// struct).
18+
pub fn is_disaligned<'a, 'tcx, L>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
19+
local_decls: &L,
20+
param_env: ty::ParamEnv<'tcx>,
21+
lvalue: &Lvalue<'tcx>)
22+
-> bool
23+
where L: HasLocalDecls<'tcx>
24+
{
25+
debug!("is_disaligned({:?})", lvalue);
26+
if !is_within_packed(tcx, local_decls, lvalue) {
27+
debug!("is_disaligned({:?}) - not within packed", lvalue);
28+
return false
29+
}
30+
31+
let ty = lvalue.ty(local_decls, tcx).to_ty(tcx);
32+
match tcx.layout_raw(param_env.and(ty)) {
33+
Ok(layout) if layout.align.abi() == 1 => {
34+
// if the alignment is 1, the type can't be further
35+
// disaligned.
36+
debug!("is_disaligned({:?}) - align = 1", lvalue);
37+
false
38+
}
39+
_ => {
40+
debug!("is_disaligned({:?}) - true", lvalue);
41+
true
42+
}
43+
}
44+
}
45+
46+
fn is_within_packed<'a, 'tcx, L>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
47+
local_decls: &L,
48+
lvalue: &Lvalue<'tcx>)
49+
-> bool
50+
where L: HasLocalDecls<'tcx>
51+
{
52+
let mut lvalue = lvalue;
53+
while let &Lvalue::Projection(box Projection {
54+
ref base, ref elem
55+
}) = lvalue {
56+
match *elem {
57+
// encountered a Deref, which is ABI-aligned
58+
ProjectionElem::Deref => break,
59+
ProjectionElem::Field(..) => {
60+
let ty = base.ty(local_decls, tcx).to_ty(tcx);
61+
match ty.sty {
62+
ty::TyAdt(def, _) if def.repr.packed() => {
63+
return true
64+
}
65+
_ => {}
66+
}
67+
}
68+
_ => {}
69+
}
70+
lvalue = base;
71+
}
72+
73+
false
74+
}

src/librustc_mir/util/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ pub mod elaborate_drops;
1313
pub mod def_use;
1414
pub mod patch;
1515

16+
mod alignment;
1617
mod graphviz;
1718
mod pretty;
1819
pub mod liveness;
1920

21+
pub use self::alignment::is_disaligned;
2022
pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty, PassWhere};
2123
pub use self::graphviz::{write_mir_graphviz};
2224
pub use self::graphviz::write_node_label as write_graphviz_node_label;

0 commit comments

Comments
 (0)