Skip to content

Commit cb41788

Browse files
committed
remove box derefs from codgen
1 parent 3e9d3d9 commit cb41788

File tree

16 files changed

+385
-146
lines changed

16 files changed

+385
-146
lines changed

compiler/rustc_codegen_ssa/src/mir/operand.rs

+6-8
Original file line numberDiff line numberDiff line change
@@ -118,22 +118,20 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
118118
}
119119

120120
pub fn deref<Cx: LayoutTypeMethods<'tcx>>(self, cx: &Cx) -> PlaceRef<'tcx, V> {
121+
if self.layout.ty.is_box() && !self.layout.abi.is_scalar() {
122+
bug!("dereferencing non-scalar box ({:?}) in codegen", self.layout.ty);
123+
}
124+
121125
let projected_ty = self
122126
.layout
123127
.ty
124128
.builtin_deref(true)
125129
.unwrap_or_else(|| bug!("deref of non-pointer {:?}", self))
126130
.ty;
131+
127132
let (llptr, llextra) = match self.val {
128133
OperandValue::Immediate(llptr) => (llptr, None),
129-
OperandValue::Pair(llptr, llextra) => {
130-
// if the box's allocator isn't a ZST, then "llextra" is actually the allocator
131-
if self.layout.ty.is_box() && !self.layout.field(cx, 1).is_zst() {
132-
(llptr, None)
133-
} else {
134-
(llptr, Some(llextra))
135-
}
136-
}
134+
OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)),
137135
OperandValue::Ref(..) => bug!("Deref of by-Ref operand {:?}", self),
138136
};
139137
let layout = cx.layout_of(projected_ty);

compiler/rustc_codegen_ssa/src/mir/place.rs

+2-22
Original file line numberDiff line numberDiff line change
@@ -446,35 +446,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
446446
mir::PlaceRef { projection: &place_ref.projection[..elem.0], ..place_ref },
447447
);
448448

