Packages
orb
0.0.32
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/ops.ex
defmodule Orb.Ops do
@moduledoc false
# See: https://webassembly.github.io/spec/core/syntax/instructions.html#numeric-instructions
@i_unary_ops ~w(clz ctz popcnt)a
@i_binary_ops ~w(add sub mul div_u div_s rem_u rem_s and or xor shl shr_u shr_s rotl rotr)a
@i_test_ops ~w(eqz)a
@i_relative_ops ~w(eq ne lt_u lt_s gt_u gt_s le_u le_s ge_u ge_s)a
# https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/Numeric/Wrap
@i32_wrap_ops ~w(wrap_i64)a
# https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/Numeric/Extend
@i64_extend_ops ~w(extend_i32_s extend_i32_u)a
# https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/Memory/Load
@i32_load_ops ~w(load load8_u load8_s load16_s load16_u)a
@i64_load_ops @i32_load_ops ++ ~w(load32_s load32_u)a
# https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/Memory/Store
@i32_store_ops ~w(store store8 store16)a
@i64_store_ops @i32_store_ops ++ ~w(store32)a
# https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/Numeric/Truncate_float_to_int
@i_trunc_ops ~w(trunc_f32_s trunc_f32_u trunc_f64_s trunc_f64_u)a
@i32_ops_1 @i_unary_ops ++ @i_test_ops ++ @i_trunc_ops ++ @i32_wrap_ops
@i32_ops_2 @i_binary_ops ++ @i_relative_ops
@i32_ops_all @i32_ops_1 ++ @i32_ops_2 ++ @i32_load_ops ++ @i32_store_ops
@i64_ops_1 @i_unary_ops ++ @i_test_ops ++ @i_trunc_ops ++ @i64_extend_ops
@i64_ops_2 @i_binary_ops ++ @i_relative_ops
@i64_ops_all @i64_ops_1 ++ @i64_ops_2 ++ @i64_load_ops ++ @i64_store_ops
@f32_ops_1 ~w(floor ceil trunc nearest abs neg sqrt convert_i32_s convert_i32_u)a
@f32_ops_2 ~w(add sub mul div eq ne lt gt le ge copysign min max)a
# TODO: add conversions ops https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/Numeric#conversion
@integer_types ~w(i64 i32)a
@float_types ~w(f64 f32)a
@primitive_types @integer_types ++ @float_types
@effects ~w(unknown_effect memory_effect global_effect local_effect)a
@elixir_types [Elixir.Integer, Elixir.Float]
# @base_types @primitive_types ++ @effects ++ @elixir_types
defguard is_primitive_type(type) when type in @primitive_types
defguard is_effect(type) when type in @effects
def to_primitive_type(type) when is_primitive_type(type), do: type
def to_primitive_type(type) when is_effect(type), do: type
def to_primitive_type(type) when type in @elixir_types, do: type
def to_primitive_type(type) when is_tuple(type) do
for nested <- Tuple.to_list(type) do
to_primitive_type(nested)
end
|> List.to_tuple()
end
def to_primitive_type(mod) when is_atom(mod) do
Orb.CustomType.resolve!(mod) |> to_primitive_type()
end
def type_stack_count(type) when is_primitive_type(type), do: 1
def type_stack_count(type) when is_effect(type), do: 0
def type_stack_count(type) when is_atom(type), do: 1
def type_stack_count(type) when is_tuple(type), do: tuple_size(type)
def types_compatible?(Elixir.Integer, b),
do: to_primitive_type(b) in @integer_types
def types_compatible?(a, Elixir.Integer),
do: to_primitive_type(a) in @integer_types
def types_compatible?(Elixir.Float, b),
do: to_primitive_type(b) in @float_types
def types_compatible?(a, Elixir.Float),
do: to_primitive_type(a) in @float_types
def types_compatible?(a, b),
do: to_primitive_type(a) === to_primitive_type(b)
def typeof(n) when is_integer(n), do: Elixir.Integer
def typeof(n) when is_float(n), do: Elixir.Float
def typeof(%{type: type}), do: type
def typeof(_), do: :unknown_effect
def typeof(value, :primitive), do: typeof(value) |> to_primitive_type()
def extract_common_type(a, b) do
case {typeof(a, :primitive), typeof(b, :primitive)} do
{same, same} -> same
{a, b} when is_effect(a) and is_effect(b) -> :unknown_effect
{type, Elixir.Integer} when type in @integer_types -> type
{Elixir.Integer, type} when type in @integer_types -> type
{type, Elixir.Float} when type in @float_types -> type
{Elixir.Float, type} when type in @float_types -> type
_ -> nil
end
end
defmacro i32(arity_or_type)
defmacro i32(1), do: @i32_ops_1 |> Macro.escape()
defmacro i32(2), do: @i32_ops_2 |> Macro.escape()
defmacro i32(:load), do: @i32_load_ops |> Macro.escape()
defmacro i32(:store), do: @i32_store_ops |> Macro.escape()
defmacro i32(:all), do: @i32_ops_all |> Macro.escape()
defmacro i64(arity_or_type)
defmacro i64(1), do: @i64_ops_1 |> Macro.escape()
defmacro i64(2), do: @i64_ops_2 |> Macro.escape()
defmacro i64(:load), do: @i64_load_ops |> Macro.escape()
defmacro i64(:store), do: @i64_store_ops |> Macro.escape()
defmacro i64(:all), do: @i64_ops_all |> Macro.escape()
defmacro f32(arity_or_type)
defmacro f32(1), do: @f32_ops_1 |> Macro.escape()
defmacro f32(2), do: @f32_ops_2 |> Macro.escape()
defp i32_arity(op) when op in @i32_ops_1, do: 1
defp i32_arity(op) when op in @i32_ops_2, do: 2
defp i64_arity(op) when op in @i64_ops_1, do: 1
defp i64_arity(op) when op in @i64_ops_2, do: 2
defp f32_arity(op) when op in @f32_ops_1, do: 1
defp f32_arity(op) when op in @f32_ops_2, do: 2
defp i32_param_type(:const, 0), do: Elixir.Integer
defp i32_param_type(op, 0) when op in @i32_load_ops, do: :i32
defp i32_param_type(op, i) when op in @i32_store_ops and i in [0, 1], do: :i32
defp i32_param_type(op, 0) when op in @i_trunc_ops, do: :f32
defp i32_param_type(op, 0) when op in @i_unary_ops, do: :i32
defp i32_param_type(op, 0) when op in @i_test_ops, do: :i32
defp i32_param_type(op, 0) when op in @i32_wrap_ops, do: :i64
defp i32_param_type(op, i) when op in @i_binary_ops and i in [0, 1], do: :i32
defp i32_param_type(op, i) when op in @i_relative_ops and i in [0, 1], do: :i32
defp i32_param_type(_op, _param_index), do: :error
defp i64_param_type(:const, 0), do: Elixir.Integer
defp i64_param_type(op, 0) when op in @i64_load_ops, do: :i64
defp i64_param_type(op, i) when op in @i64_store_ops and i in [0, 1], do: :i64
defp i64_param_type(op, 0) when op in @i_trunc_ops, do: :f64
defp i64_param_type(op, 0) when op in @i_unary_ops, do: :i64
defp i64_param_type(op, 0) when op in @i_test_ops, do: :i64
defp i64_param_type(op, 0) when op in @i64_extend_ops, do: :i32
defp i64_param_type(op, i) when op in @i_binary_ops and i in [0, 1], do: :i64
defp i64_param_type(op, i) when op in @i_relative_ops and i in [0, 1], do: :i64
defp i64_param_type(_op, _param_index), do: :error
defp f32_param_type(:const, 0), do: Elixir.Float
defp f32_param_type(op, 0) when op in @f32_ops_1, do: :f32
defp f32_param_type(op, i) when op in @f32_ops_2 and i in [0, 1], do: :f32
defp f32_param_type(_op, _param_index), do: :error
def i32_param_type!(op, param_index) do
case i32_param_type(op, param_index) do
:error ->
arity = i32_arity(op)
raise ArgumentError,
"WebAssembly instruction i32.#{op}/#{arity} does not accept a #{nth(param_index)} argument."
type ->
type
end
end
def i64_param_type!(op, param_index) do
case i64_param_type(op, param_index) do
:error ->
arity = i64_arity(op)
raise ArgumentError,
"WebAssembly instruction i64.#{op}/#{arity} does not accept a #{nth(param_index)} argument."
type ->
type
end
end
def f32_param_type!(op, param_index) do
case f32_param_type(op, param_index) do
:error ->
arity = f32_arity(op)
raise ArgumentError,
"WebAssembly instruction f32.#{op}/#{arity} does not accept a #{nth(param_index)} argument."
type ->
type
end
end
def param_type!(:i32, op, param_index), do: i32_param_type!(op, param_index)
def param_type!(:i64, op, param_index), do: i64_param_type!(op, param_index)
def param_type!(:f32, op, param_index), do: f32_param_type!(op, param_index)
defp nth(0), do: "1st"
defp nth(1), do: "2nd"
defp nth(2), do: "3rd"
defp nth(n), do: "#{n + 1}th"
end