Packages
orb
0.0.36
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/i32.ex
defmodule Orb.I32 do
@moduledoc """
Type for 32-bit integer.
"""
import Kernel, except: [and: 2, or: 2]
require alias Orb.Ops
alias Orb.Instruction
alias Orb.InstructionSequence
def wasm_type(), do: :i32
def const(n) when is_integer(n), do: Instruction.wrap_constant!(:i32, n)
def add(a, b)
def sub(a, b)
def mul(a, b)
def div_u(a, divisor)
def div_s(a, divisor)
def rem_u(a, divisor)
def rem_s(a, divisor)
def unquote(:or)(a, b)
def xor(a, b)
def shl(a, b)
def shr_u(a, b)
def shr_s(a, b)
def rotl(a, b)
def rotr(a, b)
for op <- Ops.i32(1) do
def unquote(op)(a) do
Instruction.i32(unquote(op), a)
end
end
for op <- Ops.i32(2) do
case op do
:eq ->
# TODO: is this the best place for this optimization?
# Perhaps it should be done at `def a === b`
# Otherwise it would justify adding optimization for `0 AND 1 --> 0` etc too.
def eq(0, n), do: Instruction.i32(:eqz, n)
def eq(n, 0), do: Instruction.i32(:eqz, n)
def eq(a, b), do: Instruction.i32(:eq, a, b)
:and ->
def band(a, b) do
Instruction.i32(:and, a, b)
end
_ ->
def unquote(op)(a, b) do
Instruction.i32(unquote(op), a, b)
end
end
end
def sum!([]), do: const(0)
def sum!(items) when is_list(items) do
items
|> Enum.map(&Orb.TypeNarrowable.type_narrow_to(&1, __MODULE__))
|> Enum.reduce(&add/2)
end
def in?(value, list) when is_list(list) do
instructions =
for {item, index} <- Enum.with_index(list) do
case index do
0 ->
eq(value, item)
_ ->
InstructionSequence.new(:i32, [eq(value, item), Instruction.i32(:or)])
# Instruction.i32(:or, eq(value, item))
end
end
InstructionSequence.new(:i32, instructions)
end
defmacro when?(condition, do: when_true, else: when_false) do
quote do
Orb.IfElse.new(
:i32,
unquote(condition),
Orb.InstructionSequence.new(unquote(__get_block_items(when_true))),
Orb.InstructionSequence.new(unquote(__get_block_items(when_false)))
)
end
end
# This only works with WebAssembly 1.1
# Sadly wat2wasm doesn’t like it
def select(condition, do: when_true, else: when_false) do
[
when_true,
when_false,
condition,
:select
]
end
@doc "Converts an ASCII little-endian 4-byte string to an 32-bit integer."
def from_4_byte_ascii(<<int::little-size(32)>>), do: int
defmacro match(value, do: transform) do
statements =
for {:->, _, [input, result]} <- transform do
case input do
# _ ->
# like an else clause
[{:_, _, _}] ->
__get_block_items(result)
[match] ->
quote do
Orb.IfElse.new(
Orb.I32.eq(unquote(value), unquote(match)),
Orb.InstructionSequence.new(
unquote(__get_block_items(result)) ++ [Orb.Control.break(:i32_match)]
)
)
end
matches ->
quote do
Orb.IfElse.new(
Orb.I32.in?(unquote(value), unquote(matches)),
Orb.InstructionSequence.new(
unquote(__get_block_items(result)) ++ [Orb.Control.break(:i32_match)]
)
)
end
end
end
has_catchall? = Enum.any?(transform, &match?({:->, _, [[{:_, _, _}], _]}, &1))
final_instruction =
case has_catchall? do
false -> quote do: %Orb.Unreachable{debug_reason: "Orb.I32.match/2 catchall"}
true -> quote do: Orb.InstructionSequence.empty()
end
quote do
with do
require Orb.Control
Orb.Control.block :i32_match, Orb.I32 do
unquote(statements)
unquote(final_instruction)
end
end
end
end
defmacro cond(do: transform) do
statements =
for {:->, _, [input, target]} <- transform do
case input do
# true ->
# like an else clause
[true] ->
target
[match] ->
quote do
Orb.IfElse.new(
unquote(match),
Orb.InstructionSequence.new(
unquote(__get_block_items(target)) ++ [Orb.Control.break(:i32_map)]
)
)
end
end
end
has_catchall? = Enum.any?(transform, &match?({:->, _, [[{:_, _, _}], _]}, &1))
final_instruction =
case has_catchall? do
false -> quote do: %Orb.Unreachable{debug_reason: "Orb.I32.cond/1 catchall"}
true -> quote do: Orb.InstructionSequence.empty()
end
quote do
with do
require Orb.Control
Orb.Control.block :i32_map, Orb.I32 do
unquote(statements)
unquote(final_instruction)
end
end
end
end
defp __get_block_items(block) do
case block do
nil -> nil
{:__block__, _meta, block_items} -> block_items
single -> [single]
end
end
def __global_value(value) when is_integer(value), do: const(value)
def __global_value(false), do: const(0)
def __global_value(true), do: const(1)
# TODO: stash away which module so we can do smart stuff like with local types
def __global_value(mod) when is_atom(mod),
do: const(mod.initial_i32())
defmacro global(mutability \\ :mutable, list)
when mutability in ~w{readonly mutable}a do
quote do
@wasm_globals (for {key, value} <- unquote(list) do
Orb.Global.new(
:i32,
key,
unquote(mutability),
:internal,
Orb.I32.__global_value(value)
)
end)
end
end
defmacro export_global(mutability, list)
when mutability in ~w{readonly mutable}a do
quote do
@wasm_globals (for {key, value} <- unquote(list) do
Orb.Global.new(
:i32,
key,
unquote(mutability),
:exported,
Orb.I32.__global_value(value)
)
end)
end
end
defmacro export_enum(keys, offset \\ 0) do
quote do
unquote(__MODULE__).export_global(
:readonly,
Enum.with_index(unquote(keys), unquote(offset))
)
end
end
defmacro enum(keys, offset \\ 0) do
quote do
unquote(__MODULE__).global(:readonly, Enum.with_index(unquote(keys), unquote(offset)))
end
end
defmacro attr_writer(global_name) when is_atom(global_name) do
quote do
defw unquote(String.to_atom("#{global_name}="))(new_value: Orb.I32) do
Orb.Instruction.global_set(
Orb.I32,
unquote(global_name),
Orb.Instruction.local_get(Orb.I32, :new_value)
)
end
end
end
defmacro attr_writer(global_name, as: func_name)
when is_atom(global_name) |> Kernel.and(is_atom(func_name)) do
quote do
defw unquote(func_name)(new_value: Orb.I32) do
Orb.Instruction.global_set(
Orb.I32,
unquote(global_name),
Orb.Instruction.local_get(Orb.I32, :new_value)
)
end
end
end
end