@@ -126,8 +126,63 @@ impl<'tcx> Global<'tcx> {
126
126
}
127
127
128
128
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
+
129
178
pub ( super ) fn eval_and_read_lvalue ( & mut self , lvalue : & mir:: Lvalue < ' tcx > ) -> EvalResult < ' tcx , Value > {
130
179
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
+ }
131
186
let lvalue = self . eval_lvalue ( lvalue) ?;
132
187
133
188
if ty. is_never ( ) {
@@ -233,7 +288,30 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
233
288
_ => bug ! ( "field access on non-product type: {:?}" , base_layout) ,
234
289
} ;
235
290
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
+ } ;
237
315
238
316
let offset = match base_extra {
239
317
LvalueExtra :: Vtable ( tab) => {
0 commit comments