Skip to content

Commit ed34b6f

Browse files
committed
lower write_via_move during MIR building to make it just like an assignment
1 parent 79966ae commit ed34b6f

File tree

7 files changed

+126
-98
lines changed

7 files changed

+126
-98
lines changed

compiler/rustc_mir_build/src/builder/expr/into.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use rustc_middle::mir::*;
99
use rustc_middle::span_bug;
1010
use rustc_middle::thir::*;
1111
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
12-
use rustc_span::DUMMY_SP;
1312
use rustc_span::source_map::Spanned;
13+
use rustc_span::{DUMMY_SP, sym};
1414
use rustc_trait_selection::infer::InferCtxtExt;
1515
use tracing::{debug, instrument};
1616

@@ -365,6 +365,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
365365
None
366366
})
367367
}
368+
// The `write_via_move` intrinsic needs to be special-cased very early to avoid
369+
// introducing unnecessary copies that can be hard to remove again later:
370+
// `write_via_move(ptr, val)` becomes `*ptr = val` but without any dropping.
371+
ExprKind::Call { ty, fun, ref args, .. }
372+
if let ty::FnDef(def_id, _generic_args) = ty.kind()
373+
&& let Some(intrinsic) = this.tcx.intrinsic(def_id)
374+
&& intrinsic.name == sym::write_via_move =>
375+
{
376+
// We still have to evaluate the callee expression as normal (but we don't care
377+
// about its result).
378+
let _fun = unpack!(block = this.as_local_operand(block, fun));
379+
// The destination must have unit type (so we don't actually have to store anything
380+
// into it).
381+
assert!(destination.ty(&this.local_decls, this.tcx).ty.is_unit());
382+
383+
// Compile this to an assignment of the argument into the destination.
384+
let [ptr, val] = **args else {
385+
span_bug!(expr_span, "invalid write_via_move call")
386+
};
387+
let Some(ptr) = unpack!(block = this.as_local_operand(block, ptr)).place() else {
388+
span_bug!(expr_span, "invalid write_via_move call")
389+
};
390+
let ptr_deref = ptr.project_deeper(&[ProjectionElem::Deref], this.tcx);
391+
this.expr_into_dest(ptr_deref, block, val)
392+
}
368393
ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => {
369394
let fun = unpack!(block = this.as_local_operand(block, fun));
370395
let args: Box<[_]> = args

compiler/rustc_mir_transform/src/lower_intrinsics.rs

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -171,30 +171,7 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
171171
Some(target) => TerminatorKind::Goto { target },
172172
}
173173
}
174-
sym::write_via_move => {
175-
let target = target.unwrap();
176-
let Ok([ptr, val]) = take_array(args) else {
177-
span_bug!(
178-
terminator.source_info.span,
179-
"Wrong number of arguments for write_via_move intrinsic",
180-
);
181-
};
182-
let derefed_place = if let Some(place) = ptr.node.place()
183-
&& let Some(local) = place.as_local()
184-
{
185-
tcx.mk_place_deref(local.into())
186-
} else {
187-
span_bug!(
188-
terminator.source_info.span,
189-
"Only passing a local is supported"
190-
);
191-
};
192-
block.statements.push(Statement::new(
193-
terminator.source_info,
194-
StatementKind::Assign(Box::new((derefed_place, Rvalue::Use(val.node)))),
195-
));
196-
terminator.kind = TerminatorKind::Goto { target };
197-
}
174+
// `write_via_move` is already lowered during MIR building.
198175
sym::discriminant_value => {
199176
let target = target.unwrap();
200177
let Ok([arg]) = take_array(args) else {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// MIR for `box_new` after CleanupPostBorrowck
2+
3+
fn box_new(_1: T) -> Box<[T; 1024]> {
4+
debug x => _1;
5+
let mut _0: std::boxed::Box<[T; 1024]>;
6+
let mut _2: std::boxed::Box<std::mem::MaybeUninit<[T; 1024]>>;
7+
let mut _4: &mut std::mem::MaybeUninit<[T; 1024]>;
8+
let mut _5: &mut std::mem::MaybeUninit<[T; 1024]>;
9+
let _6: ();
10+
let mut _7: *mut [T; 1024];
11+
let mut _8: T;
12+
let mut _9: std::boxed::Box<std::mem::MaybeUninit<[T; 1024]>>;
13+
scope 1 {
14+
debug b => _2;
15+
let _3: *mut [T; 1024];
16+
scope 2 {
17+
debug ptr => _3;
18+
}
19+
}
20+
21+
bb0: {
22+
StorageLive(_2);
23+
_2 = Box::<[T; 1024]>::new_uninit() -> [return: bb1, unwind: bb7];
24+
}
25+
26+
bb1: {
27+
nop;
28+
StorageLive(_3);
29+
StorageLive(_4);
30+
StorageLive(_5);
31+
_5 = &mut (*_2);
32+
_4 = &mut (*_5);
33+
_3 = MaybeUninit::<[T; 1024]>::as_mut_ptr(move _4) -> [return: bb2, unwind: bb6];
34+
}
35+
36+
bb2: {
37+
StorageDead(_4);
38+
nop;
39+
StorageDead(_5);
40+
StorageLive(_6);
41+
StorageLive(_7);
42+
_7 = copy _3;
43+
StorageLive(_8);
44+
_8 = copy _1;
45+
(*_7) = [move _8; 1024];
46+
StorageDead(_8);
47+
StorageDead(_7);
48+
StorageDead(_6);
49+
StorageLive(_9);
50+
_9 = move _2;
51+
_0 = Box::<MaybeUninit<[T; 1024]>>::assume_init(move _9) -> [return: bb3, unwind: bb5];
52+
}
53+
54+
bb3: {
55+
StorageDead(_9);
56+
StorageDead(_3);
57+
drop(_2) -> [return: bb4, unwind: bb7];
58+
}
59+
60+
bb4: {
61+
StorageDead(_2);
62+
return;
63+
}
64+
65+
bb5 (cleanup): {
66+
drop(_9) -> [return: bb6, unwind terminate(cleanup)];
67+
}
68+
69+
bb6 (cleanup): {
70+
drop(_2) -> [return: bb7, unwind terminate(cleanup)];
71+
}
72+
73+
bb7 (cleanup): {
74+
resume;
75+
}
76+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//! Ensure we don't generate unnecessary copys for `write_via_move`.
2+
//@ compile-flags: -Zmir-opt-level=0
3+
#![feature(core_intrinsics)]
4+
5+
use std::mem;
6+
7+
// Can't emit `built.after` here as that contains user type annotations which contain DefId that
8+
// change all the time.
9+
// EMIT_MIR write_via_move.box_new.CleanupPostBorrowck.after.mir
10+
// CHECK-LABEL: fn box_new
11+
#[inline(never)]
12+
fn box_new<T: Copy>(x: T) -> Box<[T; 1024]> {
13+
let mut b = Box::new_uninit();
14+
let ptr = mem::MaybeUninit::as_mut_ptr(&mut *b);
15+
// Ensure the array gets constructed directly into the deref'd pointer.
16+
// CHECK: (*[[TEMP1:_.+]]) = [{{(move|copy) _.+}}; 1024];
17+
unsafe { std::intrinsics::write_via_move(ptr, [x; 1024]) };
18+
unsafe { b.assume_init() }
19+
}
20+
21+
fn main() {
22+
box_new(0);
23+
}

tests/mir-opt/lower_intrinsics.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -197,17 +197,6 @@ pub fn read_via_copy_uninhabited(r: &Never) -> Never {
197197
unsafe { core::intrinsics::read_via_copy(r) }
198198
}
199199

200-
// EMIT_MIR lower_intrinsics.write_via_move_string.LowerIntrinsics.diff
201-
pub fn write_via_move_string(r: &mut String, v: String) {
202-
// CHECK-LABEL: fn write_via_move_string(
203-
// CHECK: [[ptr:_.*]] = &raw mut (*_1);
204-
// CHECK: [[tmp:_.*]] = move _2;
205-
// CHECK: (*[[ptr]]) = move [[tmp]];
206-
// CHECK: return;
207-
208-
unsafe { core::intrinsics::write_via_move(r, v) }
209-
}
210-
211200
pub enum Never {}
212201

213202
// EMIT_MIR lower_intrinsics.ptr_offset.LowerIntrinsics.diff

tests/mir-opt/lower_intrinsics.write_via_move_string.LowerIntrinsics.panic-abort.diff

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

tests/mir-opt/lower_intrinsics.write_via_move_string.LowerIntrinsics.panic-unwind.diff

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

0 commit comments

Comments
 (0)