Skip to content

Commit 651ee57

Browse files
author
Lukas Markeffsky
committed
make const align_offset useful
1 parent e53c33b commit 651ee57

File tree

6 files changed

+191
-59
lines changed

6 files changed

+191
-59
lines changed

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 128 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use rustc_hir::def::DefKind;
22
use rustc_middle::mir;
3+
use rustc_middle::mir::interpret::PointerArithmetic;
4+
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
35
use rustc_middle::ty::{self, Ty, TyCtxt};
46
use std::borrow::Borrow;
57
use std::collections::hash_map::Entry;
@@ -17,58 +19,12 @@ use rustc_target::abi::{Align, Size};
1719
use rustc_target::spec::abi::Abi as CallAbi;
1820

1921
use crate::interpret::{
20-
self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult,
21-
OpTy, PlaceTy, Pointer, Scalar, StackPopUnwind,
22+
self, compile_time_machine, AllocId, ConstAllocation, FnVal, Frame, ImmTy, InterpCx,
23+
InterpResult, OpTy, PlaceTy, Pointer, Scalar, StackPopUnwind,
2224
};
2325

2426
use super::error::*;
2527

26-
impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> {
27-
/// "Intercept" a function call to a panic-related function
28-
/// because we have something special to do for it.
29-
/// If this returns successfully (`Ok`), the function should just be evaluated normally.
30-
fn hook_special_const_fn(
31-
&mut self,
32-
instance: ty::Instance<'tcx>,
33-
args: &[OpTy<'tcx>],
34-
) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
35-
// All `#[rustc_do_not_const_check]` functions should be hooked here.
36-
let def_id = instance.def_id();
37-
38-
if Some(def_id) == self.tcx.lang_items().panic_display()
39-
|| Some(def_id) == self.tcx.lang_items().begin_panic_fn()
40-
{
41-
// &str or &&str
42-
assert!(args.len() == 1);
43-
44-
let mut msg_place = self.deref_operand(&args[0])?;
45-
while msg_place.layout.ty.is_ref() {
46-
msg_place = self.deref_operand(&msg_place.into())?;
47-
}
48-
49-
let msg = Symbol::intern(self.read_str(&msg_place)?);
50-
let span = self.find_closest_untracked_caller_location();
51-
let (file, line, col) = self.location_triple_for_span(span);
52-
return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into());
53-
} else if Some(def_id) == self.tcx.lang_items().panic_fmt() {
54-
// For panic_fmt, call const_panic_fmt instead.
55-
if let Some(const_panic_fmt) = self.tcx.lang_items().const_panic_fmt() {
56-
return Ok(Some(
57-
ty::Instance::resolve(
58-
*self.tcx,
59-
ty::ParamEnv::reveal_all(),
60-
const_panic_fmt,
61-
self.tcx.intern_substs(&[]),
62-
)
63-
.unwrap()
64-
.unwrap(),
65-
));
66-
}
67-
}
68-
Ok(None)
69-
}
70-
}
71-
7228
/// Extra machine state for CTFE, and the Machine instance
7329
pub struct CompileTimeInterpreter<'mir, 'tcx> {
7430
/// For now, the number of terminators that can be evaluated before we throw a resource
@@ -191,6 +147,120 @@ impl interpret::MayLeak for ! {
191147
}
192148

193149
impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
150+
/// "Intercept" a function call to a panic-related function
151+
/// because we have something special to do for it.
152+
/// If this returns successfully (`Ok`), the function should just be evaluated normally.
153+
fn hook_special_const_fn(
154+
&mut self,
155+
instance: ty::Instance<'tcx>,
156+
_abi: CallAbi,
157+
args: &[OpTy<'tcx>],
158+
dest: &PlaceTy<'tcx>,
159+
ret: Option<mir::BasicBlock>,
160+
) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
161+
// All `#[rustc_do_not_const_check]` functions should be hooked here.
162+
let def_id = instance.def_id();
163+
164+
if Some(def_id) == self.tcx.lang_items().panic_display()
165+
|| Some(def_id) == self.tcx.lang_items().begin_panic_fn()
166+
{
167+
// &str or &&str
168+
assert!(args.len() == 1);
169+
170+
let mut msg_place = self.deref_operand(&args[0])?;
171+
while msg_place.layout.ty.is_ref() {
172+
msg_place = self.deref_operand(&msg_place.into())?;
173+
}
174+
175+
let msg = Symbol::intern(self.read_str(&msg_place)?);
176+
let span = self.find_closest_untracked_caller_location();
177+
let (file, line, col) = self.location_triple_for_span(span);
178+
return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into());
179+
} else if Some(def_id) == self.tcx.lang_items().panic_fmt() {
180+
// For panic_fmt, call const_panic_fmt instead.
181+
let Some(const_def_id) = self.tcx.lang_items().const_panic_fmt() else {
182+
bug!("`const_panic_fmt` must be defined to call `panic_fmt` in const eval")
183+
};
184+
let new_instance = ty::Instance::resolve(
185+
*self.tcx,
186+
ty::ParamEnv::reveal_all(),
187+
const_def_id,
188+
instance.substs,
189+
)
190+
.unwrap()
191+
.unwrap();
192+
193+
return Ok(Some(new_instance));
194+
} else if Some(def_id) == self.tcx.lang_items().align_offset_fn() {
195+
// For align_offset, we either call const_align_offset or return usize::MAX directly.
196+
197+
let Some(const_def_id) = self.tcx.lang_items().const_align_offset_fn() else {
198+
bug!("`const_align_offset` must be defined to call `align_offset` in const eval")
199+
};
200+
let const_instance = ty::Instance::resolve(
201+
*self.tcx,
202+
ty::ParamEnv::reveal_all(),
203+
const_def_id,
204+
instance.substs,
205+
)
206+
.unwrap()
207+
.unwrap();
208+
209+
self.align_offset(const_instance, args, dest, ret)?;
210+
211+
return Ok(None);
212+
}
213+
Ok(Some(instance))
214+
}
215+
216+
fn align_offset(
217+
&mut self,
218+
const_instance: ty::Instance<'tcx>,
219+
args: &[OpTy<'tcx>],
220+
dest: &PlaceTy<'tcx>,
221+
ret: Option<mir::BasicBlock>,
222+
) -> InterpResult<'tcx> {
223+
assert_eq!(args.len(), 2);
224+
225+
let ptr = self.read_pointer(&args[0])?;
226+
let target_align = self.read_scalar(&args[1])?.to_machine_usize(self)?;
227+
228+
if !target_align.is_power_of_two() {
229+
throw_ub_format!("`align_offset` called with non-power-of-two align: {}", target_align);
230+
}
231+
232+
let addr = match self.ptr_try_get_alloc_id(ptr) {
233+
Ok((alloc_id, offset, _extra)) => {
234+
let (_size, alloc_align, _kind) = self.get_alloc_info(alloc_id);
235+
236+
if target_align > alloc_align.bytes() {
237+
let usize_max = Scalar::from_machine_usize(self.machine_usize_max(), self);
238+
self.write_scalar(usize_max, dest)?;
239+
self.return_to_block(ret)?;
240+
return Ok(());
241+
} else {
242+
offset.bytes()
243+
}
244+
}
245+
Err(addr) => addr,
246+
};
247+
248+
let usize_layout = self.layout_of(self.tcx.types.usize)?;
249+
let addr = ImmTy::from_uint(addr, usize_layout).into();
250+
let align = ImmTy::from_uint(target_align, usize_layout).into();
251+
252+
let fn_abi = self.fn_abi_of_instance(const_instance, ty::List::empty())?;
253+
self.eval_fn_call(
254+
FnVal::Instance(const_instance),
255+
(CallAbi::Rust, fn_abi),
256+
&[addr, align],
257+
false,
258+
dest,
259+
ret,
260+
StackPopUnwind::NotAllowed,
261+
)
262+
}
263+
194264
/// See documentation on the `ptr_guaranteed_cmp` intrinsic.
195265
fn guaranteed_cmp(&mut self, a: Scalar, b: Scalar) -> InterpResult<'tcx, u8> {
196266
Ok(match (a, b) {
@@ -271,8 +341,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
271341
instance: ty::Instance<'tcx>,
272342
_abi: CallAbi,
273343
args: &[OpTy<'tcx>],
274-
_dest: &PlaceTy<'tcx>,
275-
_ret: Option<mir::BasicBlock>,
344+
dest: &PlaceTy<'tcx>,
345+
ret: Option<mir::BasicBlock>,
276346
_unwind: StackPopUnwind, // unwinding is not supported in consts
277347
) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> {
278348
debug!("find_mir_or_eval_fn: {:?}", instance);
@@ -291,7 +361,11 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
291361
}
292362
}
293363

294-
if let Some(new_instance) = ecx.hook_special_const_fn(instance, args)? {
364+
let Some(new_instance) = ecx.hook_special_const_fn(instance, _abi, args, dest, ret)? else {
365+
return Ok(None);
366+
};
367+
368+
if new_instance != instance {
295369
// We call another const fn instead.
296370
// However, we return the *original* instance to make backtraces work out
297371
// (and we hope this does not confuse the FnAbi checks too much).
@@ -300,13 +374,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
300374
new_instance,
301375
_abi,
302376
args,
303-
_dest,
304-
_ret,
377+
dest,
378+
ret,
305379
_unwind,
306380
)?
307381
.map(|(body, _instance)| (body, instance)));
308382
}
309383
}
384+
310385
// This is a const fn. Call it.
311386
Ok(Some((ecx.load_mir(instance.def, None)?, instance)))
312387
}

