1- use rustc:: ty:: { Ty , layout:: { Size , LayoutOf } } ;
1+ use rustc:: ty:: { Ty , layout:: LayoutOf } ;
22use rustc:: mir;
33
44use crate :: * ;
@@ -9,21 +9,13 @@ pub trait EvalContextExt<'tcx> {
99 ptr : Pointer < Tag >
1010 ) -> InterpResult < ' tcx > ;
1111
12- fn ptr_op (
12+ fn binary_ptr_op (
1313 & self ,
1414 bin_op : mir:: BinOp ,
1515 left : ImmTy < ' tcx , Tag > ,
1616 right : ImmTy < ' tcx , Tag > ,
1717 ) -> InterpResult < ' tcx , ( Scalar < Tag > , bool ) > ;
1818
19- fn ptr_int_arithmetic (
20- & self ,
21- bin_op : mir:: BinOp ,
22- left : Pointer < Tag > ,
23- right : u128 ,
24- signed : bool ,
25- ) -> InterpResult < ' tcx , ( Scalar < Tag > , bool ) > ;
26-
2719 fn ptr_eq (
2820 & self ,
2921 left : Scalar < Tag > ,
@@ -46,7 +38,7 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
4638 ptr. check_in_alloc ( size, CheckInAllocMsg :: InboundsTest )
4739 }
4840
49- fn ptr_op (
41+ fn binary_ptr_op (
5042 & self ,
5143 bin_op : mir:: BinOp ,
5244 left : ImmTy < ' tcx , Tag > ,
@@ -56,24 +48,9 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
5648
5749 trace ! ( "ptr_op: {:?} {:?} {:?}" , * left, bin_op, * right) ;
5850
59- // Treat everything of integer *type* at integer *value*.
60- if left. layout . ty . is_integral ( ) {
61- // This is actually an integer operation, so dispatch back to the core engine.
62- // TODO: Once intptrcast is the default, librustc_mir should never even call us
63- // for integer types.
64- assert ! ( right. layout. ty. is_integral( ) ) ;
65- let l_bits = self . force_bits ( left. imm . to_scalar ( ) ?, left. layout . size ) ?;
66- let r_bits = self . force_bits ( right. imm . to_scalar ( ) ?, right. layout . size ) ?;
67-
68- let left = ImmTy :: from_scalar ( Scalar :: from_uint ( l_bits, left. layout . size ) , left. layout ) ;
69- let right = ImmTy :: from_scalar ( Scalar :: from_uint ( r_bits, left. layout . size ) , right. layout ) ;
70-
71- return self . binary_op ( bin_op, left, right) ;
72- }
73-
74- // Operations that support fat pointers
75- match bin_op {
51+ Ok ( match bin_op {
7652 Eq | Ne => {
53+ // This supports fat pointers.
7754 let eq = match ( * left, * right) {
7855 ( Immediate :: Scalar ( left) , Immediate :: Scalar ( right) ) =>
7956 self . ptr_eq ( left. not_undef ( ) ?, right. not_undef ( ) ?) ?,
@@ -82,103 +59,38 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
8259 self . ptr_eq ( left2. not_undef ( ) ?, right2. not_undef ( ) ?) ?,
8360 _ => bug ! ( "Type system should not allow comparing Scalar with ScalarPair" ) ,
8461 } ;
85- return Ok ( ( Scalar :: from_bool ( if bin_op == Eq { eq } else { !eq } ) , false ) ) ;
62+ ( Scalar :: from_bool ( if bin_op == Eq { eq } else { !eq } ) , false )
8663 }
87- _ => { } ,
88- }
8964
90- // Now we expect no more fat pointers.
91- let left_layout = left. layout ;
92- let left = left. to_scalar ( ) ?;
93- let right_layout = right. layout ;
94- let right = right. to_scalar ( ) ?;
95- debug_assert ! ( left. is_ptr( ) || right. is_ptr( ) || bin_op == Offset ) ;
65+ Lt | Le | Gt | Ge => {
66+ // Just compare the integers.
67+ // TODO: Do we really want to *always* do that, even when comparing two live in-bounds pointers?
68+ let left = self . force_bits ( left. to_scalar ( ) ?, left. layout . size ) ?;
69+ let right = self . force_bits ( right. to_scalar ( ) ?, right. layout . size ) ?;
70+ let res = match bin_op {
71+ Lt => left < right,
72+ Le => left <= right,
73+ Gt => left > right,
74+ Ge => left >= right,
75+ _ => bug ! ( "We already established it has to be one of these operators." ) ,
76+ } ;
77+ ( Scalar :: from_bool ( res) , false )
78+ }
9679
97- Ok ( match bin_op {
9880 Offset => {
99- let pointee_ty = left_layout . ty
81+ let pointee_ty = left . layout . ty
10082 . builtin_deref ( true )
10183 . expect ( "Offset called on non-ptr type" )
10284 . ty ;
10385 let ptr = self . pointer_offset_inbounds (
104- left,
86+ left. to_scalar ( ) ? ,
10587 pointee_ty,
106- right. to_isize ( self ) ?,
88+ right. to_scalar ( ) ? . to_isize ( self ) ?,
10789 ) ?;
10890 ( ptr, false )
10991 }
110- // These need both to be pointer, and fail if they are not in the same location
111- Lt | Le | Gt | Ge | Sub if left. is_ptr ( ) && right. is_ptr ( ) => {
112- let left = left. to_ptr ( ) . expect ( "we checked is_ptr" ) ;
113- let right = right. to_ptr ( ) . expect ( "we checked is_ptr" ) ;
114- if left. alloc_id == right. alloc_id {
115- let res = match bin_op {
116- Lt => left. offset < right. offset ,
117- Le => left. offset <= right. offset ,
118- Gt => left. offset > right. offset ,
119- Ge => left. offset >= right. offset ,
120- Sub => {
121- // subtract the offsets
122- let left_offset = Scalar :: from_uint ( left. offset . bytes ( ) , self . memory ( ) . pointer_size ( ) ) ;
123- let right_offset = Scalar :: from_uint ( right. offset . bytes ( ) , self . memory ( ) . pointer_size ( ) ) ;
124- let layout = self . layout_of ( self . tcx . types . usize ) ?;
125- return self . binary_op (
126- Sub ,
127- ImmTy :: from_scalar ( left_offset, layout) ,
128- ImmTy :: from_scalar ( right_offset, layout) ,
129- )
130- }
131- _ => bug ! ( "We already established it has to be one of these operators." ) ,
132- } ;
133- ( Scalar :: from_bool ( res) , false )
134- } else {
135- // Both are pointers, but from different allocations.
136- throw_unsup ! ( InvalidPointerMath )
137- }
138- }
139- Gt | Ge if left. is_ptr ( ) && right. is_bits ( ) => {
140- // "ptr >[=] integer" can be tested if the integer is small enough.
141- let left = left. to_ptr ( ) . expect ( "we checked is_ptr" ) ;
142- let right = right. to_bits ( self . memory ( ) . pointer_size ( ) ) . expect ( "we checked is_bits" ) ;
143- let ( _alloc_size, alloc_align) = self . memory ( )
144- . get_size_and_align ( left. alloc_id , AllocCheck :: MaybeDead )
145- . expect ( "alloc info with MaybeDead cannot fail" ) ;
146- let min_ptr_val = u128:: from ( alloc_align. bytes ( ) ) + u128:: from ( left. offset . bytes ( ) ) ;
147- let result = match bin_op {
148- Gt => min_ptr_val > right,
149- Ge => min_ptr_val >= right,
150- _ => bug ! ( ) ,
151- } ;
152- if result {
153- // Definitely true!
154- ( Scalar :: from_bool ( true ) , false )
155- } else {
156- // Sorry, can't tell.
157- throw_unsup ! ( InvalidPointerMath )
158- }
159- }
160- // These work if the left operand is a pointer, and the right an integer
161- Add | BitAnd | Sub | Rem if left. is_ptr ( ) && right. is_bits ( ) => {
162- // Cast to i128 is fine as we checked the kind to be ptr-sized
163- self . ptr_int_arithmetic (
164- bin_op,
165- left. to_ptr ( ) . expect ( "we checked is_ptr" ) ,
166- right. to_bits ( self . memory ( ) . pointer_size ( ) ) . expect ( "we checked is_bits" ) ,
167- right_layout. abi . is_signed ( ) ,
168- ) ?
169- }
170- // Commutative operators also work if the integer is on the left
171- Add | BitAnd if left. is_bits ( ) && right. is_ptr ( ) => {
172- // This is a commutative operation, just swap the operands
173- self . ptr_int_arithmetic (
174- bin_op,
175- right. to_ptr ( ) . expect ( "we checked is_ptr" ) ,
176- left. to_bits ( self . memory ( ) . pointer_size ( ) ) . expect ( "we checked is_bits" ) ,
177- left_layout. abi . is_signed ( ) ,
178- ) ?
179- }
180- // Nothing else works
181- _ => throw_unsup ! ( InvalidPointerMath ) ,
92+
93+ _ => bug ! ( "Invalid operator on pointers: {:?}" , bin_op)
18294 } )
18395 }
18496
@@ -195,99 +107,6 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
195107 Ok ( left == right)
196108 }
197109
198- fn ptr_int_arithmetic (
199- & self ,
200- bin_op : mir:: BinOp ,
201- left : Pointer < Tag > ,
202- right : u128 ,
203- signed : bool ,
204- ) -> InterpResult < ' tcx , ( Scalar < Tag > , bool ) > {
205- use rustc:: mir:: BinOp :: * ;
206-
207- fn map_to_primval ( ( res, over) : ( Pointer < Tag > , bool ) ) -> ( Scalar < Tag > , bool ) {
208- ( Scalar :: Ptr ( res) , over)
209- }
210-
211- Ok ( match bin_op {
212- Sub =>
213- // The only way this can overflow is by underflowing, so signdeness of the right
214- // operands does not matter.
215- map_to_primval ( left. overflowing_signed_offset ( -( right as i128 ) , self ) ) ,
216- Add if signed =>
217- map_to_primval ( left. overflowing_signed_offset ( right as i128 , self ) ) ,
218- Add if !signed =>
219- map_to_primval ( left. overflowing_offset ( Size :: from_bytes ( right as u64 ) , self ) ) ,
220-
221- BitAnd if !signed => {
222- let ptr_base_align = self . memory ( ) . get_size_and_align ( left. alloc_id , AllocCheck :: MaybeDead )
223- . expect ( "alloc info with MaybeDead cannot fail" )
224- . 1 . bytes ( ) ;
225- let base_mask = {
226- // FIXME: use `interpret::truncate`, once that takes a `Size` instead of a `Layout`.
227- let shift = 128 - self . memory ( ) . pointer_size ( ) . bits ( ) ;
228- let value = !( ptr_base_align as u128 - 1 ) ;
229- // Truncate (shift left to drop out leftover values, shift right to fill with zeroes).
230- ( value << shift) >> shift
231- } ;
232- let ptr_size = self . memory ( ) . pointer_size ( ) ;
233- trace ! ( "ptr BitAnd, align {}, operand {:#010x}, base_mask {:#010x}" ,
234- ptr_base_align, right, base_mask) ;
235- if right & base_mask == base_mask {
236- // Case 1: the base address bits are all preserved, i.e., right is all-1 there.
237- let offset = ( left. offset . bytes ( ) as u128 & right) as u64 ;
238- (
239- Scalar :: Ptr ( Pointer :: new_with_tag (
240- left. alloc_id ,
241- Size :: from_bytes ( offset) ,
242- left. tag ,
243- ) ) ,
244- false ,
245- )
246- } else if right & base_mask == 0 {
247- // Case 2: the base address bits are all taken away, i.e., right is all-0 there.
248- let v = Scalar :: from_uint ( ( left. offset . bytes ( ) as u128 ) & right, ptr_size) ;
249- ( v, false )
250- } else {
251- throw_unsup ! ( ReadPointerAsBytes ) ;
252- }
253- }
254-
255- Rem if !signed => {
256- // Doing modulo a divisor of the alignment is allowed.
257- // (Intuition: modulo a divisor leaks less information.)
258- let ptr_base_align = self . memory ( ) . get_size_and_align ( left. alloc_id , AllocCheck :: MaybeDead )
259- . expect ( "alloc info with MaybeDead cannot fail" )
260- . 1 . bytes ( ) ;
261- let right = right as u64 ;
262- let ptr_size = self . memory ( ) . pointer_size ( ) ;
263- if right == 1 {
264- // Modulo 1 is always 0.
265- ( Scalar :: from_uint ( 0u32 , ptr_size) , false )
266- } else if ptr_base_align % right == 0 {
267- // The base address would be cancelled out by the modulo operation, so we can
268- // just take the modulo of the offset.
269- (
270- Scalar :: from_uint ( ( left. offset . bytes ( ) % right) as u128 , ptr_size) ,
271- false ,
272- )
273- } else {
274- throw_unsup ! ( ReadPointerAsBytes ) ;
275- }
276- }
277-
278- _ => {
279- let msg = format ! (
280- "unimplemented binary op on pointer {:?}: {:?}, {:?} ({})" ,
281- bin_op,
282- left,
283- right,
284- if signed { "signed" } else { "unsigned" }
285- ) ;
286- throw_unsup ! ( Unimplemented ( msg) ) ;
287- }
288- } )
289- }
290-
291110 /// Raises an error if the offset moves the pointer outside of its allocation.
292111 /// We consider ZSTs their own huge allocation that doesn't overlap with anything (and nothing
293112 /// moves in there because the size is 0). We also consider the NULL pointer its own separate
0 commit comments