*******************************************
* HipHop Intermediate Representation (HHIR)
*******************************************



Introduction
------------

The HipHop Intermediate Representation (IR) is a typed, in-memory,
static-single-assignment, intermediate-level representation of HHBC
programs used for just in time compilation, with these goals:

  1. Complete.  The IR represents a program or program fragment
     entirely, without reference to HHBC or other upstream forms of
     the program.

  2. Type-Safe.  Since the IR deals directly with refined types and
     internal VM types, all operations are typesafe.  All instruction
     parameters have a parameter type P, and all variables have a type
     S.  Given an instruction with source parameter type P and variable
     type S, S must be equal to or more refined than P (S == P or S <: P).

  3. Machine Independent.  Since this IR is intended to be used in a JIT
     compiler, it will always be used in a machine specific context.
     Nevertheless, we rely on machine independence in order to separate
     concerns and increase portability of the VM.  Passes which manipulate
     IR based on PHP or HHBC semantics should be portable.  Passes which deal
     with machine specifics (such as register allocation) should be self
     contained.  For example, register allocation may insert machine-specific
     loads, stores, or swaps, according to a specific cpu model.  But those
     instructions are still well behaved Instruction classes.  Types are
     machine independent.

The unit of compilation is the Trace, which is a sequence of Blocks of
Instructions that produce and consume Variables.  Blocks are single-entry,
single-exit sequences of instructions (i.e. basic blocks).  Instructions may
be annotated with Type which modifies the instruction's behavior.  Each
Variable has a Type which describes the set of values it may hold at
the point it is defined.  Instructions may have side effects, which occur
in execution order.

The static single assignment form guarantees the following two invariants
for a well-formed Trace:

  1. Each Variable is assigned to by exactly one Instruction.

  2. Definitions dominate uses.  Every path to an Instruction using a Variable
     first executes the Instruction defining the Variable.

Any pass that generates or manipulates IR must preserve these invariants,
however it is possible and expected for the invariants to be temporarily
broken during trace creation or during an optimizer pass.


Control Flow
------------

Traces have one entry block and zero or more exit traces.  Blocks may branch
within a trace or to an exit trace; blocks form a restricted control flow graph.
This control flow may contain join points (two or more branches exiting
at the same point, or two jumps within the trace to a target in the same trace),
but may not form loops.

No Variables are defined on entry to the main Trace.

Blocks which are join points may start with a DefLabel with destination
Variables.  In that case, each predecessor must be a Jmp_ passing a matching
number of source Variables.  In this case the Jmp_ acts as a tail-call, passing
arguments the same way a plain call would.

Together, the sources of the Jmp_ instructions and the destinations of the
DefLabel instructions act as traditional SSA Phi pseudo-functions; The type
of the label's destination is the type-union of the corresponding sources.
Because the Jmp_ sources are at the ends of blocks, they do not violate the
SSA dominator rule (rule 2, above).

Implementation note: The JIT's compilation unit ("Trace" in this spec) actually
consists of one or more instances of the Trace class.  There is exactly one
main trace, and zero or more exit traces which handle side exits.
Instructions in exit traces may use Variables defined on the main trace,
so these exits are not separate compilation units.


Types
-----

Types in the IR are used in several contexts.  In all cases, a Type represents
a set of runtime values, however, a Type's meaning depends on the context
in which it appears.  A type which annotates an instruction (like IsType)
modifies its behavior, extending its opcode.  The type of each source
parameter restricts the type of any variable used by that parameter.
A variable's type describes the set of values it may hold at the point it is
defined.

Since types represent sets of values, we define relations on types in terms of
the sets of values they represent.  Two types S and T are equal (S == T) if they
represent equal sets of values.  S is more refined than T (S <: T), or a
subtype of T, if the set of values represented by S is a subset of the set of
values represented by T.  S and T are not related if neither value set is
a subtype of the other: (S <: T) and (T <: S) are both false.  A type U
is a union of S and T (U = {S|T}) if it represents the union of S and T's
value sets.  In that case, S and T are both subtypes of U (S <: U, T <: U),
even if S and T are not related.

Variables in the IR contain a mix of PHP-visible types and internal types. The
following table describes types representing PHP values. Note that the types
used here are more specific than what can be discriminated by user code (e.g.,
StaticStr and CountedStr both appear as type String at the PHP level).

  Type            HHVM representation
  ----            -------------------
  Uninit          KindOfUninit
  InitNull        KindOfNull
  Null            {Uninit|InitNull}
  Bool            false=0,true=1 (actual bit width varies)
  Int             int64_t (64-bit twos compliment binary integer)
  Dbl             double (IEEE 754 64-bit binary floating point)
  StaticStr       StringData* where isStatic() == true
  CountedStr      StringData* where isStatic() == false
  UncountedInit   TypedValue {Null|Bool|Int|Dbl|StaticStr}
  Uncounted       TypedValue {Unin|Null|Bool|Int|Dbl|StaticStr}
  Str             StringData* {CountedStr|StaticStr}
  StaticArr       ArrayData* where isStatic() == true
  CountedArr      ArrayData* where isStatic() == false
  Arr             ArrayData* {CountedArr|StaticArr}
  Obj             ObjectData*
  Obj<Class>      ObjectData* of the specific type Class
  Counted         {CountedStr|CountedArr|Obj|BoxedCell}
  Cell            {Null|Bool|Int|Dbl|Str|Arr|Obj}

A PHP reference is implemented as a container object (RefData) which contains
one value.  The contained value cannot be another PHP reference.  For every
type T in the table above, there is a corresponding type BoxedT, which is a
pointer to a RefData struct containing a value of type T.

  Type            HHVM representation
  ----            -------------------
  BoxedInitNull   RefData* containing InitNull
  ...             Everything from the first table above can be boxed
  BoxedCell       RefData* containing Cell (topmost boxed type).

Finally, there is one top-level type representing all possible PHP values:

  Gen             {Cell|BoxedCell}

The VM also manipulates values of various internal types, which are never
visible at the PHP semantic level, and do not have any type relation with the
above PHP-facing types.

  Type            HHVM representation
  ----            -------------------
  PtrToT          Exists for all T in Gen. Represents a TypedValue*
  None            No value, KindOfInvalid
  Bottom          No value, {}. Subtype of every other type
  Top             Supertype of every other type
  Cls             Class*
  Func            Func*
  VarEnv          VarEnv*
  NamedEntity     NamedEntity*
  FuncCls         Tuple of (Func*,Cctx)
  FuncObj         Tuple of (Func*,Obj)
  Cctx            A Class* with the lowest bit set (as stored in ActRec.m_cls)
  Ctx             {Obj|Cctx}
  FuncCtx         Tuple of (Func*,{Cctx|Obj})
  RetAddr         Return address
  StkPtr          Pointer into VM execution stack
  FramePtr        Pointer to a frame on the VM execution stack
  TCA             Machine code address
  Nullptr         Represents a null pointer

There is also one special type which represents all the possible types that
can be on the stack.

  StackElem       {Gen|Cls}

In the instruction descriptions below, we also use a "Const" prefix on
several types loosly, which means the operand must be the requested
type and must be a compile-time constant.  The optimizer relies on
accessing the value of those operands.


Variables
---------

A Variable represents a storage location which takes on various concrete values
at runtime.  Since HHIR uses SSA, a variable may only be assigned to by one
instruction.  A variable's type represents the set of values it may hold at
the point it is defined.  The type is invariant over the lifetime of the
variable (from the definition point to the last use).

Implementation note: this the proposed semantics. The current implementation
is not this strict; during IR generation, we overload instruction type
(IRInstruction::m_type), variable type (SSATmp::getType returns instr->type),
and types known at points *after* definition, due to guards.  e.g.  after
a guard, we change src->instr->type to the refined type, then propagate this
backwards through copies.  This effectively uses IRInstruction::m_type as
a scratch variable, which is only valid at certian times during construction).
This is probably unsound in later passes; after such a refinement,
instructions using the variable before the guard see the refined type.


Instructions
------------

An instruction is an executable operation with zero or more inputs (sources),
zero or one result (destination), and possible side effects such as accessing
memory, doing I/O, and which may branch or throw an exception.  Some
instructions have a Type parameter which modifies its behavior.

Each instruction has a signature which describes its effect, parameter types,
and return type, for example:

  r:Bool = IsType<T> s:Gen

By convention we use infix; destinations on the left, = represents assignment,
then the opcode name, and source parameters.  Types are to the right of the
entities they modify, separated by : for results, sources, and variables,
or delimited by <> for instruction modifiers.

