-- | This module represents a type-safe port of Erlang's general instructions.
--   If this is your first exposure to BEAM, __I highly recommend Erik Stenman's book:
--   <https://happi.github.io/theBeamBook>__.
--   The documentation in this module point there derives directly from there,
--   the <https://github.com/jerlang/jerlang Jerlang> project,
--   and <https://github.com/erlang/otp/blob/master/lib/compiler/src/genop.tab Erlang's source code>.

module Codec.Beam.Instructions
  ( label, func_info, on_load, line
  -- * Function and BIF calls
  , call, call_last, call_only, call_ext, call_ext_last, bif0, bif1, bif2, call_ext_only, apply, apply_last, gc_bif1, gc_bif2, gc_bif3
  -- * Allocating, deallocating and returning
  , allocate, allocate_heap, allocate_zero, allocate_heap_zero, test_heap, init', deallocate, return', trim
  -- * Sending and receiving
  , send, remove_message, timeout, loop_rec, loop_rec_end, wait, wait_timeout, recv_mark, recv_set
  -- * Comparision
  , is_lt, is_ge, is_eq, is_ne, is_eq_exact, is_ne_exact
  -- * Type tests
  , is_integer, is_float, is_number, is_atom, is_pid, is_reference, is_port, is_nil, is_binary, is_list, is_nonempty_list, is_tuple, test_arity, is_boolean, is_function, is_function2, is_bitstr, is_map, is_tagged_tuple
  -- * Indexing and jumping
  , select_val, select_tuple_arity, jump
  -- * Moving, extracting, modifying
  , move, get_list, get_tuple_element, set_tuple_element
  -- * Building terms
  , put_list, put_tuple, put
  -- * Raising errors
  , badmatch, if_end, case_end
  -- * @fun@ support
  , call_fun, make_fun2
  -- * Binary matching
  , bs_start_match2, bs_get_integer2, bs_get_float2, bs_get_binary2, bs_skip_bits2, bs_test_tail2, bs_save2, bs_restore2, bs_context_to_binary, bs_test_unit, bs_match_string, bs_append, bs_private_append
  -- * Binary construction
  , bs_init2, bs_put_integer, bs_put_binary, bs_put_float, bs_put_string, bs_add, bs_init_writable, bs_init_bits, bs_get_utf8, bs_skip_utf8, bs_get_utf16, bs_skip_utf16, bs_get_utf32, bs_skip_utf32, bs_utf8_size, bs_put_utf8, bs_utf16_size, bs_put_utf16, bs_put_utf32
  -- * Floating point arithmetic
  , fclearerror, fcheckerror, fmove, fconv, fadd, fsub, fmul, fdiv, fnegate
  -- * Try, catch, raise
  , try, try_end, try_case, try_case_end, raise, catch, catch_end
  -- * Maps
  , put_map_assoc, put_map_exact, has_map_fields, get_map_elements
  ) where

import Codec.Beam.Internal.Syntax
import Data.Text (Text)

-- | Label gives this code address a name and marks the start of
--   a basic block.
label
  :: Label -- ^ unique identifier
  -> Op
label a1 = Op 1 [FromNewLabel a1]

-- | Define a function M:F/A in the current module.
func_info
  :: Text -- ^ function name
  -> Int  -- ^ arity
  -> Op
func_info a1 a2 = Op 2 [FromNewFunction a1 a2, FromAtom a1, FromUntagged a2]

-- | Call the function at label.
--   Save the next instruction as the return address in the CP register.
call
  :: Int -- ^ arity
  -> Label
  -> Op
call a1 a2 = Op 4 [FromUntagged a1, FromLabel a2]

-- | Deallocate and do a tail recursive call to the function at label.
--   Do not update the CP register.
--   Before the call deallocate Deallocate words of stack.
call_last
  :: Int -- ^ arity
  -> Label
  -> Int -- ^ number of stack words to deallocate
  -> Op
call_last a1 a2 a3 = Op 5 [FromUntagged a1, FromLabel a2, FromUntagged a3]

-- | Do a tail recursive call to the function at Label.
--   Do not update the CP register.
call_only
  :: Int -- ^ arity
  -> Label
  -> Op
call_only a1 a2 = Op 6 [FromUntagged a1, FromLabel a2]

