swamp_vm_types/
lib.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/swamp
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5use crate::types::BasicTypeRef;
6use crate::types::{TypedRegister, VmType};
7use source_map_node::Node;
8use std::cmp::PartialOrd;
9use std::fmt::{Debug, Display, Formatter};
10use std::ops::{Add, Div, Sub};
11use swamp_vm_isa::prelude::align;
12use swamp_vm_isa::{
13    CountU32, FrameMemorySize, InstructionPosition, MemoryAlignment, MemoryOffset, MemorySize,
14};
15
16pub mod prelude;
17pub mod types;
18
19pub struct StackMemoryAddress(pub u32);
20
21impl Add<MemorySize> for StackMemoryAddress {
22    type Output = Self;
23
24    fn add(self, rhs: MemorySize) -> Self::Output {
25        Self(self.0 + rhs.0)
26    }
27}
28
29#[derive(Debug, Copy, Clone, PartialOrd, Ord, Eq, PartialEq)]
30pub struct HeapMemorySize(pub u32);
31
32impl Div<Self> for HeapMemorySize {
33    type Output = CountU32;
34
35    fn div(self, rhs: Self) -> Self::Output {
36        assert!(rhs.0 > 0, "Division by zero in MemorySize");
37        assert!(
38            self.0 > 0,
39            "Numerator must be positive in MemorySize division"
40        );
41        assert_eq!(
42            self.0 % rhs.0,
43            0,
44            "MemorySize division must be exact and positive"
45        );
46
47        CountU32(self.0 / rhs.0)
48    }
49}
50
51impl Add<MemoryOffset> for StackMemoryAddress {
52    type Output = Self;
53
54    fn add(self, rhs: MemoryOffset) -> Self::Output {
55        Self(self.0 + rhs.0)
56    }
57}
58
59#[derive(Debug, Copy, Clone)]
60pub struct CountU16(pub u16);
61
62impl Display for CountU16 {
63    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
64        write!(f, "{}", self.0)
65    }
66}
67
68impl FrameMemoryAddress {
69    #[must_use]
70    pub const fn advance(&self, memory_offset: MemoryOffset) -> Self {
71        Self(self.0 + memory_offset.0)
72    }
73}
74
75/// relative to the frame pointer
76impl FrameMemoryAddress {
77    #[must_use]
78    pub const fn add(&self, memory_size: MemorySize) -> Self {
79        Self(self.0 + memory_size.0)
80    }
81
82    #[must_use]
83    pub const fn add_offset(&self, memory_offset: MemoryOffset) -> Self {
84        Self(self.0 + memory_offset.0)
85    }
86    #[must_use]
87    pub const fn as_size(&self) -> FrameMemorySize {
88        FrameMemorySize(self.0)
89    }
90}
91
92impl StackMemoryAddress {
93    #[must_use]
94    pub const fn add(&self, memory_size: MemorySize) -> Self {
95        Self(self.0 + memory_size.0)
96    }
97}
98
99#[derive(Debug, Copy, Clone, Eq, PartialEq)]
100pub struct FrameMemoryAddress(pub u32);
101
102impl Display for FrameMemoryAddress {
103    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
104        write!(f, "${:04X}", self.0)
105    }
106}
107
108impl Add<MemoryOffset> for FrameMemoryAddress {
109    type Output = Self;
110
111    fn add(self, rhs: MemoryOffset) -> Self::Output {
112        Self(self.0 + rhs.0)
113    }
114}
115
116#[derive(Debug, Copy, Clone)]
117pub struct FrameMemoryRegion {
118    pub addr: FrameMemoryAddress,
119    pub size: MemorySize,
120}
121
122impl Display for FrameMemoryRegion {
123    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
124        write!(f, "{}:{}", self.addr, self.size)
125    }
126}
127
128impl Default for FrameMemoryRegion {
129    fn default() -> Self {
130        Self {
131            addr: FrameMemoryAddress(0),
132            size: MemorySize(0),
133        }
134    }
135}
136
137impl FrameMemoryRegion {
138    #[must_use]
139    pub const fn new(frame_addr: FrameMemoryAddress, size: MemorySize) -> Self {
140        Self {
141            addr: frame_addr,
142            size,
143        }
144    }
145
146    #[must_use]
147    pub fn last_valid_end_addr(&self) -> FrameMemoryAddress {
148        self.addr.add(MemoryOffset(self.size.0))
149    }
150}
151
152impl FrameMemoryRegion {
153    #[must_use]
154    pub const fn addr(&self) -> FrameMemoryAddress {
155        self.addr
156    }
157}
158
159#[derive(Debug, Copy, Clone)]
160pub struct FrameMemoryAddressIndirectPointer(pub FrameMemoryAddress);
161
162#[derive(Debug, Copy, Clone)]
163pub struct TempFrameMemoryAddress(pub FrameMemoryAddress);
164
165impl TempFrameMemoryAddress {
166    #[must_use]
167    pub const fn to_addr(&self) -> FrameMemoryAddress {
168        self.0
169    }
170}
171
172#[must_use]
173pub fn align_to(addr: MemoryOffset, alignment: MemoryAlignment) -> MemoryOffset {
174    MemoryOffset(align(addr.0 as usize, alignment.into()) as u32)
175}
176
177/// # Arguments
178/// * `offset` - The offset after the last field (end of layout).
179/// * `base_offset` - The starting offset of the struct/tuple/union.
180/// * `max_alignment` - The maximum alignment required by any field.
181///
182/// # Returns
183/// The total size, rounded up to `max_alignment`.
184/// # Notes
185/// The total size of a struct is always rounded up to a multiple of its alignment.
186/// It might be strange in that it "wastes" memory for the potential parent struct
187/// to place items of lower memory alignment. (reuse tail padding).
188/// It simplifies things as well with code generation and similar, that a struct
189/// is always the same size and doesn't have to rely on where the struct is contained.
190/// It also ensures that arrays of the struct are correctly aligned according to the ABI,
191/// and matches the behavior of C, C++, and Rust.
192/// Note: The tail padding at the end of a struct is not reused for subsequent fields
193/// in a parent struct-this is required for safe and predictable layout
194#[must_use]
195pub fn adjust_size_to_alignment(
196    unaligned_size: MemorySize,
197    max_alignment: MemoryAlignment,
198) -> MemorySize {
199    align_to(MemoryOffset(unaligned_size.0), max_alignment).to_size()
200}
201
202#[derive(Debug, Copy, Eq, PartialEq, Hash, Clone, Ord, PartialOrd)]
203pub struct HeapMemoryOffset(pub u32);
204
205impl HeapMemoryOffset {
206    #[must_use]
207    pub const fn to_size(&self) -> HeapMemorySize {
208        HeapMemorySize(self.0)
209    }
210}
211
212impl Display for HeapMemoryOffset {
213    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
214        write!(f, "+{:08X}]", self.0)
215    }
216}
217
218impl Add<HeapMemorySize> for HeapMemoryOffset {
219    type Output = Self;
220
221    fn add(self, rhs: HeapMemorySize) -> Self {
222        Self(self.0 + rhs.0)
223    }
224}
225
226impl Sub<Self> for HeapMemoryOffset {
227    type Output = Self;
228
229    fn sub(self, rhs: Self) -> Self {
230        assert!(rhs.0 <= self.0);
231        Self(self.0 - rhs.0)
232    }
233}
234
235impl HeapMemoryOffset {
236    #[must_use]
237    pub const fn as_size(&self) -> HeapMemorySize {
238        HeapMemorySize(self.0)
239    }
240}
241
242impl HeapMemoryOffset {
243    #[must_use]
244    pub fn add(&self, size: HeapMemorySize, alignment: MemoryAlignment) -> Self {
245        let new_start = align(self.0 as usize, alignment.into());
246        Self(new_start as u32 + size.0)
247    }
248}
249
250#[derive(Clone)]
251pub struct PointerLocation {
252    pub ptr_reg: TypedRegister,
253}
254
255impl PointerLocation {
256    #[must_use]
257    pub const fn new(ptr_reg: TypedRegister) -> Self {
258        Self { ptr_reg }
259    }
260    #[must_use]
261    pub const fn addressing(&self) -> u8 {
262        self.ptr_reg.addressing()
263    }
264}
265
266impl PointerLocation {
267    #[must_use]
268    pub fn memory_location(&self) -> MemoryLocation {
269        MemoryLocation {
270            base_ptr_reg: self.ptr_reg.clone(),
271            offset: MemoryOffset(0),
272            ty: self.ptr_reg.ty.clone(),
273        }
274    }
275}
276
277#[derive(Clone)]
278pub struct MemoryLocation {
279    pub base_ptr_reg: TypedRegister,
280    pub offset: MemoryOffset,
281    pub ty: VmType,
282}
283
284impl Display for MemoryLocation {
285    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
286        write!(f, "[{}+{} ({})]", self.base_ptr_reg, self.offset, self.ty)
287    }
288}
289
290impl MemoryLocation {
291    #[must_use]
292    pub const fn vm_type(&self) -> &VmType {
293        &self.ty
294    }
295
296    #[must_use]
297    pub fn unsafe_add_offset(&self, offset: MemoryOffset) -> Self {
298        Self {
299            base_ptr_reg: self.base_ptr_reg.clone(),
300            offset: self.offset.add(offset),
301            ty: self.ty.clone(),
302        }
303    }
304
305    #[must_use]
306    pub fn new_copy_over_whole_type_with_zero_offset(base_ptr_reg: TypedRegister) -> Self {
307        Self {
308            ty: base_ptr_reg.ty.clone(),
309            base_ptr_reg,
310            offset: MemoryOffset(0),
311        }
312    }
313}
314
315impl Debug for MemoryLocation {
316    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
317        write!(
318            f,
319            "MemLoc[{}+{:04X}] ({})",
320            self.base_ptr_reg, self.offset.0, self.ty
321        )
322    }
323}
324
325impl MemoryLocation {
326    #[must_use]
327    pub fn pointer_location(&self) -> Option<PointerLocation> {
328        if self.offset.0 == 0 {
329            Some(PointerLocation {
330                ptr_reg: self.base_ptr_reg.clone(),
331            })
332        } else {
333            None
334        }
335    }
336
337    #[must_use]
338    pub const fn reg(&self) -> &TypedRegister {
339        &self.base_ptr_reg
340    }
341
342    #[must_use]
343    pub const fn as_direct_register(&self) -> Option<&TypedRegister> {
344        if self.offset.0 == 0 {
345            Some(&self.base_ptr_reg)
346        } else {
347            None
348        }
349    }
350}
351
352#[derive(Clone)]
353pub struct ScalarMemoryLocation {
354    pub location: MemoryLocation,
355}
356#[derive(Clone)]
357pub struct AggregateMemoryLocation {
358    pub location: MemoryLocation,
359}
360
361impl AggregateMemoryLocation {
362    #[must_use]
363    pub const fn new(location: MemoryLocation) -> Self {
364        Self { location }
365    }
366}
367
368impl Display for AggregateMemoryLocation {
369    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
370        write!(f, "{}", self.location)
371    }
372}
373
374impl AggregateMemoryLocation {
375    #[must_use]
376    pub fn offset(&self, memory_offset: MemoryOffset, new_type: BasicTypeRef) -> Self {
377        let new_location = MemoryLocation {
378            base_ptr_reg: self.location.base_ptr_reg.clone(),
379            offset: self.location.offset + memory_offset,
380            ty: VmType::new_unknown_placement(new_type),
381        };
382        Self {
383            location: new_location,
384        }
385    }
386}
387
388pub enum ZFlagPolarity {
389    TrueWhenSet,
390    TrueWhenClear,
391}
392
393#[must_use]
394pub fn align_frame_addr(
395    memory_address: FrameMemoryAddress,
396    alignment: MemoryAlignment,
397) -> FrameMemoryAddress {
398    let raw_addr = align(memory_address.0 as usize, alignment.into());
399
400    FrameMemoryAddress(raw_addr as u32)
401}
402
403#[must_use]
404pub fn align_offset(memory_address: MemoryOffset, alignment: MemoryAlignment) -> MemoryOffset {
405    let raw_addr = align(memory_address.0 as usize, alignment.into());
406
407    MemoryOffset(raw_addr as u32)
408}
409
410#[derive(Clone)]
411pub struct Meta {
412    pub comment: String,
413    pub node: Node,
414}
415
416#[derive(Debug)]
417pub struct PatchPosition(pub InstructionPosition);
418
419#[derive(Debug, Clone, Eq, PartialEq, Hash)]
420pub struct InstructionPositionOffset(pub u32);
421
422#[derive(Debug, Clone, Eq, PartialEq, Hash)]
423pub struct InstructionRange {
424    pub start: InstructionPosition,
425    pub count: InstructionPositionOffset,
426}