LowLevelInterpreter64.asm   [plain text]


# Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.


# Some value representation constants.
const TagBitTypeOther = 0x2
const TagBitBool      = 0x4
const TagBitUndefined = 0x8
const ValueEmpty      = 0x0
const ValueFalse      = TagBitTypeOther | TagBitBool
const ValueTrue       = TagBitTypeOther | TagBitBool | 1
const ValueUndefined  = TagBitTypeOther | TagBitUndefined
const ValueNull       = TagBitTypeOther

# Utilities.
macro dispatch(advance)
    addp advance, PC
    jmp [PB, PC, 8]
end

macro dispatchInt(advance)
    addi advance, PC
    jmp [PB, PC, 8]
end

macro dispatchAfterCall()
    loadi ArgumentCount + TagOffset[cfr], PC
    loadp CodeBlock[cfr], PB
    loadp CodeBlock::m_instructions[PB], PB
    jmp [PB, PC, 8]
end

macro cCall2(function, arg1, arg2)
    move arg1, t5
    move arg2, t4
    call function
end

# This barely works. arg3 and arg4 should probably be immediates.
macro cCall4(function, arg1, arg2, arg3, arg4)
    move arg1, t5
    move arg2, t4
    move arg3, t1
    move arg4, t2
    call function
end

macro prepareStateForCCall()
    leap [PB, PC, 8], PC
    move PB, t3
end

macro restoreStateAfterCCall()
    move t0, PC
    move t1, cfr
    move t3, PB
    subp PB, PC
    urshiftp 3, PC
end

macro callSlowPath(slowPath)
    prepareStateForCCall()
    cCall2(slowPath, cfr, PC)
    restoreStateAfterCCall()
end

macro traceOperand(fromWhere, operand)
    prepareStateForCCall()
    cCall4(_llint_trace_operand, cfr, PC, fromWhere, operand)
    restoreStateAfterCCall()
end

macro traceValue(fromWhere, operand)
    prepareStateForCCall()
    cCall4(_llint_trace_value, cfr, PC, fromWhere, operand)
    restoreStateAfterCCall()
end

# Call a slow path for call call opcodes.
macro callCallSlowPath(advance, slowPath, action)
    addi advance, PC, t0
    storei t0, ArgumentCount + TagOffset[cfr]
    prepareStateForCCall()
    cCall2(slowPath, cfr, PC)
    move t1, cfr
    action(t0)
end

macro checkSwitchToJITForLoop()
    checkSwitchToJIT(
        1,
        macro()
            storei PC, ArgumentCount + TagOffset[cfr]
            prepareStateForCCall()
            cCall2(_llint_loop_osr, cfr, PC)
            move t1, cfr
            btpz t0, .recover
            jmp t0
        .recover:
            move t3, PB
            loadi ArgumentCount + TagOffset[cfr], PC
        end)
end

# Index and value must be different registers. Index may be clobbered.
macro loadConstantOrVariable(index, value)
    bpgteq index, FirstConstantRegisterIndex, .constant
    loadp [cfr, index, 8], value
    jmp .done
.constant:
    loadp CodeBlock[cfr], value
    loadp CodeBlock::m_constantRegisters + VectorBufferOffset[value], value
    subp FirstConstantRegisterIndex, index
    loadp [value, index, 8], value
.done:
end

macro loadConstantOrVariableInt32(index, value, slow)
    loadConstantOrVariable(index, value)
    bpb value, tagTypeNumber, slow
end

macro loadConstantOrVariableCell(index, value, slow)
    loadConstantOrVariable(index, value)
    btpnz value, tagMask, slow
end

macro writeBarrier(value)
    # Nothing to do, since we don't have a generational or incremental collector.
end

macro valueProfile(value, profile)
    if VALUE_PROFILER
        storep value, ValueProfile::m_buckets[profile]
    end
end


# Entrypoints into the interpreter.

# Expects that CodeBlock is in t1, which is what prologue() leaves behind.
macro functionArityCheck(doneLabel, slow_path)
    loadi PayloadOffset + ArgumentCount[cfr], t0
    biaeq t0, CodeBlock::m_numParameters[t1], doneLabel
    prepareStateForCCall()
    cCall2(slow_path, cfr, PC)   # This slow_path has a simple protocol: t0 = 0 => no error, t0 != 0 => error
    move t1, cfr
    btiz t0, .continue
    loadp JITStackFrame::globalData[sp], t1
    loadp JSGlobalData::callFrameForThrow[t1], t0
    jmp JSGlobalData::targetMachinePCForThrow[t1]
.continue:
    # Reload CodeBlock and reset PC, since the slow_path clobbered them.
    loadp CodeBlock[cfr], t1
    loadp CodeBlock::m_instructions[t1], PB
    move 0, PC
    jmp doneLabel
end


# Instruction implementations

_llint_op_enter:
    traceExecution()
    loadp CodeBlock[cfr], t2
    loadi CodeBlock::m_numVars[t2], t2
    btiz t2, .opEnterDone
    move ValueUndefined, t0
.opEnterLoop:
    subi 1, t2
    storep t0, [cfr, t2, 8]
    btinz t2, .opEnterLoop
.opEnterDone:
    dispatch(1)


_llint_op_create_activation:
    traceExecution()
    loadis 8[PB, PC, 8], t0
    bpneq [cfr, t0, 8], ValueEmpty, .opCreateActivationDone
    callSlowPath(_llint_slow_path_create_activation)
.opCreateActivationDone:
    dispatch(2)


_llint_op_init_lazy_reg:
    traceExecution()
    loadis 8[PB, PC, 8], t0
    storep ValueEmpty, [cfr, t0, 8]
    dispatch(2)


_llint_op_create_arguments:
    traceExecution()
    loadis 8[PB, PC, 8], t0
    bpneq [cfr, t0, 8], ValueEmpty, .opCreateArgumentsDone
    callSlowPath(_llint_slow_path_create_arguments)
.opCreateArgumentsDone:
    dispatch(2)


_llint_op_create_this:
    traceExecution()
    loadis 16[PB, PC, 8], t0
    assertNotConstant(t0)
    loadp [cfr, t0, 8], t0
    btpnz t0, tagMask, .opCreateThisSlow
    loadp JSCell::m_structure[t0], t1
    bbb Structure::m_typeInfo + TypeInfo::m_type[t1], ObjectType, .opCreateThisSlow
    loadp JSObject::m_inheritorID[t0], t2
    btpz t2, .opCreateThisSlow
    allocateBasicJSObject(JSFinalObjectSizeClassIndex, JSGlobalData::jsFinalObjectClassInfo, t2, t0, t1, t3, .opCreateThisSlow)
    loadis 8[PB, PC, 8], t1
    storep t0, [cfr, t1, 8]
    dispatch(3)

