Skip to content

Commit 0ca0676

Browse files
authored
Merge pull request #146 from oli-obk/peephole1
[Peephole Optimization 1/n] Don't allocate for structs with a single primval field
2 parents 6ee8595 + e2c5a6e commit 0ca0676

File tree

4 files changed

+70
-18
lines changed

4 files changed

+70
-18
lines changed

src/eval_context.rs

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -317,14 +317,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
317317
match global_value.value {
318318
Value::ByRef(ptr) => self.memory.mark_static_initalized(ptr.alloc_id, mutable)?,
319319
Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val {
320-
self.memory.mark_static_initalized(ptr.alloc_id, mutable)?;
320+
self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?;
321321
},
322322
Value::ByValPair(val1, val2) => {
323323
if let PrimVal::Ptr(ptr) = val1 {
324-
self.memory.mark_static_initalized(ptr.alloc_id, mutable)?;
324+
self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?;
325325
}
326326
if let PrimVal::Ptr(ptr) = val2 {
327-
self.memory.mark_static_initalized(ptr.alloc_id, mutable)?;
327+
self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?;
328328
}
329329
},
330330
}
@@ -374,7 +374,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
374374
discr_val: u128,
375375
variant_idx: usize,
376376
discr_size: u64,
377-
) -> EvalResult<'tcx> {
377+
) -> EvalResult<'tcx>
378+
where J::IntoIter: ExactSizeIterator,
379+
{
378380
// FIXME(solson)
379381
let dest_ptr = self.force_allocation(dest)?.to_ptr();
380382

@@ -397,7 +399,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
397399
dest: Lvalue<'tcx>,
398400
dest_ty: Ty<'tcx>,
399401
operands: J,
400-
) -> EvalResult<'tcx> {
402+
) -> EvalResult<'tcx>
403+
where J::IntoIter: ExactSizeIterator,
404+
{
405+
if self.type_size(dest_ty)? == Some(0) {
406+
// zst assigning is a nop
407+
return Ok(());
408+
}
409+
if self.ty_to_primval_kind(dest_ty).is_ok() {
410+
let mut iter = operands.into_iter();
411+
assert_eq!(iter.len(), 1);
412+
let (value, value_ty) = iter.next().unwrap().into_val_ty_pair(self)?;
413+
return self.write_value(value, dest, value_ty);
414+
}
401415
for (field_index, operand) in operands.into_iter().enumerate() {
402416
let (value, value_ty) = operand.into_val_ty_pair(self)?;
403417
let field_dest = self.lvalue_field(dest, field_index, dest_ty, value_ty)?;
@@ -795,7 +809,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
795809
}
796810
}
797811

798-
fn get_field_count(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, usize> {
812+
pub fn get_field_count(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, usize> {
799813
let layout = self.type_layout(ty)?;
800814

801815
use rustc::ty::layout::Layout::*;
@@ -1052,8 +1066,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
10521066
a: PrimVal,
10531067
b: PrimVal,
10541068
ptr: Pointer,
1055-
ty: Ty<'tcx>
1069+
mut ty: Ty<'tcx>
10561070
) -> EvalResult<'tcx> {
1071+
while self.get_field_count(ty)? == 1 {
1072+
ty = self.get_field_ty(ty, 0)?;
1073+
}
10571074
assert_eq!(self.get_field_count(ty)?, 2);
10581075
let field_0 = self.get_field_offset(ty, 0)?.bytes();
10591076
let field_1 = self.get_field_offset(ty, 1)?.bytes();
@@ -1109,7 +1126,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
11091126

11101127
ty::TyAdt(ref def, _) if def.is_box() => PrimValKind::Ptr,
11111128

1112-
ty::TyAdt(..) => {
1129+
ty::TyAdt(ref def, substs) => {
11131130
use rustc::ty::layout::Layout::*;
11141131
match *self.type_layout(ty)? {
11151132
CEnum { discr, signed, .. } => {
@@ -1132,6 +1149,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
11321149
}
11331150
}
11341151

1152+
// represent single field structs as their single field
1153+
Univariant { .. } => {
1154+
// enums with just one variant are no different, but `.struct_variant()` doesn't work for enums
1155+
let variant = &def.variants[0];
1156+
// FIXME: also allow structs with only a single non zst field
1157+
if variant.fields.len() == 1 {
1158+
return self.ty_to_primval_kind(variant.fields[0].ty(self.tcx, substs));
1159+
} else {
1160+
return Err(EvalError::TypeNotPrimitive(ty));
1161+
}
1162+
}
1163+
11351164
_ => return Err(EvalError::TypeNotPrimitive(ty)),
11361165
}
11371166
}
@@ -1320,8 +1349,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
13201349
}
13211350
return self.unsize_into_ptr(src, src_ty, dest, dest_ty, src_ty.boxed_ty(), dest_ty.boxed_ty());
13221351
}
1323-
// FIXME(solson)
1324-
let dest = self.force_allocation(dest)?.to_ptr();
1352+
if self.ty_to_primval_kind(src_ty).is_ok() {
1353+
let sty = self.get_field_ty(src_ty, 0)?;
1354+
let dty = self.get_field_ty(dest_ty, 0)?;
1355+
return self.unsize_into(src, sty, dest, dty);
1356+
}
13251357
// unsizing of generic struct with pointer fields
13261358
// Example: `Arc<T>` -> `Arc<Trait>`
13271359
// here we need to increase the size of every &T thin ptr field to a fat ptr
@@ -1338,6 +1370,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
13381370
_ => bug!("expected pointer, got {:?}", src),
13391371
};
13401372