-- | Call the function of arity pointed to by Destination.
--   Save the next instruction as the return address in the CP register.
call_ext :: Import -> Op
call_ext a1 = Op 7 [FromUntagged (_import_arity a1), FromImport a1]

-- | Deallocate and do a tail call to function
--   pointed to by Destination.
--   Do not update the CP register.
--   Deallocate some words from the stack before the call.
call_ext_last :: Import -> Int -> Op
call_ext_last a1 a2 = Op 8 [FromUntagged (_import_arity a1), FromImport a1, FromUntagged a2]

-- | Call the bif and store the result in register.
bif0 :: (Bif0 a1, NoGC a1, IsRegister a2) => a1 -> a2 -> Op
bif0 a1 a2 = Op 9 [FromImport (importBif0 a1), fromRegister a2]

-- | Call the bif with the source, and store the result in register.
--   On failure jump to label.
bif1 :: (Bif1 a2, NoGC a2, IsSource a3, IsRegister a4) => Label -> a2 -> a3 -> a4 -> Op
bif1 a1 a2 a3 a4 = Op 10 [FromLabel a1, FromImport (importBif1 a2), fromSource a3, fromRegister a4]

-- | Call the bif with the sources, and store the result in register.
--   On failure jump to label.
bif2 :: (Bif2 a2, NoGC a2, IsSource a3, IsSource a4, IsRegister a5) => Label -> a2 -> a3 -> a4 -> a5 -> Op
bif2 a1 a2 a3 a4 a5 = Op 11 [FromLabel a1, FromImport (importBif2 a2), fromSource a3, fromSource a4, fromRegister a5]

-- | Allocate space for some words on the stack. If a GC is needed
--   during allocation there are a number of live X registers.
--   Also save the continuation pointer (CP) on the stack.
allocate
  :: Int -- ^ stack words needed
  -> Int -- ^ live X registers
  -> Op
allocate a1 a2 = Op 12 [FromUntagged a1, FromUntagged a2]

-- | Allocate space for some words on the stack and ensure there is
--   space for words on the heap. If a GC is needed
--   save Live number of X registers.
--   Also save the continuation pointer (CP) on the stack.
allocate_heap
  :: Int -- ^ stack words needed
  -> Int -- ^ heap words needed
  -> Int -- ^ live X registers
  -> Op
allocate_heap a1 a2 a3 = Op 13 [FromUntagged a1, FromUntagged a2, FromUntagged a3]

-- | Allocate space for some words on the stack. If a GC is needed
--   during allocation there are a number of live X registers.
--   Clear the new stack words. (By writing NIL.)
--   Also save the continuation pointer (CP) on the stack.
allocate_zero
  :: Int -- ^ stack words needed
  -> Int -- ^ live X registers
  -> Op
allocate_zero a1 a2 = Op 14 [FromUntagged a1, FromUntagged a2]

-- | Allocate space for some words on the stack and ensure there is
--   space for words on the heap. If a GC is needed
--   save Live number of X registers.
--   Clear the new stack words. (By writing NIL.)
--   Also save the continuation pointer (CP) on the stack.
allocate_heap_zero
  :: Int -- ^ stack words needed
  -> Int -- ^ heap words needed
  -> Int -- ^ live X registers
  -> Op
allocate_heap_zero a1 a2 a3 = Op 15 [FromUntagged a1, FromUntagged a2, FromUntagged a3]

-- | Ensure there is space for HeapNeed words on the heap. If a GC is needed
--   save live number of X registers.
test_heap
  :: Int -- ^ heap words needed
  -> Int -- ^ live number of X registers
  -> Op
test_heap a1 a2 = Op 16 [FromUntagged a1, FromUntagged a2]

-- | Clear the stack word. (By writing NIL.)
init' :: Y -> Op
init' a1 = Op 17 [FromY a1]

-- | Restore the continuation pointer (CP) from the stack and deallocate
--   N+1 words from the stack (the + 1 is for the CP).
deallocate :: Int -> Op
deallocate a1 = Op 18 [FromUntagged a1]

-- | Return to the address in the continuation pointer (CP).
return' :: Op
return'  = Op 19 []

-- | Send argument in x(1) as a message to the destination process in x(0).
--   The message in x(1) ends up as the result of the send in x(0).
send :: Op
send  = Op 20 []

