# VM instructions
#
#
# Format: first line is the name of the instruction, including the ESI_ prefix.
# Following indented lines is documentation of what the instruction does.  After
# that, zero or more lines describing the instruction's operands follow, on the
# format "<name>: <type>".  The name is informal.  The type is one of the
# following:
#
# 'reg:in' => input register index; value is used but not modified
# 'reg:out' => output register index; value is not used but modified
# 'reg:in:out' => input/output register index; value is used and modified
#
# 'imm:int' => immediate containing a signed integer value
# 'imm:unsigned' => immediate containing an unsigned integer value
# 'imm:offset' => immediate containing a jump offset
#
# 'string' => index into strings array (ES_Code::strings)
# 'identifier' => identifier, also index into strings array (ES_Code::strings)
# 'double' => index into doubels array (ES_Code::doubles)
# 'regexp' => index into regexps array (ES_Code::regexps)
# 'function' => index into functions array (ES_Code::functions)
# 'classid' => index into the class IDs array (ES_Code::classids)
#
# 'unused' => unused operand
#
# Blank lines are insignificant everywhere.  Lines starting with '#' are
# comments (which by now you should have figured out.)
#
# Instructions with variable number of operands cannot be fully described in
# this format.  When the variable operands have the same purpose (such as the
# argument operands to ESI_CALL and ESI_CONSTRUCT) they are described as a
# single operand preceded by an asterix.
#
# Other instructions (ESI_CALL and ESI_CONSTRUCT) simply cannot be fully
# described in this format
#
# An instruction's name can be followed by a preprocessor condition inside
# square brackets; such as "ESI_LOAD [ifdef FOO]".
#
# An instruction's name can be followed by an alias specifier inside
# square brackets; such as "ESI_FOO [alias ESI_BAR]". The meaning being
# that the bytecode engine's instruction handler for ESI_BAR should be
# used for ESI_FOO also. (It is currently not possible to combine an
# alias with an ifdef inside square brackets.)
#
# A register operand can be followed by a type specifier inside angle brackets.
# For an output register, this is the type of the value stored, if known.  For
# an input register, this is the type that the value in the register will be
# converted to before it is used (sort of a 'prefered type'.)  The type (inside
# the angle brackets) can be followed by an equals sign and the value.  The
# special type 'property' means the register value will be used as a property
# name; meaning essentially 'string' but with special handling of non-negative
# integer values below 2^32-1.  The special types 'int32' and 'uint32' means
# numbers known to be integers within the corresponding ranges.  The special
# type 'primitive' means "not object".
#
# The line "=> implicit boolean register" signals that the instruction modifies
# the implicit boolean register.


ESI_LOAD_STRING <trivial>

  Load string immediate into register.

dst: reg:out <string>
value: string


ESI_LOAD_DOUBLE <trivial>

  Load double immediate into register.

dst: reg:out <double>
value: double


ESI_LOAD_INT32 <trivial>

  Load int32 immediate into register.

dst: reg:out <int32>
value: imm:signed


ESI_LOAD_NULL <trivial>

  Load null into register.

dst: reg:out <null>


ESI_LOAD_UNDEFINED <trivial>

  Load undefined into register.

dst: reg:out <undefined>


ESI_LOAD_TRUE <trivial>

  Load true into register.

dst: reg:out <boolean>


ESI_LOAD_FALSE <trivial>

  Load false into register.

dst: reg:out <boolean>


ESI_LOAD_GLOBAL_OBJECT <trivial>

  Loads global object into register.

dst: reg:out <object>


ESI_COPY <trivial>

  Copy value from one register to another.

dst: reg:out
src: reg:in


ESI_COPYN <trivial>

  Copy values from two or more registers into a consequtive range of target
  registers.  None of the destination registers can also occur as source
  registers.  The dst index of the register into which the value in the first
  source register is copied.  Additional source register's values are copied
  into registers dst+N.

dst: reg:out
count: imm:unsigned
*srcN: reg:in


ESI_TYPEOF <trivial>

  Load typeof string into register.

dst: reg:out <string>
src: reg:in


ESI_TONUMBER

  Convert value to number.  This is normally not used; instructions expecting
  numeric operands are responsible for converting their operands themselves.
  The primary situation in which this instruction is emitted is for the unary
  '+' operator; whose only function in practice is to convert its operand to a
  number.

dst: reg:out <number>
src: reg:in


