Skip to content

Commit 8722ce8

Browse files
authored
Merge pull request rust-lang#223 from oli-obk/lvalue_read
Reinstate `eval_and_read_lvalue` optimizations
2 parents 4a56083 + 917c89e commit 8722ce8

File tree

5 files changed

+102
-7
lines changed

5 files changed

+102
-7
lines changed

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ pub enum EvalError<'tcx> {
6565
Panic,
6666
NeedsRfc(String),
6767
NotConst(String),
68+
ReadFromReturnPointer,
6869
}
6970

7071
pub type EvalResult<'tcx, T = ()> = Result<T, EvalError<'tcx>>;
@@ -162,6 +163,8 @@ impl<'tcx> Error for EvalError<'tcx> {
162163
"this feature needs an rfc before being allowed inside constants",
163164
EvalError::NotConst(_) =>
164165
"this feature is not compatible with constant evaluation",
166+
EvalError::ReadFromReturnPointer =>
167+
"tried to read from the return pointer",
165168
}
166169
}
167170

src/eval_context.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
821821
Ok(())
822822
}
823823

824-
fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool {
824+
pub(super) fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool {
825825
match ty.sty {
826826
ty::TyRawPtr(ref tam) |
827827
ty::TyRef(_, ref tam) => !self.type_is_sized(tam.ty),
@@ -882,6 +882,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
882882
pub fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> {
883883
match ty.sty {
884884
ty::TyAdt(adt_def, _) if adt_def.is_box() => self.get_fat_field(ty.boxed_ty(), field_index),
885+
ty::TyAdt(adt_def, substs) if adt_def.is_enum() => {
886+
use rustc::ty::layout::Layout::*;
887+
match *self.type_layout(ty)? {
888+
RawNullablePointer { nndiscr, .. } |
889+
StructWrappedNullablePointer { nndiscr, .. } => Ok(adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs)),
890+
_ => Err(EvalError::Unimplemented(format!("get_field_ty can't handle enum type: {:?}, {:?}", ty, ty.sty))),
891+
}
892+
}
885893
ty::TyAdt(adt_def, substs) => {
886894
Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs))
887895
}
@@ -890,6 +898,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
890898

891899
ty::TyRef(_, ref tam) |
892900
ty::TyRawPtr(ref tam) => self.get_fat_field(tam.ty, field_index),
901+
902+
ty::TyArray(ref inner, _) => Ok(inner),
903+
893904
_ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))),
894905
}
895906
}
@@ -916,14 +927,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
916927
}
917928
}
918929