-- | Unlink the current message from the message queue and store a
--   pointer to the message in x(0). Remove any timeout.
remove_message :: Op
remove_message  = Op 21 []

-- | Reset the save point of the mailbox and clear the timeout flag.
timeout :: Op
timeout  = Op 22 []

-- | Loop over the message queue, if it is empty jump to label.
loop_rec :: Label -> X -> Op
loop_rec a1 a2 = Op 23 [FromLabel a1, FromX a2]

-- | Advance the save pointer to the next message and jump back to label.
loop_rec_end :: Label -> Op
loop_rec_end a1 = Op 24 [FromLabel a1]

-- | Suspend the processes and set the entry point to the beginning of the 
--   receive loop at label.
wait :: Label -> Op
wait a1 = Op 25 [FromLabel a1]

-- | Sets up a timeout of source milliseconds and saves the address of the
--   following instruction as the entry point if the timeout triggers.
wait_timeout :: (IsSource a2) => Label -> a2 -> Op
wait_timeout a1 a2 = Op 26 [FromLabel a1, fromSource a2]

-- | Compare two terms and jump to label if first is not less than second.
is_lt :: (IsSource a2, IsSource a3) => Label -> a2 -> a3 -> Op
is_lt a1 a2 a3 = Op 39 [FromLabel a1, fromSource a2, fromSource a3]

-- | Compare two terms and jump to label if first is less than second.
is_ge :: (IsSource a2, IsSource a3) => Label -> a2 -> a3 -> Op
is_ge a1 a2 a3 = Op 40 [FromLabel a1, fromSource a2, fromSource a3]

-- | Compare two terms and jump to label if first is not (numerically) equal to second.
is_eq :: (IsSource a2, IsSource a3) => Label -> a2 -> a3 -> Op
is_eq a1 a2 a3 = Op 41 [FromLabel a1, fromSource a2, fromSource a3]

-- | Compare two terms and jump to label if first is (numerically) equal to second.
is_ne :: (IsSource a2, IsSource a3) => Label -> a2 -> a3 -> Op
is_ne a1 a2 a3 = Op 42 [FromLabel a1, fromSource a2, fromSource a3]

-- | Compare two terms and jump to label if first is not exactly equal to second.
is_eq_exact :: (IsSource a2, IsSource a3) => Label -> a2 -> a3 -> Op
is_eq_exact a1 a2 a3 = Op 43 [FromLabel a1, fromSource a2, fromSource a3]

-- | Compare two terms and jump to label if first is exactly equal to second.
is_ne_exact :: (IsSource a2, IsSource a3) => Label -> a2 -> a3 -> Op
is_ne_exact a1 a2 a3 = Op 44 [FromLabel a1, fromSource a2, fromSource a3]

-- | Test the type of source and jump to label if it is not an integer.
is_integer :: (IsSource a2) => Label -> a2 -> Op
is_integer a1 a2 = Op 45 [FromLabel a1, fromSource a2]

-- | Test the type of source and jump to label if it is not a float.
is_float :: (IsSource a2) => Label -> a2 -> Op
is_float a1 a2 = Op 46 [FromLabel a1, fromSource a2]

-- | Test the type of source and jump to label if it is not a number.
is_number :: (IsSource a2) => Label -> a2 -> Op
is_number a1 a2 = Op 47 [FromLabel a1, fromSource a2]

-- | Test the type of source and jump to label if it is not a atom.
is_atom :: (IsSource a2) => Label -> a2 -> Op
is_atom a1 a2 = Op 48 [FromLabel a1, fromSource a2]

-- | Test the type of source and jump to label if it is not a pid.
is_pid :: (IsSource a2) => Label -> a2 -> Op
is_pid a1 a2 = Op 49 [FromLabel a1, fromSource a2]

-- | Test the type of source and jump to label if it is not a reference.
is_reference :: (IsSource a2) => Label -> a2 -> Op
is_reference a1 a2 = Op 50 [FromLabel a1, fromSource a2]

-- | Test the type of source and jump to label if it is not a port.
is_port :: (IsSource a2) => Label -> a2 -> Op
is_port a1 a2 = Op 51 [FromLabel a1, fromSource a2]

-- | Test the type of source and jump to label if it is not nil.
is_nil :: (IsSource a2) => Label -> a2 -> Op
is_nil a1 a2 = Op 52 [FromLabel a1, fromSource a2]