ESI_TOOBJECT

  Convert value to object.  This is normally not used; instructions expecting
  object operands are responsible for converting their operands themselves.  The
  primary situation in which this instruction is emitted is for with statements;
  that run-time only need to store an object in a register.

dst: reg:out <object>
src: reg:in


ESI_TOPRIMITIVE

  Convert value to its primitive value representation guided by a type
  conversion hint.  The instruction is normally not used; instructions
  expecting non-object operands are responsible for converting their
  operands themselves.  The main use for this instruction is when
  converting the operands of numeric instructions into primitives as
  part of compiling expressions. The conversion isn't performed by the
  corresponding instructions, as the order of evaluation of operands
  and conversion to primitive wouldn't be correct then. The type
  conversion hint immediate resolves which primitive value
  representation should be opted for and is an untyped encoding of the
  ES_Value_Internal::HintType enumeration. The ESI_ADD(N) instructions
  use hint=none, all others hint=number, as required by the
  specification.

dst: reg:out <primitive>
src: reg:in
hint: imm:unsigned


ESI_TOPRIMITIVE1

  Like ESI_TOPRIMITIVE but in-place.

dst: reg:in:out <primitive>
hint: imm:unsigned


ESI_IS_NULL_OR_UNDEFINED

  Set implicit boolean register to TRUE if value is null or undefined and FALSE
  otherwise.

src: reg:in


ESI_IS_NOT_NULL_OR_UNDEFINED

  Set implicit boolean register to FALSE if value is null or undefined and TRUE
  otherwise.

src: reg:in


ESI_ADD

  Add strings or numbers.

dst: reg:out <primitive>
lhs: reg:in
rhs: reg:in


ESI_ADD_IMM

  Add immediate integer to string or number.

dst: reg:out
lhs: reg:in
imm: imm:signed


ESI_ADDN [ifdef ES_COMBINED_ADD_SUPPORT]

  Add strings or numbers.  Multiple operand version.  Each operand is guaranteed
  to be a primitive value in a temporary register.

dst: reg:out <string>
count: imm:unsigned
*srcN: reg:in


ESI_FORMAT_STRING

  Create a string by prepending and/or appending a string constant to
  the toString:ed value in a register.  Similar to ESI_ADDN, buy
  specialized to handle a common case.

dst: reg:out <string>
value: reg:in <string>
prefix: string
suffix: string
cache: imm:unsigned


ESI_SUB

  Subtract numbers.

dst: reg:out <number>
lhs: reg:in <number>
rhs: reg:in <number>


ESI_SUB_IMM

  Subtract immediate integer from number.

dst: reg:out <number>
lhs: reg:in <number>
imm: imm:signed


ESI_MUL

  Multiply two numbers.

dst: reg:out <number>
lhs: reg:in <number>
rhs: reg:in <number>


ESI_MUL_IMM

  Multiply number and immediate integer.

dst: reg:out <number>
lhs: reg:in <number>
imm: imm:signed


ESI_DIV

  Divide number by number.

dst: reg:out <number>
dividend: reg:in <number>
divisor: reg:in <number>


ESI_DIV_IMM

  Divide number by immediate integer.

dst: reg:out <number>
dividend: reg:in <number>
imm: imm:signed


ESI_REM

  Calculate remainder.

dst: reg:out <number>
dividend: reg:in <number>
divisor: reg:in <number>


ESI_REM_IMM

  Calculate remainder.

dst: reg:out <number>
dividend: reg:in <number>
imm: imm:signed


ESI_LSHIFT

  Shift 'src' left 'count' bits and store int32 result in 'dst'.  The 'count'
  operand is actually an unsigned integer, but only the lower 5 bits in it are
  used, and the lower 5 bits are the same regardless of whether the input is
  treated as a signed or unsigned integer.

dst: reg:out <int32>
src: reg:in <int32>
count: reg:in <int32>


ESI_LSHIFT_IMM

  Shift 'src' left 'count' bits and store int32 result in 'dst'.

dst: reg:out <int32>
src: reg:in <int32>
count: imm:unsigned


ESI_RSHIFT_SIGNED

  Shift 'src' right 'count' bits and store int32 result in 'dst'.  The 'count'
  operand is actually an unsigned integer, but only the lower 5 bits in it are
  used, and the lower 5 bits are the same regardless of whether the input is
  treated as a signed or unsigned integer.

dst: reg:out <int32>
src: reg:in <int32>
count: reg:in <int32>


