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