-- | Test the type of source and jump to label if it is not a binary.
is_binary :: (IsSource a2) => Label -> a2 -> Op
is_binary a1 a2 = Op 53 [FromLabel a1, fromSource a2]

-- | Test the type of source and jump to label if it is not a cons or nil.
is_list :: (IsSource a2) => Label -> a2 -> Op
is_list a1 a2 = Op 55 [FromLabel a1, fromSource a2]

-- | Test the type of source and jump to label if it is not a cons.
is_nonempty_list :: (IsSource a2) => Label -> a2 -> Op
is_nonempty_list a1 a2 = Op 56 [FromLabel a1, fromSource a2]

-- | Test the type of source and jump to label if it is not a tuple.
is_tuple :: (IsSource a2) => Label -> a2 -> Op
is_tuple a1 a2 = Op 57 [FromLabel a1, fromSource a2]

-- | Test the arity of (the tuple in) source and jump to label if it is not equal to arity.
test_arity :: (IsSource a2) => Label -> a2 -> Int -> Op
test_arity a1 a2 a3 = Op 58 [FromLabel a1, fromSource a2, FromUntagged a3]


-- | Jump to the destination label corresponding to source
--   in the destinations list, if no arity matches, jump to fail label.
select_val :: (IsSource a1) => a1 -> Label -> [(Label, Source)] -> Op
select_val a1 a2 a3 = Op 59 [fromSource a1, FromLabel a2, fromDestinations a3]

-- | Check the arity of the source tuple and jump to the corresponding
--   destination label, if no arity matches, jump to FailLabel.
select_tuple_arity :: (IsSource a1) => a1 -> Label -> [(Label, Source)] -> Op
select_tuple_arity a1 a2 a3 = Op 60 [fromSource a1, FromLabel a2, fromDestinations a3]

-- | Jump to label.
jump :: Label -> Op
jump a1 = Op 61 [FromLabel a1]

-- | Old-style catch.
catch :: Y -> Label -> Op
catch a1 a2 = Op 62 [FromY a1, FromLabel a2]

catch_end :: Y -> Op
catch_end a1 = Op 63 [FromY a1]

-- | Move the source (a literal or a register) to
--   the destination register.
move :: (IsSource a1, IsRegister a2) => a1 -> a2 -> Op
move a1 a2 = Op 64 [fromSource a1, fromRegister a2]

-- | Get the head and tail (or car and cdr) parts of a list
--   (a cons cell) from the initial register and put them into the registers.
get_list
  :: (IsRegister a1, IsRegister a2, IsRegister a3)
  => a1 -- ^ where to get the list
  -> a2 -- ^ where to put the head (car)
  -> a3 -- ^ where to put the tail (cdr)
  -> Op
get_list a1 a2 a3 = Op 65 [fromRegister a1, fromRegister a2, fromRegister a3]

-- | Get a particular element number from the tuple in source and put
--   it in the destination register.
get_tuple_element
  :: (IsRegister a1, IsRegister a3)
  => a1  -- ^ where to get the tuple
  -> Int -- ^ target element index, __0-based__
  -> a3  -- ^ where to put the element
  -> Op
get_tuple_element a1 a2 a3 = Op 66 [fromRegister a1, FromUntagged a2, fromRegister a3]

-- | Update the element at position of the tuple in register
--   with the new source element.
set_tuple_element :: (IsSource a1, IsRegister a2) => a1 -> a2 -> Int -> Op
set_tuple_element a1 a2 a3 = Op 67 [fromSource a1, fromRegister a2, FromUntagged a3]


-- | Build a list, from the front, and puts the resulting list in the register.
--   Just like Erlang's @|@ or Haskell's @:@.
put_list
  :: (IsSource a1, IsSource a2, IsRegister a3)
    => a1 -- ^ the new head
    -> a2 -- ^ the new tail
    -> a3
    -> Op
put_list a1 a2 a3 = Op 69 [fromSource a1, fromSource a2, fromRegister a3]

-- | Constructs an empty tuple on the heap (size+1 words)
--   and places its address into the Destination register.
--   No elements are set at this moment.
--   Put_tuple instruction is always followed by multiple
--   'put' instructions which destructively set its elements one by one.
put_tuple :: (IsRegister a2) => Int -> a2 -> Op
put_tuple a1 a2 = Op 70 [FromUntagged a1, fromRegister a2]

