Skip to content

Commit 7d7de5b

Browse files
committed
Auto merge of rust-lang#139088 - spastorino:ergonomic-ref-counting-2, r=nikomatsakis
Ergonomic ref counting: optimize away clones when possible This PR build on top of rust-lang#134797. It optimizes codegen of ergonomic ref-counting when the type being `use`d is only known to be copy after monomorphization. We avoid codening a clone and generate bitwise copy instead. RFC: rust-lang/rfcs#3680 Tracking issue: rust-lang#132290 Project goal: rust-lang/rust-project-goals#107 r? `@nikomatsakis` This PR could better sit on top of rust-lang#131650 but as it did not land yet I've decided to just do minimal changes. It may be the case that doing what I'm doing regress the performance and we may need to go the full route of rust-lang#131650. cc `@saethlin` in this regard.
2 parents 9d28fe3 + 4a0ea02 commit 7d7de5b

File tree

7 files changed

+189
-60
lines changed

7 files changed

+189
-60
lines changed

compiler/rustc_codegen_ssa/src/mir/mod.rs

+72-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::iter;
33
use rustc_index::IndexVec;
44
use rustc_index::bit_set::DenseBitSet;
55
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
6-
use rustc_middle::mir::{Local, UnwindTerminateReason, traversal};
6+
use rustc_middle::mir::{Body, Local, UnwindTerminateReason, traversal};
77
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, TyAndLayout};
88
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
99
use rustc_middle::{bug, mir, span_bug};
@@ -170,19 +170,29 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
170170
) {
171171
assert!(!instance.args.has_infer());
172172

173+
let tcx = cx.tcx();
173174
let llfn = cx.get_fn(instance);
174175

175-
let mir = cx.tcx().instance_mir(instance.def);
176+
let mut mir = tcx.instance_mir(instance.def);
176177

177178
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
178179
debug!("fn_abi: {:?}", fn_abi);
179180

180-
if cx.tcx().codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) {
181+
if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) {
181182
crate::mir::naked_asm::codegen_naked_asm::<Bx>(cx, &mir, instance);
182183
return;
183184
}
184185

185-
let debug_context = cx.create_function_debug_context(instance, fn_abi, llfn, mir);
186+
if tcx.features().ergonomic_clones() {
187+
let monomorphized_mir = instance.instantiate_mir_and_normalize_erasing_regions(
188+
tcx,
189+
ty::TypingEnv::fully_monomorphized(),
190+
ty::EarlyBinder::bind(mir.clone()),
191+
);
192+
mir = tcx.arena.alloc(optimize_use_clone::<Bx>(cx, monomorphized_mir));
193+
}
194+
195+
let debug_context = cx.create_function_debug_context(instance, fn_abi, llfn, &mir);
186196

187197
let start_llbb = Bx::append_block(cx, llfn, "start");
188198
let mut start_bx = Bx::build(cx, start_llbb);
@@ -194,7 +204,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
194204
}
195205

196206
let cleanup_kinds =
197-
base::wants_new_eh_instructions(cx.tcx().sess).then(|| analyze::cleanup_kinds(mir));
207+
base::wants_new_eh_instructions(tcx.sess).then(|| analyze::cleanup_kinds(&mir));
198208

199209
let cached_llbbs: IndexVec<mir::BasicBlock, CachedLlbb<Bx::BasicBlock>> =
200210
mir.basic_blocks
@@ -217,7 +227,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
217227
cleanup_kinds,
218228
landing_pads: IndexVec::from_elem(None, &mir.basic_blocks),
219229
funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()),
220-
cold_blocks: find_cold_blocks(cx.tcx(), mir),
230+
cold_blocks: find_cold_blocks(tcx, mir),
221231
locals: locals::Locals::empty(),
222232
debug_context,
223233
per_local_var_debug_info: None,
@@ -233,7 +243,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
233243
fx.compute_per_local_var_debug_info(&mut start_bx).unzip();
234244
fx.per_local_var_debug_info = per_local_var_debug_info;
235245

236-
let traversal_order = traversal::mono_reachable_reverse_postorder(mir, cx.tcx(), instance);
246+
let traversal_order = traversal::mono_reachable_reverse_postorder(mir, tcx, instance);
237247
let memory_locals = analyze::non_ssa_locals(&fx, &traversal_order);
238248

239249
// Allocate variable and temp allocas
@@ -310,6 +320,61 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
310320
}
311321
}
312322

323+
// FIXME: Move this function to mir::transform when post-mono MIR passes land.
324+
fn optimize_use_clone<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
325+
cx: &'a Bx::CodegenCx,
326+
mut mir: Body<'tcx>,
327+
) -> Body<'tcx> {
328+
let tcx = cx.tcx();
329+
330+
if tcx.features().ergonomic_clones() {
331+
for bb in mir.basic_blocks.as_mut() {
332+
let mir::TerminatorKind::Call {
333+
args,
334+
destination,
335+
target,
336+
call_source: mir::CallSource::Use,
337+
..
338+
} = &bb.terminator().kind
339+
else {
340+
continue;
341+
};
342+
343+
// CallSource::Use calls always use 1 argument.
344+
assert_eq!(args.len(), 1);
345+
let arg = &args[0];
346+
347+
// These types are easily available from locals, so check that before
348+
// doing DefId lookups to figure out what we're actually calling.
349+
let arg_ty = arg.node.ty(&mir.local_decls, tcx);
350+
351+
let ty::Ref(_region, inner_ty, mir::Mutability::Not) = *arg_ty.kind() else { continue };
352+
353+
if !tcx.type_is_copy_modulo_regions(cx.typing_env(), inner_ty) {
354+
continue;
355+
}
356+
357+
let Some(arg_place) = arg.node.place() else { continue };
358+
359+
let destination_block = target.unwrap();
360+
361+
bb.statements.push(mir::Statement {
362+
source_info: bb.terminator().source_info,
363+
kind: mir::StatementKind::Assign(Box::new((
364+
*destination,
365+
mir::Rvalue::Use(mir::Operand::Copy(
366+
arg_place.project_deeper(&[mir::ProjectionElem::Deref], tcx),
367+
)),
368+
))),
369+
});
370+
371+
bb.terminator_mut().kind = mir::TerminatorKind::Goto { target: destination_block };
372+
}
373+
}
374+
375+
mir
376+
}
377+
313378
/// Produces, for each argument, a `Value` pointing at the
314379
/// argument's value. As arguments are places, these are always
315380
/// indirect.