compiler/rustc_hir/src/lang_items.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,8 @@ language_item_table! {
283283
MaybeUninit, sym::maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None;
284284

285285
/// Align offset for stride != 1; must not panic.
286-
AlignOffset, sym::align_offset, align_offset_fn, Target::Fn, GenericRequirement::None;
286+
AlignOffset, sym::align_offset, align_offset_fn, Target::Fn, GenericRequirement::Exact(1);
287+
ConstAlignOffset, sym::const_align_offset, const_align_offset_fn, Target::Fn, GenericRequirement::Exact(1);
287288

288289
Termination, sym::termination, termination, Target::Trait, GenericRequirement::None;
289290

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,7 @@ symbols! {
510510
concat_macro,
511511
conservative_impl_trait,
512512
console,
513+
const_align_offset,
513514
const_allocate,
514515
const_async_blocks,
515516
const_compare_raw_pointers,

library/core/src/ptr/const_ptr.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,6 +1303,21 @@ impl<T: ?Sized> *const T {
13031303
/// ```
13041304
#[stable(feature = "align_offset", since = "1.36.0")]
13051305
#[rustc_const_unstable(feature = "const_align_offset", issue = "90962")]
1306+
#[cfg(not(bootstrap))]
1307+
pub const fn align_offset(self, align: usize) -> usize
1308+
where
1309+
T: Sized,
1310+
{
1311+
assert!(align.is_power_of_two(), "align_offset: align is not a power-of-two");
1312+
1313+
// SAFETY: `align` has been checked to be a power of 2 above
1314+
unsafe { align_offset(self, align) }
1315+
}
1316+
1317+
#[stable(feature = "align_offset", since = "1.36.0")]
1318+
#[rustc_const_unstable(feature = "const_align_offset", issue = "90962")]
1319+
#[allow(missing_docs)]
1320+
#[cfg(bootstrap)]
13061321
pub const fn align_offset(self, align: usize) -> usize
13071322
where
13081323
T: Sized,

library/core/src/ptr/mod.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,20 +1554,46 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
15541554

15551555
/// Align pointer `p`.
15561556
///
1557-
/// Calculate offset (in terms of elements of `stride` stride) that has to be applied
1557+
/// Calculate offset (in terms of elements of `size_of::<T>()` stride) that has to be applied
15581558
/// to pointer `p` so that pointer `p` would get aligned to `a`.
15591559
///
1560+
/// # Safety
1561+
/// `a` must be a power of two.
1562+
#[lang = "align_offset"]
1563+
#[rustc_do_not_const_check]
1564+
#[cfg(not(bootstrap))]
1565+
pub(crate) const unsafe fn align_offset<T: Sized>(p: *const T, a: usize) -> usize {
1566+
// SAFETY: Caller ensures that `a` is a power of two.
1567+
unsafe { const_align_offset::<T>(p.addr(), a) }
1568+
}
1569+
1570+
#[lang = "align_offset"]
1571+
#[cfg(bootstrap)]
1572+
pub(crate) unsafe fn align_offset<T: Sized>(p: *const T, a: usize) -> usize {
1573+
// SAFETY: Caller ensures that `a` is a power of two.
1574+
unsafe { const_align_offset::<T>(p.addr(), a) }
1575+
}
1576+
1577+
/// Align address `addr`.
1578+
///
1579+
/// Calculate offset (in terms of elements of `size_of::<T>()` stride) that has to be applied
1580+
/// to address `addr` so that `addr` would get aligned to `a`.
1581+
///
15601582
/// Note: This implementation has been carefully tailored to not panic. It is UB for this to panic.
15611583
/// The only real change that can be made here is change of `INV_TABLE_MOD_16` and associated
15621584
/// constants.
15631585
///
1586+
/// # Safety
1587+
/// `a` must be a power of two.
1588+
///
15641589
/// If we ever decide to make it possible to call the intrinsic with `a` that is not a
15651590
/// power-of-two, it will probably be more prudent to just change to a naive implementation rather
15661591
/// than trying to adapt this to accommodate that change.
15671592
///
15681593
/// Any questions go to @nagisa.
1569-
#[lang = "align_offset"]
1570-
pub(crate) unsafe fn align_offset<T: Sized>(p: *const T, a: usize) -> usize {
1594+
#[cfg_attr(not(bootstrap), lang = "const_align_offset")]
1595+
#[rustc_allow_const_fn_unstable(const_exact_div)]
1596+
pub(crate) const unsafe fn const_align_offset<T: Sized>(addr: usize, a: usize) -> usize {
15711597
// FIXME(#75598): Direct use of these intrinsics improves codegen significantly at opt-level <=
15721598
// 1, where the method versions of these operations are not inlined.
15731599
use intrinsics::{
@@ -1584,7 +1610,7 @@ pub(crate) unsafe fn align_offset<T: Sized>(p: *const T, a: usize) -> usize {
15841610
///
15851611
/// Implementation of this function shall not panic. Ever.
15861612
#[inline]
1587-
unsafe fn mod_inv(x: usize, m: usize) -> usize {
1613+
const unsafe fn mod_inv(x: usize, m: usize) -> usize {
15881614
/// Multiplicative modular inverse table modulo 2⁴ = 16.
15891615
///
15901616
/// Note, that this table does not contain values where inverse does not exist (i.e., for
@@ -1624,7 +1650,6 @@ pub(crate) unsafe fn align_offset<T: Sized>(p: *const T, a: usize) -> usize {
16241650
}
16251651
}
16261652

1627-
let addr = p.addr();
16281653
let stride = mem::size_of::<T>();
16291654
// SAFETY: `a` is a power-of-two, therefore non-zero.
16301655
let a_minus_one = unsafe { unchecked_sub(a, 1) };

library/core/src/ptr/mut_ptr.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,6 +1574,21 @@ impl<T: ?Sized> *mut T {
15741574
/// ```
15751575
#[stable(feature = "align_offset", since = "1.36.0")]
15761576
#[rustc_const_unstable(feature = "const_align_offset", issue = "90962")]
1577+
#[cfg(not(bootstrap))]
1578+
pub const fn align_offset(self, align: usize) -> usize
1579+
where
1580+
T: Sized,
1581+
{
1582+
assert!(align.is_power_of_two(), "align_offset: align is not a power-of-two");
1583+
1584+
// SAFETY: `align` has been checked to be a power of 2 above
1585+
unsafe { align_offset(self, align) }
1586+
}
1587+
1588+
#[stable(feature = "align_offset", since = "1.36.0")]
1589+
#[rustc_const_unstable(feature = "const_align_offset", issue = "90962")]
1590+
#[allow(missing_docs)]
1591+
#[cfg(bootstrap)]
15771592
pub const fn align_offset(self, align: usize) -> usize
15781593
where
15791594
T: Sized,

0 commit comments

Comments
 (0)