put :: (IsSource a1) => a1 -> Op
put a1 = Op 71 [fromSource a1]

badmatch :: (IsSource a1) => a1 -> Op
badmatch a1 = Op 72 [fromSource a1]

if_end :: Op
if_end  = Op 73 []

case_end :: (IsSource a1) => a1 -> Op
case_end a1 = Op 74 [fromSource a1]

-- | Call @fun@ object (in x[Arity]) with args (in x[0..Arity-1])
--   Raises @badarity@ if the arity doesn’t match the function object.
--   Raises @badfun@ if a non-function is passed.
call_fun
  :: Int -- ^ arity
  -> Op
call_fun a1 = Op 75 [FromUntagged a1]

-- | Test the type of the source and jump to label if it is not a
--   function (i.e. fun or closure).
is_function :: (IsSource a2) => Label -> a2 -> Op
is_function a1 a2 = Op 77 [FromLabel a1, fromSource a2]

-- | Do a tail recursive call to the function at label.
--   Do not update the CP register.
call_ext_only :: Import -> Op
call_ext_only a1 = Op 78 [FromUntagged (_import_arity a1), FromImport a1]

bs_put_integer :: (IsSource a2, IsSource a5) => Label -> a2 -> Int -> Int -> a5 -> Op
bs_put_integer a1 a2 a3 a4 a5 = Op 89 [FromLabel a1, fromSource a2, FromUntagged a3, FromUntagged a4, fromSource a5]

bs_put_binary :: (IsSource a2, IsSource a5) => Label -> a2 -> Int -> Int -> a5 -> Op
bs_put_binary a1 a2 a3 a4 a5 = Op 90 [FromLabel a1, fromSource a2, FromUntagged a3, FromUntagged a4, fromSource a5]

bs_put_float :: (IsSource a2, IsSource a5) => Label -> a2 -> Int -> Int -> a5 -> Op
bs_put_float a1 a2 a3 a4 a5 = Op 91 [FromLabel a1, fromSource a2, FromUntagged a3, FromUntagged a4, fromSource a5]

bs_put_string :: Int -> Int -> Op
bs_put_string a1 a2 = Op 92 [FromUntagged a1, FromUntagged a2]

fclearerror :: Op
fclearerror  = Op 94 []

fcheckerror :: Label -> Op
fcheckerror a1 = Op 95 [FromLabel a1]

fmove :: (IsSourceF a1, IsRegisterF a2) => a1 -> a2 -> Op
fmove a1 a2 = Op 96 [fromSourceF a1, fromRegisterF a2]

fconv :: (IsSource a1) => a1 -> F -> Op
fconv a1 a2 = Op 97 [fromSource a1, FromF a2]

fadd :: F -> F -> F -> Op
fadd a1 a2 a3 = Op 98 [FromLabel (Label 0), FromF a1, FromF a2, FromF a3]

fsub :: F -> F -> F -> Op
fsub a1 a2 a3 = Op 99 [FromLabel (Label 0), FromF a1, FromF a2, FromF a3]

fmul :: F -> F -> F -> Op
fmul a1 a2 a3 = Op 100 [FromLabel (Label 0), FromF a1, FromF a2, FromF a3]

fdiv :: F -> F -> F -> Op
fdiv a1 a2 a3 = Op 101 [FromLabel (Label 0), FromF a1, FromF a2, FromF a3]

fnegate :: F -> F -> Op
fnegate a1 a2 = Op 102 [FromLabel (Label 0), FromF a1, FromF a2]

make_fun2 :: Lambda -> Op
make_fun2 a1 = Op 103 [FromLambda a1]

try :: Y -> Label -> Op
try a1 a2 = Op 104 [FromY a1, FromLabel a2]

try_end :: Y -> Op
try_end a1 = Op 105 [FromY a1]

try_case :: Y -> Op
try_case a1 = Op 106 [FromY a1]

try_case_end :: (IsSource a1) => a1 -> Op
try_case_end a1 = Op 107 [fromSource a1]

raise :: (IsSource a1, IsSource a2) => a1 -> a2 -> Op
raise a1 a2 = Op 108 [fromSource a1, fromSource a2]

