Skip to content

Commit 74028ba

Browse files
author
Ariel Ben-Yehuda
authored
Rollup merge of rust-lang#40377 - camlorn:optimization_fuel, r=arielb1
Implement optimization fuel and re-enable struct field reordering See [this discussion](https://internals.rust-lang.org/t/rolling-out-or-unrolling-struct-field-reorderings/4485) for background. This pull request adds two new compilation options: `-Z print-fuel=crate` prints the optimization fuel used by a crate and `-Z fuel=crate=n` sets the optimization fuel for a crate. It also turns field reordering back on. There is no way to test this feature without something consuming fuel. We can roll this back if we want, but then the optimization fuel bits will be dead code. The one notable absence from this PR is a test case. I'm not sure how to do one that's worth having. The only thing I can think of to test is `-Z fuel=foo=0`. The problem with other tests is that either (1) they're so big that future optimizations will apply, thus breaking them or (2) we don't know which order the optimizations will be applied in, so we can't guess the message that will be printed. If someone has a useful proposal for a good test, I certainly want to add one.
2 parents 8e6afbf + 3fb94b7 commit 74028ba

File tree

14 files changed

+210
-39
lines changed

14 files changed

+210
-39
lines changed

src/librustc/session/config.rs

+24
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,8 @@ macro_rules! options {
636636
Some("either `panic` or `abort`");
637637
pub const parse_sanitizer: Option<&'static str> =
638638
Some("one of: `address`, `leak`, `memory` or `thread`");
639+
pub const parse_optimization_fuel: Option<&'static str> =
640+
Some("crate=integer");
639641
}
640642

641643
#[allow(dead_code)]
@@ -772,6 +774,21 @@ macro_rules! options {
772774
}
773775
true
774776
}
777+
778+
fn parse_optimization_fuel(slot: &mut Option<(String, u64)>, v: Option<&str>) -> bool {
779+
match v {
780+
None => false,
781+
Some(s) => {
782+
let parts = s.split('=').collect::<Vec<_>>();
783+
if parts.len() != 2 { return false; }
784+
let crate_name = parts[0].to_string();
785+
let fuel = parts[1].parse::<u64>();
786+
if fuel.is_err() { return false; }
787+
*slot = Some((crate_name, fuel.unwrap()));
788+
true
789+
}
790+
}
791+
}
775792
}
776793
) }
777794

@@ -974,6 +991,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
974991
"pass `-install_name @rpath/...` to the macOS linker"),
975992
sanitizer: Option<Sanitizer> = (None, parse_sanitizer, [TRACKED],
976993
"Use a sanitizer"),
994+
fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED],
995+
"Set the optimization fuel quota for a crate."),
996+
print_fuel: Option<String> = (None, parse_opt_string, [TRACKED],
997+
"Make Rustc print the total optimization fuel used by a crate."),
977998
}
978999