.opCreateThisSlow:
    callSlowPath(_llint_slow_path_create_this)
    dispatch(3)


_llint_op_get_callee:
    traceExecution()
    loadis 8[PB, PC, 8], t0
    loadp Callee[cfr], t1
    storep t1, [cfr, t0, 8]
    dispatch(2)


_llint_op_convert_this:
    traceExecution()
    loadis 8[PB, PC, 8], t0
    loadp [cfr, t0, 8], t0
    btpnz t0, tagMask, .opConvertThisSlow
    loadp JSCell::m_structure[t0], t0
    bbb Structure::m_typeInfo + TypeInfo::m_type[t0], ObjectType, .opConvertThisSlow
    dispatch(2)

.opConvertThisSlow:
    callSlowPath(_llint_slow_path_convert_this)
    dispatch(2)


_llint_op_new_object:
    traceExecution()
    loadp CodeBlock[cfr], t0
    loadp CodeBlock::m_globalObject[t0], t0
    loadp JSGlobalObject::m_emptyObjectStructure[t0], t1
    allocateBasicJSObject(JSFinalObjectSizeClassIndex, JSGlobalData::jsFinalObjectClassInfo, t1, t0, t2, t3, .opNewObjectSlow)
    loadis 8[PB, PC, 8], t1
    storep t0, [cfr, t1, 8]
    dispatch(2)

.opNewObjectSlow:
    callSlowPath(_llint_slow_path_new_object)
    dispatch(2)


_llint_op_mov:
    traceExecution()
    loadis 16[PB, PC, 8], t1
    loadis 8[PB, PC, 8], t0
    loadConstantOrVariable(t1, t2)
    storep t2, [cfr, t0, 8]
    dispatch(3)


_llint_op_not:
    traceExecution()
    loadis 16[PB, PC, 8], t0
    loadis 8[PB, PC, 8], t1
    loadConstantOrVariable(t0, t2)
    xorp ValueFalse, t2
    btpnz t2, ~1, .opNotSlow
    xorp ValueTrue, t2
    storep t2, [cfr, t1, 8]
    dispatch(3)

.opNotSlow:
    callSlowPath(_llint_slow_path_not)
    dispatch(3)


macro equalityComparison(integerComparison, slowPath)
    traceExecution()
    loadis 24[PB, PC, 8], t0
    loadis 16[PB, PC, 8], t2
    loadis 8[PB, PC, 8], t3
    loadConstantOrVariableInt32(t0, t1, .slow)
    loadConstantOrVariableInt32(t2, t0, .slow)
    integerComparison(t0, t1, t0)
    orp ValueFalse, t0
    storep t0, [cfr, t3, 8]
    dispatch(4)

.slow:
    callSlowPath(slowPath)
    dispatch(4)
end

_llint_op_eq:
    equalityComparison(
        macro (left, right, result) cieq left, right, result end,
        _llint_slow_path_eq)


_llint_op_neq:
    equalityComparison(
        macro (left, right, result) cineq left, right, result end,
        _llint_slow_path_neq)


macro equalNullComparison()
    loadis 16[PB, PC, 8], t0
    loadp [cfr, t0, 8], t0
    btpnz t0, tagMask, .immediate
    loadp JSCell::m_structure[t0], t2
    tbnz Structure::m_typeInfo + TypeInfo::m_flags[t2], MasqueradesAsUndefined, t0
    jmp .done
.immediate:
    andp ~TagBitUndefined, t0
    cpeq t0, ValueNull, t0
.done:
end

_llint_op_eq_null:
    traceExecution()
    equalNullComparison()
    loadis 8[PB, PC, 8], t1
    orp ValueFalse, t0
    storep t0, [cfr, t1, 8]
    dispatch(3)


_llint_op_neq_null:
    traceExecution()
    equalNullComparison()
    loadis 8[PB, PC, 8], t1
    xorp ValueTrue, t0
    storep t0, [cfr, t1, 8]
    dispatch(3)


macro strictEq(equalityOperation, slowPath)
    traceExecution()
    loadis 24[PB, PC, 8], t0
    loadis 16[PB, PC, 8], t2
    loadConstantOrVariable(t0, t1)
    loadConstantOrVariable(t2, t0)
    move t0, t2
    orp t1, t2
    btpz t2, tagMask, .slow
    bpaeq t0, tagTypeNumber, .leftOK
    btpnz t0, tagTypeNumber, .slow
.leftOK:
    bpaeq t1, tagTypeNumber, .rightOK
    btpnz t1, tagTypeNumber, .slow
.rightOK:
    equalityOperation(t0, t1, t0)
    loadis 8[PB, PC, 8], t1
    orp ValueFalse, t0
    storep t0, [cfr, t1, 8]
    dispatch(4)

.slow:
    callSlowPath(slowPath)
    dispatch(4)
end

_llint_op_stricteq:
    strictEq(
        macro (left, right, result) cpeq left, right, result end,
        _llint_slow_path_stricteq)


_llint_op_nstricteq:
    strictEq(
        macro (left, right, result) cpneq left, right, result end,
        _llint_slow_path_nstricteq)


macro preOp(arithmeticOperation, slowPath)
    traceExecution()
    loadis 8[PB, PC, 8], t0
    loadp [cfr, t0, 8], t1
    bpb t1, tagTypeNumber, .slow
    arithmeticOperation(t1, .slow)
    orp tagTypeNumber, t1
    storep t1, [cfr, t0, 8]
    dispatch(2)

.slow:
    callSlowPath(slowPath)
    dispatch(2)
end

_llint_op_pre_inc:
    preOp(
        macro (value, slow) baddio 1, value, slow end,
        _llint_slow_path_pre_inc)


_llint_op_pre_dec:
    preOp(
        macro (value, slow) bsubio 1, value, slow end,
        _llint_slow_path_pre_dec)


macro postOp(arithmeticOperation, slowPath)
    traceExecution()
    loadis 16[PB, PC, 8], t0
    loadis 8[PB, PC, 8], t1
    loadp [cfr, t0, 8], t2
    bieq t0, t1, .done
    bpb t2, tagTypeNumber, .slow
    move t2, t3
    arithmeticOperation(t3, .slow)
    orp tagTypeNumber, t3
    storep t2, [cfr, t1, 8]
    storep t3, [cfr, t0, 8]
.done:
    dispatch(3)

.slow:
    callSlowPath(slowPath)
    dispatch(3)
end

_llint_op_post_inc:
    postOp(
        macro (value, slow) baddio 1, value, slow end,
        _llint_slow_path_post_inc)


_llint_op_post_dec:
    postOp(
        macro (value, slow) bsubio 1, value, slow end,
        _llint_slow_path_post_dec)