ESI_RSHIFT_SIGNED_IMM

  Shift 'src' right 'count' bits and store int32 result in 'dst'.

dst: reg:out <int32>
src: reg:in <int32>
count: imm:unsigned


ESI_RSHIFT_UNSIGNED

  Shift 'src' unsigned right 'count' bits and store uint32 result in 'dst'.  The
  'count' operand is actually an unsigned integer, but only the lower 5 bits in
  it are used, and the lower 5 bits are the same regardless of whether the input
  is treated as a signed or unsigned integer.  Since uint32 values need to be
  represented as doubles (if they are outside int32 range) this instruction
  kills type analyzis slightly.  But if the 'src' operand is an int32, the
  result will be in int32 range as well, which the type analyzer recognizes.

dst: reg:out <number>
src: reg:in <uint32>
count: reg:in <int32>


ESI_RSHIFT_UNSIGNED_IMM

  Shift 'src' unsigned right 'count' bits and store int32 result in 'dst'.

dst: reg:out <uint32>
src: reg:in <uint32>
count: imm:unsigned


ESI_NEG

  Negate number.

dst: reg:out <number>
src: reg:in <number>


ESI_COMPL

  Applies bitwise complement to 'src' and store int32 result in 'dst'.

dst: reg:out <int32>
src: reg:in <int32>


ESI_INC

  Increment number by one in-place in register.

dst: reg:in:out <number>


ESI_DEC

  Decrement number by one in-place in register.

dst: reg:in:out <number>


ESI_BITAND

  Calculate bitwise AND.

dst: reg:out <int32>
lhs: reg:in <int32>
rhs: reg:in <int32>


ESI_BITAND_IMM

  Calculate bitwise AND.

dst: reg:out <int32>
lhs: reg:in <int32>
imm: imm:signed


ESI_BITOR

  Calculate bitwise OR.

dst: reg:out <int32>
lhs: reg:in <int32>
rhs: reg:in <int32>


ESI_BITOR_IMM

  Calculate bitwise OR.

dst: reg:out <int32>
lhs: reg:in <int32>
imm: imm:signed


ESI_BITXOR

  Calculate bitwise XOR.

dst: reg:out <int32>
lhs: reg:in <int32>
rhs: reg:in <int32>


ESI_BITXOR_IMM

  Calculate bitwise XOR.

dst: reg:out <int32>
lhs: reg:in <int32>
imm: imm:signed


ESI_NOT

  Logical NOT.

dst: reg:out <boolean>
src: reg:in <boolean>


ESI_EQ

  Equality comparison.

lhs: reg:in
rhs: reg:in

=> implicit boolean register


ESI_EQ_IMM

  Equality comparison to signed integer.

lhs: reg:in
imm: imm:signed

=> implicit boolean register


ESI_NEQ

  Inequality comparison.

lhs: reg:in
rhs: reg:in

=> implicit boolean register


ESI_NEQ_IMM

  Inequality comparison to signed integer.

lhs: reg:in
imm: imm:signed

=> implicit boolean register


ESI_EQ_STRICT <trivial>

  Strict equality comparison.

lhs: reg:in
rhs: reg:in

=> implicit boolean register


ESI_EQ_STRICT_IMM <trivial>

  Strict equality comparison to signed integer.

lhs: reg:in
imm: imm:signed

=> implicit boolean register


ESI_NEQ_STRICT <trivial>

  Strict inequality comparison.

lhs: reg:in
rhs: reg:in

=> implicit boolean register


ESI_NEQ_STRICT_IMM <trivial>

  Strict inequality comparison.

lhs: reg:in
imm: imm:signed

=> implicit boolean register


ESI_LT

  Less than comparison.

lhs: reg:in
rhs: reg:in

=> implicit boolean register


ESI_LT_IMM

  Less than comparison to signed integer.

lhs: reg:in <number>
imm: imm:signed

=> implicit boolean register


ESI_LTE

  Less than or equal comparison.

lhs: reg:in
rhs: reg:in

=> implicit boolean register


ESI_LTE_IMM

  Less than or equal comparison to signed integer.

lhs: reg:in <number>
imm: imm:signed

=> implicit boolean register


ESI_GT

  Greater than comparison.

lhs: reg:in
rhs: reg:in

=> implicit boolean register


ESI_GT_IMM

  Greater than comparison to signed integer.

lhs: reg:in <number>
imm: imm:signed

=> implicit boolean register