compiler/rustc_middle/src/mir/syntax.rs

+2
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,8 @@ pub enum CallSource {
652652
/// Other types of desugaring that did not come from the HIR, but we don't care about
653653
/// for diagnostics (yet).
654654
Misc,
655+
/// Use of value, generating a clone function call
656+
Use,
655657
/// Normal function call, no special source
656658
Normal,
657659
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
328328
destination,
329329
target: Some(success),
330330
unwind: UnwindAction::Unreachable,
331-
call_source: CallSource::Misc,
331+
call_source: CallSource::Use,
332332
fn_span: expr_span,
333333
},
334334
);

rustfmt.toml

+4
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,8 @@ ignore = [
5353
# Code automatically generated and included.
5454
"compiler/rustc_codegen_gcc/src/intrinsic/archs.rs",
5555
"compiler/rustc_codegen_gcc/example",
56+
57+
# Rustfmt doesn't support use closures yet
58+
"tests/mir-opt/ergonomic-clones/closure.rs",
59+
"tests/codegen/ergonomic-clones/closure.rs",
5660
]
+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//@ compile-flags: -C no-prepopulate-passes -Copt-level=0 -Zmir-opt-level=0
2+
3+
#![crate_type = "lib"]
4+
5+
#![feature(ergonomic_clones)]
6+
#![allow(incomplete_features)]
7+
8+
use std::clone::UseCloned;
9+
10+
pub fn ergonomic_clone_closure_move() -> String {
11+
let s = String::from("hi");
12+
13+
// CHECK-NOT: ; call core::clone::impls::<impl core::clone::Clone for String>::clone
14+
let cl = use || s;
15+
cl()
16+
}
17+
18+
#[derive(Clone)]
19+
struct Foo;
20+
21+
impl UseCloned for Foo {}
22+
23+
pub fn ergonomic_clone_closure_use_cloned() -> Foo {
24+
let f = Foo;
25+
26+
// CHECK: ; call <closure::Foo as core::clone::Clone>::clone
27+
let f1 = use || f;
28+
29+
// CHECK: ; call <closure::Foo as core::clone::Clone>::clone
30+
let f2 = use || f;
31+
32+
f
33+
}
34+
35+
pub fn ergonomic_clone_closure_copy() -> i32 {
36+
let i = 1;
37+
38+
// CHECK-NOT: ; call core::clone::impls::<impl core::clone::Clone for i32>::clone
39+
let i1 = use || i;
40+
41+
// CHECK-NOT: ; call core::clone::impls::<impl core::clone::Clone for i32>::clone
42+
let i2 = use || i;
43+
44+
i
45+
}
46+
47+
pub fn ergonomic_clone_closure_use_cloned_generics<T: UseCloned>(f: T) -> T {
48+
// CHECK-NOT: ; call core::clone::impls::<impl core::clone::Clone for i32>::clone
49+
let f1 = use || f;
50+
51+
// CHECK-NOT: ; call core::clone::impls::<impl core::clone::Clone for i32>::clone
52+
let f2 = use || f;
53+
54+
f
55+
}

tests/crashes/129372.rs

-52
This file was deleted.
+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#![crate_type = "lib"]
2+
#![feature(ergonomic_clones)]
3+
#![allow(incomplete_features)]
4+
5+
use std::clone::UseCloned;
6+
7+
pub fn ergonomic_clone_closure_move() -> String {
8+
// CHECK-LABEL: fn ergonomic_clone_closure_move(
9+
// CHECK: _0 = move (_1.0: std::string::String);
10+
// CHECK-NOT: <String as Clone>::clone
11+
let s = String::from("hi");
12+
13+
let cl = use || s;
14+
cl()
15+
}
16+
17+
#[derive(Clone)]
18+
struct Foo;
19+
20+
impl UseCloned for Foo {}
21+
22+
pub fn ergonomic_clone_closure_use_cloned() -> Foo {
23+
// CHECK-LABEL: fn ergonomic_clone_closure_use_cloned(
24+
// CHECK: <Foo as Clone>::clone
25+
let f = Foo;
26+
27+
let f1 = use || f;
28+
29+
let f2 = use || f;
30+
31+
f
32+
}
33+
34+
pub fn ergonomic_clone_closure_copy() -> i32 {
35+
// CHECK-LABEL: fn ergonomic_clone_closure_copy(
36+
// CHECK: _0 = copy ((*_1).0: i32);
37+
// CHECK-NOT: <i32 as Clone>::clone
38+
let i = 1;
39+
40+
let i1 = use || i;
41+
42+
let i2 = use || i;
43+
44+
i
45+
}
46+
47+
pub fn ergonomic_clone_closure_use_cloned_generics<T: UseCloned>(f: T) -> T {
48+
// CHECK-LABEL: fn ergonomic_clone_closure_use_cloned_generics(
49+
// CHECK: <T as Clone>::clone
50+
let f1 = use || f;
51+
52+
let f2 = use || f;
53+
54+
f
55+
}

0 commit comments

Comments
 (0)