bs_init2 :: (IsSource a2, IsRegister a6) => Label -> a2 -> Int -> Int -> Int -> a6 -> Op
bs_init2 a1 a2 a3 a4 a5 a6 = Op 109 [FromLabel a1, fromSource a2, FromUntagged a3, FromUntagged a4, FromUntagged a5, fromRegister a6]

bs_add :: (IsSource a2, IsSource a3, IsRegister a5) => Label -> a2 -> a3 -> Int -> a5 -> Op
bs_add a1 a2 a3 a4 a5 = Op 111 [FromLabel a1, fromSource a2, fromSource a3, FromUntagged a4, fromRegister a5]

-- | Apply function object (in x[arity]) with args (in x[0..arity-1])
apply
  :: Int -- ^ arity
  -> Op
apply a1 = Op 112 [FromUntagged a1]

-- | Same as 'apply' but does not save the CP and deallocates words
apply_last
  :: Int -- ^ arity
  -> Int -- ^ words to deallocate
  -> Op
apply_last a1 a2 = Op 113 [FromUntagged a1, FromUntagged a2]

-- | Test the type of source and jump to label if it is not a boolean.
is_boolean :: (IsSource a2) => Label -> a2 -> Op
is_boolean a1 a2 = Op 114 [FromLabel a1, fromSource a2]

-- | Test the type of the source and jump to label if it is not a
--   function of the particular arity.
is_function2
  :: (IsSource a2, IsSource a3)
  => Label
  -> a2 -- ^ possible function
  -> a3 -- ^ possible arity
  -> Op
is_function2 a1 a2 a3 = Op 115 [FromLabel a1, fromSource a2, fromSource a3]

bs_start_match2 :: (IsSource a2, IsRegister a5) => Label -> a2 -> Int -> Int -> a5 -> Op
bs_start_match2 a1 a2 a3 a4 a5 = Op 116 [FromLabel a1, fromSource a2, FromUntagged a3, FromUntagged a4, fromRegister a5]

bs_get_integer2 :: (IsSource a4, IsRegister a7) => Label -> X -> Int -> a4 -> Int -> Int -> a7 -> Op
bs_get_integer2 a1 a2 a3 a4 a5 a6 a7 = Op 117 [FromLabel a1, FromX a2, FromUntagged a3, fromSource a4, FromUntagged a5, FromUntagged a6, fromRegister a7]

bs_get_float2 :: (IsSource a4, IsRegister a7) => Label -> X -> Int -> a4 -> Int -> Int -> a7 -> Op
bs_get_float2 a1 a2 a3 a4 a5 a6 a7 = Op 118 [FromLabel a1, FromX a2, FromUntagged a3, fromSource a4, FromUntagged a5, FromUntagged a6, fromRegister a7]

bs_get_binary2 :: (IsSource a4, IsRegister a7) => Label -> X -> Int -> a4 -> Int -> Int -> a7 -> Op
bs_get_binary2 a1 a2 a3 a4 a5 a6 a7 = Op 119 [FromLabel a1, FromX a2, FromUntagged a3, fromSource a4, FromUntagged a5, FromUntagged a6, fromRegister a7]

bs_skip_bits2 :: (IsSource a3) => Label -> X -> a3 -> Int -> Int -> Op
bs_skip_bits2 a1 a2 a3 a4 a5 = Op 120 [FromLabel a1, FromX a2, fromSource a3, FromUntagged a4, FromUntagged a5]

bs_test_tail2 :: Label -> X -> Int -> Op
bs_test_tail2 a1 a2 a3 = Op 121 [FromLabel a1, FromX a2, FromUntagged a3]

bs_save2 :: (IsRegister a1) => a1 -> Int -> Op
bs_save2 a1 a2 = Op 122 [fromRegister a1, FromUntagged a2]

bs_restore2 :: (IsRegister a1) => a1 -> Int -> Op
bs_restore2 a1 a2 = Op 123 [fromRegister a1, FromUntagged a2]


-- | Call the bif with the argument, and store the result in the register.
--   On failure jump to label.
--   Do a garbage collection if necessary to allocate space on the heap
--   for the result.
gc_bif1
  :: (Bif1 a3, IsSource a4, IsRegister a5)
  => Label -- ^ jump here on failure
  -> Int   -- ^ number of X-registers to save
  -> a3    -- ^ BIF, something like 'Codec.Beam.Bifs.erlang_localtime'
  -> a4    -- ^ argument
  -> a5    -- ^ where to put the result
  -> Op