ESI_GTE

  Greater than or equal comparison.

lhs: reg:in
rhs: reg:in

=> implicit boolean register


ESI_GTE_IMM

  Greater than or equal comparison to signed integer.

lhs: reg:in <number>
imm: imm:signed

=> implicit boolean register


ESI_CONDITION <trivial>

  Set implicit boolean register to ToBoolean(register). The
  input operand isn't modified unless it holds a hidden object,
  in which case it is updated to hold undefined. This matches
  expected behaviour for these very rare objects.

src: reg:in:out

=> implicit boolean register


ESI_JUMP <trivial>

  Unconditional jump.

offset: imm:offset


ESI_JUMP_INDIRECT <trivial>

  Indirect jump to contents of 'address'

address: reg:in


ESI_JUMP_IF_TRUE <trivial>

  Conditional jump (jumps if implicit boolean register is true.)

offset: imm:offset
data: imm:unsigned


ESI_JUMP_IF_FALSE <trivial>

  Conditional jump (jumps if implicit boolean register is false.)

offset: imm:offset
data: imm:unsigned


ESI_START_LOOP <trivial>

  Signals the start of a loop.  The operand 'end' is the absolute code word
  index of the first instruction following the loop.

end: imm:unsigned


ESI_STORE_BOOLEAN <trivial>

  Store implicit boolean register into regular register.

dst: reg:out <boolean>


ESI_GETN_IMM

  Get property where property name is known compile time.  To allow for
  conversion into ESI_GETN_IMM_CACHED, this instruction has two unused operands.

value: reg:out
object: reg:in <object>
name: identifier
cache: imm:cache:property


ESI_PUTN_IMM

  Put property where property name is known compile time.

object: reg:in <object>
name: identifier
value: reg:in
cache: imm:cache:property


ESI_INIT_PROPERTY [alias ESI_PUTN_IMM]

  Initialize property with given property name.

object: reg:in <object>
name: identifier
value: reg:in
cache: imm:cache:property


ESI_GET_LENGTH

  Special case of ESI_GETN_IMM when then property name is 'length'.  Has special
  handling of strings and arrays, otherwise very similar to ESI_GETN_IMM.  There
  is no special _CACHED variant, instead the class operand is zero initially,
  meaning the cache is not set.

value: reg:out
object: reg:in <object>
name: identifier
cache: imm:cache:property


ESI_PUT_LENGTH

  Special case of ESI_PUTN_IMM when then property name is 'length'.  Has special
  handling of arrays, otherwise very similar to ESI_PUTN_IMM.  There is no
  special _CACHED or _CACHED_NEW variants, instead the class operands is zero
  initially, meaning the cache is not set.  If oldClass is set but not newClass,
  it acts as ESI_PUTN_IMM_CACHED; if both oldClass and newClass are set it acts
  as ESI_PUTN_IMM_CACHED_NEW.

object: reg:in <object>
name: identifier
value: reg:in
cache: imm:cache:property


ESI_GETI_IMM

  Get indexed property where the index is a compile-time constant.

value: reg:out
object: reg:in <object>
index: imm:unsigned


ESI_PUTI_IMM

  Put indexed property where the index is a compile-time constant.

object: reg:in <object>
index: imm:unsigned
value: reg:in


ESI_GET

  Get property where the name is a compile-time non-constant.  The property
  accessed can be either named or indexed.

value: reg:out
object: reg:in <object>
name: reg:in <property>


ESI_PUT

  Put property where the name is a compile-time non-constant.  The property
  accessed can be either named or indexed.

object: reg:in <object>
name: reg:in <property>
value: reg:in


ESI_DEFINE_GETTER

  Define property getter function for property.

object: reg:in <object>
name: identifier
getter: function
innerScopes: imm:unsigned

ESI_DEFINE_SETTER

  Define property setter function for property.

object: reg:in <object>
name: identifier
setter: function
innerScopes: imm:unsigned

ESI_GET_SCOPE

  Get property in scope chain.  Throws ReferenceError if property is not found.
  If 'inner scopes' is not UINT_MAX, it specifies an array of register indeces
  holding objects that have been pushed onto the scope chain "inside" the local
  scope.  If 'local' is not UINT_MAX, it specifies a register index in the local
  register frame where the named local variable is stored.  The local variable
  is used if the property is not found in an inner scope.

value: reg:out
name: identifier
innerScopes: imm:unsigned
local: reg:in
cache: imm:cache:global