_llint_op_to_jsnumber:
    traceExecution()
    loadis 16[PB, PC, 8], t0
    loadis 8[PB, PC, 8], t1
    loadConstantOrVariable(t0, t2)
    bpaeq t2, tagTypeNumber, .opToJsnumberIsImmediate
    btpz t2, tagTypeNumber, .opToJsnumberSlow
.opToJsnumberIsImmediate:
    storep t2, [cfr, t1, 8]
    dispatch(3)

.opToJsnumberSlow:
    callSlowPath(_llint_slow_path_to_jsnumber)
    dispatch(3)


_llint_op_negate:
    traceExecution()
    loadis 16[PB, PC, 8], t0
    loadis 8[PB, PC, 8], t1
    loadConstantOrVariable(t0, t2)
    bpb t2, tagTypeNumber, .opNegateNotInt
    btiz t2, 0x7fffffff, .opNegateSlow
    negi t2
    orp tagTypeNumber, t2
    storep t2, [cfr, t1, 8]
    dispatch(3)
.opNegateNotInt:
    btpz t2, tagTypeNumber, .opNegateSlow
    xorp 0x8000000000000000, t2
    storep t2, [cfr, t1, 8]
    dispatch(3)

.opNegateSlow:
    callSlowPath(_llint_slow_path_negate)
    dispatch(3)


macro binaryOpCustomStore(integerOperationAndStore, doubleOperation, slowPath)
    loadis 24[PB, PC, 8], t0
    loadis 16[PB, PC, 8], t2
    loadConstantOrVariable(t0, t1)
    loadConstantOrVariable(t2, t0)
    bpb t0, tagTypeNumber, .op1NotInt
    bpb t1, tagTypeNumber, .op2NotInt
    loadis 8[PB, PC, 8], t2
    integerOperationAndStore(t1, t0, .slow, t2)
    dispatch(5)

.op1NotInt:
    # First operand is definitely not an int, the second operand could be anything.
    btpz t0, tagTypeNumber, .slow
    bpaeq t1, tagTypeNumber, .op1NotIntOp2Int
    btpz t1, tagTypeNumber, .slow
    addp tagTypeNumber, t1
    fp2d t1, ft1
    jmp .op1NotIntReady
.op1NotIntOp2Int:
    ci2d t1, ft1
.op1NotIntReady:
    loadis 8[PB, PC, 8], t2
    addp tagTypeNumber, t0
    fp2d t0, ft0
    doubleOperation(ft1, ft0)
    fd2p ft0, t0
    subp tagTypeNumber, t0
    storep t0, [cfr, t2, 8]
    dispatch(5)

.op2NotInt:
    # First operand is definitely an int, the second is definitely not.
    loadis 8[PB, PC, 8], t2
    btpz t1, tagTypeNumber, .slow
    ci2d t0, ft0
    addp tagTypeNumber, t1
    fp2d t1, ft1
    doubleOperation(ft1, ft0)
    fd2p ft0, t0
    subp tagTypeNumber, t0
    storep t0, [cfr, t2, 8]
    dispatch(5)

.slow:
    callSlowPath(slowPath)
    dispatch(5)
end

macro binaryOp(integerOperation, doubleOperation, slowPath)
    binaryOpCustomStore(
        macro (left, right, slow, index)
            integerOperation(left, right, slow)
            orp tagTypeNumber, right
            storep right, [cfr, index, 8]
        end,
        doubleOperation, slowPath)
end

_llint_op_add:
    traceExecution()
    binaryOp(
        macro (left, right, slow) baddio left, right, slow end,
        macro (left, right) addd left, right end,
        _llint_slow_path_add)


_llint_op_mul:
    traceExecution()
    binaryOpCustomStore(
        macro (left, right, slow, index)
            # Assume t3 is scratchable.
            move right, t3
            bmulio left, t3, slow
            btinz t3, .done
            bilt left, 0, slow
            bilt right, 0, slow
        .done:
            orp tagTypeNumber, t3
            storep t3, [cfr, index, 8]
        end,
        macro (left, right) muld left, right end,
        _llint_slow_path_mul)


_llint_op_sub:
    traceExecution()
    binaryOp(
        macro (left, right, slow) bsubio left, right, slow end,
        macro (left, right) subd left, right end,
        _llint_slow_path_sub)


_llint_op_div:
    traceExecution()
    binaryOpCustomStore(
        macro (left, right, slow, index)
            # Assume t3 is scratchable.
            btiz left, slow
            bineq left, -1, .notNeg2TwoThe31DivByNeg1
            bieq right, -2147483648, .slow
        .notNeg2TwoThe31DivByNeg1:
            btinz right, .intOK
            bilt left, 0, slow
        .intOK:
            move left, t3
            move right, t0
            cdqi
            idivi t3
            btinz t1, slow
            orp tagTypeNumber, t0
            storep t0, [cfr, index, 8]
        end,
        macro (left, right) divd left, right end,
        _llint_slow_path_div)


macro bitOp(operation, slowPath, advance)
    loadis 24[PB, PC, 8], t0
    loadis 16[PB, PC, 8], t2
    loadis 8[PB, PC, 8], t3
    loadConstantOrVariable(t0, t1)
    loadConstantOrVariable(t2, t0)
    bpb t0, tagTypeNumber, .slow
    bpb t1, tagTypeNumber, .slow
    operation(t1, t0, .slow)
    orp tagTypeNumber, t0
    storep t0, [cfr, t3, 8]
    dispatch(advance)

.slow:
    callSlowPath(slowPath)
    dispatch(advance)
end

_llint_op_lshift:
    traceExecution()
    bitOp(
        macro (left, right, slow) lshifti left, right end,
        _llint_slow_path_lshift,
        4)


_llint_op_rshift:
    traceExecution()
    bitOp(
        macro (left, right, slow) rshifti left, right end,
        _llint_slow_path_rshift,
        4)


_llint_op_urshift:
    traceExecution()
    bitOp(
        macro (left, right, slow)
            urshifti left, right
            bilt right, 0, slow
        end,
        _llint_slow_path_urshift,
        4)


_llint_op_bitand:
    traceExecution()
    bitOp(
        macro (left, right, slow) andi left, right end,
        _llint_slow_path_bitand,
        5)


_llint_op_bitxor:
    traceExecution()
    bitOp(
        macro (left, right, slow) xori left, right end,
        _llint_slow_path_bitxor,
        5)


_llint_op_bitor:
    traceExecution()
    bitOp(
        macro (left, right, slow) ori left, right end,
        _llint_slow_path_bitor,
        5)