919-
pub fn get_field_count(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, usize> {
930+
pub fn get_field_count(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, u64> {
920931
let layout = self.type_layout(ty)?;
921932

922933
use rustc::ty::layout::Layout::*;
923934
match *layout {
924-
Univariant { ref variant, .. } => Ok(variant.offsets.len()),
935+
Univariant { ref variant, .. } => Ok(variant.offsets.len() as u64),
925936
FatPointer { .. } => Ok(2),
926-
StructWrappedNullablePointer { ref nonnull, .. } => Ok(nonnull.offsets.len()),
937+
StructWrappedNullablePointer { ref nonnull, .. } => Ok(nonnull.offsets.len() as u64),
938+
Vector { count , .. } |
939+
Array { count, .. } => Ok(count),
940+
Scalar { .. } => Ok(0),
927941
_ => {
928942
let msg = format!("can't handle type: {:?}, with layout: {:?}", ty, layout);
929943
Err(EvalError::Unimplemented(msg))

src/lvalue.rs

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,63 @@ impl<'tcx> Global<'tcx> {
126126
}
127127

128128
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
129+
/// Reads a value from the lvalue without going through the intermediate step of obtaining
130+
/// a `miri::Lvalue`
131+
pub fn try_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Option<Value>> {
132+
use rustc::mir::Lvalue::*;
133+
match *lvalue {
134+
// Might allow this in the future, right now there's no way to do this from Rust code anyway
135+
Local(mir::RETURN_POINTER) => Err(EvalError::ReadFromReturnPointer),
136+
// Directly reading a local will always succeed
137+
Local(local) => self.frame().get_local(local).map(Some),
138+
// Directly reading a static will always succeed
139+
Static(ref static_) => {
140+
let instance = ty::Instance::mono(self.tcx, static_.def_id);
141+
let cid = GlobalId { instance, promoted: None };
142+
Ok(Some(self.globals.get(&cid).expect("global not cached").value))
143+
},
144+
Projection(ref proj) => self.try_read_lvalue_projection(proj),
145+
}
146+
}
147+
148+
fn try_read_lvalue_projection(&mut self, proj: &mir::LvalueProjection<'tcx>) -> EvalResult<'tcx, Option<Value>> {
149+
use rustc::mir::ProjectionElem::*;
150+
let base = match self.try_read_lvalue(&proj.base)? {
151+
Some(base) => base,
152+
None => return Ok(None),
153+
};
154+
let base_ty = self.lvalue_ty(&proj.base);
155+
match proj.elem {
156+
Field(field, _) => match (field.index(), base) {
157+
// the only field of a struct
158+
(0, Value::ByVal(val)) => Ok(Some(Value::ByVal(val))),
159+
// split fat pointers, 2 element tuples, ...
160+
(0...1, Value::ByValPair(a, b)) if self.get_field_count(base_ty)? == 2 => {
161+
let val = [a, b][field.index()];
162+
Ok(Some(Value::ByVal(val)))
163+
},
164+
// the only field of a struct is a fat pointer
165+
(0, Value::ByValPair(..)) => Ok(Some(base)),
166+
_ => Ok(None),
167+
},
168+
// The NullablePointer cases should work fine, need to take care for normal enums
169+
Downcast(..) |
170+
Subslice { .. } |
171+
// reading index 0 or index 1 from a ByVal or ByVal pair could be optimized
172+
ConstantIndex { .. } | Index(_) |
173+
// No way to optimize this projection any better than the normal lvalue path
174+
Deref => Ok(None),
175+
}
176+
}
177+
129178
pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> {
130179
let ty = self.lvalue_ty(lvalue);
180+
// Shortcut for things like accessing a fat pointer's field,
181+
// which would otherwise (in the `eval_lvalue` path) require moving a `ByValPair` to memory
182+
// and returning an `Lvalue::Ptr` to it
183+
if let Some(val) = self.try_read_lvalue(lvalue)? {
184+
return Ok(val);
185+
}
131186
let lvalue = self.eval_lvalue(lvalue)?;
132187

133188
if ty.is_never() {
@@ -233,7 +288,30 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
233288
_ => bug!("field access on non-product type: {:?}", base_layout),
234289
};
235290

236-
let (base_ptr, base_extra) = self.force_allocation(base)?.to_ptr_and_extra();
291+
// Do not allocate in trivial cases
292+
let (base_ptr, base_extra) = match base {
293+
Lvalue::Ptr { ptr, extra } => (ptr, extra),
294+
Lvalue::Local { frame, local } => match self.stack[frame].get_local(local)? {
295+
// in case the type has a single field, just return the value
296+
Value::ByVal(_) if self.get_field_count(base_ty).map(|c| c == 1).unwrap_or(false) => {
297+
assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0");
298+
return Ok(base);
299+
},
300+
Value::ByRef(_) |
301+
Value::ByValPair(..) |
302+
Value::ByVal(_) => self.force_allocation(base)?.to_ptr_and_extra(),
303+
},
304+
Lvalue::Global(cid) => match self.globals.get(&cid).expect("uncached global").value {
305+
// in case the type has a single field, just return the value
306+
Value::ByVal(_) if self.get_field_count(base_ty).map(|c| c == 1).unwrap_or(false) => {
307+
assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0");
308+
return Ok(base);
309+
},
310+
Value::ByRef(_) |
311+
Value::ByValPair(..) |
312+
Value::ByVal(_) => self.force_allocation(base)?.to_ptr_and_extra(),
313+
},
314+
};
237315

238316
let offset = match base_extra {
239317
LvalueExtra::Vtable(tab) => {

src/memory.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
772772
4 => !0u32 as u128,
773773
8 => !0u64 as u128,
774774
16 => !0,
775-
_ => bug!("unexpected PrimVal::Bytes size"),
775+
n => bug!("unexpected PrimVal::Bytes size: {}", n),
776776
};
777777
self.write_uint(dest.to_ptr()?, bytes & mask, size)
778778
}

src/value.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ impl<'a, 'tcx: 'a> Value {
108108
assert_eq!(len as u64 as u128, len);
109109
Ok((ptr, len as u64))
110110
},
111-
_ => unimplemented!(),
111+
ByVal(_) => unimplemented!(),
112112
}
113113
}
114114
}

0 commit comments

Comments
 (0)