ESI_GET_SCOPE_REF

  Get property in scope chain and returns both its value and the object on which
  it was found.  Throws ReferenceError if property is not found.  If 'inner
  scopes' is not UINT_MAX, it specifies an array of register indeces holding
  objects that have been pushed onto the scope chain "inside" the local scope.
  If 'local' is not UINT_MAX, it specifies a register index in the local
  register frame where the named local variable is stored.  The local variable
  is used if the property is not found in an inner scope.

value: reg:out
object: reg:out <object>
name: identifier
innerScopes: imm:unsigned
local: reg:in
cache: imm:cache:global


ESI_PUT_SCOPE

  Put property in scope chain.  If not found, the property is put on the global
  object.  If 'inner scopes' is not UINT_MAX, it specifies an array of register
  indeces holding objects that have been pushed onto the scope chain "inside"
  the local scope.  If 'local' is not UINT_MAX, it specifies a register index in
  the local register frame where the named local variable is stored.  The local
  variable is used if the property is not found in an inner scope.

name: identifier
value: reg:in
innerScopes: imm:unsigned
local: reg:out:optional


ESI_DELETE_SCOPE

  Delete property in scope chain and return true unless it the property found
  had the DontDelete flag.  If not found, true is returned.  If 'inner scopes'
  is not UINT_MAX, it specifies an array of register indeces holding objects
  that have been pushed onto the scope chain "inside" the local scope.  If
  'local' is not UINT_MAX, it specifies a register index in the local register
  frame where the named local variable is stored.  The local variable is used if
  the property is not found in an inner scope, but since the local is guaranteed
  to have the DontDelete flag, 'local' not being UINT_MAX simply means that
  false is returned.

name: identifier
innerScopes: imm:unsigned
local: reg:in


ESI_GET_GLOBAL

  Get variable in the global object.  This is a special case of ESI_GET_SCOPE
  when the scope chain is known to be empty aside from the local scope, which is
  known not to contain the name, and thus only the global object needs to be
  searched.  At program start time, this instruction can be transformed into
  ESI_GET_GLOBAL_IMM if the named variable is a DontDelete property of the
  global object.

value: reg:out
name: identifier
cache: imm:cache:global


ESI_PUT_GLOBAL

  Get variable in the global object.  This is a special case of ESI_PUT_SCOPE
  when the scope chain is known to be empty aside from the local scope, which is
  known not to contain the name, and thus no scope chain search is necessary.
  At program start time, this instruction can be transformed into
  ESI_PUT_GLOBAL_IMM if the named variable is a DontDelete property of the
  global object.

name: identifier
value: reg:in
cache: imm:cache:global


ESI_GET_LEXICAL

  Get a local variable in an enclosing function's scope.  The 'scope' operand is
  an index into the current function's ES_Function::scope_chain array, and the
  'index' operand is a regular property index on the ES_Object found there.

value: reg:out
scope: imm:unsigned
index: imm:unsigned


ESI_PUT_LEXICAL

  Put a local variable in an enclosing function's scope.  The 'scope' operand is
  an index into the current function's ES_Function::scope_chain array, and the
  'index' operand is a regular property index on the ES_Object found there.

scope: imm:unsigned
index: imm:unsigned
value: reg:in


ESI_DELETEN_IMM

  Delete a property where the name is known compile time.  If the property is
  found and has the DontDelete flag, the implicit boolean register is set to
  false, otherwise it is set to true.

object: reg:in <object>
name: identifier

=> implicit boolean register


ESI_DELETEI_IMM

  Delete indexed property where the index is a compile-time constant.

object: reg:in <object>
index: imm:unsigned

=> implicit boolean register


ESI_DELETE

  Delete property where the name is a compile-time non-constant.

object: reg:in <object>
name: reg:in <property>

=> implicit boolean register


ESI_DECLARE_GLOBAL <trivial>

  Declares a global variable.  Only used when it is not initialized to
  determine if it should be set to undefined or not, i.e., if it has
  already been declared.

name: identifier


ESI_HASPROPERTY

  Check object or its prototypes has property.  This instruction corresponds to
  the 'in' operator and is not used otherwise.

object: reg:in <object>
name: reg:in <property>

=> implicit boolean register


ESI_INSTANCEOF

  Check if an object is an "instance of" a constructor.  This instruction
  corresponds to the 'instanceof' operator and is not used otherwise.

object: reg:in <object>
constructor: reg:in <object>