gc_bif1 a1 a2 a3 a4 a5 = Op 124 [FromLabel a1, FromUntagged a2, FromImport (importBif1 a3), fromSource a4, fromRegister a5]

-- | Same as 'gc_bif1', but with two source arguments.
gc_bif2 :: (Bif2 a3, IsSource a4, IsSource a5, IsRegister a6) => Label -> Int -> a3 -> a4 -> a5 -> a6 -> Op
gc_bif2 a1 a2 a3 a4 a5 a6 = Op 125 [FromLabel a1, FromUntagged a2, FromImport (importBif2 a3), fromSource a4, fromSource a5, fromRegister a6]

is_bitstr :: (IsSource a2) => Label -> a2 -> Op
is_bitstr a1 a2 = Op 129 [FromLabel a1, fromSource a2]

bs_context_to_binary :: (IsRegister a1) => a1 -> Op
bs_context_to_binary a1 = Op 130 [fromRegister a1]

bs_test_unit :: Label -> X -> Int -> Op
bs_test_unit a1 a2 a3 = Op 131 [FromLabel a1, FromX a2, FromUntagged a3]

bs_match_string :: Label -> X -> Int -> Int -> Op
bs_match_string a1 a2 a3 a4 = Op 132 [FromLabel a1, FromX a2, FromUntagged a3, FromUntagged a4]

bs_init_writable :: Op
bs_init_writable  = Op 133 []

bs_append :: (IsSource a2, IsSource a6, IsRegister a8) => Label -> a2 -> Int -> Int -> Int -> a6 -> Int -> a8 -> Op
bs_append a1 a2 a3 a4 a5 a6 a7 a8 = Op 134 [FromLabel a1, fromSource a2, FromUntagged a3, FromUntagged a4, FromUntagged a5, fromSource a6, FromUntagged a7, fromRegister a8]

bs_private_append :: (IsSource a2, IsSource a4, IsRegister a6) => Label -> a2 -> Int -> a4 -> Int -> a6 -> Op
bs_private_append a1 a2 a3 a4 a5 a6 = Op 135 [FromLabel a1, fromSource a2, FromUntagged a3, fromSource a4, FromUntagged a5, fromRegister a6]

-- | Reduce the stack usage by some number of words,
--   keeping the CP on the top of the stack.
trim
  :: Int -- ^ words to remove
  -> Int -- ^ words ro keep
  -> Op
trim a1 a2 = Op 136 [FromUntagged a1, FromUntagged a2]

bs_init_bits :: (IsSource a2, IsRegister a6) => Label -> a2 -> Int -> Int -> Int -> a6 -> Op
bs_init_bits a1 a2 a3 a4 a5 a6 = Op 137 [FromLabel a1, fromSource a2, FromUntagged a3, FromUntagged a4, FromUntagged a5, fromRegister a6]

bs_get_utf8 :: (IsRegister a5) => Label -> X -> Int -> Int -> a5 -> Op
bs_get_utf8 a1 a2 a3 a4 a5 = Op 138 [FromLabel a1, FromX a2, FromUntagged a3, FromUntagged a4, fromRegister a5]

bs_skip_utf8 :: Label -> X -> Int -> Int -> Op
bs_skip_utf8 a1 a2 a3 a4 = Op 139 [FromLabel a1, FromX a2, FromUntagged a3, FromUntagged a4]

bs_get_utf16 :: (IsRegister a5) => Label -> X -> Int -> Int -> a5 -> Op
bs_get_utf16 a1 a2 a3 a4 a5 = Op 140 [FromLabel a1, FromX a2, FromUntagged a3, FromUntagged a4, fromRegister a5]

bs_skip_utf16 :: Label -> X -> Int -> Int -> Op
bs_skip_utf16 a1 a2 a3 a4 = Op 141 [FromLabel a1, FromX a2, FromUntagged a3, FromUntagged a4]

bs_get_utf32 :: (IsRegister a5) => Label -> X -> Int -> Int -> a5 -> Op
bs_get_utf32 a1 a2 a3 a4 a5 = Op 142 [FromLabel a1, FromX a2, FromUntagged a3, FromUntagged a4, fromRegister a5]