_llint_op_check_has_instance:
    traceExecution()
    loadis 8[PB, PC, 8], t1
    loadConstantOrVariableCell(t1, t0, .opCheckHasInstanceSlow)
    loadp JSCell::m_structure[t0], t0
    btbz Structure::m_typeInfo + TypeInfo::m_flags[t0], ImplementsHasInstance, .opCheckHasInstanceSlow
    dispatch(2)

.opCheckHasInstanceSlow:
    callSlowPath(_llint_slow_path_check_has_instance)
    dispatch(2)


_llint_op_instanceof:
    traceExecution()
    # Check that baseVal implements the default HasInstance behavior.
    # FIXME: This should be deprecated.
    loadis 24[PB, PC, 8], t1
    loadConstantOrVariable(t1, t0)
    loadp JSCell::m_structure[t0], t0
    btbz Structure::m_typeInfo + TypeInfo::m_flags[t0], ImplementsDefaultHasInstance, .opInstanceofSlow
    
    # Actually do the work.
    loadis 32[PB, PC, 8], t0
    loadis 8[PB, PC, 8], t3
    loadConstantOrVariableCell(t0, t1, .opInstanceofSlow)
    loadp JSCell::m_structure[t1], t2
    bbb Structure::m_typeInfo + TypeInfo::m_type[t2], ObjectType, .opInstanceofSlow
    loadis 16[PB, PC, 8], t0
    loadConstantOrVariableCell(t0, t2, .opInstanceofSlow)
    
    # Register state: t1 = prototype, t2 = value
    move 1, t0
.opInstanceofLoop:
    loadp JSCell::m_structure[t2], t2
    loadp Structure::m_prototype[t2], t2
    bpeq t2, t1, .opInstanceofDone
    btpz t2, tagMask, .opInstanceofLoop

    move 0, t0
.opInstanceofDone:
    orp ValueFalse, t0
    storep t0, [cfr, t3, 8]
    dispatch(5)

.opInstanceofSlow:
    callSlowPath(_llint_slow_path_instanceof)
    dispatch(5)


_llint_op_is_undefined:
    traceExecution()
    loadis 16[PB, PC, 8], t1
    loadis 8[PB, PC, 8], t2
    loadConstantOrVariable(t1, t0)
    btpz t0, tagMask, .opIsUndefinedCell
    cpeq t0, ValueUndefined, t3
    orp ValueFalse, t3
    storep t3, [cfr, t2, 8]
    dispatch(3)
.opIsUndefinedCell:
    loadp JSCell::m_structure[t0], t0
    tbnz Structure::m_typeInfo + TypeInfo::m_flags[t0], MasqueradesAsUndefined, t1
    orp ValueFalse, t1
    storep t1, [cfr, t2, 8]
    dispatch(3)


_llint_op_is_boolean:
    traceExecution()
    loadis 16[PB, PC, 8], t1
    loadis 8[PB, PC, 8], t2
    loadConstantOrVariable(t1, t0)
    xorp ValueFalse, t0
    tpz t0, ~1, t0
    orp ValueFalse, t0
    storep t0, [cfr, t2, 8]
    dispatch(3)


_llint_op_is_number:
    traceExecution()
    loadis 16[PB, PC, 8], t1
    loadis 8[PB, PC, 8], t2
    loadConstantOrVariable(t1, t0)
    tpnz t0, tagTypeNumber, t1
    orp ValueFalse, t1
    storep t1, [cfr, t2, 8]
    dispatch(3)


_llint_op_is_string:
    traceExecution()
    loadis 16[PB, PC, 8], t1
    loadis 8[PB, PC, 8], t2
    loadConstantOrVariable(t1, t0)
    btpnz t0, tagMask, .opIsStringNotCell
    loadp JSCell::m_structure[t0], t0
    cbeq Structure::m_typeInfo + TypeInfo::m_type[t0], StringType, t1
    orp ValueFalse, t1
    storep t1, [cfr, t2, 8]
    dispatch(3)
.opIsStringNotCell:
    storep ValueFalse, [cfr, t2, 8]
    dispatch(3)


macro resolveGlobal(size, slow)
    # Operands are as follows:
    # 8[PB, PC, 8]   Destination for the load.
    # 16[PB, PC, 8]  Property identifier index in the code block.
    # 24[PB, PC, 8]  Structure pointer, initialized to 0 by bytecode generator.
    # 32[PB, PC, 8]  Offset in global object, initialized to 0 by bytecode generator.
    loadp CodeBlock[cfr], t0
    loadp CodeBlock::m_globalObject[t0], t0
    loadp JSCell::m_structure[t0], t1
    bpneq t1, 24[PB, PC, 8], slow
    loadis 32[PB, PC, 8], t1
    loadp JSObject::m_propertyStorage[t0], t0
    loadp [t0, t1, 8], t2
    loadis 8[PB, PC, 8], t0
    storep t2, [cfr, t0, 8]
    loadp (size - 1) * 8[PB, PC, 8], t0
    valueProfile(t2, t0)
end

_llint_op_resolve_global:
    traceExecution()
    resolveGlobal(6, .opResolveGlobalSlow)
    dispatch(6)

.opResolveGlobalSlow:
    callSlowPath(_llint_slow_path_resolve_global)
    dispatch(6)


# Gives you the scope in t0, while allowing you to optionally perform additional checks on the
# scopes as they are traversed. scopeCheck() is called with two arguments: the register
# holding the scope, and a register that can be used for scratch. Note that this does not
# use t3, so you can hold stuff in t3 if need be.
macro getScope(deBruijinIndexOperand, scopeCheck)
    loadp ScopeChain[cfr], t0
    loadis deBruijinIndexOperand, t2
    
    btiz t2, .done
    
    loadp CodeBlock[cfr], t1
    bineq CodeBlock::m_codeType[t1], FunctionCode, .loop
    btbz CodeBlock::m_needsFullScopeChain[t1], .loop
    
    loadis CodeBlock::m_activationRegister[t1], t1

    # Need to conditionally skip over one scope.
    btpz [cfr, t1, 8], .noActivation
    scopeCheck(t0, t1)
    loadp ScopeChainNode::next[t0], t0
.noActivation:
    subi 1, t2
    
    btiz t2, .done
.loop:
    scopeCheck(t0, t1)
    loadp ScopeChainNode::next[t0], t0
    subi 1, t2
    btinz t2, .loop

.done:
end

_llint_op_resolve_global_dynamic:
    traceExecution()
    loadp JITStackFrame::globalData[sp], t3
    loadp JSGlobalData::activationStructure[t3], t3
    getScope(
        40[PB, PC, 8],
        macro (scope, scratch)
            loadp ScopeChainNode::object[scope], scratch
            bpneq JSCell::m_structure[scratch], t3, .opResolveGlobalDynamicSuperSlow
        end)
    resolveGlobal(7, .opResolveGlobalDynamicSlow)
    dispatch(7)