=> implicit boolean register


ESI_ENUMERATE

  Initiate enumeration of an object's properties.  The value stored in the
  register operand 'enum' is an intermediate value of undefined type used by the
  VM. The register is the same as the 'enum' operand to ESI_NEXT_PROPERTY. The
  object stored in 'object' is the object whose properties is enumerated. The
  the incoming value of object need not be an object, but it should be convertible
  to an object. The result of that conversion is written back to 'object'.
  If 'object' is null or undefined ESI_ENUMERATE does nothing. The 'count'
  operand is a property counter used by ESI_NEXT_PROPERTY but initialised by
  ESI_ENUMERATE.

enum: reg:out
object: reg:in:out <object>
count: reg:out


ESI_NEXT_PROPERTY

  Continue property enumeration previously started by ESI_ENUMERATE. If there
  are more properties, the next one's name is stored in the register specified
  by the 'name' operand, and the implicit boolean register is set to true.  If
  all properties have been processed already, the implicit boolean register is
  set to false.  The 'object' operand is the same object as the corresponding
  operand to ESI_ENUMERATE. If 'object' is null or undefined, the enumeration
  is stopped at once. The 'count' operand keeps track of how many properties
  that has been enumerated when it isn't clear from the enumeration structure
  in 'enum' how many properties that has been enumerated or how many
  properties that are left to enumerate.

name: reg:out <string>
enum: reg:in
object: reg:in <object>
count: reg:in:out

=> implicit boolean register


ESI_EVAL

  Calls eval function in the plain case, i.e., eval().  If the function is not
  eval it fallbacks to ESI_CALL.  If the the innerScopes operand is UINT_MAX,
  the eval call is not within a with() or catch().  If the innerScopes operand
  is UINT_MAX-1, the same is true, and also the scope situation is simple as
  long as the eval call itself doesn't declare any local names.  This means
  essentially that the eval call is the only one in the function, and that it
  will only run once (that is, it is not in a loop.)  In this case, the eval:ed
  code can be generated assuming that the enclosing function's local scope is
  predictable and stable.

frame: reg:in:out
argc: imm:unsigned
innerScopes: imm:unsigned
cache: imm:unsigned


ESI_CALL

  Call function.  The register operand 'frame' specifies the first of 2+argc
  registers that the instruction uses.  The instruction can modify all its
  register operands.  The first register doubles as 'this' object and return
  value.  The second register is the function object to call.  The rest are the
  arguments.  If the most signifcant bit in 'argc' is set, the global object
  should be used as the 'this' object, and the corresponding register's initial
  value is undefined.

  Note: this instruction's impact on the register set is too complex to be
  described in this format, so it needs to be handled especially.

frame: reg:in:out
argc: imm:unsigned


ESI_REDIRECTED_CALL

  Call function with same this object and arguments as current call.  This
  instruction is generated in special cases when ".apply(this, arguments)" is
  used, as an optimization.  What it essentially does is it verifies that the
  object in the register operand 'apply' is the real built-in apply function,
  and if it is, copies the value in the register operand 'function' to register
  1 and calls it with a fully overlapping register frame.

  This instruction must be followed by an ESI_RETURN_NO_VALUE instruction, or an
  ESI_RETURN_VALUE instruction whose value operand is zero (that is, one which
  returns the value returned by redirected function call.)  No other instruction
  can be relied on to work correctly after this instruction has finished.

function: reg:in
apply: reg:in


ESI_APPLY

  Used for function calls on the form "x.apply(a, [b, c, d])" on the assumption
  that '.apply' will be the built-in apply function.  Flattens the array literal
  on the stack instead of allocating an object.  The three registers beginning
  with the 'frame' register contains 'apply', 'a' and 'x' and the 'argc' operand
  is the flattened number of arguments (4 in the example.)  That is, if the
  apply function checks out, the call can be performed directly as if ESI_CALL
  had been used with 'frame' being one higher.  Otherwise, the register frame is
  shuffled, and an array object is created, and the call to 'not actually apply'
  is performed instead.

frame: reg:in:out
argc: imm:unsigned



ESI_CONSTRUCT

  Construct object.  This instruction is very similar to ESI_CALL.  The first
  register operand is used only for the return value, since the 'this' object,
  if there is one, is automatically created before the constructor is called.
  The return value is always an object; if the constructor tries to return
  anything else it is either ignored, or an error is thrown.

  Note: this instruction's impact on the register set is too complex to be
  described in this format, so it needs to be handled especially.