449-
// a box with a non-zst allocator should not be directly dereferenced
450-
if cg_base.layout.ty.is_box() && !cg_base.layout.field(cx, 1).is_zst() {
451-
// Extract `Box<T>` -> `Unique<T>` -> `NonNull<T>` -> `*const T`
452-
let ptr =
453-
cg_base.extract_field(bx, 0).extract_field(bx, 0).extract_field(bx, 0);
454-
455-
ptr.deref(bx.cx())
456-
} else {
457-
cg_base.deref(bx.cx())
458-
}
449+
cg_base.deref(bx.cx())
459450
} else {
460451
bug!("using operand local {:?} as place", place_ref);
461452
}
462453
}
463454
};
464455
for elem in place_ref.projection[base..].iter() {
465456
cg_base = match *elem {
466-
mir::ProjectionElem::Deref => {
467-
// a box with a non-zst allocator should not be directly dereferenced
468-
if cg_base.layout.ty.is_box() && !cg_base.layout.field(cx, 1).is_zst() {
469-
// Project `Box<T>` -> `Unique<T>` -> `NonNull<T>` -> `*const T`
470-
let ptr =
471-
cg_base.project_field(bx, 0).project_field(bx, 0).project_field(bx, 0);
472-
473-
bx.load_operand(ptr).deref(bx.cx())
474-
} else {
475-
bx.load_operand(cg_base).deref(bx.cx())
476-
}
477-
}
457+
mir::ProjectionElem::Deref => bx.load_operand(cg_base).deref(bx.cx()),
478458
mir::ProjectionElem::Field(ref field, _) => {
479459
cg_base.project_field(bx, field.index())
480460
}

compiler/rustc_const_eval/src/transform/validate.rs

+64-50
Original file line numberDiff line numberDiff line change
@@ -240,65 +240,79 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
240240
context: PlaceContext,
241241
location: Location,
242242
) {
243-
if let ProjectionElem::Index(index) = elem {
244-
let index_ty = self.body.local_decls[index].ty;
245-
if index_ty != self.tcx.types.usize {
246-
self.fail(location, format!("bad index ({:?} != usize)", index_ty))
243+
match elem {
244+
ProjectionElem::Index(index) => {
245+
let index_ty = self.body.local_decls[index].ty;
246+
if index_ty != self.tcx.types.usize {
247+
self.fail(location, format!("bad index ({:?} != usize)", index_ty))
248+
}
247249
}
248-
}
249-
if let ProjectionElem::Field(f, ty) = elem {
250-
let parent = Place { local, projection: self.tcx.intern_place_elems(proj_base) };
251-
let parent_ty = parent.ty(&self.body.local_decls, self.tcx);
252-
let fail_out_of_bounds = |this: &Self, location| {
253-
this.fail(location, format!("Out of bounds field {:?} for {:?}", f, parent_ty));
254-
};
255-
let check_equal = |this: &Self, location, f_ty| {
256-
if !this.mir_assign_valid_types(ty, f_ty) {
257-
this.fail(
250+
ProjectionElem::Deref if self.mir_phase >= MirPhase::GeneratorsLowered => {
251+
let base_ty = Place::ty_from(local, proj_base, &self.body.local_decls, self.tcx).ty;
252+
253+
if base_ty.is_box() {
254+
self.fail(
255+
location,
256+
format!("{:?} dereferenced after ElaborateBoxDerefs", base_ty),
257+
)
258+
}
259+
}
260+
ProjectionElem::Field(f, ty) => {
261+
let parent = Place { local, projection: self.tcx.intern_place_elems(proj_base) };
262+
let parent_ty = parent.ty(&self.body.local_decls, self.tcx);
263+
let fail_out_of_bounds = |this: &Self, location| {
264+
this.fail(location, format!("Out of bounds field {:?} for {:?}", f, parent_ty));
265+
};
266+
let check_equal = |this: &Self, location, f_ty| {
267+
if !this.mir_assign_valid_types(ty, f_ty) {
268+
this.fail(
258269
location,
259270
format!(
260271
"Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is {:?}",
261272
parent, f, ty, f_ty
262273
)
263274
)
264-
}
265-
};
266-
match parent_ty.ty.kind() {
267-
ty::Tuple(fields) => {
268-
let Some(f_ty) = fields.get(f.as_usize()) else {
269-
fail_out_of_bounds(self, location);
270-
return;
271-
};
272-
check_equal(self, location, *f_ty);
273-
}
274-
ty::Adt(adt_def, substs) => {
275-
let var = parent_ty.variant_index.unwrap_or(VariantIdx::from_u32(0));
276-
let Some(field) = adt_def.variant(var).fields.get(f.as_usize()) else {
277-
fail_out_of_bounds(self, location);
278-
return;
279-
};
280-
check_equal(self, location, field.ty(self.tcx, substs));
281-
}
282-
ty::Closure(_, substs) => {
283-
let substs = substs.as_closure();
284-
let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else {
285-
fail_out_of_bounds(self, location);
286-
return;
287-
};
288-
check_equal(self, location, f_ty);
289-
}
290-
ty::Generator(_, substs, _) => {
291-
let substs = substs.as_generator();
292-
let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else {
293-
fail_out_of_bounds(self, location);
294-
return;
295-
};
296-
check_equal(self, location, f_ty);
297-
}
298-
_ => {
299-
self.fail(location, format!("{:?} does not have fields", parent_ty.ty));
275+
}
276+
};
277+
278+
match parent_ty.ty.kind() {
279+
ty::Tuple(fields) => {
280+
let Some(f_ty) = fields.get(f.as_usize()) else {
281+
fail_out_of_bounds(self, location);
282+
return;
283+
};
284+
check_equal(self, location, *f_ty);
285+
}
286+
ty::Adt(adt_def, substs) => {
287+
let var = parent_ty.variant_index.unwrap_or(VariantIdx::from_u32(0));
288+
let Some(field) = adt_def.variant(var).fields.get(f.as_usize()) else {
289+
fail_out_of_bounds(self, location);
290+
return;
291+
};
292+
check_equal(self, location, field.ty(self.tcx, substs));
293+
}
294+
ty::Closure(_, substs) => {
295+
let substs = substs.as_closure();
296+
let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else {
297+
fail_out_of_bounds(self, location);
298+
return;
299+
};
300+
check_equal(self, location, f_ty);
301+
}
302+
ty::Generator(_, substs, _) => {
303+
let substs = substs.as_generator();
304+
let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else {
305+
fail_out_of_bounds(self, location);
306+
return;
307+
};
308+
check_equal(self, location, f_ty);
309+
}
310+
_ => {
311+
self.fail(location, format!("{:?} does not have fields", parent_ty.ty));
312+
}
300313
}
301314
}
315+
_ => {}
302316
}
303317
self.super_projection_elem(local, proj_base, elem, context, location);
304318
}

compiler/rustc_middle/src/mir/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,9 @@ pub enum MirPhase {
176176
DropsLowered = 3,
177177
/// After this projections may only contain deref projections as the first element.
178178
Derefered = 4,
179-
/// Beginning with this phase, the following variant is disallowed:
179+
/// Beginning with this phase, the following variants are disallowed:
180180
/// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array`
181+
/// * [`ProjectionElem::Deref`] of `Box`
181182
///
182183
/// And the following variant is allowed:
183184
/// * [`StatementKind::SetDiscriminant`]

compiler/rustc_mir_dataflow/src/elaborate_drops.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,18 @@ where
410410
fn open_drop_for_box(&mut self, adt: ty::AdtDef<'tcx>, substs: SubstsRef<'tcx>) -> BasicBlock {
411411
debug!("open_drop_for_box({:?}, {:?}, {:?})", self, adt, substs);
412412

413-
let interior = self.tcx().mk_place_deref(self.place);
413+
// drop glue is sent straight to codegen
414+
// box cannot be directly dereferenced
415+
let unique_ty = adt.non_enum_variant().fields[0].ty(self.tcx(), substs);
416+
let nonnull_ty =
417+
unique_ty.ty_adt_def().unwrap().non_enum_variant().fields[0].ty(self.tcx(), substs);
418+
let ptr_ty = self.tcx().mk_imm_ptr(substs[0].expect_ty());
419+
420+
let unique_place = self.tcx().mk_place_field(self.place, Field::new(0), unique_ty);
421+
let nonnull_place = self.tcx().mk_place_field(unique_place, Field::new(0), nonnull_ty);
422+
let ptr_place = self.tcx().mk_place_field(nonnull_place, Field::new(0), ptr_ty);
423+
let interior = self.tcx().mk_place_deref(ptr_place);
424+
414425
let interior_path = self.elaborator.deref_subpath(self.path);
415426

416427
let succ = self.box_free_block(adt, substs, self.succ, self.unwind);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
//! This pass transforms derefs of Box into a deref of the pointer inside Box
2+
//! Codegen does not allow box to be directly dereferenced
3+
4+
use crate::MirPass;
5+
use rustc_hir::def_id::DefId;
6+
use rustc_index::vec::Idx;
7+
use rustc_middle::mir::patch::MirPatch;
8+
use rustc_middle::mir::visit::MutVisitor;
9+
use rustc_middle::mir::*;
10+
use rustc_middle::ty::subst::Subst;
11+
use rustc_middle::ty::TyCtxt;
12+
13+
struct ElaborateBoxDerefVistor<'tcx, 'a> {
14+
tcx: TyCtxt<'tcx>,
15+
unique_did: DefId,
16+
nonnull_did: DefId,
17+
local_decls: &'a mut LocalDecls<'tcx>,
18+
patch: MirPatch<'tcx>,
19+
}
20+
21+
impl<'tcx, 'a> MutVisitor<'tcx> for ElaborateBoxDerefVistor<'tcx, 'a> {
22+
fn tcx(&self) -> TyCtxt<'tcx> {
23+
self.tcx
24+
}
25+
26+
fn visit_place(
27+
&mut self,
28+
place: &mut Place<'tcx>,
29+
context: visit::PlaceContext,
30+
location: Location,
31+
) {
32+
let tcx = self.tcx;
33+
34+
let base_ty = self.local_decls[place.local].ty;
35+
36+
// Derefer ensures that derefs are always the first projection
37+
if place.projection.first() == Some(&PlaceElem::Deref) && base_ty.is_box() {
38+
let source_info = self.local_decls[place.local].source_info;
39+
40+
let substs = tcx.intern_substs(&[base_ty.boxed_ty().into()]);
41+
let unique_ty = tcx.bound_type_of(self.unique_did).subst(tcx, substs);
42+
let nonnull_ty = tcx.bound_type_of(self.nonnull_did).subst(tcx, substs);
43+
let ptr_ty = tcx.mk_imm_ptr(base_ty.boxed_ty());
44+
45+
let ptr_local = self.patch.new_temp(ptr_ty, source_info.span);
46+
self.local_decls.push(LocalDecl::new(ptr_ty, source_info.span));
47+
48+
self.patch.add_statement(location, StatementKind::StorageLive(ptr_local));
49+
50+
self.patch.add_assign(
51+
location,
52+
Place::from(ptr_local),
53+
Rvalue::Use(Operand::Copy(Place::from(place.local).project_deeper(
54+
&[
55+
PlaceElem::Field(Field::new(0), unique_ty),
56+
PlaceElem::Field(Field::new(0), nonnull_ty),
57+
PlaceElem::Field(Field::new(0), ptr_ty),
58+
],
59+
tcx,
60+
))),
61+
);
62+
63+
place.local = ptr_local;
64+
65+
self.patch.add_statement(
66+
Location { block: location.block, statement_index: location.statement_index + 1 },
67+
StatementKind::StorageDead(ptr_local),
68+
);
69+
}
70+
71+
self.super_place(place, context, location);
72+
}
73+
}
74+
75+
pub struct ElaborateBoxDerefs;
76+
77+
impl<'tcx> MirPass<'tcx> for ElaborateBoxDerefs {
78+
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
79+
if let Some(def_id) = tcx.lang_items().owned_box() {
80+
let unique_did = tcx.adt_def(def_id).non_enum_variant().fields[0].did;
81+
82+
let Some(nonnull_def) = tcx.type_of(unique_did).ty_adt_def() else {
83+
span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique")
84+
};
85+
86+
let nonnull_did = nonnull_def.non_enum_variant().fields[0].did;
87+
88+
let patch = MirPatch::new(body);
89+
90+
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
91+
92+
let mut visitor =
93+
ElaborateBoxDerefVistor { tcx, unique_did, nonnull_did, local_decls, patch };
94+
95+
for (block, BasicBlockData { statements, terminator, .. }) in
96+
basic_blocks.iter_enumerated_mut()
97+
{
98+
let mut index = 0;
99+
for statement in statements {
100+
let location = Location { block, statement_index: index };
101+
visitor.visit_statement(statement, location);
102+
index += 1;
103+
}
104+
105+
if let Some(terminator) = terminator
106+
&& !matches!(terminator.kind, TerminatorKind::Yield{..})
107+
{
108+
let location = Location { block, statement_index: index };
109+
visitor.visit_terminator(terminator, location);
110+
}
111+
112+
let location = Location { block, statement_index: index };
113+
match terminator {
114+
// yielding into a box is handed when lowering generators
115+
Some(Terminator { kind: TerminatorKind::Yield { value, .. }, .. }) => {
116+
visitor.visit_operand(value, location);
117+
}
118+
Some(terminator) => {
119+
visitor.visit_terminator(terminator, location);
120+
}
121+
None => {}
122+
}
123+
}
124+
125+
visitor.patch.apply(body);
126+
} else {
127+
// box is not present, this pass doesn't need to do anything
128+
}
129+
}
130+
}

0 commit comments

Comments
 (0)