]>
Commit | Line | Data |
---|---|---|
b7449926 XL |
1 | //! Functions concerning immediate values and operands, and reading from operands. |
2 | //! All high-level functions to read from memory work on operands as sources. | |
3 | ||
487cf647 FG |
4 | use either::{Either, Left, Right}; |
5 | ||
ba9703b0 | 6 | use rustc_hir::def::Namespace; |
c295e0f8 | 7 | use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout}; |
f2b60f7d | 8 | use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter}; |
487cf647 | 9 | use rustc_middle::ty::{ConstInt, Ty, ValTree}; |
ba9703b0 | 10 | use rustc_middle::{mir, ty}; |
487cf647 | 11 | use rustc_span::Span; |
064997fb | 12 | use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size, TagEncoding}; |
ba9703b0 XL |
13 | use rustc_target::abi::{VariantIdx, Variants}; |
14 | ||
15 | use super::{ | |
064997fb FG |
16 | alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId, |
17 | InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Place, PlaceTy, Pointer, | |
f2b60f7d | 18 | Provenance, Scalar, |
ba9703b0 | 19 | }; |
0bf4aa26 | 20 | |
e74abb32 | 21 | /// An `Immediate` represents a single immediate self-contained Rust value. |
b7449926 XL |
22 | /// |
23 | /// For optimization of a few very common cases, there is also a representation for a pair of | |
24 | /// primitive values (`ScalarPair`). It allows Miri to avoid making allocations for checked binary | |
60c5eb7d | 25 | /// operations and wide pointers. This idea was taken from rustc's codegen. |
b7449926 | 26 | /// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely |
a1dfa0c6 | 27 | /// defined on `Immediate`, and do not have to work with a `Place`. |
064997fb FG |
28 | #[derive(Copy, Clone, Debug)] |
29 | pub enum Immediate<Prov: Provenance = AllocId> { | |
30 | /// A single scalar value (must have *initialized* `Scalar` ABI). | |
f2b60f7d | 31 | Scalar(Scalar<Prov>), |
064997fb FG |
32 | /// A pair of two scalar value (must have `ScalarPair` ABI where both fields are |
33 | /// `Scalar::Initialized`). | |
f2b60f7d | 34 | ScalarPair(Scalar<Prov>, Scalar<Prov>), |
064997fb FG |
35 | /// A value of fully uninitialized memory. Can have and size and layout. |
36 | Uninit, | |
b7449926 XL |
37 | } |
38 | ||
064997fb | 39 | impl<Prov: Provenance> From<Scalar<Prov>> for Immediate<Prov> { |
416331ca | 40 | #[inline(always)] |
064997fb | 41 | fn from(val: Scalar<Prov>) -> Self { |
416331ca | 42 | Immediate::Scalar(val.into()) |
9fa01778 | 43 | } |
416331ca | 44 | } |
9fa01778 | 45 | |
f2b60f7d | 46 | impl<Prov: Provenance> Immediate<Prov> { |
064997fb | 47 | pub fn from_pointer(p: Pointer<Prov>, cx: &impl HasDataLayout) -> Self { |
f2b60f7d | 48 | Immediate::Scalar(Scalar::from_pointer(p, cx)) |
136023e0 XL |
49 | } |
50 | ||
064997fb | 51 | pub fn from_maybe_pointer(p: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self { |
f2b60f7d | 52 | Immediate::Scalar(Scalar::from_maybe_pointer(p, cx)) |
60c5eb7d | 53 | } |
60c5eb7d | 54 | |
064997fb | 55 | pub fn new_slice(val: Scalar<Prov>, len: u64, cx: &impl HasDataLayout) -> Self { |
ba9703b0 | 56 | Immediate::ScalarPair(val.into(), Scalar::from_machine_usize(len, cx).into()) |
b7449926 XL |
57 | } |
58 | ||
94222f64 | 59 | pub fn new_dyn_trait( |
064997fb FG |
60 | val: Scalar<Prov>, |
61 | vtable: Pointer<Option<Prov>>, | |
94222f64 XL |
62 | cx: &impl HasDataLayout, |
63 | ) -> Self { | |
f2b60f7d | 64 | Immediate::ScalarPair(val.into(), Scalar::from_maybe_pointer(vtable, cx)) |
b7449926 XL |
65 | } |
66 | ||
67 | #[inline] | |
064997fb | 68 | #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) |
f2b60f7d | 69 | pub fn to_scalar(self) -> Scalar<Prov> { |
b7449926 | 70 | match self { |
a1dfa0c6 | 71 | Immediate::Scalar(val) => val, |
94222f64 | 72 | Immediate::ScalarPair(..) => bug!("Got a scalar pair where a scalar was expected"), |
f2b60f7d | 73 | Immediate::Uninit => bug!("Got uninit where a scalar was expected"), |
b7449926 XL |
74 | } |
75 | } | |
76 | ||
77 | #[inline] | |
064997fb | 78 | #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) |
f2b60f7d | 79 | pub fn to_scalar_pair(self) -> (Scalar<Prov>, Scalar<Prov>) { |
94222f64 | 80 | match self { |
04454e1e FG |
81 | Immediate::ScalarPair(val1, val2) => (val1, val2), |
82 | Immediate::Scalar(..) => bug!("Got a scalar where a scalar pair was expected"), | |
f2b60f7d | 83 | Immediate::Uninit => bug!("Got uninit where a scalar pair was expected"), |
94222f64 XL |
84 | } |
85 | } | |
b7449926 XL |
86 | } |
87 | ||
a1dfa0c6 | 88 | // ScalarPair needs a type to interpret, so we often have an immediate and a type together |
b7449926 | 89 | // as input for binary and cast operations. |
064997fb FG |
90 | #[derive(Clone, Debug)] |
91 | pub struct ImmTy<'tcx, Prov: Provenance = AllocId> { | |
92 | imm: Immediate<Prov>, | |
ba9703b0 | 93 | pub layout: TyAndLayout<'tcx>, |
b7449926 XL |
94 | } |
95 | ||
064997fb | 96 | impl<Prov: Provenance> std::fmt::Display for ImmTy<'_, Prov> { |
ba9703b0 XL |
97 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
98 | /// Helper function for printing a scalar to a FmtPrinter | |
064997fb | 99 | fn p<'a, 'tcx, Prov: Provenance>( |
5e7ed085 | 100 | cx: FmtPrinter<'a, 'tcx>, |
f2b60f7d | 101 | s: Scalar<Prov>, |
ba9703b0 | 102 | ty: Ty<'tcx>, |
5e7ed085 | 103 | ) -> Result<FmtPrinter<'a, 'tcx>, std::fmt::Error> { |
ba9703b0 | 104 | match s { |
f2b60f7d FG |
105 | Scalar::Int(int) => cx.pretty_print_const_scalar_int(int, ty, true), |
106 | Scalar::Ptr(ptr, _sz) => { | |
136023e0 XL |
107 | // Just print the ptr value. `pretty_print_const_scalar_ptr` would also try to |
108 | // print what is points to, which would fail since it has no access to the local | |
109 | // memory. | |
110 | cx.pretty_print_const_pointer(ptr, ty, true) | |
ba9703b0 | 111 | } |
74b04a01 | 112 | } |
ba9703b0 XL |
113 | } |
114 | ty::tls::with(|tcx| { | |
115 | match self.imm { | |
116 | Immediate::Scalar(s) => { | |
29967ef6 | 117 | if let Some(ty) = tcx.lift(self.layout.ty) { |
5e7ed085 FG |
118 | let cx = FmtPrinter::new(tcx, Namespace::ValueNS); |
119 | f.write_str(&p(cx, s, ty)?.into_buffer())?; | |
ba9703b0 | 120 | return Ok(()); |
60c5eb7d | 121 | } |
5e7ed085 | 122 | write!(f, "{:x}: {}", s, self.layout.ty) |
ba9703b0 XL |
123 | } |
124 | Immediate::ScalarPair(a, b) => { | |
125 | // FIXME(oli-obk): at least print tuples and slices nicely | |
064997fb FG |
126 | write!(f, "({:x}, {:x}): {}", a, b, self.layout.ty) |
127 | } | |
128 | Immediate::Uninit => { | |
129 | write!(f, "uninit: {}", self.layout.ty) | |
dfeec247 | 130 | } |
74b04a01 | 131 | } |
ba9703b0 | 132 | }) |
60c5eb7d XL |
133 | } |
134 | } | |
135 | ||
064997fb FG |
136 | impl<'tcx, Prov: Provenance> std::ops::Deref for ImmTy<'tcx, Prov> { |
137 | type Target = Immediate<Prov>; | |
b7449926 | 138 | #[inline(always)] |
064997fb | 139 | fn deref(&self) -> &Immediate<Prov> { |
9fa01778 | 140 | &self.imm |
b7449926 XL |
141 | } |
142 | } | |
143 | ||
144 | /// An `Operand` is the result of computing a `mir::Operand`. It can be immediate, | |
9fa01778 | 145 | /// or still in memory. The latter is an optimization, to delay reading that chunk of |
b7449926 | 146 | /// memory and to avoid having to store arbitrary-sized data here. |
064997fb FG |
147 | #[derive(Copy, Clone, Debug)] |
148 | pub enum Operand<Prov: Provenance = AllocId> { | |
149 | Immediate(Immediate<Prov>), | |
150 | Indirect(MemPlace<Prov>), | |
b7449926 XL |
151 | } |
152 | ||
064997fb FG |
153 | #[derive(Clone, Debug)] |
154 | pub struct OpTy<'tcx, Prov: Provenance = AllocId> { | |
155 | op: Operand<Prov>, // Keep this private; it helps enforce invariants. | |
ba9703b0 | 156 | pub layout: TyAndLayout<'tcx>, |
064997fb FG |
157 | /// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct: |
158 | /// it needs to have a different alignment than the field type would usually have. | |
159 | /// So we represent this here with a separate field that "overwrites" `layout.align`. | |
160 | /// This means `layout.align` should never be used for an `OpTy`! | |
161 | /// `None` means "alignment does not matter since this is a by-value operand" | |
162 | /// (`Operand::Immediate`); this field is only relevant for `Operand::Indirect`. | |
163 | /// Also CTFE ignores alignment anyway, so this is for Miri only. | |
164 | pub align: Option<Align>, | |
b7449926 XL |
165 | } |
166 | ||
064997fb FG |
167 | impl<'tcx, Prov: Provenance> std::ops::Deref for OpTy<'tcx, Prov> { |
168 | type Target = Operand<Prov>; | |
b7449926 | 169 | #[inline(always)] |
064997fb | 170 | fn deref(&self) -> &Operand<Prov> { |
b7449926 XL |
171 | &self.op |
172 | } | |
173 | } | |
174 | ||
064997fb | 175 | impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> { |
b7449926 | 176 | #[inline(always)] |
064997fb FG |
177 | fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self { |
178 | OpTy { op: Operand::Indirect(*mplace), layout: mplace.layout, align: Some(mplace.align) } | |
b7449926 XL |
179 | } |
180 | } | |
181 | ||
064997fb | 182 | impl<'tcx, Prov: Provenance> From<&'_ MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> { |
6a06907d | 183 | #[inline(always)] |
064997fb FG |
184 | fn from(mplace: &MPlaceTy<'tcx, Prov>) -> Self { |
185 | OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout, align: Some(mplace.align) } | |
6a06907d XL |
186 | } |
187 | } | |
188 | ||
064997fb | 189 | impl<'tcx, Prov: Provenance> From<&'_ mut MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> { |
b7449926 | 190 | #[inline(always)] |
064997fb FG |
191 | fn from(mplace: &mut MPlaceTy<'tcx, Prov>) -> Self { |
192 | OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout, align: Some(mplace.align) } | |
b7449926 XL |
193 | } |
194 | } | |
195 | ||
064997fb FG |
196 | impl<'tcx, Prov: Provenance> From<ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> { |
197 | #[inline(always)] | |
198 | fn from(val: ImmTy<'tcx, Prov>) -> Self { | |
199 | OpTy { op: Operand::Immediate(val.imm), layout: val.layout, align: None } | |
200 | } | |
201 | } | |
202 | ||
203 | impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { | |
9fa01778 | 204 | #[inline] |
064997fb | 205 | pub fn from_scalar(val: Scalar<Prov>, layout: TyAndLayout<'tcx>) -> Self { |
416331ca | 206 | ImmTy { imm: val.into(), layout } |
9fa01778 XL |
207 | } |
208 | ||
dfeec247 | 209 | #[inline] |
064997fb | 210 | pub fn from_immediate(imm: Immediate<Prov>, layout: TyAndLayout<'tcx>) -> Self { |
ba9703b0 XL |
211 | ImmTy { imm, layout } |
212 | } | |
213 | ||
064997fb FG |
214 | #[inline] |
215 | pub fn uninit(layout: TyAndLayout<'tcx>) -> Self { | |
216 | ImmTy { imm: Immediate::Uninit, layout } | |
217 | } | |
218 | ||
ba9703b0 XL |
219 | #[inline] |
220 | pub fn try_from_uint(i: impl Into<u128>, layout: TyAndLayout<'tcx>) -> Option<Self> { | |
dfeec247 XL |
221 | Some(Self::from_scalar(Scalar::try_from_uint(i, layout.size)?, layout)) |
222 | } | |
e1599b0c | 223 | #[inline] |
ba9703b0 | 224 | pub fn from_uint(i: impl Into<u128>, layout: TyAndLayout<'tcx>) -> Self { |
e1599b0c XL |
225 | Self::from_scalar(Scalar::from_uint(i, layout.size), layout) |
226 | } | |
227 | ||
dfeec247 | 228 | #[inline] |
ba9703b0 | 229 | pub fn try_from_int(i: impl Into<i128>, layout: TyAndLayout<'tcx>) -> Option<Self> { |
dfeec247 XL |
230 | Some(Self::from_scalar(Scalar::try_from_int(i, layout.size)?, layout)) |
231 | } | |
232 | ||
e1599b0c | 233 | #[inline] |
ba9703b0 | 234 | pub fn from_int(i: impl Into<i128>, layout: TyAndLayout<'tcx>) -> Self { |
e1599b0c XL |
235 | Self::from_scalar(Scalar::from_int(i, layout.size), layout) |
236 | } | |
f035d41b XL |
237 | |
238 | #[inline] | |
239 | pub fn to_const_int(self) -> ConstInt { | |
240 | assert!(self.layout.ty.is_integral()); | |
f2b60f7d | 241 | let int = self.to_scalar().assert_int(); |
29967ef6 | 242 | ConstInt::new(int, self.layout.ty.is_signed(), self.layout.ty.is_ptr_sized_integral()) |
f035d41b | 243 | } |
9fa01778 XL |
244 | } |
245 | ||
064997fb FG |
246 | impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { |
247 | pub fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { | |
248 | if self.layout.is_unsized() { | |
249 | // There are no unsized immediates. | |
250 | self.assert_mem_place().len(cx) | |
251 | } else { | |
252 | match self.layout.fields { | |
253 | abi::FieldsShape::Array { count, .. } => Ok(count), | |
254 | _ => bug!("len not supported on sized type {:?}", self.layout.ty), | |
255 | } | |
256 | } | |
257 | } | |
258 | ||
259 | pub fn offset_with_meta( | |
260 | &self, | |
261 | offset: Size, | |
262 | meta: MemPlaceMeta<Prov>, | |
263 | layout: TyAndLayout<'tcx>, | |
264 | cx: &impl HasDataLayout, | |
265 | ) -> InterpResult<'tcx, Self> { | |
487cf647 FG |
266 | match self.as_mplace_or_imm() { |
267 | Left(mplace) => Ok(mplace.offset_with_meta(offset, meta, layout, cx)?.into()), | |
268 | Right(imm) => { | |
064997fb FG |
269 | assert!( |
270 | matches!(*imm, Immediate::Uninit), | |
271 | "Scalar/ScalarPair cannot be offset into" | |
272 | ); | |
273 | assert!(!meta.has_meta()); // no place to store metadata here | |
274 | // Every part of an uninit is uninit. | |
275 | Ok(ImmTy::uninit(layout).into()) | |
276 | } | |
277 | } | |
278 | } | |
279 | ||
280 | pub fn offset( | |
281 | &self, | |
282 | offset: Size, | |
283 | layout: TyAndLayout<'tcx>, | |
284 | cx: &impl HasDataLayout, | |
285 | ) -> InterpResult<'tcx, Self> { | |
487cf647 | 286 | assert!(layout.is_sized()); |
064997fb FG |
287 | self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx) |
288 | } | |
289 | } | |
290 | ||
ba9703b0 | 291 | impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { |
dc9dc135 | 292 | /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`. |
9fa01778 | 293 | /// Returns `None` if the layout does not permit loading this as a value. |
04454e1e FG |
294 | /// |
295 | /// This is an internal function; call `read_immediate` instead. | |
296 | fn read_immediate_from_mplace_raw( | |
b7449926 | 297 | &self, |
064997fb | 298 | mplace: &MPlaceTy<'tcx, M::Provenance>, |
064997fb | 299 | ) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::Provenance>>> { |
b7449926 | 300 | if mplace.layout.is_unsized() { |
0bf4aa26 | 301 | // Don't touch unsized |
b7449926 XL |
302 | return Ok(None); |
303 | } | |
b7449926 | 304 | |
04454e1e | 305 | let Some(alloc) = self.get_place_alloc(mplace)? else { |
064997fb FG |
306 | // zero-sized type can be left uninit |
307 | return Ok(Some(ImmTy::uninit(mplace.layout))); | |
dc9dc135 | 308 | }; |
b7449926 | 309 | |
04454e1e FG |
310 | // It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point. |
311 | // However, `MaybeUninit<u64>` is considered a `Scalar` as far as its layout is concerned -- | |
312 | // and yet cannot be represented by an interpreter `Scalar`, since we have to handle the | |
313 | // case where some of the bytes are initialized and others are not. So, we need an extra | |
314 | // check that walks over the type of `mplace` to make sure it is truly correct to treat this | |
315 | // like a `Scalar` (or `ScalarPair`). | |
f2b60f7d FG |
316 | Ok(match mplace.layout.abi { |
317 | Abi::Scalar(abi::Scalar::Initialized { value: s, .. }) => { | |
318 | let size = s.size(self); | |
319 | assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size"); | |
320 | let scalar = alloc.read_scalar( | |
321 | alloc_range(Size::ZERO, size), | |
322 | /*read_provenance*/ s.is_ptr(), | |
323 | )?; | |
324 | Some(ImmTy { imm: scalar.into(), layout: mplace.layout }) | |
325 | } | |
04454e1e FG |
326 | Abi::ScalarPair( |
327 | abi::Scalar::Initialized { value: a, .. }, | |
328 | abi::Scalar::Initialized { value: b, .. }, | |
f2b60f7d FG |
329 | ) => { |
330 | // We checked `ptr_align` above, so all fields will have the alignment they need. | |
331 | // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`, | |
332 | // which `ptr.offset(b_offset)` cannot possibly fail to satisfy. | |
333 | let (a_size, b_size) = (a.size(self), b.size(self)); | |
334 | let b_offset = a_size.align_to(b.align(self).abi); | |
335 | assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields | |
336 | let a_val = alloc.read_scalar( | |
337 | alloc_range(Size::ZERO, a_size), | |
338 | /*read_provenance*/ a.is_ptr(), | |
339 | )?; | |
340 | let b_val = alloc.read_scalar( | |
341 | alloc_range(b_offset, b_size), | |
342 | /*read_provenance*/ b.is_ptr(), | |
343 | )?; | |
344 | Some(ImmTy { | |
345 | imm: Immediate::ScalarPair(a_val.into(), b_val.into()), | |
346 | layout: mplace.layout, | |
347 | }) | |
348 | } | |
349 | _ => { | |
350 | // Neither a scalar nor scalar pair. | |
351 | None | |
352 | } | |
353 | }) | |
b7449926 XL |
354 | } |
355 | ||
04454e1e FG |
356 | /// Try returning an immediate for the operand. If the layout does not permit loading this as an |
357 | /// immediate, return where in memory we can find the data. | |
487cf647 FG |
358 | /// Note that for a given layout, this operation will either always return Left or Right! |
359 | /// succeed! Whether it returns Left depends on whether the layout can be represented | |
94222f64 | 360 | /// in an `Immediate`, not on which data is stored there currently. |
04454e1e | 361 | /// |
04454e1e | 362 | /// This is an internal function that should not usually be used; call `read_immediate` instead. |
064997fb | 363 | /// ConstProp needs it, though. |
04454e1e | 364 | pub fn read_immediate_raw( |
b7449926 | 365 | &self, |
064997fb | 366 | src: &OpTy<'tcx, M::Provenance>, |
487cf647 FG |
367 | ) -> InterpResult<'tcx, Either<MPlaceTy<'tcx, M::Provenance>, ImmTy<'tcx, M::Provenance>>> { |
368 | Ok(match src.as_mplace_or_imm() { | |
369 | Left(ref mplace) => { | |
f2b60f7d | 370 | if let Some(val) = self.read_immediate_from_mplace_raw(mplace)? { |
487cf647 | 371 | Right(val) |
b7449926 | 372 | } else { |
487cf647 | 373 | Left(*mplace) |
b7449926 | 374 | } |
dfeec247 | 375 | } |
487cf647 | 376 | Right(val) => Right(val), |
b7449926 XL |
377 | }) |
378 | } | |
379 | ||
a1dfa0c6 | 380 | /// Read an immediate from a place, asserting that that is possible with the given layout. |
f2b60f7d | 381 | /// |
487cf647 | 382 | /// If this succeeds, the `ImmTy` is never `Uninit`. |
b7449926 | 383 | #[inline(always)] |
a1dfa0c6 | 384 | pub fn read_immediate( |
0bf4aa26 | 385 | &self, |
064997fb FG |
386 | op: &OpTy<'tcx, M::Provenance>, |
387 | ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { | |
f2b60f7d FG |
388 | if !matches!( |
389 | op.layout.abi, | |
390 | Abi::Scalar(abi::Scalar::Initialized { .. }) | |
391 | | Abi::ScalarPair(abi::Scalar::Initialized { .. }, abi::Scalar::Initialized { .. }) | |
392 | ) { | |
393 | span_bug!(self.cur_span(), "primitive read not possible for type: {:?}", op.layout.ty); | |
b7449926 | 394 | } |
487cf647 | 395 | let imm = self.read_immediate_raw(op)?.right().unwrap(); |
f2b60f7d FG |
396 | if matches!(*imm, Immediate::Uninit) { |
397 | throw_ub!(InvalidUninitBytes(None)); | |
398 | } | |
399 | Ok(imm) | |
b7449926 XL |
400 | } |
401 | ||
402 | /// Read a scalar from a place | |
0bf4aa26 XL |
403 | pub fn read_scalar( |
404 | &self, | |
064997fb | 405 | op: &OpTy<'tcx, M::Provenance>, |
f2b60f7d FG |
406 | ) -> InterpResult<'tcx, Scalar<M::Provenance>> { |
407 | Ok(self.read_immediate(op)?.to_scalar()) | |
b7449926 XL |
408 | } |
409 | ||
136023e0 XL |
410 | /// Read a pointer from a place. |
411 | pub fn read_pointer( | |
412 | &self, | |
064997fb FG |
413 | op: &OpTy<'tcx, M::Provenance>, |
414 | ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> { | |
415 | self.read_scalar(op)?.to_pointer(self) | |
136023e0 XL |
416 | } |
417 | ||
064997fb FG |
418 | /// Turn the wide MPlace into a string (must already be dereferenced!) |
419 | pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, &str> { | |
b7449926 | 420 | let len = mplace.len(self)?; |
f2b60f7d | 421 | let bytes = self.read_bytes_ptr_strip_provenance(mplace.ptr, Size::from_bytes(len))?; |
29967ef6 | 422 | let str = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?; |
b7449926 XL |
423 | Ok(str) |
424 | } | |
425 | ||
3c0e092e XL |
426 | /// Converts a repr(simd) operand into an operand where `place_index` accesses the SIMD elements. |
427 | /// Also returns the number of elements. | |
064997fb FG |
428 | /// |
429 | /// Can (but does not always) trigger UB if `op` is uninitialized. | |
3c0e092e XL |
430 | pub fn operand_to_simd( |
431 | &self, | |
064997fb FG |
432 | op: &OpTy<'tcx, M::Provenance>, |
433 | ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, u64)> { | |
3c0e092e XL |
434 | // Basically we just transmute this place into an array following simd_size_and_type. |
435 | // This only works in memory, but repr(simd) types should never be immediates anyway. | |
064997fb | 436 | assert!(op.layout.ty.is_simd()); |
487cf647 FG |
437 | match op.as_mplace_or_imm() { |
438 | Left(mplace) => self.mplace_to_simd(&mplace), | |
439 | Right(imm) => match *imm { | |
064997fb FG |
440 | Immediate::Uninit => { |
441 | throw_ub!(InvalidUninitBytes(None)) | |
442 | } | |
443 | Immediate::Scalar(..) | Immediate::ScalarPair(..) => { | |
444 | bug!("arrays/slices can never have Scalar/ScalarPair layout") | |
445 | } | |
446 | }, | |
447 | } | |
3c0e092e XL |
448 | } |
449 | ||
f2b60f7d | 450 | /// Read from a local. |
f035d41b XL |
451 | /// Will not access memory, instead an indirect `Operand` is returned. |
452 | /// | |
453 | /// This is public because it is used by [priroda](https://github.com/oli-obk/priroda) to get an | |
064997fb FG |
454 | /// OpTy from a local. |
455 | pub fn local_to_op( | |
0bf4aa26 | 456 | &self, |
064997fb | 457 | frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>, |
0bf4aa26 | 458 | local: mir::Local, |
ba9703b0 | 459 | layout: Option<TyAndLayout<'tcx>>, |
064997fb | 460 | ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { |
9fa01778 | 461 | let layout = self.layout_of_local(frame, local, layout)?; |
f2b60f7d | 462 | let op = *frame.locals[local].access()?; |
064997fb | 463 | Ok(OpTy { op, layout, align: Some(layout.align.abi) }) |
0bf4aa26 XL |
464 | } |
465 | ||
ba9703b0 XL |
466 | /// Every place can be read from, so we can turn them into an operand. |
467 | /// This will definitely return `Indirect` if the place is a `Ptr`, i.e., this | |
468 | /// will never actually read from memory. | |
9fa01778 XL |
469 | #[inline(always)] |
470 | pub fn place_to_op( | |
471 | &self, | |
064997fb FG |
472 | place: &PlaceTy<'tcx, M::Provenance>, |
473 | ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { | |
6a06907d | 474 | let op = match **place { |
dfeec247 | 475 | Place::Ptr(mplace) => Operand::Indirect(mplace), |
ba9703b0 | 476 | Place::Local { frame, local } => { |
064997fb | 477 | *self.local_to_op(&self.stack()[frame], local, None)? |
ba9703b0 | 478 | } |
9fa01778 | 479 | }; |
064997fb | 480 | Ok(OpTy { op, layout: place.layout, align: Some(place.align) }) |
9fa01778 XL |
481 | } |
482 | ||
04454e1e FG |
483 | /// Evaluate a place with the goal of reading from it. This lets us sometimes |
484 | /// avoid allocations. | |
e74abb32 | 485 | pub fn eval_place_to_op( |
b7449926 | 486 | &self, |
064997fb | 487 | mir_place: mir::Place<'tcx>, |
ba9703b0 | 488 | layout: Option<TyAndLayout<'tcx>>, |
064997fb | 489 | ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { |
f9f354fc XL |
490 | // Do not use the layout passed in as argument if the base we are looking at |
491 | // here is not the entire place. | |
064997fb | 492 | let layout = if mir_place.projection.is_empty() { layout } else { None }; |
b7449926 | 493 | |
064997fb FG |
494 | let mut op = self.local_to_op(self.frame(), mir_place.local, layout)?; |
495 | // Using `try_fold` turned out to be bad for performance, hence the loop. | |
496 | for elem in mir_place.projection.iter() { | |
497 | op = self.operand_projection(&op, elem)? | |
498 | } | |
e1599b0c XL |
499 | |
500 | trace!("eval_place_to_op: got {:?}", *op); | |
f9f354fc | 501 | // Sanity-check the type we ended up with. |
064997fb FG |
502 | debug_assert!( |
503 | mir_assign_valid_types( | |
504 | *self.tcx, | |
505 | self.param_env, | |
506 | self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( | |
507 | mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty | |
508 | )?)?, | |
509 | op.layout, | |
510 | ), | |
511 | "eval_place of a MIR place with type {:?} produced an interpreter operand with type {:?}", | |
512 | mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty, | |
513 | op.layout.ty, | |
514 | ); | |
e1599b0c | 515 | Ok(op) |
b7449926 XL |
516 | } |
517 | ||
518 | /// Evaluate the operand, returning a place where you can then find the data. | |
dc9dc135 | 519 | /// If you already know the layout, you can save two table lookups |
b7449926 | 520 | /// by passing it in here. |
6a06907d | 521 | #[inline] |
b7449926 XL |
522 | pub fn eval_operand( |
523 | &self, | |
524 | mir_op: &mir::Operand<'tcx>, | |
ba9703b0 | 525 | layout: Option<TyAndLayout<'tcx>>, |
064997fb | 526 | ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { |
ba9703b0 | 527 | use rustc_middle::mir::Operand::*; |
b7449926 XL |
528 | let op = match *mir_op { |
529 | // FIXME: do some more logic on `move` to invalidate the old location | |
ba9703b0 | 530 | Copy(place) | Move(place) => self.eval_place_to_op(place, layout)?, |
b7449926 | 531 | |
e1599b0c | 532 | Constant(ref constant) => { |
487cf647 | 533 | let c = |
a2a8927a | 534 | self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal)?; |
064997fb | 535 | |
5869c6ff | 536 | // This can still fail: |
5e7ed085 | 537 | // * During ConstProp, with `TooGeneric` or since the `required_consts` were not all |
5869c6ff XL |
538 | // checked yet. |
539 | // * During CTFE, since promoteds in `const`/`static` initializer bodies can fail. | |
487cf647 | 540 | self.eval_mir_constant(&c, Some(constant.span), layout)? |
e1599b0c | 541 | } |
b7449926 XL |
542 | }; |
543 | trace!("{:?}: {:?}", mir_op, *op); | |
544 | Ok(op) | |
545 | } | |
546 | ||
547 | /// Evaluate a bunch of operands at once | |
548 | pub(super) fn eval_operands( | |
549 | &self, | |
550 | ops: &[mir::Operand<'tcx>], | |
064997fb | 551 | ) -> InterpResult<'tcx, Vec<OpTy<'tcx, M::Provenance>>> { |
74b04a01 | 552 | ops.iter().map(|op| self.eval_operand(op, None)).collect() |
b7449926 XL |
553 | } |
554 | ||
487cf647 FG |
555 | fn eval_ty_constant( |
556 | &self, | |
557 | val: ty::Const<'tcx>, | |
558 | span: Option<Span>, | |
559 | ) -> InterpResult<'tcx, ValTree<'tcx>> { | |
560 | Ok(match val.kind() { | |
561 | ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(..) => { | |
562 | throw_inval!(TooGeneric) | |
563 | } | |
564 | // FIXME(generic_const_exprs): `ConstKind::Expr` should be able to be evaluated | |
565 | ty::ConstKind::Expr(_) => throw_inval!(TooGeneric), | |
566 | ty::ConstKind::Error(reported) => { | |
567 | throw_inval!(AlreadyReported(reported)) | |
568 | } | |
569 | ty::ConstKind::Unevaluated(uv) => { | |
570 | let instance = self.resolve(uv.def, uv.substs)?; | |
571 | let cid = GlobalId { instance, promoted: None }; | |
572 | self.ctfe_query(span, |tcx| tcx.eval_to_valtree(self.param_env.and(cid)))? | |
573 | .unwrap_or_else(|| bug!("unable to create ValTree for {uv:?}")) | |
574 | } | |
575 | ty::ConstKind::Bound(..) | ty::ConstKind::Infer(..) => { | |
576 | span_bug!(self.cur_span(), "unexpected ConstKind in ctfe: {val:?}") | |
577 | } | |
578 | ty::ConstKind::Value(valtree) => valtree, | |
579 | }) | |
580 | } | |
581 | ||
582 | pub fn eval_mir_constant( | |
6a06907d XL |
583 | &self, |
584 | val: &mir::ConstantKind<'tcx>, | |
487cf647 | 585 | span: Option<Span>, |
6a06907d | 586 | layout: Option<TyAndLayout<'tcx>>, |
064997fb | 587 | ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { |
f2b60f7d FG |
588 | // FIXME(const_prop): normalization needed b/c const prop lint in |
589 | // `mir_drops_elaborated_and_const_checked`, which happens before | |
590 | // optimized MIR. Only after optimizing the MIR can we guarantee | |
591 | // that the `RevealAll` pass has happened and that the body's consts | |
592 | // are normalized, so any call to resolve before that needs to be | |
593 | // manually normalized. | |
594 | let val = self.tcx.normalize_erasing_regions(self.param_env, *val); | |
6a06907d | 595 | match val { |
2b03887a | 596 | mir::ConstantKind::Ty(ct) => { |
487cf647 FG |
597 | let ty = ct.ty(); |
598 | let valtree = self.eval_ty_constant(ct, span)?; | |
599 | let const_val = self.tcx.valtree_to_const_val((ty, valtree)); | |
600 | self.const_val_to_op(const_val, ty, layout) | |
2b03887a | 601 | } |
f2b60f7d FG |
602 | mir::ConstantKind::Val(val, ty) => self.const_val_to_op(val, ty, layout), |
603 | mir::ConstantKind::Unevaluated(uv, _) => { | |
604 | let instance = self.resolve(uv.def, uv.substs)?; | |
487cf647 | 605 | Ok(self.eval_global(GlobalId { instance, promoted: uv.promoted }, span)?.into()) |
f2b60f7d | 606 | } |
6a06907d XL |
607 | } |
608 | } | |
609 | ||
487cf647 | 610 | pub(super) fn const_val_to_op( |
6a06907d XL |
611 | &self, |
612 | val_val: ConstValue<'tcx>, | |
613 | ty: Ty<'tcx>, | |
614 | layout: Option<TyAndLayout<'tcx>>, | |
064997fb | 615 | ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { |
dc9dc135 | 616 | // Other cases need layout. |
064997fb | 617 | let adjust_scalar = |scalar| -> InterpResult<'tcx, _> { |
6a06907d | 618 | Ok(match scalar { |
136023e0 | 619 | Scalar::Ptr(ptr, size) => Scalar::Ptr(self.global_base_pointer(ptr)?, size), |
6a06907d XL |
620 | Scalar::Int(int) => Scalar::Int(int), |
621 | }) | |
622 | }; | |
623 | let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?; | |
60c5eb7d | 624 | let op = match val_val { |
416331ca | 625 | ConstValue::ByRef { alloc, offset } => { |
f9f354fc | 626 | let id = self.tcx.create_memory_alloc(alloc); |
dc9dc135 XL |
627 | // We rely on mutability being set correctly in that allocation to prevent writes |
628 | // where none should happen. | |
3dfed10e | 629 | let ptr = self.global_base_pointer(Pointer::new(id, offset))?; |
064997fb | 630 | Operand::Indirect(MemPlace::from_ptr(ptr.into())) |
dfeec247 | 631 | } |
064997fb FG |
632 | ConstValue::Scalar(x) => Operand::Immediate(adjust_scalar(x)?.into()), |
633 | ConstValue::ZeroSized => Operand::Immediate(Immediate::Uninit), | |
dc9dc135 XL |
634 | ConstValue::Slice { data, start, end } => { |
635 | // We rely on mutability being set correctly in `data` to prevent writes | |
636 | // where none should happen. | |
637 | let ptr = Pointer::new( | |
f9f354fc | 638 | self.tcx.create_memory_alloc(data), |
ba9703b0 | 639 | Size::from_bytes(start), // offset: `start` |
dc9dc135 XL |
640 | ); |
641 | Operand::Immediate(Immediate::new_slice( | |
136023e0 | 642 | Scalar::from_pointer(self.global_base_pointer(ptr)?, &*self.tcx), |
ba9703b0 | 643 | u64::try_from(end.checked_sub(start).unwrap()).unwrap(), // len: `end - start` |
dc9dc135 XL |
644 | self, |
645 | )) | |
646 | } | |
dc9dc135 | 647 | }; |
064997fb | 648 | Ok(OpTy { op, layout, align: Some(layout.align.abi) }) |
b7449926 | 649 | } |
b7449926 XL |
650 | |
651 | /// Read discriminant, return the runtime value as well as the variant index. | |
c295e0f8 | 652 | /// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)! |
b7449926 XL |
653 | pub fn read_discriminant( |
654 | &self, | |
064997fb FG |
655 | op: &OpTy<'tcx, M::Provenance>, |
656 | ) -> InterpResult<'tcx, (Scalar<M::Provenance>, VariantIdx)> { | |
f9f354fc | 657 | trace!("read_discriminant_value {:#?}", op.layout); |
f9f354fc XL |
658 | // Get type and layout of the discriminant. |
659 | let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?; | |
660 | trace!("discriminant type: {:?}", discr_layout.ty); | |
661 | ||
662 | // We use "discriminant" to refer to the value associated with a particular enum variant. | |
663 | // This is not to be confused with its "variant index", which is just determining its position in the | |
664 | // declared list of variants -- they can differ with explicitly assigned discriminants. | |
665 | // We use "tag" to refer to how the discriminant is encoded in memory, which can be either | |
f035d41b XL |
666 | // straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`). |
667 | let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants { | |
ba9703b0 | 668 | Variants::Single { index } => { |
f9f354fc XL |
669 | let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) { |
670 | Some(discr) => { | |
671 | // This type actually has discriminants. | |
672 | assert_eq!(discr.ty, discr_layout.ty); | |
673 | Scalar::from_uint(discr.val, discr_layout.size) | |
674 | } | |
675 | None => { | |
676 | // On a type without actual discriminants, variant is 0. | |
677 | assert_eq!(index.as_u32(), 0); | |
678 | Scalar::from_uint(index.as_u32(), discr_layout.size) | |
679 | } | |
680 | }; | |
681 | return Ok((discr, index)); | |
b7449926 | 682 | } |
c295e0f8 | 683 | Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => { |
f035d41b | 684 | (tag, tag_encoding, tag_field) |
ba9703b0 | 685 | } |
532ac7d7 XL |
686 | }; |
687 | ||
f9f354fc XL |
688 | // There are *three* layouts that come into play here: |
689 | // - The discriminant has a type for typechecking. This is `discr_layout`, and is used for | |
690 | // the `Scalar` we return. | |
691 | // - The tag (encoded discriminant) has layout `tag_layout`. This is always an integer type, | |
692 | // and used to interpret the value we read from the tag field. | |
693 | // For the return value, a cast to `discr_layout` is performed. | |
694 | // - The field storing the tag has a layout, which is very similar to `tag_layout` but | |
695 | // may be a pointer. This is `tag_val.layout`; we just use it for sanity checks. | |
696 | ||
697 | // Get layout for tag. | |
04454e1e | 698 | let tag_layout = self.layout_of(tag_scalar_layout.primitive().to_int_ty(*self.tcx))?; |
f9f354fc XL |
699 | |
700 | // Read tag and sanity-check `tag_layout`. | |
6a06907d | 701 | let tag_val = self.read_immediate(&self.operand_field(op, tag_field)?)?; |
f9f354fc XL |
702 | assert_eq!(tag_layout.size, tag_val.layout.size); |
703 | assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed()); | |
5e7ed085 | 704 | trace!("tag value: {}", tag_val); |
f9f354fc XL |
705 | |
706 | // Figure out which discriminant and variant this corresponds to. | |
f035d41b XL |
707 | Ok(match *tag_encoding { |
708 | TagEncoding::Direct => { | |
f2b60f7d | 709 | let scalar = tag_val.to_scalar(); |
5e7ed085 FG |
710 | // Generate a specific error if `tag_val` is not an integer. |
711 | // (`tag_bits` itself is only used for error messages below.) | |
04454e1e | 712 | let tag_bits = scalar |
136023e0 XL |
713 | .try_to_int() |
714 | .map_err(|dbg_val| err_ub!(InvalidTag(dbg_val)))? | |
715 | .assert_bits(tag_layout.size); | |
f9f354fc | 716 | // Cast bits from tag layout to discriminant layout. |
04454e1e FG |
717 | // After the checks we did above, this cannot fail, as |
718 | // discriminants are int-like. | |
5e7ed085 | 719 | let discr_val = |
04454e1e | 720 | self.cast_from_int_like(scalar, tag_val.layout, discr_layout.ty).unwrap(); |
f035d41b | 721 | let discr_bits = discr_val.assert_bits(discr_layout.size); |
f9f354fc | 722 | // Convert discriminant to variant index, and catch invalid discriminants. |
1b1a35ee | 723 | let index = match *op.layout.ty.kind() { |
dfeec247 | 724 | ty::Adt(adt, _) => { |
f035d41b | 725 | adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits) |
dfeec247 | 726 | } |
e74abb32 XL |
727 | ty::Generator(def_id, substs, _) => { |
728 | let substs = substs.as_generator(); | |
729 | substs | |
f035d41b | 730 | .discriminants(def_id, *self.tcx) |
f9f354fc | 731 | .find(|(_, var)| var.val == discr_bits) |
e74abb32 | 732 | } |
f035d41b | 733 | _ => span_bug!(self.cur_span(), "tagged layout for non-adt non-generator"), |
dfeec247 | 734 | } |
136023e0 | 735 | .ok_or_else(|| err_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size))))?; |
f9f354fc | 736 | // Return the cast value, and the index. |
f035d41b | 737 | (discr_val, index.0) |
dfeec247 | 738 | } |
f2b60f7d FG |
739 | TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => { |
740 | let tag_val = tag_val.to_scalar(); | |
f9f354fc XL |
741 | // Compute the variant this niche value/"tag" corresponds to. With niche layout, |
742 | // discriminant (encoded in niche/tag) and variant index are the same. | |
e1599b0c XL |
743 | let variants_start = niche_variants.start().as_u32(); |
744 | let variants_end = niche_variants.end().as_u32(); | |
136023e0 XL |
745 | let variant = match tag_val.try_to_int() { |
746 | Err(dbg_val) => { | |
747 | // So this is a pointer then, and casting to an int failed. | |
748 | // Can only happen during CTFE. | |
136023e0 XL |
749 | // The niche must be just 0, and the ptr not null, then we know this is |
750 | // okay. Everything else, we conservatively reject. | |
dfeec247 XL |
751 | let ptr_valid = niche_start == 0 |
752 | && variants_start == variants_end | |
04454e1e | 753 | && !self.scalar_may_be_null(tag_val)?; |
a1dfa0c6 | 754 | if !ptr_valid { |
136023e0 | 755 | throw_ub!(InvalidTag(dbg_val)) |
a1dfa0c6 | 756 | } |
f2b60f7d | 757 | untagged_variant |
dfeec247 | 758 | } |
f9f354fc | 759 | Ok(tag_bits) => { |
136023e0 | 760 | let tag_bits = tag_bits.assert_bits(tag_layout.size); |
e1599b0c | 761 | // We need to use machine arithmetic to get the relative variant idx: |
f9f354fc XL |
762 | // variant_index_relative = tag_val - niche_start_val |
763 | let tag_val = ImmTy::from_uint(tag_bits, tag_layout); | |
764 | let niche_start_val = ImmTy::from_uint(niche_start, tag_layout); | |
dfeec247 | 765 | let variant_index_relative_val = |
6a06907d | 766 | self.binary_op(mir::BinOp::Sub, &tag_val, &niche_start_val)?; |
f2b60f7d FG |
767 | let variant_index_relative = |
768 | variant_index_relative_val.to_scalar().assert_bits(tag_val.layout.size); | |
e1599b0c XL |
769 | // Check if this is in the range that indicates an actual discriminant. |
770 | if variant_index_relative <= u128::from(variants_end - variants_start) { | |
771 | let variant_index_relative = u32::try_from(variant_index_relative) | |
772 | .expect("we checked that this fits into a u32"); | |
773 | // Then computing the absolute variant idx should not overflow any more. | |
774 | let variant_index = variants_start | |
775 | .checked_add(variant_index_relative) | |
74b04a01 | 776 | .expect("overflow computing absolute variant idx"); |
f9f354fc | 777 | let variants_len = op |
dfeec247 XL |
778 | .layout |
779 | .ty | |
a1dfa0c6 XL |
780 | .ty_adt_def() |
781 | .expect("tagged layout for non adt") | |
5e7ed085 | 782 | .variants() |
dfeec247 | 783 | .len(); |
ba9703b0 | 784 | assert!(usize::try_from(variant_index).unwrap() < variants_len); |
f9f354fc | 785 | VariantIdx::from_u32(variant_index) |
b7449926 | 786 | } else { |
f2b60f7d | 787 | untagged_variant |
b7449926 | 788 | } |
dfeec247 | 789 | } |
f9f354fc XL |
790 | }; |
791 | // Compute the size of the scalar we need to return. | |
792 | // No need to cast, because the variant index directly serves as discriminant and is | |
793 | // encoded in the tag. | |
794 | (Scalar::from_uint(variant.as_u32(), discr_layout.size), variant) | |
b7449926 XL |
795 | } |
796 | }) | |
797 | } | |
b7449926 | 798 | } |
064997fb FG |
799 | |
800 | // Some nodes are used a lot. Make sure they don't unintentionally get bigger. | |
2b03887a | 801 | #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] |
064997fb FG |
802 | mod size_asserts { |
803 | use super::*; | |
f2b60f7d | 804 | use rustc_data_structures::static_assert_size; |
2b03887a | 805 | // tidy-alphabetical-start |
f2b60f7d FG |
806 | static_assert_size!(Immediate, 48); |
807 | static_assert_size!(ImmTy<'_>, 64); | |
808 | static_assert_size!(Operand, 56); | |
809 | static_assert_size!(OpTy<'_>, 80); | |
2b03887a | 810 | // tidy-alphabetical-end |
064997fb | 811 | } |