frame: reg:out <object>
argc: imm:unsigned


ESI_RETURN_VALUE <trivial>

  Return from function with a return value provided.

value: reg:in


ESI_RETURN_NO_VALUE <trivial>

  Return from function without return value provided (return value then defaults
  to 'undefined'.)


ESI_RETURN_FROM_EVAL <trivial>

  Return from a "program" started via eval().

value: reg:in


ESI_NEW_OBJECT <trivial>

  Create plain object.  Same as "new Object" assuming the Object constructor
  hasn't been overridden.  Generated by object literal expressions.

object: reg:out <object>
class: imm:unsigned


ESI_CONSTRUCT_OBJECT <trivial>

  Create plain object and initialize a given set of properties from registers.
  The specified class determines the number of properties to initialize, and the
  values are given as a variable number of extra register operands.  This
  instruction is essentially equivalent to ESI_NEW_OBJECT followed by a set of
  ESI_PUTN_IMM instructions.

object: reg:out <object>
class: imm:unsigned
*srcN: reg:in


ESI_NEW_ARRAY <trivial>

  Create an Array object.  Same as "new Array" assuming the Array constructor
  hasn't been overridden.  Generated by array literal expressions.

array: reg:out <object>
length: imm:unsigned


ESI_CONSTRUCT_ARRAY <trivial>

  Create an Array object and initialize it from an element from the array
  ES_Code::constant_array_literals.  Used for array literals where many
  initializer elements are constants.  Non-constant elements may be added later
  using ESI_PUTI_IMM instructions.

array: reg:out <object>
length: imm:unsigned
template: imm:unsigned


ESI_NEW_FUNCTION <trivial>

  Create a Function object for a nested function or function expression.  The
  'function' operand is an index into the ES_Code::functions array.  The created
  function's scope chain is set to the current scope.

object: reg:out <object>
function: function
innerScopes: imm:unsigned


ESI_NEW_REGEXP <trivial>

  Create a RegExp object.  The 'regexp' operand is an index into the
  ES_Code::regexps array.

object: reg:out <object>
regexp: regexp


ESI_TABLE_SWITCH

  Perform jump based on value in register.  This is used for switch statements
  where all cases are constant integers.

value: reg:in <int32>
table: imm:unsigned


ESI_CATCH <trivial>

  "Catch" exception by copying the 'current exception' register into a regular
  register and then clearing the 'current exception' register.  It is possible
  that the 'current exception' register is already empty when this instruction
  is executed, in which case the 'exception' register is set to a special value
  representing this.  That will however only happen for 'finally' clauses, in
  which case the 'exception' register value will only be used by a subsequent
  ESI_RETHROW instruction which is prepared to handle the special value.  (That
  is, the ESI_CATCH belonging to a 'catch' clause will never be executed unless
  there is a current exception to catch.)

exception: reg:out


ESI_CATCH_SCOPE <trivial>

  "Catch" exception by creating an empty object and putting a property named
  'name' on it whose initial value is copied from the 'current exception'
  register.  The 'current exception' register is then cleared.

object: reg:out
name: identifier


ESI_THROW

  Throw exception.

exception: reg:in


ESI_THROW_BUILTIN

  Throw built-in exception.  Operands value is interpreted as follows:

    1: assignment to non-lvalue

exception: imm:unsigned


ESI_RETHROW

  If the 'exception' operand contains an exception value, throw it as by ESI_THROW.
  Otherwise, lookup the closest finally handler that would be crossed by jumping to
  target. If such a handler exists set ip to this, set the implicit boolean
  register and copy the contents of target to nexttarget.

exception: reg:in
target: reg:in
nexttarget: reg:out

=> implicit boolean register


ESI_EXIT <trivial>

  Exit execution of program.  This instruction is not generated by the compiler,
  it is injected automatically by the VM to detect when the top-most "real"
  stack frame finishes.


ESI_EXIT_TO_BUILTIN <trivial>

  Like ESI_EXIT but used when a built-in function calls a native function.


ESI_EXIT_TO_EVAL <trivial>

  Like ESI_EXIT but used when eval() executes code.


ESI_DEBUGGER_STOP [ifdef ECMASCRIPT_DEBUGGER]

  Calls debugger callback with the event type provided.

index: imm:unsigned


ESI_LAST_INSTRUCTION

  Not a real instruction.  :-)