9791000
pub fn default_lib_output() -> CrateType {
@@ -1766,11 +1787,13 @@ mod dep_tracking {
17661787

17671788
impl_dep_tracking_hash_via_hash!(bool);
17681789
impl_dep_tracking_hash_via_hash!(usize);
1790+
impl_dep_tracking_hash_via_hash!(u64);
17691791
impl_dep_tracking_hash_via_hash!(String);
17701792
impl_dep_tracking_hash_via_hash!(lint::Level);
17711793
impl_dep_tracking_hash_via_hash!(Option<bool>);
17721794
impl_dep_tracking_hash_via_hash!(Option<usize>);
17731795
impl_dep_tracking_hash_via_hash!(Option<String>);
1796+
impl_dep_tracking_hash_via_hash!(Option<(String, u64)>);
17741797
impl_dep_tracking_hash_via_hash!(Option<PanicStrategy>);
17751798
impl_dep_tracking_hash_via_hash!(Option<lint::Level>);
17761799
impl_dep_tracking_hash_via_hash!(Option<PathBuf>);
@@ -1792,6 +1815,7 @@ mod dep_tracking {
17921815
impl_dep_tracking_hash_for_sortable_vec_of!((String, lint::Level));
17931816
impl_dep_tracking_hash_for_sortable_vec_of!((String, Option<String>,
17941817
Option<cstore::NativeLibraryKind>));
1818+
impl_dep_tracking_hash_for_sortable_vec_of!((String, u64));
17951819
impl DepTrackingHash for SearchPaths {
17961820
fn hash(&self, hasher: &mut DefaultHasher, _: ErrorOutputType) {
17971821
let mut elems: Vec<_> = self

src/librustc/session/mod.rs

+52
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,20 @@ pub struct Session {
123123
pub code_stats: RefCell<CodeStats>,
124124

125125
next_node_id: Cell<ast::NodeId>,
126+
127+
/// If -zfuel=crate=n is specified, Some(crate).
128+
optimization_fuel_crate: Option<String>,
129+
/// If -zfuel=crate=n is specified, initially set to n. Otherwise 0.
130+
optimization_fuel_limit: Cell<u64>,
131+
/// We're rejecting all further optimizations.
132+
out_of_fuel: Cell<bool>,
133+
134+
// The next two are public because the driver needs to read them.
135+
136+
/// If -zprint-fuel=crate, Some(crate).
137+
pub print_fuel_crate: Option<String>,
138+
/// Always set to zero and incremented so that we can print fuel expended by a crate.
139+
pub print_fuel: Cell<u64>,
126140
}
127141

128142
pub struct PerfStats {
@@ -504,6 +518,33 @@ impl Session {
504518
println!("Total time spent decoding DefPath tables: {}",
505519
duration_to_secs_str(self.perf_stats.decode_def_path_tables_time.get()));
506520
}
521+
522+
/// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n.
523+
/// This expends fuel if applicable, and records fuel if applicable.
524+
pub fn consider_optimizing<T: Fn() -> String>(&self, crate_name: &str, msg: T) -> bool {
525+
let mut ret = true;
526+
match self.optimization_fuel_crate {
527+
Some(ref c) if c == crate_name => {
528+
let fuel = self.optimization_fuel_limit.get();
529+
ret = fuel != 0;
530+
if fuel == 0 && !self.out_of_fuel.get(){
531+
println!("optimization-fuel-exhausted: {}", msg());
532+
self.out_of_fuel.set(true);
533+
}
534+
else if fuel > 0{
535+
self.optimization_fuel_limit.set(fuel-1);
536+
}
537+
}
538+
_ => {}
539+
}
540+
match self.print_fuel_crate {
541+
Some(ref c) if c == crate_name=> {
542+
self.print_fuel.set(self.print_fuel.get()+1);
543+
},
544+
_ => {}
545+
}
546+
ret
547+
}
507548
}
508549

509550
pub fn build_session(sopts: config::Options,
@@ -599,6 +640,12 @@ pub fn build_session_(sopts: config::Options,
599640
}
600641
);
601642

643+
let optimization_fuel_crate = sopts.debugging_opts.fuel.as_ref().map(|i| i.0.clone());
644+
let optimization_fuel_limit = Cell::new(sopts.debugging_opts.fuel.as_ref()
645+
.map(|i| i.1).unwrap_or(0));
646+
let print_fuel_crate = sopts.debugging_opts.print_fuel.clone();
647+
let print_fuel = Cell::new(0);
648+
602649
let sess = Session {
603650
dep_graph: dep_graph.clone(),
604651
target: target_cfg,
@@ -640,6 +687,11 @@ pub fn build_session_(sopts: config::Options,
640687
decode_def_path_tables_time: Cell::new(Duration::from_secs(0)),
641688
},
642689
code_stats: RefCell::new(CodeStats::new()),
690+
optimization_fuel_crate: optimization_fuel_crate,
691+
optimization_fuel_limit: optimization_fuel_limit,
692+
print_fuel_crate: print_fuel_crate,
693+
print_fuel: print_fuel,
694+
out_of_fuel: Cell::new(false),
643695
};
644696

645697
init_llvm(&sess);

src/librustc/ty/context.rs

+5
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
728728
ast_ty_to_ty_cache: RefCell::new(NodeMap()),
729729
}, f)
730730
}
731+
732+
pub fn consider_optimizing<T: Fn() -> String>(&self, msg: T) -> bool {
733+
let cname = self.crate_name(LOCAL_CRATE).as_str();
734+
self.sess.consider_optimizing(&cname, msg)
735+
}
731736
}
732737

733738
impl<'gcx: 'tcx, 'tcx> GlobalCtxt<'gcx> {

src/librustc/ty/layout.rs

+2-7
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,6 @@ enum StructKind {
555555
}
556556

557557
impl<'a, 'gcx, 'tcx> Struct {
558-
// FIXME(camlorn): reprs need a better representation to deal with multiple reprs on one type.
559558
fn new(dl: &TargetDataLayout, fields: &Vec<&'a Layout>,
560559
repr: &ReprOptions, kind: StructKind,
561560
scapegoat: Ty<'gcx>) -> Result<Struct, LayoutError<'gcx>> {
@@ -573,12 +572,8 @@ impl<'a, 'gcx, 'tcx> Struct {
573572
// Neither do 1-member and 2-member structs.
574573
// In addition, code in trans assume that 2-element structs can become pairs.
575574
// It's easier to just short-circuit here.
576-
let mut can_optimize = (fields.len() > 2 || StructKind::EnumVariant == kind)
577-
&& ! (repr.c || repr.packed);
578-
579-
// Disable field reordering until we can decide what to do.
580-
// The odd pattern here avoids a warning about the value never being read.
581-
if can_optimize { can_optimize = false; }
575+
let can_optimize = (fields.len() > 2 || StructKind::EnumVariant == kind)
576+
&& ! (repr.c || repr.packed || repr.linear || repr.simd);
582577

583578
let (optimize, sort_ascending) = match kind {
584579
StructKind::AlwaysSizedUnivariant => (can_optimize, false),

src/librustc/ty/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,8 @@ pub struct ReprOptions {
13761376
pub packed: bool,
13771377
pub simd: bool,
13781378
pub int: Option<attr::IntType>,
1379+
// Internal only for now. If true, don't reorder fields.
1380+
pub linear: bool,
13791381
}
13801382

13811383
impl ReprOptions {
@@ -1398,6 +1400,9 @@ impl ReprOptions {
13981400
ret.simd = true;
13991401
}
14001402

1403+
// This is here instead of layout because the choice must make it into metadata.
1404+
ret.linear = !tcx.consider_optimizing(|| format!("Reorder fields of {:?}",
1405+
tcx.item_path_str(did)));
14011406
ret
14021407
}
14031408

src/librustc_driver/lib.rs

+8
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,14 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
514514
control.make_glob_map = resolve::MakeGlobMap::Yes;
515515
}
516516

517+
if sess.print_fuel_crate.is_some() {
518+
control.compilation_done.callback = box |state| {
519+
let sess = state.session;
520+
println!("Fuel used by {}: {}",
521+
sess.print_fuel_crate.as_ref().unwrap(),
522+
sess.print_fuel.get());
523+
}
524+
}
517525
control
518526
}
519527
}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2012 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+
#![crate_name="foo"]
12+
13+
use std::mem::size_of;
14+
15+
// compile-flags: -Z fuel=foo=0
16+
17+
struct S1(u8, u16, u8);
18+
struct S2(u8, u16, u8);
19+
20+
fn main() {
21+
assert_eq!(size_of::<S1>(), 6);
22+
assert_eq!(size_of::<S2>(), 6);
23+
}
24+
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2012 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+
#![crate_name="foo"]
12+
13+
use std::mem::size_of;
14+
15+
// compile-flags: -Z fuel=foo=1
16+
17+
struct S1(u8, u16, u8);
18+
struct S2(u8, u16, u8);
19+
20+
fn main() {
21+
let optimized = (size_of::<S1>() == 4) as usize
22+
+(size_of::<S2>() == 4) as usize;
23+
assert_eq!(optimized, 1);
24+
}
25+
26+

src/test/run-pass/type-sizes.rs

+13
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,17 @@ enum e3 {
3131
a([u16; 0], u8), b
3232
}
3333

34+
struct ReorderedStruct {
35+
a: u8,
36+
b: u64,
37+
c: u8
38+
}
39+
40+
enum ReorderedEnum {
41+
A(u8, u64, u8),
42+
B(u8, u64, u8),
43+
}
44+
3445
pub fn main() {
3546
assert_eq!(size_of::<u8>(), 1 as usize);
3647
assert_eq!(size_of::<u32>(), 4 as usize);
@@ -54,4 +65,6 @@ pub fn main() {
5465
assert_eq!(size_of::<e1>(), 8 as usize);
5566
assert_eq!(size_of::<e2>(), 8 as usize);
5667
assert_eq!(size_of::<e3>(), 4 as usize);
68+
assert_eq!(size_of::<ReorderedStruct>(), 16);
69+
assert_eq!(size_of::<ReorderedEnum>(), 16);
5770
}

src/test/ui/print-fuel/print-fuel.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2016 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+
#![crate_name="foo"]
12+
#![allow(dead_code)]
13+
14+
// compile-flags: -Z print-fuel=foo
15+
16+
struct S1(u8, u16, u8);
17+
struct S2(u8, u16, u8);
18+
struct S3(u8, u16, u8);
19+
20+
fn main() {
21+
}
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fuel used by foo: 3

src/test/ui/print_type_sizes/nullable.stdout

+16-19
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,22 @@
1-
print-type-size type: `IndirectNonZero<u32>`: 20 bytes, alignment: 4 bytes
2-
print-type-size field `.pre`: 1 bytes
3-
print-type-size padding: 3 bytes
4-
print-type-size field `.nested`: 12 bytes, alignment: 4 bytes
1+
print-type-size type: `IndirectNonZero<u32>`: 12 bytes, alignment: 4 bytes
2+
print-type-size field `.nested`: 8 bytes
53
print-type-size field `.post`: 2 bytes
6-
print-type-size end padding: 2 bytes
7-
print-type-size type: `MyOption<IndirectNonZero<u32>>`: 20 bytes, alignment: 4 bytes
8-
print-type-size variant `Some`: 20 bytes
9-
print-type-size field `.0`: 20 bytes
10-
print-type-size type: `EmbeddedDiscr`: 12 bytes, alignment: 4 bytes
11-
print-type-size variant `Record`: 10 bytes
12-
print-type-size field `.pre`: 1 bytes
13-
print-type-size padding: 3 bytes
14-
print-type-size field `.val`: 4 bytes, alignment: 4 bytes
15-
print-type-size field `.post`: 2 bytes
16-
print-type-size end padding: 2 bytes
17-
print-type-size type: `NestedNonZero<u32>`: 12 bytes, alignment: 4 bytes
184
print-type-size field `.pre`: 1 bytes
19-
print-type-size padding: 3 bytes
20-
print-type-size field `.val`: 4 bytes, alignment: 4 bytes
5+
print-type-size end padding: 1 bytes
6+
print-type-size type: `MyOption<IndirectNonZero<u32>>`: 12 bytes, alignment: 4 bytes
7+
print-type-size variant `Some`: 12 bytes
8+
print-type-size field `.0`: 12 bytes
9+
print-type-size type: `EmbeddedDiscr`: 8 bytes, alignment: 4 bytes
10+
print-type-size variant `Record`: 7 bytes
11+
print-type-size field `.val`: 4 bytes
12+
print-type-size field `.post`: 2 bytes
13+
print-type-size field `.pre`: 1 bytes
14+
print-type-size end padding: 1 bytes
15+
print-type-size type: `NestedNonZero<u32>`: 8 bytes, alignment: 4 bytes
16+
print-type-size field `.val`: 4 bytes
2117
print-type-size field `.post`: 2 bytes
22-
print-type-size end padding: 2 bytes
18+
print-type-size field `.pre`: 1 bytes
19+
print-type-size end padding: 1 bytes
2320
print-type-size type: `MyOption<core::nonzero::NonZero<u32>>`: 4 bytes, alignment: 4 bytes
2421
print-type-size variant `Some`: 4 bytes
2522
print-type-size field `.0`: 4 bytes

src/test/ui/print_type_sizes/packed.stdout

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
print-type-size type: `Padded`: 16 bytes, alignment: 4 bytes
1+
print-type-size type: `Padded`: 12 bytes, alignment: 4 bytes
2+
print-type-size field `.g`: 4 bytes
3+
print-type-size field `.h`: 2 bytes
24
print-type-size field `.a`: 1 bytes
35
print-type-size field `.b`: 1 bytes
4-
print-type-size padding: 2 bytes
5-
print-type-size field `.g`: 4 bytes, alignment: 4 bytes
66
print-type-size field `.c`: 1 bytes
7-
print-type-size padding: 1 bytes
8-
print-type-size field `.h`: 2 bytes, alignment: 2 bytes
97
print-type-size field `.d`: 1 bytes
10-
print-type-size end padding: 3 bytes
8+
print-type-size end padding: 2 bytes
119
print-type-size type: `Packed`: 10 bytes, alignment: 1 bytes
1210
print-type-size field `.a`: 1 bytes
1311
print-type-size field `.b`: 1 bytes

0 commit comments

Comments
 (0)