Packages
orb
0.0.48
0.2.2
0.2.1
0.2.0
0.1.1
0.1.0
0.0.51
0.0.49
0.0.48
0.0.47
0.0.46
0.0.45
0.0.44
0.0.43
0.0.42
0.0.41
0.0.40
0.0.39
0.0.38
0.0.37
0.0.36
0.0.35
0.0.34
0.0.33
0.0.32
0.0.31
0.0.30
0.0.28
0.0.27
0.0.26
0.0.25
0.0.24
0.0.23
0.0.22
0.0.21
0.0.20
0.0.19
0.0.18
0.0.17
0.0.16
0.0.15
0.0.14
0.0.13
0.0.12
retired
0.0.11
0.0.10
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.4
0.0.3
0.0.2
0.0.1
DSL for WebAssembly
Current section
Files
Jump to
Current section
Files
lib/orb/memory.ex
defmodule Orb.Memory do
@moduledoc """
Work with memory: load, store, declare pages & initial data.
"""
defstruct name: "", min: 0, exported?: false
@doc false
def new(page_definitions, constants)
# def new(nil), do: nil
# def new([]), do: nil
def new(list, %{offset: constants_offset, byte_size: constants_byte_size}) when is_list(list) do
bytes_per_page = page_byte_size()
requested_page_count = Enum.sum(list)
constants_page_count =
if constants_byte_size > 0 do
div(constants_offset + constants_byte_size + bytes_per_page - 1, bytes_per_page)
else
0
end
total_page_count = requested_page_count + constants_page_count
case total_page_count do
0 ->
nil
min ->
%__MODULE__{min: min}
end
end
@page_byte_size 64 * 1024
def page_byte_size(), do: @page_byte_size
@doc """
Declare how many 64Kib pages of memory your module needs.
Can be called multiple times, with each summed up.
Returns the previous page count, which can be used as a start offset.
"""
defmacro pages(page_count) do
quote do
start_offset = Enum.sum(@wasm_memory)
@wasm_memory unquote(page_count)
# FIXME: the idea is this returns the page offset, but constants will push this down
# making this offset incorrect.
start_offset
end
end
@doc "Initializes data in memory. In Wat is `(data …)`"
defmacro initial_data!(offset, value) do
quote bind_quoted: [offset: offset, value: value] do
@wasm_section_data %Orb.Data{offset: offset, value: value, nul_terminated: false}
end
end
@doc """
Load value of `type` from memory `address`.
```elixir
Memory.load!(I32, 0x100)
Memory.load!(I32.U8, 0x100)
```
## Examples
iex> use Orb
iex> Memory.load!(I32, 0x100) |> Orb.to_wat()
"(i32.load (i32.const 256))"
iex> Memory.load!(I32.U8, 0x100) |> Orb.to_wat()
"(i32.load8_u (i32.const 256))"
iex> Memory.load!(I32, 0x100, align: 2) |> Orb.to_wat()
"(i32.load align=2 (i32.const 256))"
iex> Memory.load!(I32, 0x100, align: 4) |> Orb.to_wat()
"(i32.load align=4 (i32.const 256))"
iex> use Orb
iex> Memory.load!(I32, 0x100, align: 3)
** (ArgumentError) malformed alignment 3
iex> use Orb
iex> Memory.load!(I32, 0x100, align: 8)
** (ArgumentError) alignment 8 must not be larger than natural 4
iex> use Orb
iex> Memory.load!(I32.U8, 0x100, align: 2)
** (ArgumentError) alignment 2 must not be larger than natural 1
"""
def load!(type, address, opts \\ []) do
Orb.Memory.Load.new(type, address, opts)
end
@doc """
Store `value` of `type` at memory `address`.
```elixir
Memory.store!(I32, 0x100, 42)
Memory.store!(I32.U8, 0x100, ?a)
```
## Examples
iex> use Orb
iex> Memory.store!(I32, 0x100, 42) |> Orb.to_wat()
"(i32.store (i32.const 256) (i32.const 42))"
iex> Memory.store!(I32.U8, 0x100, ?a) |> Orb.to_wat()
"(i32.store8 (i32.const 256) (i32.const 97))"
iex> Memory.store!(I32, 0x100, 42, align: 2) |> Orb.to_wat()
"(i32.store align=2 (i32.const 256) (i32.const 42))"
iex> Memory.store!(I32, 0x100, 42, align: 4) |> Orb.to_wat()
"(i32.store align=4 (i32.const 256) (i32.const 42))"
iex> use Orb
iex> Memory.store!(I32, 0x100, 42, align: 3)
** (ArgumentError) malformed alignment 3
iex> use Orb
iex> Memory.store!(I32, 0x100, 42, align: 8)
** (ArgumentError) alignment 8 must not be larger than natural 4
iex> use Orb
iex> Memory.store!(I32.U8, 0x100, 42, align: 2)
** (ArgumentError) alignment 2 must not be larger than natural 1
"""
def store!(type, address, value, opts \\ []) do
Orb.Memory.Store.new(type, address, value, opts)
end
@doc """
A convenience for swapping two values of `type` at addresses `address_a` and `address_b` in memory.
Bubble sort is an example of an algorithm which uses a swap.
"""
def swap!(type, address_a, address_b, opts \\ []) do
alias Orb.I32
opts = Keyword.take(opts, [:align])
Orb.InstructionSequence.new(nil, [
address_b,
load!(type, address_a, opts),
address_a,
load!(type, address_b, opts),
store!(type, Orb.Stack.pop(I32), Orb.Stack.pop(I32), opts),
store!(type, Orb.Stack.pop(I32), Orb.Stack.pop(I32), opts)
])
end
@doc """
Returns the number of pages the memory instance currently has. Each page is 64KiB.
To increase the number of pages call `grow!/1`.
https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/Memory/Size
```elixir
Memory.size()
```
"""
def size() do
Orb.Instruction.memory_size()
end
@doc """
Increases the size of the memory instance by a specified number of pages. Each page is 64KiB.
Note: only `0` and `1` are supported in today’s runtimes.
It returns the previous size of memory in pages if the operation was successful, or returns -1 if the operation failed.
https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/Memory/Grow
```elixir
Memory.grow!(1)
```
"""
def grow!(delta) do
Orb.Instruction.memory_grow(delta)
end
defimpl Orb.ToWat do
def to_wat(%Orb.Memory{min: min}, indent) do
[
indent,
~S{(memory (export "memory")},
case min do
nil -> []
int -> [" ", to_string(int)]
end,
~S{)},
"\n"
]
end
end
end