]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_middle/src/mir/interpret/pointer.rs
New upstream version 1.67.1+dfsg1
[rustc.git] / compiler / rustc_middle / src / mir / interpret / pointer.rs
CommitLineData
5869c6ff 1use super::{AllocId, InterpResult};
dc9dc135 2
532ac7d7 3use rustc_macros::HashStable;
ba9703b0 4use rustc_target::abi::{HasDataLayout, Size};
a1dfa0c6 5
c295e0f8 6use std::convert::{TryFrom, TryInto};
f9f354fc 7use std::fmt;
dc9dc135 8
a1dfa0c6
XL
9////////////////////////////////////////////////////////////////////////////////
10// Pointer arithmetic
11////////////////////////////////////////////////////////////////////////////////
12
ba9703b0 13pub trait PointerArithmetic: HasDataLayout {
a1dfa0c6
XL
14 // These are not supposed to be overridden.
15
16 #[inline(always)]
17 fn pointer_size(&self) -> Size {
18 self.data_layout().pointer_size
19 }
20
5e7ed085
FG
21 #[inline(always)]
22 fn max_size_of_val(&self) -> Size {
23 Size::from_bytes(self.machine_isize_max())
24 }
25
60c5eb7d 26 #[inline]
ba9703b0 27 fn machine_usize_max(&self) -> u64 {
c295e0f8 28 self.pointer_size().unsigned_int_max().try_into().unwrap()
60c5eb7d
XL
29 }
30
f9f354fc
XL
31 #[inline]
32 fn machine_isize_min(&self) -> i64 {
c295e0f8 33 self.pointer_size().signed_int_min().try_into().unwrap()
f9f354fc
XL
34 }
35
60c5eb7d 36 #[inline]
ba9703b0 37 fn machine_isize_max(&self) -> i64 {
c295e0f8 38 self.pointer_size().signed_int_max().try_into().unwrap()
60c5eb7d
XL
39 }
40
136023e0
XL
41 #[inline]
42 fn machine_usize_to_isize(&self, val: u64) -> i64 {
43 let val = val as i64;
c295e0f8 44 // Now wrap-around into the machine_isize range.
136023e0 45 if val > self.machine_isize_max() {
2b03887a 46 // This can only happen if the ptr size is < 64, so we know max_usize_plus_1 fits into
136023e0 47 // i64.
c295e0f8 48 debug_assert!(self.pointer_size().bits() < 64);
136023e0
XL
49 let max_usize_plus_1 = 1u128 << self.pointer_size().bits();
50 val - i64::try_from(max_usize_plus_1).unwrap()
51 } else {
52 val
53 }
54 }
55
dc9dc135
XL
56 /// Helper function: truncate given value-"overflowed flag" pair to pointer size and
57 /// update "overflowed flag" if there was an overflow.
58 /// This should be called by all the other methods before returning!
a1dfa0c6 59 #[inline]
dc9dc135 60 fn truncate_to_ptr(&self, (val, over): (u64, bool)) -> (u64, bool) {
ba9703b0 61 let val = u128::from(val);
a1dfa0c6 62 let max_ptr_plus_1 = 1u128 << self.pointer_size().bits();
ba9703b0 63 (u64::try_from(val % max_ptr_plus_1).unwrap(), over || val >= max_ptr_plus_1)
a1dfa0c6
XL
64 }
65
66 #[inline]
67 fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) {
f9f354fc
XL
68 // We do not need to check if i fits in a machine usize. If it doesn't,
69 // either the wrapping_add will wrap or res will not fit in a pointer.
dc9dc135
XL
70 let res = val.overflowing_add(i);
71 self.truncate_to_ptr(res)
a1dfa0c6
XL
72 }
73
a1dfa0c6 74 #[inline]
ba9703b0 75 fn overflowing_signed_offset(&self, val: u64, i: i64) -> (u64, bool) {
f9f354fc 76 // We need to make sure that i fits in a machine isize.
5869c6ff 77 let n = i.unsigned_abs();
f9f354fc
XL
78 if i >= 0 {
79 let (val, over) = self.overflowing_offset(val, n);
80 (val, over || i > self.machine_isize_max())
a1dfa0c6 81 } else {
f9f354fc
XL
82 let res = val.overflowing_sub(n);
83 let (val, over) = self.truncate_to_ptr(res);
84 (val, over || i < self.machine_isize_min())
a1dfa0c6
XL
85 }
86 }
dc9dc135
XL
87
88 #[inline]
89 fn offset<'tcx>(&self, val: u64, i: u64) -> InterpResult<'tcx, u64> {
90 let (res, over) = self.overflowing_offset(val, i);
60c5eb7d 91 if over { throw_ub!(PointerArithOverflow) } else { Ok(res) }
dc9dc135
XL
92 }
93
94 #[inline]
95 fn signed_offset<'tcx>(&self, val: u64, i: i64) -> InterpResult<'tcx, u64> {
ba9703b0 96 let (res, over) = self.overflowing_signed_offset(val, i);
60c5eb7d 97 if over { throw_ub!(PointerArithOverflow) } else { Ok(res) }
dc9dc135 98 }
a1dfa0c6
XL
99}
100
ba9703b0 101impl<T: HasDataLayout> PointerArithmetic for T {}
a1dfa0c6 102
136023e0
XL
103/// This trait abstracts over the kind of provenance that is associated with a `Pointer`. It is
104/// mostly opaque; the `Machine` trait extends it with some more operations that also have access to
105/// some global state.
487cf647 106/// The `Debug` rendering is used to distplay bare provenance, and for the default impl of `fmt`.
136023e0
XL
107pub trait Provenance: Copy + fmt::Debug {
108 /// Says whether the `offset` field of `Pointer`s with this provenance is the actual physical address.
f2b60f7d
FG
109 /// - If `false`, the offset *must* be relative. This means the bytes representing a pointer are
110 /// different from what the Abstract Machine prescribes, so the interpreter must prevent any
111 /// operation that would inspect the underlying bytes of a pointer, such as ptr-to-int
112 /// transmutation. A `ReadPointerAsBytes` error will be raised in such situations.
113 /// - If `true`, the interpreter will permit operations to inspect the underlying bytes of a
114 /// pointer, and implement ptr-to-int transmutation by stripping provenance.
136023e0
XL
115 const OFFSET_IS_ADDR: bool;
116
117 /// Determines how a pointer should be printed.
487cf647
FG
118 ///
119 /// Default impl is only good for when `OFFSET_IS_ADDR == true`.
136023e0
XL
120 fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result
121 where
487cf647
FG
122 Self: Sized,
123 {
124 assert!(Self::OFFSET_IS_ADDR);
125 let (prov, addr) = ptr.into_parts(); // address is absolute
126 write!(f, "{:#x}", addr.bytes())?;
127 if f.alternate() {
128 write!(f, "{prov:#?}")?;
129 } else {
130 write!(f, "{prov:?}")?;
131 }
132 Ok(())
133 }
136023e0 134
04454e1e
FG
135 /// If `OFFSET_IS_ADDR == false`, provenance must always be able to
136 /// identify the allocation this ptr points to (i.e., this must return `Some`).
137 /// Otherwise this function is best-effort (but must agree with `Machine::ptr_get_alloc`).
136023e0 138 /// (Identifying the offset in that allocation, however, is harder -- use `Memory::ptr_get_alloc` for that.)
04454e1e 139 fn get_alloc_id(self) -> Option<AllocId>;
f2b60f7d
FG
140
141 /// Defines the 'join' of provenance: what happens when doing a pointer load and different bytes have different provenance.
142 fn join(left: Option<Self>, right: Option<Self>) -> Option<Self>;
136023e0
XL
143}
144
145impl Provenance for AllocId {
146 // With the `AllocId` as provenance, the `offset` is interpreted *relative to the allocation*,
147 // so ptr-to-int casts are not possible (since we do not know the global physical offset).
148 const OFFSET_IS_ADDR: bool = false;
149
150 fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 // Forward `alternate` flag to `alloc_id` printing.
152 if f.alternate() {
153 write!(f, "{:#?}", ptr.provenance)?;
154 } else {
155 write!(f, "{:?}", ptr.provenance)?;
156 }
157 // Print offset only if it is non-zero.
158 if ptr.offset.bytes() > 0 {
064997fb 159 write!(f, "+{:#x}", ptr.offset.bytes())?;
136023e0
XL
160 }
161 Ok(())
162 }
163
04454e1e
FG
164 fn get_alloc_id(self) -> Option<AllocId> {
165 Some(self)
136023e0 166 }
f2b60f7d
FG
167
168 fn join(_left: Option<Self>, _right: Option<Self>) -> Option<Self> {
169 panic!("merging provenance is not supported when `OFFSET_IS_ADDR` is false")
170 }
136023e0
XL
171}
172
f9f354fc 173/// Represents a pointer in the Miri engine.
a1dfa0c6 174///
136023e0 175/// Pointers are "tagged" with provenance information; typically the `AllocId` they belong to.
487cf647 176#[derive(Copy, Clone, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
ba9703b0 177#[derive(HashStable)]
064997fb
FG
178pub struct Pointer<Prov = AllocId> {
179 pub(super) offset: Size, // kept private to avoid accidental misinterpretation (meaning depends on `Prov` type)
180 pub provenance: Prov,
a1dfa0c6
XL
181}
182
48663c56 183static_assert_size!(Pointer, 16);
064997fb 184// `Option<Prov>` pointers are also passed around quite a bit
04454e1e
FG
185// (but not stored in permanent machine state).
186static_assert_size!(Pointer<Option<AllocId>>, 16);
9fa01778 187
f9f354fc
XL
188// We want the `Debug` output to be readable as it is used by `derive(Debug)` for
189// all the Miri types.
064997fb 190impl<Prov: Provenance> fmt::Debug for Pointer<Prov> {
f9f354fc 191 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136023e0 192 Provenance::fmt(self, f)
f9f354fc
XL
193 }
194}
195
064997fb 196impl<Prov: Provenance> fmt::Debug for Pointer<Option<Prov>> {
dc9dc135 197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136023e0 198 match self.provenance {
064997fb
FG
199 Some(prov) => Provenance::fmt(&Pointer::new(prov, self.offset), f),
200 None => write!(f, "{:#x}[noalloc]", self.offset.bytes()),
201 }
202 }
203}
204
205impl<Prov: Provenance> fmt::Display for Pointer<Option<Prov>> {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 if self.provenance.is_none() && self.offset.bytes() == 0 {
208 write!(f, "null pointer")
209 } else {
210 fmt::Debug::fmt(self, f)
136023e0 211 }
dc9dc135
XL
212 }
213}
214
60c5eb7d 215/// Produces a `Pointer` that points to the beginning of the `Allocation`.
a1dfa0c6
XL
216impl From<AllocId> for Pointer {
217 #[inline(always)]
218 fn from(alloc_id: AllocId) -> Self {
219 Pointer::new(alloc_id, Size::ZERO)
220 }
221}
222
064997fb 223impl<Prov> From<Pointer<Prov>> for Pointer<Option<Prov>> {
a1dfa0c6 224 #[inline(always)]
064997fb
FG
225 fn from(ptr: Pointer<Prov>) -> Self {
226 let (prov, offset) = ptr.into_parts();
227 Pointer::new(Some(prov), offset)
a1dfa0c6 228 }
136023e0 229}
a1dfa0c6 230
064997fb
FG
231impl<Prov> Pointer<Option<Prov>> {
232 /// Convert this pointer that *might* have a provenance into a pointer that *definitely* has a
233 /// provenance, or an absolute address.
04454e1e
FG
234 ///
235 /// This is rarely what you want; call `ptr_try_get_alloc_id` instead.
064997fb 236 pub fn into_pointer_or_addr(self) -> Result<Pointer<Prov>, Size> {
136023e0 237 match self.provenance {
064997fb 238 Some(prov) => Ok(Pointer::new(prov, self.offset)),
136023e0
XL
239 None => Err(self.offset),
240 }
241 }
04454e1e
FG
242
243 /// Returns the absolute address the pointer points to.
064997fb 244 /// Only works if Prov::OFFSET_IS_ADDR is true!
04454e1e
FG
245 pub fn addr(self) -> Size
246 where
064997fb 247 Prov: Provenance,
04454e1e 248 {
064997fb 249 assert!(Prov::OFFSET_IS_ADDR);
04454e1e
FG
250 self.offset
251 }
136023e0
XL
252}
253
064997fb
FG
254impl<Prov> Pointer<Option<Prov>> {
255 #[inline(always)]
256 pub fn from_addr(addr: u64) -> Self {
257 Pointer { provenance: None, offset: Size::from_bytes(addr) }
258 }
259
48663c56 260 #[inline(always)]
136023e0 261 pub fn null() -> Self {
064997fb 262 Pointer::from_addr(0)
48663c56 263 }
a1dfa0c6
XL
264}
265
064997fb 266impl<'tcx, Prov> Pointer<Prov> {
a1dfa0c6 267 #[inline(always)]
064997fb 268 pub fn new(provenance: Prov, offset: Size) -> Self {
136023e0
XL
269 Pointer { provenance, offset }
270 }
271
064997fb 272 /// Obtain the constituents of this pointer. Not that the meaning of the offset depends on the type `Prov`!
136023e0
XL
273 /// This function must only be used in the implementation of `Machine::ptr_get_alloc`,
274 /// and when a `Pointer` is taken apart to be stored efficiently in an `Allocation`.
275 #[inline(always)]
064997fb 276 pub fn into_parts(self) -> (Prov, Size) {
136023e0
XL
277 (self.provenance, self.offset)
278 }
279
064997fb 280 pub fn map_provenance(self, f: impl FnOnce(Prov) -> Prov) -> Self {
136023e0 281 Pointer { provenance: f(self.provenance), ..self }
a1dfa0c6
XL
282 }
283
284 #[inline]
dc9dc135 285 pub fn offset(self, i: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> {
136023e0
XL
286 Ok(Pointer {
287 offset: Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?),
288 ..self
289 })
a1dfa0c6
XL
290 }
291
292 #[inline]
293 pub fn overflowing_offset(self, i: Size, cx: &impl HasDataLayout) -> (Self, bool) {
294 let (res, over) = cx.data_layout().overflowing_offset(self.offset.bytes(), i.bytes());
136023e0
XL
295 let ptr = Pointer { offset: Size::from_bytes(res), ..self };
296 (ptr, over)
a1dfa0c6
XL
297 }
298
299 #[inline(always)]
300 pub fn wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self {
301 self.overflowing_offset(i, cx).0
302 }
303
304 #[inline]
dc9dc135 305 pub fn signed_offset(self, i: i64, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> {
136023e0
XL
306 Ok(Pointer {
307 offset: Size::from_bytes(cx.data_layout().signed_offset(self.offset.bytes(), i)?),
308 ..self
309 })
a1dfa0c6
XL
310 }
311
312 #[inline]
ba9703b0 313 pub fn overflowing_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> (Self, bool) {
a1dfa0c6 314 let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset.bytes(), i);
136023e0
XL
315 let ptr = Pointer { offset: Size::from_bytes(res), ..self };
316 (ptr, over)
a1dfa0c6
XL
317 }
318
319 #[inline(always)]
320 pub fn wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self {
ba9703b0 321 self.overflowing_signed_offset(i, cx).0
a1dfa0c6 322 }
a1dfa0c6 323}