summaryrefslogtreecommitdiffstats
path: root/third_party/rust/lucet-module-wasmsbx/src/linear_memory.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/lucet-module-wasmsbx/src/linear_memory.rs')
-rw-r--r--third_party/rust/lucet-module-wasmsbx/src/linear_memory.rs191
1 files changed, 191 insertions, 0 deletions
diff --git a/third_party/rust/lucet-module-wasmsbx/src/linear_memory.rs b/third_party/rust/lucet-module-wasmsbx/src/linear_memory.rs
new file mode 100644
index 0000000000..d2107c421a
--- /dev/null
+++ b/third_party/rust/lucet-module-wasmsbx/src/linear_memory.rs
@@ -0,0 +1,191 @@
+use crate::Error;
+use serde::{Deserialize, Serialize};
+
+/// Specification of the linear memory of a module
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct LinearMemorySpec<'a> {
+ /// Specification of the heap used to implement the linear memory
+ pub heap: HeapSpec,
+ /// Initialization values for linear memory
+ #[serde(borrow)]
+ pub initializer: SparseData<'a>,
+}
+
+/// Specification of the linear memory of a module
+///
+/// This is a version of [`LinearMemorySpec`](../struct.LinearMemorySpec.html) with an
+/// `OwnedSparseData` for the initializer.
+/// This type is useful when directly building up a value to be serialized.
+pub struct OwnedLinearMemorySpec {
+ /// Specification of the heap used to implement the linear memory
+ pub heap: HeapSpec,
+ /// Initialization values for linear memory
+ pub initializer: OwnedSparseData,
+}
+
+impl OwnedLinearMemorySpec {
+ pub fn to_ref<'a>(&'a self) -> LinearMemorySpec<'a> {
+ LinearMemorySpec {
+ heap: self.heap.clone(),
+ initializer: self.initializer.to_ref(),
+ }
+ }
+}
+
+/// Specifications about the heap of a Lucet module.
+#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
+pub struct HeapSpec {
+ /// Total bytes of memory for the heap to possibly expand into, as configured for Cranelift
+ /// codegen.
+ ///
+ /// All of this memory is addressable. Only some part of it is accessible - from 0 to the
+ /// initial size, guaranteed, and up to the `max_size`. This size allows Cranelift to elide
+ /// checks of the *base pointer*. At the moment that just means checking if it is greater than
+ /// 4gb, in which case it can elide the base pointer check completely. In the future, Cranelift
+ /// could use a solver to elide more base pointer checks if it can prove the calculation will
+ /// always be less than this bound.
+ ///
+ /// Specified in bytes, and must be evenly divisible by the host page size (4K).
+ pub reserved_size: u64,
+
+ /// Total bytes of memory *after* the reserved area, as configured for Cranelift codegen.
+ ///
+ /// All of this memory is addressable, but it is never accessible - it is guaranteed to trap if
+ /// an access happens in this region. This size allows Cranelift to use *common subexpression
+ /// elimination* to reduce checks of the *sum of base pointer and offset* (where the offset is
+ /// always rounded up to a multiple of the guard size, to be friendly to CSE).
+ ///
+ /// Specified in bytes, and must be evenly divisible by the host page size (4K).
+ pub guard_size: u64,
+
+ /// Total bytes of memory for the WebAssembly program's linear memory upon initialization.
+ ///
+ /// Specified in bytes, must be evenly divisible by the WebAssembly page size (64K), and must be
+ /// less than or equal to `reserved_size`.
+ pub initial_size: u64,
+
+ /// Maximum bytes of memory for the WebAssembly program's linear memory at any time.
+ ///
+ /// This is not necessarily the same as `reserved_size` - we want to be able to tune the check
+ /// bound there separately than the declaration of a max size in the client program.
+ ///
+ /// The program may optionally define this value. If it does, it must be less than the
+ /// `reserved_size`. If it does not, the max size is left up to the runtime, and is allowed to
+ /// be less than `reserved_size`.
+ pub max_size: Option<u64>,
+}
+
+impl HeapSpec {
+ pub fn new(
+ reserved_size: u64,
+ guard_size: u64,
+ initial_size: u64,
+ max_size: Option<u64>,
+ ) -> Self {
+ Self {
+ reserved_size,
+ guard_size,
+ initial_size,
+ max_size,
+ }
+ }
+
+ /// Some very small test programs dont specify a memory import or definition.
+ pub fn empty() -> Self {
+ Self {
+ reserved_size: 0,
+ guard_size: 0,
+ initial_size: 0,
+ max_size: None,
+ }
+ }
+}
+
+/// A sparse representation of a Lucet module's initial heap.
+///
+/// The lifetime parameter exists to support zero-copy deserialization for the `&[u8]` slices
+/// representing non-zero pages. For a variant with owned `Vec<u8>` pages, see
+/// [`OwnedSparseData`](owned/struct.OwnedSparseData.html).
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct SparseData<'a> {
+ /// Indices into the vector correspond to the offset, in host page (4k) increments, from the
+ /// base of the instance heap.
+ ///
+ /// If the option at a given index is None, the page is initialized as zeros. Otherwise,
+ /// the contents of the page are given as a slice of exactly 4k bytes.
+ ///
+ /// The deserializer of this datastructure does not make sure the 4k invariant holds,
+ /// but the constructor on the serializier side does.
+ #[serde(borrow)]
+ pages: Vec<Option<&'a [u8]>>,
+}
+
+impl<'a> SparseData<'a> {
+ /// Create a new `SparseData` from its constituent pages.
+ ///
+ /// Entries in the `pages` argument which are `Some` must contain a slice of exactly the host
+ /// page size (4096), otherwise this function returns `Error::IncorrectPageSize`. Entries which
+ /// are `None` are interpreted as empty pages, which will be zeroed by the runtime.
+ pub fn new(pages: Vec<Option<&'a [u8]>>) -> Result<Self, Error> {
+ if !pages.iter().all(|page| match page {
+ Some(contents) => contents.len() == 4096,
+ None => true,
+ }) {
+ return Err(Error::IncorrectPageSize);
+ }
+
+ Ok(Self { pages })
+ }
+
+ pub fn pages(&self) -> &[Option<&'a [u8]>] {
+ &self.pages
+ }
+
+ pub fn get_page(&self, offset: usize) -> &Option<&'a [u8]> {
+ self.pages.get(offset).unwrap_or(&None)
+ }
+
+ pub fn len(&self) -> usize {
+ self.pages.len()
+ }
+}
+
+/// A sparse representation of a Lucet module's initial heap.
+///
+/// This is a version of [`SparseData`](../struct.SparseData.html) with owned `Vec<u8>`s
+/// representing pages. This type is useful when directly building up a value to be serialized.
+pub struct OwnedSparseData {
+ pages: Vec<Option<Vec<u8>>>,
+}
+
+impl OwnedSparseData {
+ /// Create a new `OwnedSparseData` from its consitutent pages.
+ ///
+ /// Entries in the `pages` argument which are `Some` must contain a vector of exactly the host
+ /// page size (4096), otherwise this function returns `Error::IncorrectPageSize`. Entries which
+ /// are `None` are interpreted as empty pages, which will be zeroed by the runtime.
+ pub fn new(pages: Vec<Option<Vec<u8>>>) -> Result<Self, Error> {
+ if !pages.iter().all(|page| match page {
+ Some(contents) => contents.len() == 4096,
+ None => true,
+ }) {
+ return Err(Error::IncorrectPageSize);
+ }
+ Ok(Self { pages })
+ }
+
+ /// Create a [`SparseData`](../struct.SparseData.html) backed by the values in this
+ /// `OwnedSparseData`.
+ pub fn to_ref<'a>(&'a self) -> SparseData<'a> {
+ SparseData::new(
+ self.pages
+ .iter()
+ .map(|c| match c {
+ Some(data) => Some(data.as_slice()),
+ None => None,
+ })
+ .collect(),
+ )
+ .expect("SparseData invariant enforced by OwnedSparseData constructor")
+ }
+}