.opResolveGlobalDynamicSuperSlow:
    callSlowPath(_llint_slow_path_resolve_for_resolve_global_dynamic)
    dispatch(7)

.opResolveGlobalDynamicSlow:
    callSlowPath(_llint_slow_path_resolve_global_dynamic)
    dispatch(7)


_llint_op_get_scoped_var:
    traceExecution()
    # Operands are as follows:
    # 8[PB, PC, 8]   Destination for the load
    # 16[PB, PC, 8]  Index of register in the scope
    # 24[PB, PC, 8]  De Bruijin index.
    getScope(24[PB, PC, 8], macro (scope, scratch) end)
    loadis 8[PB, PC, 8], t1
    loadis 16[PB, PC, 8], t2
    loadp ScopeChainNode::object[t0], t0
    loadp JSVariableObject::m_registers[t0], t0
    loadp [t0, t2, 8], t3
    storep t3, [cfr, t1, 8]
    loadp 32[PB, PC, 8], t1
    valueProfile(t3, t1)
    dispatch(5)


_llint_op_put_scoped_var:
    traceExecution()
    getScope(16[PB, PC, 8], macro (scope, scratch) end)
    loadis 24[PB, PC, 8], t1
    loadConstantOrVariable(t1, t3)
    loadis 8[PB, PC, 8], t1
    writeBarrier(t3)
    loadp ScopeChainNode::object[t0], t0
    loadp JSVariableObject::m_registers[t0], t0
    storep t3, [t0, t1, 8]
    dispatch(4)


_llint_op_get_global_var:
    traceExecution()
    loadis 16[PB, PC, 8], t1
    loadis 8[PB, PC, 8], t3
    loadp CodeBlock[cfr], t0
    loadp CodeBlock::m_globalObject[t0], t0
    loadp JSGlobalObject::m_registers[t0], t0
    loadp [t0, t1, 8], t2
    storep t2, [cfr, t3, 8]
    loadp 24[PB, PC, 8], t3
    valueProfile(t2, t3)
    dispatch(4)


_llint_op_put_global_var:
    traceExecution()
    loadis 16[PB, PC, 8], t1
    loadp CodeBlock[cfr], t0
    loadp CodeBlock::m_globalObject[t0], t0
    loadp JSGlobalObject::m_registers[t0], t0
    loadConstantOrVariable(t1, t2)
    loadis 8[PB, PC, 8], t1
    writeBarrier(t2)
    storep t2, [t0, t1, 8]
    dispatch(3)


_llint_op_get_by_id:
    traceExecution()
    # We only do monomorphic get_by_id caching for now, and we do not modify the
    # opcode. We do, however, allow for the cache to change anytime if fails, since
    # ping-ponging is free. At best we get lucky and the get_by_id will continue
    # to take fast path on the new cache. At worst we take slow path, which is what
    # we would have been doing anyway.
    loadis 16[PB, PC, 8], t0
    loadp 32[PB, PC, 8], t1
    loadConstantOrVariableCell(t0, t3, .opGetByIdSlow)
    loadis 40[PB, PC, 8], t2
    loadp JSObject::m_propertyStorage[t3], t0
    bpneq JSCell::m_structure[t3], t1, .opGetByIdSlow
    loadis 8[PB, PC, 8], t1
    loadp [t0, t2], t3
    storep t3, [cfr, t1, 8]
    loadp 64[PB, PC, 8], t1
    valueProfile(t3, t1)
    dispatch(9)

.opGetByIdSlow:
    callSlowPath(_llint_slow_path_get_by_id)
    dispatch(9)


_llint_op_get_arguments_length:
    traceExecution()
    loadis 16[PB, PC, 8], t0
    loadis 8[PB, PC, 8], t1
    btpnz [cfr, t0, 8], .opGetArgumentsLengthSlow
    loadi ArgumentCount + PayloadOffset[cfr], t2
    subi 1, t2
    orp tagTypeNumber, t2
    storep t2, [cfr, t1, 8]
    dispatch(4)

.opGetArgumentsLengthSlow:
    callSlowPath(_llint_slow_path_get_arguments_length)
    dispatch(4)


_llint_op_put_by_id:
    traceExecution()
    loadis 8[PB, PC, 8], t3
    loadp 32[PB, PC, 8], t1
    loadConstantOrVariableCell(t3, t0, .opPutByIdSlow)
    loadis 24[PB, PC, 8], t2
    loadp JSObject::m_propertyStorage[t0], t3
    bpneq JSCell::m_structure[t0], t1, .opPutByIdSlow
    loadis 40[PB, PC, 8], t1
    loadConstantOrVariable(t2, t0)
    writeBarrier(t0)
    storep t0, [t3, t1]
    dispatch(9)

.opPutByIdSlow:
    callSlowPath(_llint_slow_path_put_by_id)
    dispatch(9)


macro putByIdTransition(additionalChecks)
    traceExecution()
    loadis 8[PB, PC, 8], t3
    loadp 32[PB, PC, 8], t1
    loadConstantOrVariableCell(t3, t0, .opPutByIdSlow)
    loadis 24[PB, PC, 8], t2
    bpneq JSCell::m_structure[t0], t1, .opPutByIdSlow
    additionalChecks(t1, t3, .opPutByIdSlow)
    loadis 40[PB, PC, 8], t1
    loadp JSObject::m_propertyStorage[t0], t3
    addp t1, t3
    loadConstantOrVariable(t2, t1)
    writeBarrier(t1)
    storep t1, [t3]
    loadp 48[PB, PC, 8], t1
    storep t1, JSCell::m_structure[t0]
    dispatch(9)
end

_llint_op_put_by_id_transition_direct:
    putByIdTransition(macro (oldStructure, scratch, slow) end)


_llint_op_put_by_id_transition_normal:
    putByIdTransition(
        macro (oldStructure, scratch, slow)
            const protoCell = oldStructure    # Reusing the oldStructure register for the proto
            loadp 56[PB, PC, 8], scratch
            assert(macro (ok) btpnz scratch, ok end)
            loadp StructureChain::m_vector[scratch], scratch
            assert(macro (ok) btpnz scratch, ok end)
            bpeq Structure::m_prototype[oldStructure], ValueNull, .done
        .loop:
            loadp Structure::m_prototype[oldStructure], protoCell
            loadp JSCell::m_structure[protoCell], oldStructure
            bpneq oldStructure, [scratch], slow
            addp 8, scratch
            bpneq Structure::m_prototype[oldStructure], ValueNull, .loop
        .done:
        end)