Instruction flags further describe their behavior:

HasDest
NaryDest

  The instruction defines exactly one destination variable (HasDest) or
  a varying number of destination variables (NaryDest).  These flags are
  mutually exclusive.  An instruction with neither of these flags set has
  zero destination variables.

  Note that an instruction's destination variable may sometimes be a
  copy of one of the input variables.  (For example, AddElem returns the
  array it took as an input.)

CanCSE

  The instruction is safe to elide through common subexpression
  elimination.

Essential

  Whether the instruction is essential indicates whether it can be
  elided through certain types of optimizations.

  Currently this is just used to flag whether we are allowed to do
  dead code elimination on it.

MemEffects

  Indicates that the instruction has side effects on memory that
  cannot be rolled back.  If this bit isn't set, it means it is ok to
  redo whatever work the instruction did without changing observable
  program behavior.

  This flag is currently unused, but may be used later to share exit
  traces.

CallsNative

  Indicates that the instruction will call a native helper.

  The register allocator uses this to optimize register spills around
  native calls and to bias register allocation toward arguments and
  return values.

ConsumesRC

  The instruction consumes a reference to one or more of its sources, either by
  decreasing its refcount or storing the reference to memory.

KillsSource

  The instruction calls decref on one or more of its sources. Unless a source
  is known to have a refcount > 1 before the instruction executes, it cannot
  safely be used after the instruction has executed.

ProducesRC

  The instruction produces an incref'd value.

  This flag is currently unused.

MayModifyRefs

  The instruction may modify inner cells on KindOfRef values.

MayRaiseError

  The instruction may raise an error, and therefore has an implicit
  exit edge.

Terminal

  The instruction has no next instruction; it either jumps, returns,
  or throws.

Passthrough

  The value of the instruction's dest is the same as one of its inputs; it
  differs only in the refcount of the underlying object, the type of the
  variable, or some other property that doesn't affect the value of the
  variable itself.

ModifiesStack

  The instruction modifies the in-memory evaluation stack in the process of
  performing its primary work. It will have a StkPtr destination in addition to
  its primary destination.

HasStackVersion

  This instruction has a counterpart that returns a StkPtr in addition to any
  primary destination. The behavior of the stack-modifying version is otherwise
  identical.

VectorProp

  The instruction may affect the type and/or value of its base operand,
  operating on object properties.

VectorElem

  The instruction may affect the type and/or value of its base operand,
  operating on array elements.

Instruction set
---------------

1. Checks and Asserts

Note: Instructions that guard or check boxed types only check that the
operand is boxed, and they ignore the type of the value inside the box
(the inner type).  The inner type is normally checked when the value
within the box is loaded by a LdRef instruction (which takes box/ref
values, and only checks the inner type).  The reasoning for this
approach is that boxed values may alias.  Therefore, in lack of memory
alias analysis, the inner types generally need to be rechecked before
each use.

D:T = CheckType<T> S0:{Gen|Nullptr} -> L

  Check that the type of the src S0 is T, and if so copy it to D.  If
  S0 is not type T, branch to the label L. If T is Nullptr, the
  instruction is being used to check for a C++ nullptr, usually from a
  type like {Str|Nullptr}.

D:T = AssertType<T> S0:{Gen|Nullptr}

  Assert that the type of S0 is T, copying it to D.

CheckTypeMem<T> S0:PtrToGen -> L

  If the value pointed to by S0 is not type T, branch to the label L.

GuardLoc<T,localId> S0:FramePtr

  Guard that type of the given localId on the frame S0 is a subtype of
  the type T; if not, make a fallback jump.  (A jump to a service
  request that chains to a retranslation for this tracelet.)

CheckLoc<T,localId> S0:FramePtr -> L

  Check that type of the given localId on the frame S0 is T; if not,
  branch to the label L.

AssertLoc<T,localId> S0:FramePtr

  Asserts that type of the supplied local on the frame S0 is T.  This
  is used as a hint for local type information, and is similar to
  GuardLoc except it doesn't imply a runtime check and cannot cause
  control flow.

OverrideLoc<T,localId> S0:FramePtr

  Overrides tracked information about the type of a local in frame S0.
  T doesn't have to be related to the local's current type, so this
  may be used to update tracked state after InterpOne instructions.

SmashLocals S0:FramePtr

  Erases the tracked types and values of all locals in the frame
  represented by S0.

D:StkPtr = GuardStk<T,offset> S0:StkPtr

  Guard that the type of the cell on the stack pointed to by S0 at
  offset (in cells) is T.  If not, make a fallback jump.  (A jump to a
  service request that chains to a retranslation for this tracelet.)

  Returns a new StkPtr that represents the same stack but with the
  knowledge that the slot at the index S1 has type T.

D:StkPtr = CheckStk<T,offset> S0:StkPtr -> L

  Check that the type of the cell on the stack pointed to by S0 at
  offset (in cells) is T; if not, branch to the label L.

  Returns a new StkPtr that represents the same stack but with the
  knowledge that the slot at the index S1 has type T.

D:StkPtr = AssertStk<T,offset> S0:StkPtr

  Returns a new StkPtr that represents the same stack as S0, but with
  the knowledge that the slot at offset (in cells) has type T.  This
  is similar to a GuardStk except that it does not imply a runtime
  check and cannot cause control flow.

D:StkPtr = AssertStkVal<offset> S0:StkPtr S1:Gen

  Returns a new StkPtr that represents the same stack as S0, but with the
  knowledge that the slot at offset has the value S1.

D:StkPtr = CastStk<T,offset> S0:StkPtr

  Returns a new StkPtr that represents the same stack as S0, but with
  the slot at offset (in cells) converted to type T.

D:StkPtr = CoerceStk<T,offset> S0:StkPtr -> L

  Returns a new StkPtr that represents the same stack as S0, but with
  the slot at offset (in cells) converted to type T. If the type conversion
  can't be done then branches to label L.

CheckInit S0:Gen -> L

  If S0's type is Uninit, branch to label L.

CheckInitMem S0:PtrToGen S1:ConstInt -> L

  If the value at S0 + S1 (in bytes) has type Uninit, branch to L.

