@@ -2,10 +2,11 @@ use rustc_hir::def::DefKind;
22use rustc_hir:: LangItem ;
33use rustc_middle:: mir;
44use rustc_middle:: mir:: interpret:: PointerArithmetic ;
5- use rustc_middle:: ty:: layout:: LayoutOf ;
5+ use rustc_middle:: ty:: layout:: FnAbiOf ;
66use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
77use std:: borrow:: Borrow ;
88use std:: hash:: Hash ;
9+ use std:: ops:: ControlFlow ;
910
1011use rustc_data_structures:: fx:: FxIndexMap ;
1112use rustc_data_structures:: fx:: IndexEntry ;
@@ -20,8 +21,8 @@ use rustc_target::abi::{Align, Size};
2021use rustc_target:: spec:: abi:: Abi as CallAbi ;
2122
2223use 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 ,
2526} ;
2627
2728use super :: error:: * ;
@@ -191,21 +192,24 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
191192
192193 return Ok ( Some ( new_instance) ) ;
193194 } 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+ }
197200 }
198201 Ok ( Some ( instance) )
199202 }
200203
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.
203206 ///
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.
205209 ///
206210 /// 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.
209213 ///
210214 /// If `ptr` doesn't have an address and `target_align` is stricter than the underlying
211215 /// allocation's alignment, then we return `usize::MAX` immediately.
@@ -215,103 +219,53 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
215219 args : & [ OpTy < ' tcx > ] ,
216220 dest : & PlaceTy < ' tcx > ,
217221 ret : Option < mir:: BasicBlock > ,
218- ) -> InterpResult < ' tcx > {
222+ ) -> InterpResult < ' tcx , ControlFlow < ( ) > > {
219223 assert_eq ! ( args. len( ) , 2 ) ;
220224
221225 let ptr = self . read_pointer ( & args[ 0 ] ) ?;
222226 let target_align = self . read_scalar ( & args[ 1 ] ) ?. to_machine_usize ( self ) ?;
223227
224- let pointee_ty = instance. substs . type_at ( 0 ) ;
225- let stride = self . layout_of ( pointee_ty) ?. size . bytes ( ) ;
226-
227228 if !target_align. is_power_of_two ( ) {
228229 throw_ub_format ! ( "`align_offset` called with non-power-of-two align: {}" , target_align) ;
229230 }
230231
231- let mut align_offset = match self . ptr_try_get_alloc_id ( ptr) {
232+ match self . ptr_try_get_alloc_id ( ptr) {
232233 Ok ( ( alloc_id, offset, _extra) ) => {
233- // Extract the address relative to a base that is definitely sufficiently aligned.
234234 let ( _size, alloc_align, _kind) = self . get_alloc_info ( alloc_id) ;
235235
236236 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 )
241256 } 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 )
245262 }
246263 }
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 )
287267 }
288268 }
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
315269 }
316270
317271 /// See documentation on the `ptr_guaranteed_cmp` intrinsic.
0 commit comments