_llint_op_get_by_val:
    traceExecution()
    loadp CodeBlock[cfr], t1
    loadis 16[PB, PC, 8], t2
    loadis 24[PB, PC, 8], t3
    loadp CodeBlock::m_globalData[t1], t1
    loadConstantOrVariableCell(t2, t0, .opGetByValSlow)
    loadp JSGlobalData::jsArrayClassInfo[t1], t2
    loadConstantOrVariableInt32(t3, t1, .opGetByValSlow)
    sxi2p t1, t1
    bpneq [t0], t2, .opGetByValSlow
    loadp JSArray::m_storage[t0], t3
    biaeq t1, JSArray::m_vectorLength[t0], .opGetByValSlow
    loadis 8[PB, PC, 8], t0
    loadp ArrayStorage::m_vector[t3, t1, 8], t2
    btpz t2, .opGetByValSlow
    storep t2, [cfr, t0, 8]
    loadp 32[PB, PC, 8], t0
    valueProfile(t2, t0)
    dispatch(5)

.opGetByValSlow:
    callSlowPath(_llint_slow_path_get_by_val)
    dispatch(5)


_llint_op_get_argument_by_val:
    traceExecution()
    loadis 16[PB, PC, 8], t0
    loadis 24[PB, PC, 8], t1
    btpnz [cfr, t0, 8], .opGetArgumentByValSlow
    loadConstantOrVariableInt32(t1, t2, .opGetArgumentByValSlow)
    addi 1, t2
    loadi ArgumentCount + PayloadOffset[cfr], t1
    biaeq t2, t1, .opGetArgumentByValSlow
    negi t2
    sxi2p t2, t2
    loadis 8[PB, PC, 8], t3
    loadp ThisArgumentOffset[cfr, t2, 8], t0
    storep t0, [cfr, t3, 8]
    dispatch(5)

.opGetArgumentByValSlow:
    callSlowPath(_llint_slow_path_get_argument_by_val)
    dispatch(5)


_llint_op_get_by_pname:
    traceExecution()
    loadis 24[PB, PC, 8], t1
    loadConstantOrVariable(t1, t0)
    loadis 32[PB, PC, 8], t1
    assertNotConstant(t1)
    bpneq t0, [cfr, t1, 8], .opGetByPnameSlow
    loadis 16[PB, PC, 8], t2
    loadis 40[PB, PC, 8], t3
    loadConstantOrVariableCell(t2, t0, .opGetByPnameSlow)
    assertNotConstant(t3)
    loadp [cfr, t3, 8], t1
    loadp JSCell::m_structure[t0], t2
    bpneq t2, JSPropertyNameIterator::m_cachedStructure[t1], .opGetByPnameSlow
    loadis 48[PB, PC, 8], t3
    loadi PayloadOffset[cfr, t3, 8], t3
    subi 1, t3
    biaeq t3, JSPropertyNameIterator::m_numCacheableSlots[t1], .opGetByPnameSlow
    loadp JSObject::m_propertyStorage[t0], t0
    loadp [t0, t3, 8], t0
    loadis 8[PB, PC, 8], t1
    storep t0, [cfr, t1, 8]
    dispatch(7)

.opGetByPnameSlow:
    callSlowPath(_llint_slow_path_get_by_pname)
    dispatch(7)


_llint_op_put_by_val:
    traceExecution()
    loadis 8[PB, PC, 8], t0
    loadConstantOrVariableCell(t0, t1, .opPutByValSlow)
    loadis 16[PB, PC, 8], t0
    loadConstantOrVariableInt32(t0, t2, .opPutByValSlow)
    sxi2p t2, t2
    loadp CodeBlock[cfr], t0
    loadp CodeBlock::m_globalData[t0], t0
    loadp JSGlobalData::jsArrayClassInfo[t0], t0
    bpneq [t1], t0, .opPutByValSlow
    biaeq t2, JSArray::m_vectorLength[t1], .opPutByValSlow
    loadp JSArray::m_storage[t1], t0
    btpz ArrayStorage::m_vector[t0, t2, 8], .opPutByValEmpty
.opPutByValStoreResult:
    loadis 24[PB, PC, 8], t3
    loadConstantOrVariable(t3, t1)
    writeBarrier(t1)
    storep t1, ArrayStorage::m_vector[t0, t2, 8]
    dispatch(4)

.opPutByValEmpty:
    addi 1, ArrayStorage::m_numValuesInVector[t0]
    bib t2, ArrayStorage::m_length[t0], .opPutByValStoreResult
    addi 1, t2, t1
    storei t1, ArrayStorage::m_length[t0]
    jmp .opPutByValStoreResult

.opPutByValSlow:
    callSlowPath(_llint_slow_path_put_by_val)
    dispatch(4)


_llint_op_loop:
    nop
_llint_op_jmp:
    traceExecution()
    dispatchInt(8[PB, PC, 8])


macro jumpTrueOrFalse(conditionOp, slow)
    loadis 8[PB, PC, 8], t1
    loadConstantOrVariable(t1, t0)
    xorp ValueFalse, t0
    btpnz t0, -1, .slow
    conditionOp(t0, .target)
    dispatch(3)

.target:
    dispatchInt(16[PB, PC, 8])

.slow:
    callSlowPath(slow)
    dispatch(0)
end


macro equalNull(cellHandler, immediateHandler)
    loadis 8[PB, PC, 8], t0
    assertNotConstant(t0)
    loadp [cfr, t0, 8], t0
    btpnz t0, tagMask, .immediate
    loadp JSCell::m_structure[t0], t2
    cellHandler(Structure::m_typeInfo + TypeInfo::m_flags[t2], .target)
    dispatch(3)

.target:
    dispatch(16[PB, PC, 8])

.immediate:
    andp ~TagBitUndefined, t0
    immediateHandler(t0, .target)
    dispatch(3)
end

_llint_op_jeq_null:
    traceExecution()
    equalNull(
        macro (value, target) btbnz value, MasqueradesAsUndefined, target end,
        macro (value, target) bpeq value, ValueNull, target end)


_llint_op_jneq_null:
    traceExecution()
    equalNull(
        macro (value, target) btbz value, MasqueradesAsUndefined, target end,
        macro (value, target) bpneq value, ValueNull, target end)


_llint_op_jneq_ptr:
    traceExecution()
    loadis 8[PB, PC, 8], t0
    loadp 16[PB, PC, 8], t1
    bpneq t1, [cfr, t0, 8], .opJneqPtrTarget
    dispatch(4)

.opJneqPtrTarget:
    dispatchInt(24[PB, PC, 8])


macro compare(integerCompare, doubleCompare, slowPath)
    loadis 8[PB, PC, 8], t2
    loadis 16[PB, PC, 8], t3
    loadConstantOrVariable(t2, t0)
    loadConstantOrVariable(t3, t1)
    bpb t0, tagTypeNumber, .op1NotInt
    bpb t1, tagTypeNumber, .op2NotInt
    integerCompare(t0, t1, .jumpTarget)
    dispatch(4)

