@@ -2,10 +2,11 @@ use rustc_hir::def::DefKind;
2
2
use rustc_hir:: LangItem ;
3
3
use rustc_middle:: mir;
4
4
use rustc_middle:: mir:: interpret:: PointerArithmetic ;
5
- use rustc_middle:: ty:: layout:: LayoutOf ;
5
+ use rustc_middle:: ty:: layout:: FnAbiOf ;
6
6
use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
7
7
use std:: borrow:: Borrow ;
8
8
use std:: hash:: Hash ;
9
+ use std:: ops:: ControlFlow ;
9
10
10
11
use rustc_data_structures:: fx:: FxIndexMap ;
11
12
use rustc_data_structures:: fx:: IndexEntry ;
@@ -20,8 +21,8 @@ use rustc_target::abi::{Align, Size};
20
21
use rustc_target:: spec:: abi:: Abi as CallAbi ;
21
22
22
23
use crate :: interpret:: {
23
- self , compile_time_machine, AllocId , ConstAllocation , Frame , ImmTy , InterpCx , InterpResult ,
24
- OpTy , PlaceTy , Pointer , Scalar , StackPopUnwind ,
24
+ self , compile_time_machine, AllocId , ConstAllocation , FnVal , Frame , ImmTy , InterpCx ,
25
+ InterpResult , OpTy , PlaceTy , Pointer , Scalar , StackPopUnwind ,
25
26
} ;
26
27
27
28
use super :: error:: * ;
@@ -191,21 +192,24 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
191
192
192
193
return Ok ( Some ( new_instance) ) ;
193
194
} else if Some ( def_id) == self . tcx . lang_items ( ) . align_offset_fn ( ) {
194
- // For align_offset, we replace the function call entirely.
195
- self . align_offset ( instance, args, dest, ret) ?;
196
- return Ok ( None ) ;
195
+ // For align_offset, we replace the function call if the pointer has no address.
196
+ match self . align_offset ( instance, args, dest, ret) ? {
197
+ ControlFlow :: Continue ( ( ) ) => return Ok ( Some ( instance) ) ,
198
+ ControlFlow :: Break ( ( ) ) => return Ok ( None ) ,
199
+ }
197
200
}
198
201
Ok ( Some ( instance) )
199
202
}
200
203
201
- /// This function replaces `align_offset(ptr, target_align)` in const eval, because the
202
- /// pointer may not have an address.
204
+ /// `align_offset(ptr, target_align)` needs special handling in const eval, because the pointer
205
+ /// may not have an address.
203
206
///
204
- /// If `ptr` does have a known address, we forward it to [`Self::align_offset_impl`].
207
+ /// If `ptr` does have a known address, then we return `CONTINUE` and the function call should
208
+ /// proceed as normal.
205
209
///
206
210
/// If `ptr` doesn't have an address, but its underlying allocation's alignment is at most
207
- /// `target_align`, then we call [`Self::align_offset_impl`] with an dummy address relative
208
- /// to the allocation.
211
+ /// `target_align`, then we call the function again with an dummy address relative to the
212
+ /// allocation.
209
213
///
210
214
/// If `ptr` doesn't have an address and `target_align` is stricter than the underlying
211
215
/// allocation's alignment, then we return `usize::MAX` immediately.
@@ -215,103 +219,53 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
215
219
args : & [ OpTy < ' tcx > ] ,
216
220
dest : & PlaceTy < ' tcx > ,
217
221
ret : Option < mir:: BasicBlock > ,
218
- ) -> InterpResult < ' tcx > {
222
+ ) -> InterpResult < ' tcx , ControlFlow < ( ) > > {
219
223
assert_eq ! ( args. len( ) , 2 ) ;
220
224
221
225
let ptr = self . read_pointer ( & args[ 0 ] ) ?;
222
226
let target_align = self . read_scalar ( & args[ 1 ] ) ?. to_machine_usize ( self ) ?;
223
227
224
- let pointee_ty = instance. substs . type_at ( 0 ) ;
225
- let stride = self . layout_of ( pointee_ty) ?. size . bytes ( ) ;
226
-
227
228
if !target_align. is_power_of_two ( ) {
228
229
throw_ub_format ! ( "`align_offset` called with non-power-of-two align: {}" , target_align) ;
229
230
}
230
231
231
- let mut align_offset = match self . ptr_try_get_alloc_id ( ptr) {
232
+ match self . ptr_try_get_alloc_id ( ptr) {
232
233
Ok ( ( alloc_id, offset, _extra) ) => {
233
- // Extract the address relative to a base that is definitely sufficiently aligned.
234
234
let ( _size, alloc_align, _kind) = self . get_alloc_info ( alloc_id) ;
235
235
236
236
if target_align <= alloc_align. bytes ( ) {
237
- // The pointer *is* alignable in const. We use an address relative to the
238
- // allocation base that is definitely sufficiently aligned.
239
- let addr = offset. bytes ( ) ;
240
- Self :: align_offset_impl ( addr, stride, target_align)
237
+ // Extract the address relative to the allocation base that is definitely
238
+ // sufficiently aligned and call `align_offset` again.
239
+ let addr = ImmTy :: from_uint ( offset. bytes ( ) , args[ 0 ] . layout ) . into ( ) ;
240
+ let align = ImmTy :: from_uint ( target_align, args[ 1 ] . layout ) . into ( ) ;
241
+ let fn_abi = self . fn_abi_of_instance ( instance, ty:: List :: empty ( ) ) ?;
242
+
243
+ // We replace the entire entire function call with a "tail call".
244
+ // Note that this happens before the frame of the original function
245
+ // is pushed on the stack.
246
+ self . eval_fn_call (
247
+ FnVal :: Instance ( instance) ,
248
+ ( CallAbi :: Rust , fn_abi) ,
249
+ & [ addr, align] ,
250
+ /* with_caller_location = */ false ,
251
+ dest,
252
+ ret,
253
+ StackPopUnwind :: NotAllowed ,
254
+ ) ?;
255
+ Ok ( ControlFlow :: BREAK )
241
256
} else {
242
- // The pointer *is not* alignable in const, return `usize::MAX`.
243
- // (We clamp this to machine `usize` below.)
244
- u64:: MAX
257
+ // Not alignable in const, return `usize::MAX`.
258
+ let usize_max = Scalar :: from_machine_usize ( self . machine_usize_max ( ) , self ) ;
259
+ self . write_scalar ( usize_max, dest) ?;
260
+ self . return_to_block ( ret) ?;
261
+ Ok ( ControlFlow :: BREAK )
245
262
}
246
263
}
247
- Err ( addr) => {
248
- // The pointer has a known address.
249
- Self :: align_offset_impl ( addr, stride, target_align)
250
- }
251
- } ;
252
-
253
- let usize_max = self . machine_usize_max ( ) ;
254
- if align_offset > usize_max {
255
- align_offset = usize_max;
256
- }
257
-
258
- self . write_scalar ( Scalar :: from_machine_usize ( align_offset, self ) , dest) ?;
259
- self . return_to_block ( ret) ?;
260
-
261
- Ok ( ( ) )
262
- }
263
-
264
- /// Const eval implementation of `#[lang = "align_offset"]`.
265
- /// See the runtime version for a detailed explanation how this works.
266
- fn align_offset_impl ( addr : u64 , stride : u64 , align : u64 ) -> u64 {
267
- assert ! ( align. is_power_of_two( ) ) ;
268
-
269
- let addr_mod_align = addr % align;
270
-
271
- if addr_mod_align == 0 {
272
- // The address is already sufficiently aligned.
273
- return 0 ;
274
- }
275
-
276
- if stride == 0 {
277
- // The address cannot be aligned.
278
- return u64:: MAX ;
279
- }
280
-
281
- if align % stride == 0 {
282
- let byte_offset = align - addr_mod_align;
283
- if byte_offset % stride == 0 {
284
- return byte_offset / stride;
285
- } else {
286
- return u64:: MAX ;
264
+ Err ( _addr) => {
265
+ // The pointer has an address, continue with function call.
266
+ Ok ( ControlFlow :: CONTINUE )
287
267
}
288
268
}
289
-
290
- // This only works, because `align` is a power of two.
291
- let gcd = 1u64 << ( stride | align) . trailing_zeros ( ) ;
292
-
293
- if addr % gcd != 0 {
294
- // The address cannot be aligned.
295
- return u64:: MAX ;
296
- }
297
-
298
- // Instead of `(addr + offset * stride) % align == 0`, we solve
299
- // `((addr + offset * stride) / gcd) % (align / gcd) == 0`.
300
- let addr2 = addr / gcd;
301
- let align2 = align / gcd;
302
- let stride2 = stride / gcd;
303
-
304
- let mut stride_inv = 1u64 ;
305
- let mut mod_gate = 2u64 ;
306
- let mut overflow = false ;
307
- while !overflow && mod_gate < align2 {
308
- stride_inv =
309
- stride_inv. wrapping_mul ( 2u64 . wrapping_sub ( stride2. wrapping_mul ( stride_inv) ) ) ;
310
- ( mod_gate, overflow) = mod_gate. overflowing_mul ( mod_gate) ;
311
- }
312
-
313
- let byte_offset = align2 - addr2 % align2;
314
- byte_offset. wrapping_mul ( stride_inv) % align2
315
269
}
316
270
317
271
/// See documentation on the `ptr_guaranteed_cmp` intrinsic.
0 commit comments