1373+
// FIXME(solson)
1374+
let dest = self.force_allocation(dest)?.to_ptr();
13411375
let iter = src_fields.zip(dst_fields).enumerate();
13421376
for (i, (src_f, dst_f)) in iter {
13431377
let src_fty = monomorphize_field_ty(self.tcx, src_f, substs_a);

src/lvalue.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,14 +219,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
219219
(ptr, LvalueExtra::None)
220220
},
221221
Value::ByVal(PrimVal::Undef) => {
222-
// FIXME: add some logic for when to not allocate
223-
(self.force_allocation(base)?.to_ptr(), LvalueExtra::None)
222+
// FIXME: allocate in fewer cases
223+
if self.ty_to_primval_kind(base_ty).is_ok() {
224+
return Ok(base);
225+
} else {
226+
(self.force_allocation(base)?.to_ptr(), LvalueExtra::None)
227+
}
224228
},
225229
Value::ByVal(_) => {
226-
assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0");
230+
assert_eq!(field_index, 0, "ByVal can only have 1 non zst field with offset 0");
227231
return Ok(base);
228232
},
229233
Value::ByValPair(_, _) => {
234+
let field_count = self.get_field_count(base_ty)?;
235+
if field_count == 1 {
236+
assert_eq!(field_index, 0, "{:?} has only one field", base_ty);
237+
return Ok(base);
238+
}
239+
assert_eq!(field_count, 2);
230240
assert!(field_index < 2);
231241
return Ok(Lvalue::Local {
232242
frame,

src/memory.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -658,13 +658,24 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
658658
impl<'a, 'tcx> Memory<'a, 'tcx> {
659659
/// mark an allocation as being the entry point to a static (see `static_alloc` field)
660660
pub fn mark_static(&mut self, alloc_id: AllocId) {
661+
trace!("mark_static: {:?}", alloc_id);
661662
if alloc_id != NEVER_ALLOC_ID && alloc_id != ZST_ALLOC_ID && !self.static_alloc.insert(alloc_id) {
662663
bug!("tried to mark an allocation ({:?}) as static twice", alloc_id);
663664
}
664665
}
665666

667+
/// mark an allocation pointed to by a static as static and initialized
668+
pub fn mark_inner_allocation(&mut self, alloc: AllocId, mutable: bool) -> EvalResult<'tcx> {
669+
// relocations into other statics are not "inner allocations"
670+
if !self.static_alloc.contains(&alloc) {
671+
self.mark_static_initalized(alloc, mutable)?;
672+
}
673+
Ok(())
674+
}
675+
666676
/// mark an allocation as static and initialized, either mutable or not
667677
pub fn mark_static_initalized(&mut self, alloc_id: AllocId, mutable: bool) -> EvalResult<'tcx> {
678+
trace!("mark_static_initialized {:?}, mutable: {:?}", alloc_id, mutable);
668679
// do not use `self.get_mut(alloc_id)` here, because we might have already marked a
669680
// sub-element or have circular pointers (e.g. `Rc`-cycles)
670681
let relocations = match self.alloc_map.get_mut(&alloc_id) {
@@ -684,10 +695,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
684695
};
685696
// recurse into inner allocations
686697
for &alloc in relocations.values() {
687-
// relocations into other statics are not "inner allocations"
688-
if !self.static_alloc.contains(&alloc) {
689-
self.mark_static_initalized(alloc, mutable)?;
690-
}
698+
self.mark_inner_allocation(alloc, mutable)?;
691699
}
692700
// put back the relocations
693701
self.alloc_map.get_mut(&alloc_id).expect("checked above").relocations = relocations;

src/terminator/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
551551
args.push((undef, field_ty));
552552
}
553553
},
554-
_ => bug!("rust-call ABI tuple argument was {:?}", last),
554+
_ => bug!("rust-call ABI tuple argument was {:?}, but {:?} were expected", last, fields),
555555
}
556556
}
557557
ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty),

0 commit comments

Comments
 (0)