bs_skip_utf32 :: Label -> X -> Int -> Int -> Op
bs_skip_utf32 a1 a2 a3 a4 = Op 143 [FromLabel a1, FromX a2, FromUntagged a3, FromUntagged a4]

bs_utf8_size :: (IsSource a2, IsRegister a3) => Label -> a2 -> a3 -> Op
bs_utf8_size a1 a2 a3 = Op 144 [FromLabel a1, fromSource a2, fromRegister a3]

bs_put_utf8 :: (IsSource a3) => Label -> Int -> a3 -> Op
bs_put_utf8 a1 a2 a3 = Op 145 [FromLabel a1, FromUntagged a2, fromSource a3]

bs_utf16_size :: (IsSource a2, IsRegister a3) => Label -> a2 -> a3 -> Op
bs_utf16_size a1 a2 a3 = Op 146 [FromLabel a1, fromSource a2, fromRegister a3]

bs_put_utf16 :: (IsSource a3) => Label -> Int -> a3 -> Op
bs_put_utf16 a1 a2 a3 = Op 147 [FromLabel a1, FromUntagged a2, fromSource a3]

bs_put_utf32 :: (IsSource a3) => Label -> Int -> a3 -> Op
bs_put_utf32 a1 a2 a3 = Op 148 [FromLabel a1, FromUntagged a2, fromSource a3]

on_load :: Op
on_load  = Op 149 []

-- | Save the end of the message queue and the address of
--   the label so that a recv_set instruction can start
-- | scanning the inbox from this position.
recv_mark :: Label -> Op
recv_mark a1 = Op 150 [FromLabel a1]

-- | Check that the saved mark points to label and set the
--   save pointer in the message queue to the last position
--   of the message queue saved by the recv_mark instruction.
recv_set :: Label -> Op
recv_set a1 = Op 151 [FromLabel a1]

-- | Same as 'gc_bif1', but with three source arguments.
gc_bif3 :: (Bif3 a3, IsSource a4, IsSource a5, IsSource a6, IsRegister a7) => Label -> Int -> a3 -> a4 -> a5 -> a6 -> a7 -> Op
gc_bif3 a1 a2 a3 a4 a5 a6 a7 = Op 152 [FromLabel a1, FromUntagged a2, FromImport (importBif3 a3), fromSource a4, fromSource a5, fromSource a6, fromRegister a7]

line :: Int -> Op
line a1 = Op 153 [FromUntagged a1]

put_map_assoc :: (IsSource a2, IsRegister a3) => Label -> a2 -> a3 -> [(Source, Source)] -> Op
put_map_assoc a1 a2 a3 a4 = Op 154 [FromLabel a1, fromSource a2, fromRegister a3, FromUntagged (length a4 + 1), fromPairs fromSource fromSource a4]

put_map_exact :: (IsSource a2, IsRegister a3) => Label -> a2 -> a3 -> [(Source, Source)] -> Op
put_map_exact a1 a2 a3 a4 = Op 155 [FromLabel a1, fromSource a2, fromRegister a3, FromUntagged (length a4 + 1), fromPairs fromSource fromSource a4]

is_map :: (IsSource a2) => Label -> a2 -> Op
is_map a1 a2 = Op 156 [FromLabel a1, fromSource a2]

has_map_fields :: (IsSource a2) => Label -> a2 -> [Source] -> Op
has_map_fields a1 a2 a3 = Op 157 [FromLabel a1, fromSource a2, FromList (map fromSource a3)]

-- | Get multiple values out of a map
get_map_elements
  :: (IsSource a2)
    => Label                  -- ^ jump here on failure
    -> a2                     -- ^ where the map is
    -> [(Source, Register)] -- ^ list of (what key to use, where to put value)
    -> Op
get_map_elements a1 a2 a3 = Op 158 [FromLabel a1, fromSource a2, fromPairs fromSource fromRegister a3]

-- | Test the type of source and jumps to label if it is not a tuple.
--   Test the arity of Reg and jumps to label if it is not of the given size.
--   Test the first element of the tuple and jumps to label if it is not given atom.
is_tagged_tuple :: (IsSource a2) => Label -> a2 -> Int -> Text -> Op
is_tagged_tuple a1 a2 a3 a4 = Op 159 [FromLabel a1, fromSource a2, FromUntagged a3, FromAtom a4]