1
1
//! Functions concerning immediate values and operands, and reading from operands.
2
2
//! All high-level functions to read from memory work on operands as sources.
3
3
4
- use std:: convert:: TryFrom ;
5
4
use std:: fmt:: Write ;
6
5
7
6
use rustc_hir:: def:: Namespace ;
@@ -15,7 +14,7 @@ use rustc_target::abi::{VariantIdx, Variants};
15
14
16
15
use super :: {
17
16
alloc_range, from_known_layout, mir_assign_valid_types, AllocId , ConstValue , Frame , GlobalId ,
18
- InterpCx , InterpResult , MPlaceTy , Machine , MemPlace , Place , PlaceTy , Pointer ,
17
+ InterpCx , InterpResult , MPlaceTy , Machine , MemPlace , MemPlaceMeta , Place , PlaceTy , Pointer ,
19
18
PointerArithmetic , Provenance , Scalar , ScalarMaybeUninit ,
20
19
} ;
21
20
@@ -253,6 +252,11 @@ impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> {
253
252
ImmTy { imm, layout }
254
253
}
255
254
255
+ #[ inline]
256
+ pub fn uninit ( layout : TyAndLayout < ' tcx > ) -> Self {
257
+ ImmTy { imm : Immediate :: Uninit , layout }
258
+ }
259
+
256
260
#[ inline]
257
261
pub fn try_from_uint ( i : impl Into < u128 > , layout : TyAndLayout < ' tcx > ) -> Option < Self > {
258
262
Some ( Self :: from_scalar ( Scalar :: try_from_uint ( i, layout. size ) ?, layout) )
@@ -280,6 +284,41 @@ impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> {
280
284
}
281
285
}
282
286
287
+ impl < ' tcx , Tag : Provenance > OpTy < ' tcx , Tag > {
288
+ pub fn len ( & self , cx : & impl HasDataLayout ) -> InterpResult < ' tcx , u64 > {
289
+ if self . layout . is_unsized ( ) {
290
+ // There are no unsized immediates.
291
+ self . assert_mem_place ( ) . len ( cx)
292
+ } else {
293
+ match self . layout . fields {
294
+ abi:: FieldsShape :: Array { count, .. } => Ok ( count) ,
295
+ _ => bug ! ( "len not supported on sized type {:?}" , self . layout. ty) ,
296
+ }
297
+ }
298
+ }
299
+
300
+ pub fn offset (
301
+ & self ,
302
+ offset : Size ,
303
+ meta : MemPlaceMeta < Tag > ,
304
+ layout : TyAndLayout < ' tcx > ,
305
+ cx : & impl HasDataLayout ,
306
+ ) -> InterpResult < ' tcx , Self > {
307
+ match self . try_as_mplace ( ) {
308
+ Ok ( mplace) => Ok ( mplace. offset ( offset, meta, layout, cx) ?. into ( ) ) ,
309
+ Err ( imm) => {
310
+ assert ! (
311
+ matches!( * imm, Immediate :: Uninit ) ,
312
+ "Scalar/ScalarPair cannot be offset into"
313
+ ) ;
314
+ assert ! ( !meta. has_meta( ) ) ; // no place to store metadata here
315
+ // Every part of an uninit is uninit.
316
+ Ok ( ImmTy :: uninit ( layout) . into ( ) )
317
+ }
318
+ }
319
+ }
320
+ }
321
+
283
322
impl < ' mir , ' tcx : ' mir , M : Machine < ' mir , ' tcx > > InterpCx < ' mir , ' tcx , M > {
284
323
/// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`.
285
324
/// Returns `None` if the layout does not permit loading this as a value.
@@ -296,11 +335,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
296
335
}
297
336
298
337
let Some ( alloc) = self . get_place_alloc ( mplace) ? else {
299
- return Ok ( Some ( ImmTy {
300
- // zero-sized type can be left uninit
301
- imm : Immediate :: Uninit ,
302
- layout : mplace. layout ,
303
- } ) ) ;
338
+ // zero-sized type can be left uninit
339
+ return Ok ( Some ( ImmTy :: uninit ( mplace. layout ) ) ) ;
304
340
} ;
305
341
306
342
// It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point.
@@ -367,6 +403,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
367
403
/// This flag exists only for validity checking.
368
404
///
369
405
/// This is an internal function that should not usually be used; call `read_immediate` instead.
406
+ /// ConstProp needs it, though.
370
407
pub fn read_immediate_raw (
371
408
& self ,
372
409
src : & OpTy < ' tcx , M :: PointerTag > ,
@@ -421,123 +458,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
421
458
Ok ( str)
422
459
}
423
460
424
- /// Projection functions
425
- pub fn operand_field (
426
- & self ,
427
- op : & OpTy < ' tcx , M :: PointerTag > ,
428
- field : usize ,
429
- ) -> InterpResult < ' tcx , OpTy < ' tcx , M :: PointerTag > > {
430
- let base = match op. try_as_mplace ( ) {
431
- Ok ( ref mplace) => {
432
- // We can reuse the mplace field computation logic for indirect operands.
433
- let field = self . mplace_field ( mplace, field) ?;
434
- return Ok ( field. into ( ) ) ;
435
- }
436
- Err ( value) => value,
437
- } ;
438
-
439
- let field_layout = base. layout . field ( self , field) ;
440
- let offset = base. layout . fields . offset ( field) ;
441
- // This makes several assumptions about what layouts we will encounter; we match what
442
- // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
443
- let field_val: Immediate < _ > = match ( * base, base. layout . abi ) {
444
- // the field contains no information, can be left uninit
445
- _ if field_layout. is_zst ( ) => Immediate :: Uninit ,
446
- // the field covers the entire type
447
- _ if field_layout. size == base. layout . size => {
448
- assert ! ( match ( base. layout. abi, field_layout. abi) {
449
- ( Abi :: Scalar ( ..) , Abi :: Scalar ( ..) ) => true ,
450
- ( Abi :: ScalarPair ( ..) , Abi :: ScalarPair ( ..) ) => true ,
451
- _ => false ,
452
- } ) ;
453
- assert ! ( offset. bytes( ) == 0 ) ;
454
- * base
455
- }
456
- // extract fields from types with `ScalarPair` ABI
457
- ( Immediate :: ScalarPair ( a_val, b_val) , Abi :: ScalarPair ( a, b) ) => {
458
- assert ! ( matches!( field_layout. abi, Abi :: Scalar ( ..) ) ) ;
459
- Immediate :: from ( if offset. bytes ( ) == 0 {
460
- debug_assert_eq ! ( field_layout. size, a. size( self ) ) ;
461
- a_val
462
- } else {
463
- debug_assert_eq ! ( offset, a. size( self ) . align_to( b. align( self ) . abi) ) ;
464
- debug_assert_eq ! ( field_layout. size, b. size( self ) ) ;
465
- b_val
466
- } )
467
- }
468
- _ => span_bug ! (
469
- self . cur_span( ) ,
470
- "invalid field access on immediate {}, layout {:#?}" ,
471
- base,
472
- base. layout
473
- ) ,
474
- } ;
475
-
476
- Ok ( OpTy { op : Operand :: Immediate ( field_val) , layout : field_layout, align : None } )
477
- }
478
-
479
- pub fn operand_index (
480
- & self ,
481
- op : & OpTy < ' tcx , M :: PointerTag > ,
482
- index : u64 ,
483
- ) -> InterpResult < ' tcx , OpTy < ' tcx , M :: PointerTag > > {
484
- if let Ok ( index) = usize:: try_from ( index) {
485
- // We can just treat this as a field.
486
- self . operand_field ( op, index)
487
- } else {
488
- // Indexing into a big array. This must be an mplace.
489
- let mplace = op. assert_mem_place ( ) ;
490
- Ok ( self . mplace_index ( & mplace, index) ?. into ( ) )
491
- }
492
- }
493
-
494
- pub fn operand_downcast (
495
- & self ,
496
- op : & OpTy < ' tcx , M :: PointerTag > ,
497
- variant : VariantIdx ,
498
- ) -> InterpResult < ' tcx , OpTy < ' tcx , M :: PointerTag > > {
499
- Ok ( match op. try_as_mplace ( ) {
500
- Ok ( ref mplace) => self . mplace_downcast ( mplace, variant) ?. into ( ) ,
501
- Err ( ..) => {
502
- // Downcasts only change the layout.
503
- // (In particular, no check about whether this is even the active variant -- that's by design,
504
- // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.)
505
- let layout = op. layout . for_variant ( self , variant) ;
506
- OpTy { layout, ..* op }
507
- }
508
- } )
509
- }
510
-
511
- #[ instrument( skip( self ) , level = "debug" ) ]
512
- pub fn operand_projection (
513
- & self ,
514
- base : & OpTy < ' tcx , M :: PointerTag > ,
515
- proj_elem : mir:: PlaceElem < ' tcx > ,
516
- ) -> InterpResult < ' tcx , OpTy < ' tcx , M :: PointerTag > > {
517
- use rustc_middle:: mir:: ProjectionElem :: * ;
518
- Ok ( match proj_elem {
519
- Field ( field, _) => self . operand_field ( base, field. index ( ) ) ?,
520
- Downcast ( _, variant) => self . operand_downcast ( base, variant) ?,
521
- Deref => self . deref_operand ( base) ?. into ( ) ,
522
- Subslice { .. } | ConstantIndex { .. } | Index ( _) => {
523
- // The rest should only occur as mplace, we do not use Immediates for types
524
- // allowing such operations. This matches place_projection forcing an allocation.
525
- let mplace = base. assert_mem_place ( ) ;
526
- self . mplace_projection ( & mplace, proj_elem) ?. into ( )
527
- }
528
- } )
529
- }
530
-
531
461
/// Converts a repr(simd) operand into an operand where `place_index` accesses the SIMD elements.
532
462
/// Also returns the number of elements.
463
+ ///
464
+ /// Can (but does not always) trigger UB if `op` is uninitialized.
533
465
pub fn operand_to_simd (
534
466
& self ,
535
- base : & OpTy < ' tcx , M :: PointerTag > ,
467
+ op : & OpTy < ' tcx , M :: PointerTag > ,
536
468
) -> InterpResult < ' tcx , ( MPlaceTy < ' tcx , M :: PointerTag > , u64 ) > {
537
469
// Basically we just transmute this place into an array following simd_size_and_type.
538
470
// This only works in memory, but repr(simd) types should never be immediates anyway.
539
- assert ! ( base. layout. ty. is_simd( ) ) ;
540
- self . mplace_to_simd ( & base. assert_mem_place ( ) )
471
+ assert ! ( op. layout. ty. is_simd( ) ) ;
472
+ match op. try_as_mplace ( ) {
473
+ Ok ( mplace) => self . mplace_to_simd ( & mplace) ,
474
+ Err ( imm) => match * imm {
475
+ Immediate :: Uninit => {
476
+ throw_ub ! ( InvalidUninitBytes ( None ) )
477
+ }
478
+ Immediate :: Scalar ( ..) | Immediate :: ScalarPair ( ..) => {
479
+ bug ! ( "arrays/slices can never have Scalar/ScalarPair layout" )
480
+ }
481
+ } ,
482
+ }
541
483
}
542
484
543
485
/// Read from a local. Will not actually access the local if reading from a ZST.
@@ -582,30 +524,34 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
582
524
/// avoid allocations.
583
525
pub fn eval_place_to_op (
584
526
& self ,
585
- place : mir:: Place < ' tcx > ,
527
+ mir_place : mir:: Place < ' tcx > ,
586
528
layout : Option < TyAndLayout < ' tcx > > ,
587
529
) -> InterpResult < ' tcx , OpTy < ' tcx , M :: PointerTag > > {
588
530
// Do not use the layout passed in as argument if the base we are looking at
589
531
// here is not the entire place.
590
- let layout = if place . projection . is_empty ( ) { layout } else { None } ;
532
+ let layout = if mir_place . projection . is_empty ( ) { layout } else { None } ;
591
533
592
- let base_op = self . local_to_op ( self . frame ( ) , place. local , layout) ?;
593
-
594
- let op = place
595
- . projection
596
- . iter ( )
597
- . try_fold ( base_op, |op, elem| self . operand_projection ( & op, elem) ) ?;
534
+ let mut op = self . local_to_op ( self . frame ( ) , mir_place. local , layout) ?;
535
+ // Using `try_fold` turned out to be bad for performance, hence the loop.
536
+ for elem in mir_place. projection . iter ( ) {
537
+ op = self . operand_projection ( & op, elem) ?
538
+ }
598
539
599
540
trace ! ( "eval_place_to_op: got {:?}" , * op) ;
600
541
// Sanity-check the type we ended up with.
601
- debug_assert ! ( mir_assign_valid_types(
602
- * self . tcx,
603
- self . param_env,
604
- self . layout_of( self . subst_from_current_frame_and_normalize_erasing_regions(
605
- place. ty( & self . frame( ) . body. local_decls, * self . tcx) . ty
606
- ) ?) ?,
607
- op. layout,
608
- ) ) ;
542
+ debug_assert ! (
543
+ mir_assign_valid_types(
544
+ * self . tcx,
545
+ self . param_env,
546
+ self . layout_of( self . subst_from_current_frame_and_normalize_erasing_regions(
547
+ mir_place. ty( & self . frame( ) . body. local_decls, * self . tcx) . ty
548
+ ) ?) ?,
549
+ op. layout,
550
+ ) ,
551
+ "eval_place of a MIR place with type {:?} produced an interpreter operand with type {:?}" ,
552
+ mir_place. ty( & self . frame( ) . body. local_decls, * self . tcx) . ty,
553
+ op. layout. ty,
554
+ ) ;
609
555
Ok ( op)
610
556
}
611
557
0 commit comments