.op1NotInt:
    btpz t0, tagTypeNumber, .slow
    bpb t1, tagTypeNumber, .op1NotIntOp2NotInt
    ci2d t1, ft1
    jmp .op1NotIntReady
.op1NotIntOp2NotInt:
    btpz t1, tagTypeNumber, .slow
    addp tagTypeNumber, t1
    fp2d t1, ft1
.op1NotIntReady:
    addp tagTypeNumber, t0
    fp2d t0, ft0
    doubleCompare(ft0, ft1, .jumpTarget)
    dispatch(4)

.op2NotInt:
    ci2d t0, ft0
    btpz t1, tagTypeNumber, .slow
    addp tagTypeNumber, t1
    fp2d t1, ft1
    doubleCompare(ft0, ft1, .jumpTarget)
    dispatch(4)

.jumpTarget:
    dispatchInt(24[PB, PC, 8])

.slow:
    callSlowPath(slowPath)
    dispatch(0)
end


_llint_op_switch_imm:
    traceExecution()
    loadis 24[PB, PC, 8], t2
    loadis 8[PB, PC, 8], t3
    loadConstantOrVariable(t2, t1)
    loadp CodeBlock[cfr], t2
    loadp CodeBlock::m_rareData[t2], t2
    muli sizeof SimpleJumpTable, t3    # FIXME: would be nice to peephole this!
    loadp CodeBlock::RareData::m_immediateSwitchJumpTables + VectorBufferOffset[t2], t2
    addp t3, t2
    bpb t1, tagTypeNumber, .opSwitchImmNotInt
    subi SimpleJumpTable::min[t2], t1
    biaeq t1, SimpleJumpTable::branchOffsets + VectorSizeOffset[t2], .opSwitchImmFallThrough
    loadp SimpleJumpTable::branchOffsets + VectorBufferOffset[t2], t3
    loadis [t3, t1, 4], t1
    btiz t1, .opSwitchImmFallThrough
    dispatch(t1)

.opSwitchImmNotInt:
    btpnz t1, tagTypeNumber, .opSwitchImmSlow   # Go slow if it's a double.
.opSwitchImmFallThrough:
    dispatchInt(16[PB, PC, 8])

.opSwitchImmSlow:
    callSlowPath(_llint_slow_path_switch_imm)
    dispatch(0)


_llint_op_switch_char:
    traceExecution()
    loadis 24[PB, PC, 8], t2
    loadis 8[PB, PC, 8], t3
    loadConstantOrVariable(t2, t1)
    loadp CodeBlock[cfr], t2
    loadp CodeBlock::m_rareData[t2], t2
    muli sizeof SimpleJumpTable, t3
    loadp CodeBlock::RareData::m_characterSwitchJumpTables + VectorBufferOffset[t2], t2
    addp t3, t2
    btpnz t1, tagMask, .opSwitchCharFallThrough
    loadp JSCell::m_structure[t1], t0
    bbneq Structure::m_typeInfo + TypeInfo::m_type[t0], StringType, .opSwitchCharFallThrough
    bineq JSString::m_length[t1], 1, .opSwitchCharFallThrough
    loadp JSString::m_value[t1], t0
    btpz  t0, .opSwitchOnRope
    loadp StringImpl::m_data8[t0], t1
    btinz StringImpl::m_hashAndFlags[t0], HashFlags8BitBuffer, .opSwitchChar8Bit
    loadh [t1], t0
    jmp .opSwitchCharReady
.opSwitchChar8Bit:
    loadb [t1], t0
.opSwitchCharReady:
    subi SimpleJumpTable::min[t2], t0
    biaeq t0, SimpleJumpTable::branchOffsets + VectorSizeOffset[t2], .opSwitchCharFallThrough
    loadp SimpleJumpTable::branchOffsets + VectorBufferOffset[t2], t2
    loadis [t2, t0, 4], t1
    btiz t1, .opSwitchCharFallThrough
    dispatch(t1)

.opSwitchCharFallThrough:
    dispatchInt(16[PB, PC, 8])

.opSwitchOnRope:
    callSlowPath(_llint_slow_path_switch_char)
    dispatch(0)


_llint_op_new_func:
    traceExecution()
    btiz 24[PB, PC, 8], .opNewFuncUnchecked
    loadis 8[PB, PC, 8], t1
    btpnz [cfr, t1, 8], .opNewFuncDone
.opNewFuncUnchecked:
    callSlowPath(_llint_slow_path_new_func)
.opNewFuncDone:
    dispatch(4)


macro doCall(slowPath)
    loadis 8[PB, PC, 8], t0
    loadp 32[PB, PC, 8], t1
    loadp LLIntCallLinkInfo::callee[t1], t2
    loadConstantOrVariable(t0, t3)
    bpneq t3, t2, .opCallSlow
    loadis 24[PB, PC, 8], t3
    addi 6, PC
    lshifti 3, t3
    addp cfr, t3
    loadp JSFunction::m_scopeChain[t2], t0
    storep t2, Callee[t3]
    storep t0, ScopeChain[t3]
    loadis 16 - 48[PB, PC, 8], t2
    storei PC, ArgumentCount + TagOffset[cfr]
    storep cfr, CallerFrame[t3]
    storei t2, ArgumentCount + PayloadOffset[t3]
    move t3, cfr
    call LLIntCallLinkInfo::machineCodeTarget[t1]
    dispatchAfterCall()

.opCallSlow:
    slowPathForCall(6, slowPath)
end


_llint_op_tear_off_activation:
    traceExecution()
    loadis 8[PB, PC, 8], t0
    loadis 16[PB, PC, 8], t1
    btpnz [cfr, t0, 8], .opTearOffActivationCreated
    btpz [cfr, t1, 8], .opTearOffActivationNotCreated
.opTearOffActivationCreated:
    callSlowPath(_llint_slow_path_tear_off_activation)
.opTearOffActivationNotCreated:
    dispatch(3)


_llint_op_tear_off_arguments:
    traceExecution()
    loadis 8[PB, PC, 8], t0
    subi 1, t0   # Get the unmodifiedArgumentsRegister
    btpz [cfr, t0, 8], .opTearOffArgumentsNotCreated
    callSlowPath(_llint_slow_path_tear_off_arguments)
.opTearOffArgumentsNotCreated:
    dispatch(2)


_llint_op_ret:
    traceExecution()
    checkSwitchToJITForEpilogue()
    loadis 8[PB, PC, 8], t2
    loadConstantOrVariable(t2, t0)
    doReturn()


