Skip to content

Commit

Permalink
Compiling if/else & loop to wasm binary
Browse files Browse the repository at this point in the history
  • Loading branch information
RoyalIcing committed Jul 21, 2024
1 parent 95f5759 commit 6f4ef36
Show file tree
Hide file tree
Showing 13 changed files with 436 additions and 79 deletions.
2 changes: 2 additions & 0 deletions lib/orb.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,7 @@ defmodule Orb do
end

def to_wat(term) when is_struct(term) do
# TODO: should we leave this as iodata? Or allow an option to be passed in?
Orb.ToWat.to_wat(term, "") |> IO.chardata_to_string()
end

Expand All @@ -1032,6 +1033,7 @@ defmodule Orb do
# <<0x0A, 0x06, 0x01, 0x04, 0x00, 0x41, 0x2A, 0x0B>>

context = Orb.ToWasm.Context.new()
# TODO: should we leave this as iodata? Or allow an option to be passed in?
Orb.ToWasm.to_wasm(term, context) |> IO.iodata_to_binary()
end

Expand Down
7 changes: 4 additions & 3 deletions lib/orb/dsl/dsl.ex
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,8 @@ defmodule Orb.DSL do
Declare a loop.
"""
defmacro loop(identifier, options \\ [], do: block) do
identifier = __expand_identifier(identifier, __CALLER__)
env = __CALLER__
identifier = __expand_identifier(identifier, env)

result_type =
Keyword.get(options, :result, nil) |> Macro.expand_literals(__CALLER__)
Expand All @@ -481,11 +482,11 @@ defmodule Orb.DSL do
block_items =
Macro.prewalk(block_items, fn
{{:., _, [{:__aliases__, _, [identifier]}, :continue]}, _, []} ->
quote do: %Orb.Loop.Branch{identifier: unquote(identifier)}
quote do: %Orb.Loop.Branch{identifier: unquote(__expand_identifier(identifier, env))}

{{:., _, [{:__aliases__, _, [identifier]}, :continue]}, _, [[if: condition]]} ->
quote do: %Orb.Loop.Branch{
identifier: unquote(identifier),
identifier: unquote(__expand_identifier(identifier, env)),
if: unquote(condition)
}

Expand Down
13 changes: 2 additions & 11 deletions lib/orb/func.ex
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,8 @@ defmodule Orb.Func do

defstruct [:name, :type]

def to_wasm_type(:i32), do: 0x7F
def to_wasm_type(:i64), do: 0x7E
def to_wasm_type(:f32), do: 0x7D
def to_wasm_type(:f64), do: 0x7C
def to_wasm_type(:v128), do: 0x7B

def to_wasm_type(custom_type) when is_atom(custom_type),
do: Orb.Ops.to_primitive_type(custom_type) |> to_wasm_type()

def to_wasm_type(%Param{type: type}), do: to_wasm_type(type)
def to_wasm_type(type) when is_atom(type), do: Orb.ToWasm.Helpers.to_wasm_type(type)
def to_wasm_type(%Param{type: type}), do: Orb.ToWasm.Helpers.to_wasm_type(type)

defimpl Orb.ToWat do
import Orb.ToWat.Helpers
Expand All @@ -126,7 +118,6 @@ defmodule Orb.Func do
]
end

@spec to_wat(%Orb.Func.Param{}, any()) :: [...]
def to_wat(%Param{name: name, type: type}, indent) do
[
indent,
Expand Down
22 changes: 22 additions & 0 deletions lib/orb/if_else.ex
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,28 @@ defmodule Orb.IfElse do
end
end

defimpl Orb.ToWasm do
import Orb.ToWasm.Helpers

def to_wasm(
%Orb.IfElse{
push_type: result,
condition: condition,
when_true: when_true,
when_false: when_false
},
context
) do
[
Orb.ToWasm.to_wasm(condition, context),
[0x04, if(result, do: to_wasm_type(result), else: 0x40)],
Orb.ToWasm.to_wasm(when_true, context),
if(when_false, do: [0x05, Orb.ToWasm.to_wasm(when_false, context)], else: []),
0x0B
]
end
end

defimpl Orb.TypeNarrowable do
def type_narrow_to(%Orb.IfElse{push_type: current_type} = if_else, narrower_type) do
case Ops.types_compatible?(current_type, narrower_type) do
Expand Down
52 changes: 26 additions & 26 deletions lib/orb/instruction.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ defmodule Orb.Instruction do
}
end

@spec wrap_constant!(any(), number() | %{:push_type => any(), optional(any()) => any()}) :: %{
:push_type => any(),
optional(any()) => any()
}
def wrap_constant!(type, value)

def wrap_constant!(Elixir.Integer, value) when is_number(value),
Expand All @@ -45,6 +41,7 @@ defmodule Orb.Instruction do
def wrap_constant!(Elixir.Float, value) when is_number(value),
do: raise("Need concrete type not Elixir.Float for constants.")

# TODO: replace with Orb.Instruction.Const
def wrap_constant!(type, value) when is_number(value),
do: %__MODULE__{
push_type: type,
Expand Down Expand Up @@ -118,7 +115,9 @@ defmodule Orb.Instruction do
end

def local_set(type, local_name, value) do
new(nil, {:local_set, local_name, type}, [value])
new(nil, {:local_set, local_name, type}, [
Orb.Instruction.Const.wrap(type, value, "local.set $#{local_name}")
])
end

def global_set(type, global_name, value) do
Expand Down Expand Up @@ -159,7 +158,7 @@ defmodule Orb.Instruction do
received_type =
cond do
is_integer(param) -> Elixir.Integer
# TODO: Change to Elixir.Float
# FIXME: Change to Elixir.Float
is_float(param) -> :f32
%{push_type: type} = param -> type
end
Expand Down Expand Up @@ -379,7 +378,7 @@ defmodule Orb.Instruction do
indent
) do
[
for(operand <- operands, do: [indent, Instructions.do_wat(operand), "\n"]),
for(operand <- operands, do: [Instructions.do_wat(operand, indent), "\n"]),
indent,
"(local.set $",
to_string(local_name),
Expand Down Expand Up @@ -484,11 +483,12 @@ defmodule Orb.Instruction do
defimpl Orb.ToWasm do
import Orb.Leb

def type_const(:i32), do: 0x41
def type_const(:i64), do: 0x42
def type_const(:f32), do: 0x43
def type_const(:f64), do: 0x44
defp type_const(:i32), do: 0x41
defp type_const(:i64), do: 0x42
defp type_const(:f32), do: 0x43
defp type_const(:f64), do: 0x44

# TODO: remove, replace with Orb.Instruction.Const
def to_wasm(
%Orb.Instruction{
push_type: type,
Expand Down Expand Up @@ -559,21 +559,21 @@ defmodule Orb.Instruction do
defp i32_operation(:rotl), do: 0x77
defp i32_operation(:rotr), do: 0x78

def i64_operation(:add), do: 0x7C
def i64_operation(:sub), do: 0x7D
def i64_operation(:mul), do: 0x7E
def i64_operation(:div_s), do: 0x7F
def i64_operation(:div_u), do: 0x80
def i64_operation(:rem_s), do: 0x81
def i64_operation(:rem_u), do: 0x82
def i64_operation(:and), do: 0x83
def i64_operation(:or), do: 0x84
def i64_operation(:xor), do: 0x85
def i64_operation(:shl), do: 0x86
def i64_operation(:shr_s), do: 0x87
def i64_operation(:shr_u), do: 0x88
def i64_operation(:rotl), do: 0x89
def i64_operation(:rotr), do: 0x8A
defp i64_operation(:add), do: 0x7C
defp i64_operation(:sub), do: 0x7D
defp i64_operation(:mul), do: 0x7E
defp i64_operation(:div_s), do: 0x7F
defp i64_operation(:div_u), do: 0x80
defp i64_operation(:rem_s), do: 0x81
defp i64_operation(:rem_u), do: 0x82
defp i64_operation(:and), do: 0x83
defp i64_operation(:or), do: 0x84
defp i64_operation(:xor), do: 0x85
defp i64_operation(:shl), do: 0x86
defp i64_operation(:shr_s), do: 0x87
defp i64_operation(:shr_u), do: 0x88
defp i64_operation(:rotl), do: 0x89
defp i64_operation(:rotr), do: 0x8A

# def to_wasm(
# %Orb.Instruction{
Expand Down
81 changes: 79 additions & 2 deletions lib/orb/instruction/const.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,95 @@ defmodule Orb.Instruction.Const do
@moduledoc false
defstruct [:push_type, :value]

def new(push_type, value) when push_type in ~w(i32 i64 f32 f64)a do
require alias Orb.Ops

def new(push_type, value) do
%__MODULE__{push_type: push_type, value: value}
end

def wrap(push_type, value) do
wrap(push_type, value, "#{Ops.to_primitive_type(push_type)}.const")
end

def wrap(push_type, value, instruction_identifier) when is_number(value) do
received_type =
cond do
is_integer(value) -> Elixir.Integer
is_float(value) -> Elixir.Float
end

if Ops.types_compatible?(received_type, push_type) do
new(push_type, value)
else
raise Orb.TypeCheckError,
expected_type: push_type,
received_type: received_type,
instruction_identifier: instruction_identifier
end
end

def wrap(push_type, %{push_type: push_type} = instruction, _)
when Ops.is_primitive_type(push_type) do
instruction
end

def wrap(push_type, %{push_type: other_type} = instruction, instruction_identifier) do
case Orb.Ops.types_compatible?(push_type, other_type) do
true ->
instruction

false ->
raise Orb.TypeCheckError,
expected_type: push_type,
received_type: other_type,
instruction_identifier: instruction_identifier
end
end

defimpl Orb.ToWat do
alias Orb.ToWat.Helpers

def to_wat(%Orb.Instruction.Const{push_type: push_type, value: value}, indent) do
[
indent,
["(", Atom.to_string(push_type), ".const "],
["(", Helpers.do_type(push_type), ".const "],
to_string(value),
")"
]
end
end

defimpl Orb.ToWasm do
import Orb.Leb

defp type_const(:i32), do: 0x41
defp type_const(:i64), do: 0x42
defp type_const(:f32), do: 0x43
defp type_const(:f64), do: 0x44

defp type_const(custom_type) when is_atom(custom_type),
do: Orb.CustomType.resolve!(custom_type) |> type_const()

def to_wasm(
%Orb.Instruction.Const{
push_type: type,
value: number
},
_
) do
[
type_const(type),
cond do
is_integer(number) ->
uleb128(number)

is_float(number) and type === :f32 ->
<<number::32-float-little>>

is_float(number) and type === :f64 ->
<<number::64-float-little>>
end
]
end
end
end
60 changes: 57 additions & 3 deletions lib/orb/instruction/relative.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,11 @@ defmodule Orb.Instruction.Relative do
}
end

alias __MODULE__, as: Self

defimpl Orb.ToWat do
alias Orb.ToWat.Instructions

def to_wat(
%Self{
%Orb.Instruction.Relative{
input_type: input_type,
operation: operation,
lhs: lhs,
Expand All @@ -53,4 +51,60 @@ defmodule Orb.Instruction.Relative do
]
end
end

defimpl Orb.ToWasm do
def to_wasm(
%Orb.Instruction.Relative{
input_type: input_type,
operation: operation,
lhs: lhs,
rhs: rhs
},
options
) do
[
Orb.Instruction.Const.wrap(input_type, lhs) |> Orb.ToWasm.to_wasm(options),
Orb.Instruction.Const.wrap(input_type, rhs) |> Orb.ToWasm.to_wasm(options),
do_operation(input_type, operation)
]
end

defp do_operation(:i32, :eqz), do: 0x45
defp do_operation(:i32, :eq), do: 0x46
defp do_operation(:i32, :ne), do: 0x47
defp do_operation(:i32, :lt_s), do: 0x48
defp do_operation(:i32, :lt_u), do: 0x49
defp do_operation(:i32, :gt_s), do: 0x4A
defp do_operation(:i32, :gt_u), do: 0x4B
defp do_operation(:i32, :le_s), do: 0x4C
defp do_operation(:i32, :le_u), do: 0x4D
defp do_operation(:i32, :ge_s), do: 0x4E
defp do_operation(:i32, :ge_u), do: 0x4F

defp do_operation(:i64, :eqz), do: 0x50
defp do_operation(:i64, :eq), do: 0x51
defp do_operation(:i64, :ne), do: 0x52
defp do_operation(:i64, :lt_s), do: 0x53
defp do_operation(:i64, :lt_u), do: 0x54
defp do_operation(:i64, :gt_s), do: 0x55
defp do_operation(:i64, :gt_u), do: 0x56
defp do_operation(:i64, :le_s), do: 0x57
defp do_operation(:i64, :le_u), do: 0x58
defp do_operation(:i64, :ge_s), do: 0x59
defp do_operation(:i64, :ge_u), do: 0x5A

defp do_operation(:f32, :eq), do: 0x5B
defp do_operation(:f32, :ne), do: 0x5C
defp do_operation(:f32, :lt), do: 0x5D
defp do_operation(:f32, :gt), do: 0x5E
defp do_operation(:f32, :le), do: 0x5F
defp do_operation(:f32, :ge), do: 0x60

defp do_operation(:f64, :eq), do: 0x61
defp do_operation(:f64, :ne), do: 0x62
defp do_operation(:f64, :lt), do: 0x63
defp do_operation(:f64, :gt), do: 0x64
defp do_operation(:f64, :le), do: 0x65
defp do_operation(:f64, :ge), do: 0x66
end
end
8 changes: 1 addition & 7 deletions lib/orb/instruction_sequence.ex
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,7 @@ defmodule Orb.InstructionSequence do
options
) do
for instruction <- instructions do
case instruction do
%Orb.InstructionSequence{} ->
to_wasm(instruction, options)

_ ->
Orb.ToWasm.to_wasm(instruction, options)
end
Orb.ToWasm.to_wasm(instruction, options)
end
end
end
Expand Down
Loading

0 comments on commit 6f4ef36

Please sign in to comment.