CheckDefinedClsEq<className,classPtr> -> L

  Compare the currently defined class of name `className' with
  `classPtr'; if they aren't equal or if `className' is not defined,
  branch to L.

GuardRefs S0:FuncPtr S1:Int S2:Int S3:Int S4:Int S5:Int

  Perform reffiness guard checks.  Operands:

    S0 - function pointer for the frame
    S1 - num params expected in the func
    S2 - first bit to check, must be a multiple of 64
    S3 - mask to check (RefDeps::Record::m_mask entires)
    S4 - values to check (RefDeps::Record::m_vals entires)

  If any of the checks fail, make a fallback jump.  (Jump to a service
  request that will chain to a retranslation of this tracelet.)

D:CountedStr = AssertNonNull S0:{Nullptr|CountedStr}

  Returns S0, with Nullptr removed from its type. This instruction currently
  supports a very limited range of types but can be expanded if needed.

2. Arithmetic


D:{Int|Dbl}      = OpAdd    S0:{Int|Dbl} S1:{Int|Dbl}
D:{Int|Dbl}      = OpSub    S0:{Int|Dbl} S1:{Int|Dbl}
D:{Int|Dbl}      = OpMul    S0:{Int|Dbl} S1:{Int|Dbl}
D:Int            = OpMod    S0:Int       S1:Int
D:{Int|Bool|Dbl} = OpDiv    S0:{Int|Dbl} S1:{Int|Dbl}
D:Int            = OpBitAnd S0:Int S1:Int
D:Int            = OpBitOr  S0:Int S1:Int
D:Int            = OpBitXor S0:Int S1:Int

  Integer/boolean arithmetic.  Performs the operation described by the
  opcode name on S0 and S1, and puts the result in D.

  Undefined behavior occurs if OpMod is given a divisor of zero, or if
  the divisor is -1 and the dividend is the minimum representable integer.

  OpDiv produces boolean false when the divisor is zero-like. Ints are
  not closed under OpDiv, as OpDiv produces doubles when there is a
  non-zero remainder.

D:Bool = OpLogicXor S0:Bool S1:Bool

  Logical XOR of the two sources. (Note that && and || do not have
  corresponding opcodes because they're handled at the bytecode level,
  to implement short-circuiting.)

D:Bool = OpNot S0:Bool

  Logical negation of the source.


3. Type conversions


D:Arr  = ConvBoolToArr S0:Bool
D:Arr  = ConvDblToArr S0:Dbl
D:Arr  = ConvIntToArr S0:Int
D:Arr  = ConvObjToArr S0:Obj
D:Arr  = ConvStrToArr S0:Str
D:Arr  = ConvCellToArr S0:Cell

D:Bool = ConvArrToBool S0:Arr
D:Bool = ConvDblToBool S0:Dbl
D:Bool = ConvIntToBool S0:Int
D:Bool = ConvStrToBool S0:Str
D:Bool = ConvCellToBool S0:Cell

D:Dbl  = ConvArrToDbl S0:Arr
D:Dbl  = ConvBoolToDbl S0:Bool
D:Dbl  = ConvIntToDbl S0:Int
D:Dbl  = ConvObjToDbl S0:Obj
D:Dbl  = ConvStrToDbl S0:Str
D:Dbl  = ConvCellToDbl S0:Cell

D:Int  = ConvArrToInt S0:Arr
D:Int  = ConvDblToInt S0:Dbl
D:Int  = ConvObjToInt S0:Obj
D:Int  = ConvStrToInt S0:Str
D:Int  = ConvCellToInt S0:Cell

D:Obj  = ConvCellToObj S0:Cell

D:StaticStr = ConvBoolToStr S0:Bool
D:Str  = ConvDblToStr S0:Dbl
D:Str  = ConvIntToStr S0:Int
D:Str  = ConvObjToStr S0:Obj
D:Str  = ConvCellToStr S0:Cell

  Convert S0 from its current type to the destination type, according to the PHP
  semantics of such a conversion.


4. Boolean predicates


D:Bool = OpGt    S0:Gen S1:Gen
D:Bool = OpGte   S0:Gen S1:Gen
D:Bool = OpLt    S0:Gen S1:Gen
D:Bool = OpLte   S0:Gen S1:Gen
D:Bool = OpEq    S0:Gen S1:Gen
D:Bool = OpNeq   S0:Gen S1:Gen
D:Bool = OpSame  S0:Gen S1:Gen
D:Bool = OpNSame S0:Gen S1:Gen

  Perform comparisons with PHP semantics on S0 and S1, and put the
  result in D.

D:Bool = InstanceOf S0:Cls S1:Cls

  Sets D based on whether S0 is a descendant of the class, interface,
  or trait in S1.  (Note that this is always false for a trait).  S1
  may be null at runtime if the class is not defined.

D:Bool = InstanceOfIface S0:Cls S1:Cls

  Fast path for interface checks. Sets D based on whether S0 implements
  S1, but S1 must be a unique interface. This should only be used in
  repo-authoritative mode.

D:Bool = ExtendsClass S0:Cls S1:Cls

  A fast-path for instanceof checks.  Sets D based on whether S0 is a
  descendant of the class in S1, where S1 must be a unique class that
  is not an interface or a trait.

D:Bool =  InstanceOfBitmask S0:Cls S1:ConstStr
D:Bool = NInstanceOfBitmask S0:Cls S1:ConstStr

  A fast-path for instanceof checks.  Sets D based on whether S0 is a
  descendant of the class named by S1, where S1 must have a bit
  allocated for it in the fast instance check bitvector (see class.h).

D:Bool =  IsType<T> S0:Cell
D:Bool = IsNType<T> S0:Cell

  IsType (IsNType) sets D to true iff S0 holds a value that is (not)
  of type T.

D:Bool =  IsTypeMem<T> S0:PtrToGen
D:Bool = IsNTypeMem<T> S0:PtrToGen

  IsTypeMem (IsNTypeMem) sets D to true iff the value referenced by S0
  is (not) of type T.


5. Branches


There is a conditional branch instruction for each predicate above,
to enable generating efficient compare-and-branch instruction sequences.

JmpGt
JmpGte
JmpLt
JmpLte
JmpEq
JmpNeq
JmpSame
JmpNSame
JmpInstanceOf
JmpInstanceOfBitmask
JmpNInstanceOfBitmask
JmpIsType
JmpIsNType

  Fused jump instructions.  These all operate exactly as their
  corresponding query op, but also take a label to jump to when the
  condition is true.

D:None = JmpZero S0:{Bool|Int} L:Label
D:None = JmpNZero S0:{Bool|Int} L:Label

  Conditionally jump to L based on S0.

D:None JmpSwitchDest<JmpSwitchData> S0:Int

  Jump to the target of a switch stamement using table metadata
  <JmpSwitchData> and index S0.

ExitWhenSurprised -> L

  Tests the surprise flags and if they are set, jumps to the
  exit-trace label L.

ExitOnVarEnv S0:FramePtr -> L

  Loads the VarEnv slot off the ActRec pointed to by S0.  If it is
  non-zero, jumps to the exit-trace label L.

Jmp_ [S:T ...] -> L

  Unconditional jump to L.  L is the name of a DefLabel instruction.
  Each the sources are copied to the corresponding destinations of the
  target L.  The number of destinations on L must match the number of
  sources of this Jmp_.

DefLabel
D:T ... = DefLabel

  Denote the position of a jump target.  Branch instructions, and
  instructions which implicitly guard, take a Label argument which refers
  to a specific DefLabel instruction.  Additionally, every Trace must begin
  with a DefLabel instruction.

  DefLabel can optionally define one or more destination variables.  In that
  case, the values are passed to it by Jmp_ instruction(s) with the same
  number of sources.  The type of each destination variable is the least common
  supertype of each of the corresponding Jmp_ sources.

  Note that when a label defines destination variables, it is only reachable
  from the Jmp_ instruction.  Other branch instructions cannot pass arguments.


6. Reference manipulation


D:Cell = Unbox S0:Gen

  Unbox S0 if it is boxed, and put the resulting Cell in D.

D:BoxedCell = Box S0:Gen

  Box S0 if it is unboxed, and put the resulting BoxedCell in D.

D:PtrToCell = UnboxPtr S0:PtrToGen

  If S0 points to a cell that is KindOfRef, dereference the pointer in
  the TypedValue and return a pointer to the inner-cell in D.

D:PtrToBoxedCell = BoxPtr S0:PtrToGen

  Boxes the TypeValue that S0 points to if it is not boxed. The result
  D points to the same TypedValue as S0 but has a more refined type.


7. Loads


Some of the load instructions have an optional label parameter that
make the load also perform a runtime type check. A load that has a
label parameter present checks that the value it loads has the type T,
where T is load instruction's type parameter, and branches to its
label if the type check fails.

LdLoc and LdStack have corresponding GuardLoc and GuardStk
instructions that allow the compiler to decouple type checks from the
load operation. The type guards for locals and stacks are generated
eagerly at the start of a tracelet, while other guards (typically
generated due to type prediction) are generated at the point where the
guarded value is loaded. Allowing the checks for locals and stack to
be decoupled gives the compiler the flexibility to avoid partially
dead loads of local and stack values and to avoid unnecessarily long
live ranges due to eagerly loaded values at the start of the tracelet.

D:T = LdStack<T,offset> S0:StkPtr

  Loads from S0 at offset (in cells), and puts the value in D as type
  T.

D:T = LdLoc<T,localId> S0:FramePtr

  Loads local number localId from the frame S0 and puts the value in D
  as type T.

D:PtrToGen = LdStackAddr<offset> S0:StkPtr

  Loads the address of the stack slot given by the pointer in S0 at
  the offset (in cells).

D:T* = LdLocAddr<T, localId> S0:FramePtr

  Loads the address of the local slot localId from the frame S0 into D.

D:T = LdMem<T> S0:PtrToGen S1:ConstInt [ -> L ]

  Loads from S0 + S1 (in bytes) and puts the value in D. If the
  optional label L is specified and the loaded value's type does not
  match T, this instruction does not load into D and transfers control
  to L.

D:T = LdProp<T> S0:Obj S1:ConstInt [ -> L ]

  Loads a property from the object referenced by S0 at the offset
  given by S1 and puts the value in D. If the optional label L is
  specified and the loaded value's type does not match T, this
  instruction does not load into D and transfers control to L.

D:T = LdRef<T> S0:Cell& [ -> L ]

  Loads the value held in the box referenced by S0 and puts the value
  in D.  If the optional label L is specified and the loaded value's
  type does not match T, this instruction does not load into D and
  transfers control to L.

D:Obj = LdThis S0:FramePtr [ -> L ]

  Loads the this pointer out of the ActRec pointed to by S0, and puts
  it in D.  If the optional label L is supplied, if the this pointer
  in S0 is not, this instruction does not load it into D and transfers
  control to the exit label L.

D:Ctx = LdCtx S0:FramePtr S1:Func

  Loads into D the value of the m_this/m_cls field out of the frame
  pointer S0 in the function given by S1. The result could be either
  an object representing the this pointer or a class context.

D:Cctx = LdCctx S0:FramePtr

  Loads into D the value of the m_cls field out of the frame pointer
  S0. The compiler should generate this only if it can prove that the
  frame does not contain a $this pointer.

D:Cls = LdClsCtx S0:Ctx

  Loads into D the class representing the current context. Extracts
  the class from the S0, which can be either the this pointer or the
  context class.

D:Cls = LdClsCctx S0:Cctx

  Loads into D the class representing the current context. Extracts
  the class from the S0, which is a context class.

D:Func = LdClsCtor S0:Cls

  Loads into D the constructor of class S0. If the constructor cannot
  be called from the current context, raise an error.

D:Home = LdHome S0:StkPtr S1:ConstInt

  Logically packages the location of a pointer into the VM stack and
  an offset.  S0 will usually be the output of a DefFP.

D:T = LdConst<T,value>

  Load a constant value into D.  This differs from DefConst in that D
  will be allocated a register.  It is currently used only for the
  GuardRefs instruction.

D:ConstT = DefConst<T,value>

  Define a constant value of type T.  D is presumed to be globally
  available and the DefConst instruction will not actually appear in
  the IR instruction stream.

D:Cls = LdCls S0:Str S1:ConstCls

  Loads the class named S0 in the context of the class S1. Invokes autoload and
  may raise an error if the class is not defined. The explicit context
  parameter allows the compiler to simplify this instruction to a DefConst in
  some cases. If S0 is constant, this instruction may be simplified to a
  LdClsCached.

D:Cls = LdClsCached S0:ConstStr

  Loads the class named S0 via the target cache. Invokes autoload and
  may raise an error if the class is not defined.

D:Cls = LdClsCachedSafe S0:ConstStr [ -> L ]

  Loads the class whose name is S0 out of the target cache. If the class is not
  defined, returns null and optionally branches to L.

D:T = LdClsCns<T,className,constName> [ -> L ]

  Loads the named class constant for a class via the target cache.
  This instruction should generally be followed by CheckInit, unless
  we know the class is already loaded.

  If the optional label L is specified and the loaded value's type
  does not match T, this instruction does not load into D and
  transfers control to L.

  The result may be uninitialized if the class is not defined.  Note
  that no decref of the result is necessary because class constants
  will always be static.

D:T = LookupClsCns<T,className,constName>

  Load a class constant for a class via the target cache, invoking
  autoload if it is not defined.  This instruction may raise an
  undefined constant error if autoload cannot define the constant.

D:FuncCtx = LdClsMethodFCache S0:ConstStr S1:ConstStr
                              S2:{Obj|Cls|Ctx} S3:FramePtr L:Label

  Loads from the MethodFCache a pointer to the callee function and the
  context (ActRec's m_this/m_cls slot) into D.  S0 and S1 hold the
  names of the callee's class and method, respectively.  S2 is the
  current context.  This instruction loads the corresponding entry in
  the MethodFCache if needed.  In case the given method is not found,
  control is transferred to label L.  The current frame pointer is
  passed in S3.

D:Ctx = GetCtxFwdCall S0:Ctx S1:Func

  If S0 is an object and S1 is static, this opcode returns S0's
  class. If S0 is an object and S1 is not static, this opcode increfs
  S0 and returns it. If S0 is a Cctx, this opcode returns S0.

D:FuncCls = LdClsMethodCache S0:ConstStr S1:ConstStr S2:ConstNamedEntity*
                             S3:FramePtr S4:StkPtr

  Lookup a function and class in the class method targetcache.  The
  sources to this instruction are:

    S0 - class name
    S1 - method name
    S2 - the NamedEntity* for the class
    S3 - the current frame pointer
    S4 - the current stack pointer

D:Func = LdClsMethod S0:Cls S1:ConstInt

  Load a Func* off of the class method table for S0, at offset S1 (in
  method slots).

D:PtrToGen = LdPropAddr S0:Obj S1:ConstInt

  Load the address of the object property for S0 at offset S1 (in
  bytes) into D.

D:PtrToGen = LdGblAddr S0:Str -> L

  Loads a pointer to a global. S0 is the global's name. Branches to L
  if the global is not defined.

D:PtrToGen = LdGblAddrDef S0:Str

  Loads a pointer to a global. S0 is the global's name. Defines the
  global if it is not already defined. Decrements the reference count
  of S0.

D:PtrToGen = LdClsPropAddr S0:Cls S1:Str S2:ConstCls [ -> L ]

  Loads a pointer to a static class property. S0 points to the class, S1 is the
  property name, and S2 is the class representing the context of the code
  accessing the property. If class S0 does not have a visible and accessible
  static property named S1, then this instruction will either (1) jump to L if
  it is present and not a catch trace, or (2) throw a fatal error.

D:PtrToGen = LdClsPropAddrCached S0:Cls S1:ConstStr S2:ConstStr
                                 S3:ConstCls [ -> L ]

  Loads a pointer to a static class property via the target cache. S0
  points to the class, S1 is the property name, S2 is the class name,
  and S3 is the class representing the context of the code accessing
  the property.  If class S0 does not have a visible and accessible
  static property named S1, then this instruction will either (1)
  throw a fatal error if the optional label L is not present, or (2)
  jump to L if it is present.

LdObjMethod S0:Cls S1:ConstStr S2:StkPtr

  Stores a pointer to an object's method into an activation record. S0
  points to the object's class, S1 is the method name, and S3 points
  to the activation record. Caches the mapping in the target
  cache. Fatals if the class does not have an accessible method with
  the given name and does not have a __call method.

D:Cls = LdObjClass S0:Obj

  Load the class out of the object in S0 and put it in D.

D:Func = LdFunc S0:Str

  Loads the Func whose name is S0.  Fatal if the named function is
  not defined, and the function autoloader fails to define it.

D:Func = LdFuncCached S0:ConstStr

  Loads the Func whose name is S0 from the target cache. Fatal if the
  named function is not defined, and the function autoloader fails to
  define it.

D:Func = LdFuncCachedSafe S0:ConstStr [ -> L ]

  Loads the Func named S0 from the target cache. If the function is not
  defined, returns null and optionally branches to L.

D:Func& = LdARFuncPtr S0:{StkPtr|FramePtr} S1:ConstInt

  Loads the m_func member of an ActRec.  S0 is the base address, and
  S1 is an offset, such that S0 + S1 points to the base of the ActRec.

D:T = LdRaw<T> S0:?? S1:ConstInt

  "Raw" load from memory---i.e. outside of our type-system.

  S0 is the base address, and is an untyped pointer.  S1 is a constant
  from the RawMemSlot::Kind enum, which describes the offset from the
  base, and the size.  The value in D is assumed to be of type T.

D:BoxedCell = LdStaticLocCached S0:ConstCacheHandle -> L

  Load static local value from the targetcache from handle S0. If the
  slot is uninitialized, branch to L.


8. Allocation


D:Obj = AllocObj S1:Cls

  Allocates a new object of class S1.

D:Obj = AllocObjFast S1:ConstCls

  Allocate a new object of class S1 where S1 is persistent and able to
  be instantiated (i.e., S1 is not an abstract class, interface, or
  trait).

D:Arr = NewArray S0:ConstInt

  Allocate a new array with the expected capacity S0.

D:Arr = NewTuple S0:ConstInt S1:StkPtr

  Allocate a new array by taking the top S0 elements off the stack
  given by S1.  Note that this instruction assumes it can take the
  values from the stack without increfing them.


9. Call & Return


D:StkPtr = SpillFrame<numArgs,invName> S0:StkPtr
                                       S1:FramePtr
                                       S2:Func
                                       S3:{Ctx|Class|Null}

  Operands:

     S0 - caller stack pointer
     S1 - caller frame pointer
     S2 - callee Func
     S3 - object (for FPushObjMethod*), class (for FPushClsMethod*), or
          null (for FPushFunc*).

  Defines the fields for an activation record and writes them to the
  stack pointed to by S1.

D:StkPtr = CufIterSpillFrame<numArgs,iterId> S0:StkPtr
                                             S1:FramePtr

  Operands:

     S0 - caller stack pointer
     S1 - caller frame pointer

  Defines the fields for an activation record using data from
  the iterator iterId, and writes them to the stack pointed to by S1

D:FramePtr = FreeActRec S0:FramePtr

  Load the saved frame pointer from the activation record pointed to
  by S0 into D.

D:FramePtr = DefInlineFP<func,retBCOff,retSPOff> S0:StkPtr S1:StkPtr

  Defines a frame pointer for an inlined function.  S0 is a StkPtr
  that points to the ActRec for the callee (i.e. after parameters have
  been popped).  S1 is a StkPtr that represents what the inlined
  function will return to (i.e. it points to the stack after the
  ActRec in S0 is popped).

  `func' is the function being inlined.  `retBCOff' and `retSPOff'
  represent what the bytecode and stack offsets should be after the
  FCall instruction in ther caller.

  This instruction is primarily used to represent a frame in the IR in
  a way that allows us to eliminate it entirely.  When it cannot be
  eliminated (or if it is pushed into an unlikely path) it performs
  callee-side responsibilities for setting up an activation record
  (i.e. setting the return ip and m_soff, storing the frame pointer
  into D).

InlineReturn S0:FramePtr

  Unlinks a frame constructed by DefInlineFP.

D:StkPtr = CallArray S0:StkPtr

  Invoke function corresponding to the current FPI with array args.
  S0 points to the stack resulting after the ActRec for the function
  and the array of its arguments is pushed. CallArray pops the array
  off the stack, pushes the elements of the array as arguments, and
  invokes the function in the ActRec.

D:StkPtr = Call S0:StkPtr S1:ConstInt S2:Func S3...

  Invoke the function S2 with ActRec S0 and variadic arguments S3...
  representing values to pass to the function.  A value of type None
  means the value to be passed is already on the stack.  S2 is the
  bytecode offset of the next instruction to execute when the call
  returns.

NativeImpl = S0:ConstFunc S1:FramePtr

  Execute a call to the native builtin specified by the func in S0.

D:T = CallBuiltin<T> S0:FuncPtr S1:StkPtr S2...SN

  Call builtin function with N-1 arguments.  Operands:

    S0: callee Func
    S1..SN: arguments to builtin function

  The source and destination types correspond to C++ parameter and return
  types as follows:

    C++ type            HHIR type         Position
    -----------------   ---------         --------
    bool                Bool              source, destination
    int64_t             Int               source, destination
    double              Dbl               source, destination
    const String&       PtrToString       source
    const Array&        PtrToArray        source
    const Object&       PtrToObject       source
    const Variant&      PtrToGen          source
    Variant&            PtrToGen          source (ref param)
    String              {Str|InitNull}    destination
    Array               {Arr|InitNull}    destination
    Object              {Obj|InitNull}    destination
    Variant             {Gen-UninitNull}  destination

D:RetAddr = LdRetAddr S0:FramePtr

  Load the return address off of the activation record pointed to by
  S0.  The return address D is normally provided to a RetCtrl.
  Between a LdRetAddr and a RetCtrl, a FreeActRec is normally emitted
  to free the activation record S0.  This is the reason why LdRetAddr
  is a separate instruction from RetCtrl.

RetCtrl S0:StkPtr S1:FramePtr S2:RetAddr

  Ensure that S0 is stored in rVmSp and S1 is stored in rVmFp and then
  execute a hardware procedure-return using the return address
  specified by S2.

StRetVal S0:FramePtr S1:Gen

  Writes the value in S1 to the return value slot on the activation
  record pointed to by S0.

D:StkPtr = RetAdjustStack S0:FramePtr

  Loads the new VM stack pointer into the destination.  S0 is a
  pointer to the current activation record.

ReleaseVVOrExit S0:FramePtr -> L

  Loads the VarEnv slot off the ActRec pointed to by S0.  If it is
  null, does nothing.  If it is an ExtraArgs, deallocates the
  ExtraArgs structure.  Otherwise jumps to the exit-trace label L.

D:StkPtr = GenericRetDecRefs S0:FramePtr S1:ConstInt

  Does decrefs of all the current function's locals, where S0 is a
  pointer to the relevant activation record, and S1 is the number of
  locals in the current function.

  Returns the adjusted VM stack pointer (pointing at the return value
  location).

  Semantically similar to a series of DecRefLoc followed by
  RetAdjustStack.


10. Stores


StMem S0:PtrToGen S1:ConstInt S2:Gen

  Store S2 into the location S0 + S1 (in bytes).

StMemNT S0:PtrToGen S1:ConstInt S2:Gen

  Store S2 into the location S0 + S1 (in bytes), without storing the
  type tag.

StProp S0:Obj S1:Int S2:Gen

  Store S2 into the location S0 + S1 (in bytes).

StPropNT S0:Obj S1:Int S2:Gen

  Store S2 into the location S0 + S1 (in bytes), without storing the
  type tag.

StLoc<localId> S0:FramePtr S1:Gen

  Store S1 to local number localId on the frame pointed to by S0.

StLocNT<localId> S0:FramePtr S1:Gen

  Store S1 to local number localId on the frame pointed to by S0,
  without storing the type.

D:BoxedCell = StRef S0:BoxedCell S1:Cell

  Store the value in S1 into the RefData pointed to by S0.  Stores the
  RefData::m_type also.  Returns a new value for S0, which will now
  have the type of S1 after boxing.

D:BoxedCell = StRefNT S0:BoxedCell S1:Cell

  Store the value in S1 into the RefData pointed to by S0.  Does not
  change RefData::m_type.  Returns a new value for S0, which will now
  have the type of S1 after boxing.

StRaw S0:?? S1:ConstInt S2:??

  "Raw" store to memory---i.e. outside of our type-system.

  S0 is the base address, and is an untyped pointer.  S1 is a constant
  from the RawMemSlow::Kind enum, which describes the offset from the
  base, and the size.  The value in S2 depends on the value of S1.

D:StkPtr = SpillStack S0:StkP S1:ConstInt, S2...

  SpillStack synchronizes the virtual execution stack with the
  physical stack by storing a variadic list of SSATmps to the physical
  stack.

  Operands:

     S0      - current stack pointer

     S1      - stack deficit; indicates the number of elements that
               need to be logically popped before the variadic list
               is pushed

     S2...   - variadic list of elements to spill, with values
               representing cells.  A temp with type None means to
               keep the previous value on the stack.


11. Trace exits


SyncABIRegs S0:FramePtr S1:StkPtr

  Ensures the cross-tracelet ABI registers are in a consistent state
  in preparation for an instruction that may leave the trace.

  In practice this instruction currently doesn't do anything except
  act as an essential instruction that prevents the current stack from
  being dce'd.

ReqBindJmp<bcOff>

  Emit a jump to a REQ_BIND_JMP service request to the target offset
  bcOff.

ReqBindJmpNoIR<bcOff>

  Emit a jump to a REQ_BIND_JMP_NO_IR service request to the target
  offset bcOff.  This is used for unsupported situations, where we
  need to leave the trace and want the "retranslation" to occur
  without using HHIR (i.e. interp).

ReqRetranslate

  Emit a jump to a service request that will chain to a retranslation
  of this tracelet.

  This instruction is used in exit traces for a type prediction that
  occurs at the first bytecode offset of a tracelet.

ReqBindJmpGt
ReqBindJmpGte
ReqBindJmpLt
ReqBindJmpLte
ReqBindJmpEq
ReqBindJmpNeq
ReqBindJmpSame
ReqBindJmpNSame
ReqBindJmpInstanceOfBitmask
ReqBindJmpNInstanceOfBitmask
ReqBindJmpZero
ReqBindJmpNZero

  Test the condition based on the Jmp* op of similar name, and then
  emit a pair of smashable jumps to a REQ_BIND_JMPCC_FIRST service
  request.

SideExitGuardLoc<T,locId,takenOff> S0:FramePtr

  Test the type of a local, and if it fails jump to a side exit
  service request.

D:StkPtr = SideExitGuardStk<T,stackOff,takenOff> S0:StkPtr

  Test the type of a stack cell, and if it fails jump to a side exit
  service request.


12. Refcounting and copies


D:T = Mov S0:T

  Defines D as S0.  May imply register-to-register moves at code
  generation time.  Does not imply an incref or any other manipulation
  of S0.

D:T* = LdAddr S0:T* S1:ConstInt

  Load address. Returns a pointer to (S0 + S1) with the same type as S0.

D:Gen = IncRef S0:Gen

  Increment the reference count of S0, defining D to be S0 to allow
  the ud-chain to be used to track whether the reference count is
  consumed or elidable.

DecRefLoc<localId> S0:FramePtr

  DecRef the local given by localId on the frame S0.

DecRefStack<T,offset> S0:StkPtr

  DecRef a value of type T at offset on the stack pointed to by S0.

DecRefThis S0:FramePtr

  DecRef the $this pointer in the ActRec S0, if it had one.  Does
  nothing if there was a class context or null $this on the ActRec.

DecRef S0:Gen

  Decrease the reference count of S0 by one, and call a destructor for
  types that require it if it goes to zero.

  Note that although DecRef takes a Gen, we don't allow it to use
  information about the inner types of a BoxedCell.  This is because
  we don't guard on the inner types of a BoxedCell except when doing
  LdRef.  For any S0 that is a strict subtype of BoxedCell, the DecRef
  must just decref it as if it were a BoxedCell.

DecRefMem S0:PtrToGen S1:ConstInt

  Decref the value pointed to by S0 at offset S1 (in bytes), calling
  any appropriate destructor if the refcount goes to zero.

DecRefNZ S0:Gen

  Decrease the reference count of S0 by one, do not check if it goes
  to zero.  This instruction can be used for more efficient code when
  it is provable that the reference count cannot go to zero.


13. Misc


D:FramePtr = DefFP

  Creates a temporary D representing the current vm frame pointer.

D:StkPtr = DefSP<stackOff> S0:StkPtr

  Creates a temporary D representing the current vm stack pointer.  S0
  is a pointer to the current frame.  The 'stackOff' is the logical
  offset between S0 and the stack pointer, but in the case of
  continuations this is not the physical offset at runtime.

  This instruction is used at the beginning of tracelets to represent
  the state of the stack on entry and does not emit code.

D:StkPtr = ReDefSP<offset> S0:FramePtr S1:StkPtr

  Re-define a stack in terms of a frame pointer S0 and an offset,
  putting the resulting pointer in D.  The resulting stack is assumed
  to give the same view as S1, which is a previous stack pointer.
  (I.e. for getStackValue we just chain to S1.)

  This instruction is used when entering or "returning" from an
  inlined call.  The one used on entry will be DCE'd when the actrec
  can be eliminated.  The one on exit is only needed until TODO(#2288359).

D:StkPtr = StashGeneratorSP S0:StkPtr

  Store a generator stack pointer, for later use.

  This instruction has exactly the effects of Mov.

  The point of this is a bit of a hack to handle the fact that we
  currently assign the same registers to frame (rVmFp) and stack
  pointers (rVmSp) through the IR.  During an inline call, we want to
  be able to have uses of the stack after the call, but we can't
  restore it using a frame pointer like with ReDefSP, because the
  values of rVmSp and rVmFp are not related like that in a generator.

  This instruction moves the value of rVmSp into a temporary, which we
  can get back using ReDefGeneratorSP.

  TODO(#2288359): this shouldn't be needed.

D:StkPtr = ReDefGeneratorSP<offset> S0:StkPtr

  Restore a generator stack pointer.  See StashGeneratorSP.

  This instruction has exactly the effects of Mov, except that the
  destination is allocated to rVmSp.

Nop

  Does nothing.  It's sometimes useful for the simplifier to insert
  one of these in the instruction stream.


14. Runtime helpers


VerifyParamCls S0:Cls S1:Cls S2:ConstInt

  Verify parameter type for classes or traits. If S0 does not extend
  (if S1 is a class) or implement (if S1 is an interface) S1, this
  instruction will raise a recoverable fatal error describing the type
  mismatch.

VerifyParamCallable S0:Cell S1:ConstInt

  If S0 is not callable, as defined by the php function is_callable,
  this instruction will raise a recoverable fatal error describing the
  type mismatch.

VerifyParamFail S0:ConstInt

  Assumes that parameter number S0 in the current function has failed
  its typehint and raises a recoverable fatal error describing the
  type mismatch.

RaiseUninitLoc<localId>

  Raise a notice for an uninitialized local variable.

DefCns S0:ConstStr S0:Cell

  Defines the constant with name S0 to be the value S1. If S0 has a refcounted
  type, this instruction consumes its refcount.

WarnNonObjProp

  Raise a warning for property access on a non-object base.

ThrowNonObjProp

  Throws a fatal error for trying to set a property on a non-object base.

RaiseUndefProp S0:Obj S1:ConstStr

  Raise a notice for an undefined property named S1 on the class of S0.

RaiseError S0:Str

  Raises a fatal error with the text in S0 as its message.

RaiseWarning S0:Str

  Raises a warning with the text in S0 as its message.

D:BoxedCell = StaticLocInit S0:ConstStr S1:FramePtr S2:Cell

  Get boxed value to initialize static local named S0 in frame S1. If
  the static local has not yet been initialized, its value will be set
  to S2. The returned value is not stored in the frame S1.

D:BoxedCell = StaticLocInitCached S0:ConstStr S1:FramePtr S2:Cell
                                  S3:ConstCacheHandle

  Get boxed value to initialize static local named S0 in frame S1. If
  the static local has not yet been initialized, its value will be set
  to S2. The returned value is stored in the targetcache at handle S3,
  but is not stored in the frame S1. This instruction assumes that the
  current function is not a closure body or a generator from a
  closure.

PrintStr S0:Str
PrintInt S1:Int
PrintBool S1:Bool

  Print for various types.

D:Str = Concat S0:Gen S1:Gen

  Concatenate S0 and S1 after converting them to strings in a way that
  obeys php semantics.  (Calling object __toString methods, etc.)
  TODO(#2438120): split this opcode up.

D:Arr = AddElemStrKey S0:Arr S1:Str S2:Cell

  Add S2 to the array S0 for the key S1, and return the resulting
  array.

D:Arr = AddElemIntKey S0:Arr S1:Int S2:Cell

  Add S2 to the array S0 for the key S1, and return the resulting
  array.

D:Arr = AddNewElem S0:Arr S1:Gen

  Add S1 as a new element to the array S0.  TODO(#2437059): this is
  broken.

D:Arr = ArrayAdd S0:Arr S1:Arr

  Has the effects of the php + operator on the two source arrays.

D:Bool = AKExists S0:Cell S1:Cell

  Has the effects of array_key_exists(S0, S1).  Note that S0 does not
  need to be an array---if it is an Obj the ArrayAccess API will be
  used, etc.

D:Cell = ArrayIdx S0:ConstTCA S1:Arr S2:{Int,Str} S3:Cell

  Checks if the array S1 contains the key S2, and returns the result
  if found.  Otherwise S3 is returned.

D:Int LdSwitchDblIndex S0:Dbl S1:Int S2:Int
D:Int LdSwitchStrIndex S0:Str S1:Int S2:Int
D:Int LdSwitchObjIndex S0:Obj S1:Int S2:Int

  These instructions are used to determine the target of a switch
  statement with target range [S1:S1 + S2), when invoked with the value S0.
  They call helper functions to check whether S0 is an numeric integer
  in the range [S1:S1 + S2), and if so return the value S1 - (Int)S0.
  Else, they return the target of the default target, S2 + 1.

D:StkPtr InterpOne<T, bcOff, numPopped, numPushed> S0:FramePtr S1:StkPtr

  Call the interpreter implementation function for one opcode. S0 and S1 are,
  respectively, the VM frame and stack pointers before this instruction. T is
  the type of the top stack element after the call, or None if it pushes
  nothing. bcOff is the bytecode offset. numPopped is the number of stack cells
  consumed by the instruction, and numPushed is the number of stack cells
  produced by the instruction. This instruction returns the updated VM stack
  pointer.

InterpOneCF S0:FramePtr S1:StkPtr S2:ConstInt S3:ConstInt

  Call the interpreter implementation function for one control-flow
  opcode.  S0 and S1 are, respectively, the VM frame and stack
  pointers before this instruction.  S2 is the bytecode offset.


15. Register allocation


D:T = Spill S0:T

  Stores S0 to a spill location specified by the destination.  The
  destination temporary will not have registers allocated to it.

D:T = Reload S0:T

  Loads from a spilled temporary S0, and stores the result in D.


16. Continuations & Closures


D:Obj = CreateCl S0:ConstCls S1:ConstInt S2:FramePtr S3:StkPtr

  Creates an object of class S0 (which must be a subclass of Closure),
  copies S1 number of parameters from S2 into the properties on the
  object.  Saves the $this and Func* from ActRec S2 into the object,
  and returns the object.

D:Obj = CreateContFunc S0:ConstFunc S1:ConstFunc

  Create a continuation object from function.  Arguments:

     S0 - the "original" function
     S1 - the generator function

D:Obj = CreateContMeth S0:ConstFunc S1:ConstFunc S2:Ctx

  Create a continuation object from method.  Arguments:

     S0 - the "original" method
     S1 - the generator function
     S2 - the m_this/m_cls field of the current frame pointer

ContEnter S0:FramePtr S1:TCA S2:ConstInt S3:FramePtr

  Enters a generator body. S0 is the ActRec embedded in the Continuation
  object. S1 is the address to jump to. S2 is the bytecode offset in the
  caller to return to when the generator body yields. S3 is the current frame.

ContRaiseCheck S0:Obj -> L

  Checks whethre the continuation object S0 has raised an exception,
  and if so branches to the label L.

ContPreNext S0:Obj -> L

  Performs operations needed for the Continuation::next() method.  This
  includes checking m_done and m_running---if either is not the case
  branches to the label L.

ContStartedCheck S0:Obj -> L

  Checks if the continuation object S0 has started, and if not
  branches to the label L.

ContSetRunning S0:Obj S1:ConstBool

  Sets the continuation object S0 to running if S1 is true, or
  sets it as stopped otherwise.

ContIncKey S0:Obj

  Special-case key update for continuation S0, which increments the
  key of a continuation if that continuation's key is an Int. This will
  cause undefined behavior if the continuation's key is not an Int.

ContUpdateIdx S0:Obj S1:Int

  Updates the internal index of continuation S0 from S1 if necessary,
  i.e. if S1 is larger than the index.


17. Debugging and instrumentation


IncStat S0:ConstInt S1:ConstInt S2:ConstBool

  Increment stat counter. S0 is the implementation defined stat counter index,
  S1 is the amount to increment the counter (may be negative), and S2 is a
  'force' flag. This opcode becomes a noop iff (force == false and runtime
  stats are not enabled) at translation time.

IncStatGrouped S0:ConstStr S1:ConstStr S2:ConstInt

  Adds the value S2 to the counter named S1, in the category S0.

DbgAssertRefCount S0:{Counted|StaticStr|StaticArr}

  Assert that S0 has a valid refcount.  S0 must be a type with a valid
  (counted or static) _count field.  If S0's count is implausible then
  execute a hardware trap instruction (int3 on x64).

DbgAssertPtr S0:PtrToGen

  Assert that S0 points to plausible TypedValue.  If the TypedValue is a
  type with a _count field, check that the count field is plausible using
  the same logic as DbgAssertRefCount.  Internally this uses always_assert();
  failures cause an abort (whatever always_assert does).

DbgAssertType<T> S0:Cell

  Assert that S0 is of type T at runtime.  If the assertion fails,
  execution is aborted via a hardware exception.


18. Iterators


D:Bool = IterInit S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt
D:Bool = IterInitK S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt S4:ConstInt
D:Bool = WIterInit S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt
D:Bool = WIterInitK S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt S4:ConstInt
D:Bool = MIterInit S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt
D:Bool = MIterInitK S0:{Arr|Obj} S1:FramePtr S2:ConstInt S3:ConstInt S4:ConstInt

  Initializes the iterator variable whose index is given by S2. This
  instruction creates the appropriate iterator for the array or object
  that S0 references, and rewinds the new iterator to its start.  S3
  and S4 are local variable indices. S0 points to the stack frame
  containing the iterator and local variables S2, S3, and S4.

  If the new iterator is at its end (i.e., has no elements to iterate
  over), this instruction decrements the refcount of S0 and returns
  false; otheriwse, it stores a reference to S0 in the new iterator
  and returns true. If the iterator is not at its end, then this
  instruction stores the iterator's first value (and key) into the
  local variable S3 (and S4, respectively).

  The IterInit and IterInitK instructions always copy the array
  element by value.

  The WIterInit and WIterInitK instructions copy referenced array
  elements by reference, and non-referenced array elements by value.

  The MIterInit and MIterInitK instructions always copy the array element
  by reference.

  This instruction has the ConsumesRC property because it either
  decrements the reference count of s0 or stores a reference to S0
  into the new iterator.

D:Bool = IterNext S0:FramePtr S1:ConstInt S2:ConstInt
D:Bool = IterNextK S0:FramePtr S1:ConstInt S2:ConstInt S3:ConstInt
D:Bool = WIterNext S0:FramePtr S1:ConstInt S2:ConstInt
D:Bool = WIterNextK S0:FramePtr S1:ConstInt S2:ConstInt S3:ConstInt
D:Bool = MIterNext S0:FramePtr S1:ConstInt S2:ConstInt
D:Bool = MIterNextK S0:FramePtr S1:ConstInt S2:ConstInt S3:ConstInt

  Advances the iterator variable whose index is given by S1. S2 and S3
  are local variable indices. S0 points to the stack frame containing
  the iterator and local variables S1, S2, and S3.

  If the iterator has reached the end, this instruction frees the
  iterator variable and returns false; otherwise, it returns true.  If
  the iterator has not reached its end, then this instruction stores
  the iterator's next value (and key) into the local variable S3 (and
  S4, respectively).

  The IterInit and IterInitK instructions always copy the array
  element by value.
  The WIterInit and WIterInitK instructions copy referenced array
  elements by reference, and non-referenced array elements by value.
  The MIterNext and MIterNextK instructions always copy the array element
  by reference.

IterFree<IterId> S0:FramePtr
MIterFree<IterId> S0:FramePtr

  Free the iterator variable whose index is given by S1 in the stack
  frame pointed to by S0.

D:Bool = DecodeCufIter<IterId> S0:{Arr|Obj|Str} S1:FramePtr

  Decode S0 as a callable, and write the decoded values to the iterator
  specified by IterId. Returns true iff it successfully decoded the
  callable. Does not raise warnings, or throw exceptions.

CIterFree<IterId> S0:FramePtr

  Free the iterator variable whose index is given by IterId in the stack
  frame pointed to by S0.


19. Vector instruction support


D:PtrToCell = DefMIStateBase

  Declares a base register for MInstrState. Currently this is always %rsp.

All of the remaining opcodes in this section are simple wrappers
around helper functions (specified in S0) to perform the corresponding
vector operation. If S1 is a ConstCls it represents the context class
for the operation.

SetElem, SetProp, and SetNewElem are used to implement part of the SetM hhbc
opcode, which almost always pushes its first stack input or a CountedStr as its
stack result. The combinations of input types that cause SetM to push anything
other than those two values are vanishingly rare in correct PHP programs, so
these three instructions have been optimized for the common cases. SetNewElem
and SetProp have no destination, allowing the compiler to predict that the
SetM's output will be the same as its input (and optimize accordingly). If that
turns out to not be the case at runtime, the instruction will throw an
InvalidSetMException. The exception will hold a Cell containing the value the
SetM should push on the stack instead of its input value. The runtime is
responsible for catching this exception, finishing execution of the SetM
instruction, pushing the value from the exception on the stack, and proceeding
as appropriate (most likely with a side exit to the next bytecode instruction,
since it has pushed an unexpected type onto the stack).

SetElem is similar to SetProp and SetNewElem but can also be used for setting
characters within strings. When given a string base and a valid offset, SetElem
returns a string representation of the newly inserted character. In all other
cases it returns nullptr or throws an InvalidSetMException. It will throw this
exception when it detects invalid input types, or when trying to set a string
offset that would grow the string beyond the maximum supported size.

The input types that will cause the errors described above are listed here:

SetNewElem will fail if the base is not a subtype of {Null|Str|Arr|Obj} and not
           Bool<false>.
SetElem has the same base constraint as SetNewElem. In addition, the key must
        not be a subtype of {Arr|Obj}.
SetProp will fail if the base is not a subtype of {Obj|Null}.

Any instructions that take a pointer to an MInstrState struct use the various
fields of that struct for holding intermediate values.

D:PtrToGen = PropX S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen S4:PtrToCell

  Lookup intermediate property in S2, with key S3. S4 should point to
  an MInstrState struct.

D:PtrToGen = PropDX S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen
                    S4:PtrToCell
D:PtrToGen, D:StkPtr = PropDXStk S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen}
                                 S3:Gen S4:PtrToCell

  Like PropX, but used for intermediate element lookups that may modify the
  base.

D:Cell = CGetProp S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen S4:PtrToCell

  Get property with key S3 from S2. S4 should point to an MInstrState
  struct.

D:BoxedCell = VGetProp S0:ConstTCA S1:ConstCls S2:{Obj|PtrtoGen} S3:Gen
                       S4:PtrToCell
D:BoxedCell, D:StkPtr = VGetPropStk S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen}
                                    S3:Gen S4:PtrToCell

  Get property with key S3 from base S2 as a reference. S4 should point to an
  MInstrState struct.

BindProp S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen S4:BoxedCell
         S5:PtrToCell
D:StkPtr = BindPropStk S0:ConstTCA S1:ConstCls S2:{Obj|PtrtoGen} S3:Gen
                       S4:BoxedCell S5:PtrToCell

  Bind property with key S3 in base S2 to the reference in S4. S5 should point
  to an MInstrState struct.

SetProp S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen S4:Cell
D:StkPtr = SetPropStk S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen S4:Cell

  Set property with key S3 in S2 to S4.

D:Cell = SetOpProp S0:ConstTCA S1:{Obj|PtrToGen} S2:Gen S3:Cell S4:PtrToCell
D:Cell, D:StkPtr = SetOpPropStk S0:ConstTCA S1:{Obj|PtrtoGen} S2:Gen S3:Cell
                                S4:PtrtoCell

  Set op propery with key S2 in base S1, using S3 as the right hand side. S4
  should point to an MInstrState struct.

D:Cell = IncDecProp S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen
                    S4:PtrToCell
D:Cell, D:StkPtr = IncDecPropStk S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen}
                                 S3:Gen S4:PtrToCell

  Increment/decrement property with key S3 in base S2. S4 should point to an
  MInstrState struct.

D:Bool = EmptyProp S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen

  Returns true iff the property with key S3 in base S2 is empty.

D:Bool = IssetProp S0:ConstTCA S1:ConstCls S2:{Obj|PtrToGen} S3:Gen

  Returns true iff the property with key S3 in base S2 is set.

D:PtrToGen = ElemX S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell

  Get intermediate element with key S2 from base S1. The base will not be
  modified. S3 should point to an MInstrState struct.

D:PtrToGen = ElemDX S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell
D:PtrToGen, D:StkPtr = ElemDXStk S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell

  Like ElemX, but used for intermediate element lookups that may
  modify the base.

D:PtrToGen = ElemUX S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell
D:PtrToGen, D:StkPtr = ElemUXStk S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell

  Like ElemX, but used for intermediate element lookups that may
  modify the base as part of an unset operation.

D:Cell = ArrayGet S0:ConstTCA S1:Arr S2:{Int|Str}

  Get element with key S2 from base S1.

D:Cell = VectorGet S0:ConstTCA S1:Obj<Vector> S2:{Int}

  Get element with key S2 from base S1. S1 is a Vector instance.

D:Cell = PairGet S0:ConstTCA S1:Obj<Pair> S2:{Int}

  Get element with key S2 from base S1. S1 is a Pair instance.

D:Cell = MapGet S0:ConstTCA S1:Obj<Map> S2:{Int|Str}

  Get element with key S2 from base S1. S1 is a Map instance.

D:Cell = StableMapGet S0:ConstTCA S1:Obj<StableMap> S2:{Int|Str}

  Get element with key S2 from base S1. S1 is a StableMap instance.

D:Cell = CGetElem S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell

  Get element with key S2 from S1. S3 should point to an MInstrState
  struct.

D:BoxedCell = VGetElem S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell
D:BoxedCell, D:StkPtr = VGetElemStk S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell

  Get element with key S2 from base S1 as a reference. S3 should point to an
  MInstrState struct.

BindElem S0:ConstTCA S1:PtrToGen S2:Gen S3:BoxedCell S4:PtrToCell
D:StkPtr = BindElemStk S0:ConstTCA S1:PtrToGen S2:Gen S3:BoxedCell S4:PtrToCell

  Bind element with key S2 in base S1 to the reference S3. S4 should point to
  an MInstrState struct.

D:Arr = ArraySet S0:ConstTCA S1:Arr S2:{Int|Str} S3:Cell

  Set element with key S2 in S1 to S3. The dest will be a new Array
  that should replace S1.

ArraySet S0:ConstTCA S1:Arr S2:{Int|Str} S3:Cell S4:BoxedArr

  Set element with key S2 in S1 to S3. If S4 points to S1 after the set
  operation, it will be replaced with the new Array resulting from the set
  operation.

VectorSet S0:ConstTCA S1:Obj<Vector> S2:{Int} S3:Cell

  Set element with key S2 in S1 to S3. S1 is a Vector instance.

MapSet S0:ConstTCA S1:Obj<Map> S2:{Int|Str} S3:Cell

  Set element with key S2 in S1 to S3. S1 is a Map instance.

StableMapSet S0:ConstTCA S1:Obj<StableMap> S2:{Int|Str} S3:Cell

  Set element with key S2 in S1 to S3. S1 is a StableMap instance.

D:{CountedStr|Nullptr} = SetElem S0:ConstTCA S1:PtrToGen S2:Gen S3:Cell
D:{CountedStr|Nullptr} D:StkPtr = SetElemStk S0:ConstTCA S1:PtrToGen S2:Gen S3:Cell

  Set element with key S2 in S1 to S3.

SetWithRefElem S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToGen S4:PtrToCell
D:StkPtr = SetWithRefElemStk S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToGen S4:PtrToCell

  Set element with key S2 in S1 to S3. S4 should point to
  an MInstrState struct.

UnsetElem S0:ConstTCA S1:PtrToGen S2:Gen
D:StkPtr = UnsetElemStk S0:ConstTCA S1:PtrToGen S2:Gen

  Unsets the element at key S2 in the base S1.

D:Cell = SetOpElem S0:ConstTCA S1:PtrToGen S2:Gen S3:Cell S4:PtrToCell
D:Cell, D:StkPtr = SetOpElemStk S0:ConstTCA S1:PtrToGen S2:Gen S3:Cell
                                S4:PtrToCell

  Set op elem with key S2 in base S1, using S3 as the right hand side. S4
  should point to an MInstrState struct.

D:Cell = IncDecElem S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell
D:Cell, D:StkPtr = IncDecElemStk S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell

  Increment/decrement element with key S2 in base S1. S3 should point to an
  MInstrState struct.

SetNewElem S0:PtrToGen S1:Cell
D:StkPtr = SetNewElemStk S0:PtrToGen S1:Cell

  Append the value in S1 to S0.

SetWithRefNewElem S0:ConstTCA S1:PtrToGen S2:PtrToGen S3:PtrToCell
D:StkPtr = SetWithRefNewElemStk S0:ConstTCA S1:PtrToGen S2:PtrToGen
                                          S3:PtrToCell

  AppendWithRef the value in S2 to S1. S3 should point to an MInstrState struct.

BindNewElem S0:ConstTCA S0:PtrToGen S1:BoxedCell S2:PtrToCell
D:StkPtr = BindNewElemStk S0:PtrToGen S1:BoxedCell S2:PtrToCell

  Append the reference in S1 to S0. S2 should point to an MInstrState struct.

D:Bool = ArrayIsset S0:ConstTCA S1:Arr S2:{Int|Str}

  Returns true iff the element at key S2 in the base S1 is set.

D:Bool = VectorIsset S0:ConstTCA S1:Obj<Vector> S2:{Int}

  Returns true iff the element at key S2 in the base S1 is set. S1 is a
  Vector instance.

D:Bool = PairIsset S0:ConstTCA S1:Obj<Pair> S2:{Int}

  Returns true iff the element at key S2 in the base S1 is set. S1 is a
  Pair instance.

D:Bool = MapIsset S0:ConstTCA S1:Obj<Map> S2:{Int|Str}

  Returns true iff the element at key S2 in the base S1 is set. S1 is a
  Map instance.

D:Bool = StableMapIsset S0:ConstTCA S1:Obj<StableMap> S2:{Int|Str}

  Returns true iff the element at key S2 in the base S1 is set. S1 is a
  StableMap instance.

D:Bool = IssetElem S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell

  Returns true iff the element at key S2 in S1 is set.

D:Bool = EmptyElem S0:ConstTCA S1:PtrToGen S2:Gen S3:PtrToCell

  Returns true iff the element at key S2 in S1 is set and not equal
  (as defined by the hhbc Eq instruction) to false.

20. Exception/unwinding support

BeginCatch

  Marks the beginning of a catch region. Exact behavior is implementation and
  architecture specific.

EndCatch S0:StkPtr

  Marks the end of a catch region and returns control to the unwinder if
  appropriate. Exact behavior is implementation and architecture specific.

D:Cell = LdUnwinderValue

  Loads the value contained by the current unwinder exception.

DeleteUnwinderException

  Deletes the current unwinder exception.