_llint_op_call_put_result:
    loadis 8[PB, PC, 8], t2
    loadp 16[PB, PC, 8], t3
    storep t0, [cfr, t2, 8]
    valueProfile(t0, t3)
    traceExecution()
    dispatch(3)


_llint_op_ret_object_or_this:
    traceExecution()
    checkSwitchToJITForEpilogue()
    loadis 8[PB, PC, 8], t2
    loadConstantOrVariable(t2, t0)
    btpnz t0, tagMask, .opRetObjectOrThisNotObject
    loadp JSCell::m_structure[t0], t2
    bbb Structure::m_typeInfo + TypeInfo::m_type[t2], ObjectType, .opRetObjectOrThisNotObject
    doReturn()

.opRetObjectOrThisNotObject:
    loadis 16[PB, PC, 8], t2
    loadConstantOrVariable(t2, t0)
    doReturn()


_llint_op_to_primitive:
    traceExecution()
    loadis 16[PB, PC, 8], t2
    loadis 8[PB, PC, 8], t3
    loadConstantOrVariable(t2, t0)
    btpnz t0, tagMask, .opToPrimitiveIsImm
    loadp JSCell::m_structure[t0], t2
    bbneq Structure::m_typeInfo + TypeInfo::m_type[t2], StringType, .opToPrimitiveSlowCase
.opToPrimitiveIsImm:
    storep t0, [cfr, t3, 8]
    dispatch(3)

.opToPrimitiveSlowCase:
    callSlowPath(_llint_slow_path_to_primitive)
    dispatch(3)


_llint_op_next_pname:
    traceExecution()
    loadis 24[PB, PC, 8], t1
    loadis 32[PB, PC, 8], t2
    assertNotConstant(t1)
    assertNotConstant(t2)
    loadi PayloadOffset[cfr, t1, 8], t0
    bieq t0, PayloadOffset[cfr, t2, 8], .opNextPnameEnd
    loadis 40[PB, PC, 8], t2
    assertNotConstant(t2)
    loadp [cfr, t2, 8], t2
    loadp JSPropertyNameIterator::m_jsStrings[t2], t3
    loadp [t3, t0, 8], t3
    addi 1, t0
    storei t0, PayloadOffset[cfr, t1, 8]
    loadis 8[PB, PC, 8], t1
    storep t3, [cfr, t1, 8]
    loadis 16[PB, PC, 8], t3
    assertNotConstant(t3)
    loadp [cfr, t3, 8], t3
    loadp JSCell::m_structure[t3], t1
    bpneq t1, JSPropertyNameIterator::m_cachedStructure[t2], .opNextPnameSlow
    loadp JSPropertyNameIterator::m_cachedPrototypeChain[t2], t0
    loadp StructureChain::m_vector[t0], t0
    btpz [t0], .opNextPnameTarget
.opNextPnameCheckPrototypeLoop:
    bpeq Structure::m_prototype[t1], ValueNull, .opNextPnameSlow
    loadp Structure::m_prototype[t1], t2
    loadp JSCell::m_structure[t2], t1
    bpneq t1, [t0], .opNextPnameSlow
    addp 8, t0
    btpnz [t0], .opNextPnameCheckPrototypeLoop
.opNextPnameTarget:
    dispatchInt(48[PB, PC, 8])

.opNextPnameEnd:
    dispatch(7)

.opNextPnameSlow:
    callSlowPath(_llint_slow_path_next_pname) # This either keeps the PC where it was (causing us to loop) or sets it to target.
    dispatch(0)


_llint_op_catch:
    # This is where we end up from the JIT's throw trampoline (because the
    # machine code return address will be set to _llint_op_catch), and from
    # the interpreter's throw trampoline (see _llint_throw_trampoline).
    # The JIT throwing protocol calls for the cfr to be in t0. The throwing
    # code must have known that we were throwing to the interpreter, and have
    # set JSGlobalData::targetInterpreterPCForThrow.
    move t0, cfr
    loadp CodeBlock[cfr], PB
    loadp CodeBlock::m_instructions[PB], PB
    loadp JITStackFrame::globalData[sp], t3
    loadp JSGlobalData::targetInterpreterPCForThrow[t3], PC
    subp PB, PC
    urshiftp 3, PC
    loadp JSGlobalData::exception[t3], t0
    storep 0, JSGlobalData::exception[t3]
    loadis 8[PB, PC, 8], t2
    storep t0, [cfr, t2, 8]
    traceExecution()
    dispatch(2)


_llint_op_end:
    traceExecution()
    checkSwitchToJITForEpilogue()
    loadis 8[PB, PC, 8], t0
    assertNotConstant(t0)
    loadp [cfr, t0, 8], t0
    doReturn()


_llint_throw_from_slow_path_trampoline:
    # When throwing from the interpreter (i.e. throwing from LLIntSlowPaths), so
    # the throw target is not necessarily interpreted code, we come to here.
    # This essentially emulates the JIT's throwing protocol.
    loadp JITStackFrame::globalData[sp], t1
    loadp JSGlobalData::callFrameForThrow[t1], t0
    jmp JSGlobalData::targetMachinePCForThrow[t1]


_llint_throw_during_call_trampoline:
    preserveReturnAddressAfterCall(t2)
    loadp JITStackFrame::globalData[sp], t1
    loadp JSGlobalData::callFrameForThrow[t1], t0
    jmp JSGlobalData::targetMachinePCForThrow[t1]


macro nativeCallTrampoline(executableOffsetToFunction)
    storep 0, CodeBlock[cfr]
    loadp JITStackFrame::globalData + 8[sp], t0
    storep cfr, JSGlobalData::topCallFrame[t0]
    loadp CallerFrame[cfr], t0
    loadp ScopeChain[t0], t1
    storep t1, ScopeChain[cfr]
    peek 0, t1
    storep t1, ReturnPC[cfr]
    move cfr, t5  # t5 = rdi
    subp 16 - 8, sp
    loadp Callee[cfr], t4 # t4 = rsi
    loadp JSFunction::m_executable[t4], t1
    move t0, cfr # Restore cfr to avoid loading from stack
    call executableOffsetToFunction[t1]
    addp 16 - 8, sp
    loadp JITStackFrame::globalData + 8[sp], t3
    btpnz JSGlobalData::exception[t3], .exception
    ret
.exception:
    preserveReturnAddressAfterCall(t1)
    loadi ArgumentCount + TagOffset[cfr], PC
    loadp CodeBlock[cfr], PB
    loadp CodeBlock::m_instructions[PB], PB
    loadp JITStackFrame::globalData[sp], t0
    storep cfr, JSGlobalData::topCallFrame[t0]
    callSlowPath(_llint_throw_from_native_call)
    jmp _llint_throw_from_slow_path_trampoline
end