Changeset 252800 in webkit


Ignore:
Timestamp:
Nov 22, 2019 2:13:28 PM (4 years ago)
Author:
Tadeu Zagallo
Message:

[WebAssembly] Improve Wasm::LLIntGenerator
https://bugs.webkit.org/show_bug.cgi?id=204092

Reviewed by Saam Barati.

JSTests:

  • wasm/stress/set-local-enclosed-stack.js: Added.

(assert.eq.instance.exports.foo):
(assert.eq):

Source/JavaScriptCore:

This improves the Wasm::LLIntGenerator by:

  • Changing LLIntGenerator::ExpressionType from RefPtr<RegisterID> to VirtualRegister: Instead of allocating and retaining RegisterIDs we use VirtualRegisters directly and ensure that they match the WebAssembly stack, i.e. the parser's expression stack should match the virtual registers.
  • Removing redundant moves when materializing constants and performing local.get: instead of creating a new temporary for each constant and local.get, we return the VirtualRegister for the constant/local slot directly. In order for this to work, we still allocate the stack slot for the temporaries, since we have to materialize them before loops and branches.
  • Adding a constructor to ControlType that takes the results ExpressionList as an rvalue instead of copying it
  • Optimizing callInformationFor, which is now split into two functions. The callee does not care about arguments, and should never allocate temporaries, and the caller case was optimized by avoiding unnecessary calls to newTemporary
  • Delay holding the lock in LLintPlan::compileFunction, since we do not need to hold it while compiling the js-to-wasm entrypoint
  • bytecode/BytecodeList.rb:
  • bytecompiler/Label.h:

(JSC::GenericLabel::location const):
(JSC::GenericLabel::unresolvedJumps const):

  • generator/Wasm.rb:
  • llint/WebAssembly.asm:
  • wasm/WasmAirIRGenerator.cpp:

(JSC::Wasm::AirIRGenerator::endTopLevel):
(JSC::Wasm::AirIRGenerator::didPopValueFromStack):

  • wasm/WasmB3IRGenerator.cpp:

(JSC::Wasm::B3IRGenerator::endTopLevel):
(JSC::Wasm::B3IRGenerator::didPopValueFromStack):

  • wasm/WasmFunctionCodeBlock.cpp:

(JSC::Wasm::FunctionCodeBlock::addJumpTable):

  • wasm/WasmFunctionCodeBlock.h:
  • wasm/WasmFunctionParser.h:

(JSC::Wasm::FunctionParser::expressionStack):
(JSC::Wasm::FunctionParser<Context>::parseBody):
(JSC::Wasm::FunctionParser<Context>::parseExpression):

  • wasm/WasmLLIntGenerator.cpp:

(JSC::Wasm::LLIntGenerator::ControlType::loop):
(JSC::Wasm::LLIntGenerator::ControlType::topLevel):
(JSC::Wasm::LLIntGenerator::ControlType::block):
(JSC::Wasm::LLIntGenerator::ControlType::if_):
(JSC::Wasm::LLIntGenerator::ControlType::targetArity const):
(JSC::Wasm::LLIntGenerator::ControlType::stackSize const):
(JSC::Wasm::LLIntGenerator::ControlType::ControlType):
(JSC::Wasm::LLIntGenerator::unifyValuesWithBlock):
(JSC::Wasm::LLIntGenerator::push):
(JSC::Wasm::LLIntGenerator::didPopValueFromStack):
(JSC::Wasm::LLIntGenerator::emptyExpression):
(JSC::Wasm::LLIntGenerator::addEndToUnreachable):
(JSC::Wasm::LLIntGenerator::dump):
(JSC::Wasm::LLIntGenerator::virtualRegisterForWasmLocal):
(JSC::Wasm::LLIntGenerator::jsNullConstant):
(JSC::Wasm::LLIntGenerator::zeroConstant):
(JSC::Wasm::LLIntGenerator::getDropKeepCount):
(JSC::Wasm::LLIntGenerator::dropKeep):
(JSC::Wasm::LLIntGenerator::walkExpressionStack):
(JSC::Wasm::LLIntGenerator::checkConsistency):
(JSC::Wasm::LLIntGenerator::materializeConstantsAndLocals):
(JSC::Wasm::LLIntGenerator::materializeLocals):
(JSC::Wasm::LLIntGenerator::ConstantMapHashTraits::constructDeletedValue):
(JSC::Wasm::LLIntGenerator::ConstantMapHashTraits::isDeletedValue):
(JSC::Wasm::LLIntGenerator::LLIntGenerator):
(JSC::Wasm::LLIntGenerator::finalize):
(JSC::Wasm::LLIntGenerator::callInformationForCaller):
(JSC::Wasm::LLIntGenerator::callInformationForCallee):
(JSC::Wasm::LLIntGenerator::addArguments):
(JSC::Wasm::LLIntGenerator::addLocal):
(JSC::Wasm::LLIntGenerator::didFinishParsingLocals):
(JSC::Wasm::LLIntGenerator::addConstant):
(JSC::Wasm::LLIntGenerator::getLocal):
(JSC::Wasm::LLIntGenerator::setLocal):
(JSC::Wasm::LLIntGenerator::getGlobal):
(JSC::Wasm::LLIntGenerator::addLoop):
(JSC::Wasm::LLIntGenerator::addTopLevel):
(JSC::Wasm::LLIntGenerator::addBlock):
(JSC::Wasm::LLIntGenerator::addIf):
(JSC::Wasm::LLIntGenerator::addElse):
(JSC::Wasm::LLIntGenerator::addElseToUnreachable):
(JSC::Wasm::LLIntGenerator::addReturn):
(JSC::Wasm::LLIntGenerator::addBranch):
(JSC::Wasm::LLIntGenerator::addSwitch):
(JSC::Wasm::LLIntGenerator::endBlock):
(JSC::Wasm::LLIntGenerator::endTopLevel):
(JSC::Wasm::LLIntGenerator::addCall):
(JSC::Wasm::LLIntGenerator::addCallIndirect):
(JSC::Wasm::LLIntGenerator::addRefIsNull):
(JSC::Wasm::LLIntGenerator::addRefFunc):
(JSC::Wasm::LLIntGenerator::addTableGet):
(JSC::Wasm::LLIntGenerator::addTableSize):
(JSC::Wasm::LLIntGenerator::addTableGrow):
(JSC::Wasm::LLIntGenerator::addCurrentMemory):
(JSC::Wasm::LLIntGenerator::addGrowMemory):
(JSC::Wasm::LLIntGenerator::addSelect):
(JSC::Wasm::LLIntGenerator::load):
(JSC::GenericLabel<Wasm::GeneratorTraits>::setLocation):

  • wasm/WasmLLIntPlan.cpp:

(JSC::Wasm::LLIntPlan::compileFunction):

  • wasm/WasmValidate.cpp:

(JSC::Wasm::Validate::endTopLevel):
(JSC::Wasm::Validate::didPopValueFromStack):

Location:
trunk
Files:
1 added
14 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r252789 r252800  
     12019-11-22  Tadeu Zagallo  <tzagallo@apple.com>
     2
     3        [WebAssembly] Improve Wasm::LLIntGenerator
     4        https://bugs.webkit.org/show_bug.cgi?id=204092
     5
     6        Reviewed by Saam Barati.
     7
     8        * wasm/stress/set-local-enclosed-stack.js: Added.
     9        (assert.eq.instance.exports.foo):
     10        (assert.eq):
     11
    1122019-11-22  Yusuke Suzuki  <ysuzuki@apple.com>
    213
  • trunk/Source/JavaScriptCore/ChangeLog

    r252789 r252800  
     12019-11-22  Tadeu Zagallo  <tzagallo@apple.com>
     2
     3        [WebAssembly] Improve Wasm::LLIntGenerator
     4        https://bugs.webkit.org/show_bug.cgi?id=204092
     5
     6        Reviewed by Saam Barati.
     7
     8        This improves the Wasm::LLIntGenerator by:
     9        - Changing LLIntGenerator::ExpressionType from RefPtr<RegisterID> to VirtualRegister: Instead of allocating and retaining
     10          RegisterIDs we use VirtualRegisters directly and ensure that they match the WebAssembly stack, i.e. the parser's expression
     11          stack should match the virtual registers.
     12        - Removing redundant moves when materializing constants and performing local.get: instead of creating a new temporary
     13          for each constant and local.get, we return the VirtualRegister for the constant/local slot directly. In order for this
     14          to work, we still allocate the stack slot for the temporaries, since we have to materialize them before loops and branches.
     15        - Adding a constructor to ControlType that takes the results ExpressionList as an rvalue instead of copying it
     16        - Optimizing callInformationFor, which is now split into two functions. The callee does not care about arguments, and should
     17          never allocate temporaries, and the caller case was optimized by avoiding unnecessary calls to newTemporary
     18        - Delay holding the lock in LLintPlan::compileFunction, since we do not need to hold it while compiling the js-to-wasm entrypoint
     19
     20        * bytecode/BytecodeList.rb:
     21        * bytecompiler/Label.h:
     22        (JSC::GenericLabel::location const):
     23        (JSC::GenericLabel::unresolvedJumps const):
     24        * generator/Wasm.rb:
     25        * llint/WebAssembly.asm:
     26        * wasm/WasmAirIRGenerator.cpp:
     27        (JSC::Wasm::AirIRGenerator::endTopLevel):
     28        (JSC::Wasm::AirIRGenerator::didPopValueFromStack):
     29        * wasm/WasmB3IRGenerator.cpp:
     30        (JSC::Wasm::B3IRGenerator::endTopLevel):
     31        (JSC::Wasm::B3IRGenerator::didPopValueFromStack):
     32        * wasm/WasmFunctionCodeBlock.cpp:
     33        (JSC::Wasm::FunctionCodeBlock::addJumpTable):
     34        * wasm/WasmFunctionCodeBlock.h:
     35        * wasm/WasmFunctionParser.h:
     36        (JSC::Wasm::FunctionParser::expressionStack):
     37        (JSC::Wasm::FunctionParser<Context>::parseBody):
     38        (JSC::Wasm::FunctionParser<Context>::parseExpression):
     39        * wasm/WasmLLIntGenerator.cpp:
     40        (JSC::Wasm::LLIntGenerator::ControlType::loop):
     41        (JSC::Wasm::LLIntGenerator::ControlType::topLevel):
     42        (JSC::Wasm::LLIntGenerator::ControlType::block):
     43        (JSC::Wasm::LLIntGenerator::ControlType::if_):
     44        (JSC::Wasm::LLIntGenerator::ControlType::targetArity const):
     45        (JSC::Wasm::LLIntGenerator::ControlType::stackSize const):
     46        (JSC::Wasm::LLIntGenerator::ControlType::ControlType):
     47        (JSC::Wasm::LLIntGenerator::unifyValuesWithBlock):
     48        (JSC::Wasm::LLIntGenerator::push):
     49        (JSC::Wasm::LLIntGenerator::didPopValueFromStack):
     50        (JSC::Wasm::LLIntGenerator::emptyExpression):
     51        (JSC::Wasm::LLIntGenerator::addEndToUnreachable):
     52        (JSC::Wasm::LLIntGenerator::dump):
     53        (JSC::Wasm::LLIntGenerator::virtualRegisterForWasmLocal):
     54        (JSC::Wasm::LLIntGenerator::jsNullConstant):
     55        (JSC::Wasm::LLIntGenerator::zeroConstant):
     56        (JSC::Wasm::LLIntGenerator::getDropKeepCount):
     57        (JSC::Wasm::LLIntGenerator::dropKeep):
     58        (JSC::Wasm::LLIntGenerator::walkExpressionStack):
     59        (JSC::Wasm::LLIntGenerator::checkConsistency):
     60        (JSC::Wasm::LLIntGenerator::materializeConstantsAndLocals):
     61        (JSC::Wasm::LLIntGenerator::materializeLocals):
     62        (JSC::Wasm::LLIntGenerator::ConstantMapHashTraits::constructDeletedValue):
     63        (JSC::Wasm::LLIntGenerator::ConstantMapHashTraits::isDeletedValue):
     64        (JSC::Wasm::LLIntGenerator::LLIntGenerator):
     65        (JSC::Wasm::LLIntGenerator::finalize):
     66        (JSC::Wasm::LLIntGenerator::callInformationForCaller):
     67        (JSC::Wasm::LLIntGenerator::callInformationForCallee):
     68        (JSC::Wasm::LLIntGenerator::addArguments):
     69        (JSC::Wasm::LLIntGenerator::addLocal):
     70        (JSC::Wasm::LLIntGenerator::didFinishParsingLocals):
     71        (JSC::Wasm::LLIntGenerator::addConstant):
     72        (JSC::Wasm::LLIntGenerator::getLocal):
     73        (JSC::Wasm::LLIntGenerator::setLocal):
     74        (JSC::Wasm::LLIntGenerator::getGlobal):
     75        (JSC::Wasm::LLIntGenerator::addLoop):
     76        (JSC::Wasm::LLIntGenerator::addTopLevel):
     77        (JSC::Wasm::LLIntGenerator::addBlock):
     78        (JSC::Wasm::LLIntGenerator::addIf):
     79        (JSC::Wasm::LLIntGenerator::addElse):
     80        (JSC::Wasm::LLIntGenerator::addElseToUnreachable):
     81        (JSC::Wasm::LLIntGenerator::addReturn):
     82        (JSC::Wasm::LLIntGenerator::addBranch):
     83        (JSC::Wasm::LLIntGenerator::addSwitch):
     84        (JSC::Wasm::LLIntGenerator::endBlock):
     85        (JSC::Wasm::LLIntGenerator::endTopLevel):
     86        (JSC::Wasm::LLIntGenerator::addCall):
     87        (JSC::Wasm::LLIntGenerator::addCallIndirect):
     88        (JSC::Wasm::LLIntGenerator::addRefIsNull):
     89        (JSC::Wasm::LLIntGenerator::addRefFunc):
     90        (JSC::Wasm::LLIntGenerator::addTableGet):
     91        (JSC::Wasm::LLIntGenerator::addTableSize):
     92        (JSC::Wasm::LLIntGenerator::addTableGrow):
     93        (JSC::Wasm::LLIntGenerator::addCurrentMemory):
     94        (JSC::Wasm::LLIntGenerator::addGrowMemory):
     95        (JSC::Wasm::LLIntGenerator::addSelect):
     96        (JSC::Wasm::LLIntGenerator::load):
     97        (JSC::GenericLabel<Wasm::GeneratorTraits>::setLocation):
     98        * wasm/WasmLLIntPlan.cpp:
     99        (JSC::Wasm::LLIntPlan::compileFunction):
     100        * wasm/WasmValidate.cpp:
     101        (JSC::Wasm::Validate::endTopLevel):
     102        (JSC::Wasm::Validate::didPopValueFromStack):
     103
    11042019-11-22  Yusuke Suzuki  <ysuzuki@apple.com>
    2105
  • trunk/Source/JavaScriptCore/bytecode/BytecodeList.rb

    r252680 r252800  
    13001300        scrutinee: VirtualRegister,
    13011301        tableIndex: unsigned,
    1302         defaultTarget: WasmBoundLabel,
    13031302    }
    13041303
     
    13071306op :unreachable
    13081307op :ret_void
     1308
     1309op :drop_keep,
     1310    args: {
     1311        startOffset: unsigned,
     1312        dropCount: unsigned,
     1313        keepCount: unsigned,
     1314    }
    13091315
    13101316op :ref_is_null,
  • trunk/Source/JavaScriptCore/bytecompiler/Label.h

    r251886 r252800  
    129129        using BytecodeGenerator = BytecodeGeneratorBase<Traits>;
    130130        using BoundLabel = GenericBoundLabel<Traits>;
     131        using JumpVector = Vector<int, 8>;
    131132
    132133    public:
     
    171172        bool isBound() const { return m_bound; }
    172173
     174        unsigned location() const
     175        {
     176            ASSERT(!isForward());
     177            m_bound = true;
     178            return m_location;
     179        };
     180
     181        const JumpVector& unresolvedJumps() const { return  m_unresolvedJumps; }
     182
    173183    private:
    174184        friend BoundLabel;
    175 
    176         typedef Vector<int, 8> JumpVector;
    177185
    178186        static constexpr unsigned invalidLocation = UINT_MAX;
  • trunk/Source/JavaScriptCore/generator/Wasm.rb

    r252231 r252800  
    5959auto LLIntGenerator::addOp<#{op_type(op)}>(ExpressionType lhs, ExpressionType rhs, ExpressionType& result) -> PartialResult
    6060{
    61     result = lhs;
     61    result = push();
    6262    #{op.capitalized_name}::emit(this, result, lhs, rhs);
    6363    return { };
     
    7171auto LLIntGenerator::addOp<#{op_type(op)}>(ExpressionType operand, ExpressionType& result) -> PartialResult
    7272{
    73     result = operand;
     73    result = push();
    7474    #{op.capitalized_name}::emit(this, result, operand);
    7575    return { };
  • trunk/Source/JavaScriptCore/llint/WebAssembly.asm

    r252231 r252800  
    298298    negi ws1
    299299    sxi2q ws1, ws1
    300     leap (NumberOfWasmArguments + CalleeSaveSpaceAsVirtualRegisters) * -8[cfr], ws0
     300    leap (NumberOfWasmArguments + CalleeSaveSpaceAsVirtualRegisters + 1) * -8[cfr], ws0
    301301.zeroInitializeLocalsLoop:
    302302    addq 1, ws1
     
    589589
    590590    loadi VectorSizeOffset[t2], t3
    591     biaeq t0, t3, .default
     591    bib t0, t3, .inBounds
     592
     593.outOfBounds:
     594    subi t3, 1, t0
     595
     596.inBounds:
    592597    loadp VectorBufferOffset[t2], t2
    593     loadi [t2, t0, 4], t3
     598    muli sizeof Wasm::FunctionCodeBlock::JumpTableEntry, t0
     599
     600    loadi Wasm::FunctionCodeBlock::JumpTableEntry::startOffset[t2, t0], t1
     601    loadi Wasm::FunctionCodeBlock::JumpTableEntry::dropCount[t2, t0], t3
     602    loadi Wasm::FunctionCodeBlock::JumpTableEntry::keepCount[t2, t0], t5
     603    dropKeep(t1, t3, t5)
     604
     605    loadis Wasm::FunctionCodeBlock::JumpTableEntry::target[t2, t0], t3
    594606    assert(macro(ok) btinz t3, .ok end)
    595607    wasmDispatchIndirect(t3)
    596 
    597 .default:
    598     jump(ctx, m_defaultTarget)
    599608end)
    600609
     
    19942003    returnq(ctx, t0)
    19952004end)
     2005
     2006macro dropKeep(startOffset, drop, keep)
     2007    lshifti 3, startOffset
     2008    subp cfr, startOffset, startOffset
     2009    negi drop
     2010    sxi2q drop, drop
     2011
     2012.copyLoop:
     2013    btiz keep, .done
     2014    loadq [startOffset, drop, 8], t6
     2015    storeq t6, [startOffset]
     2016    subi 1, keep
     2017    subp 8, startOffset
     2018    jmp .copyLoop
     2019
     2020.done:
     2021end
     2022
     2023wasmOp(drop_keep, WasmDropKeep, macro(ctx)
     2024    wgetu(ctx, m_startOffset, t0)
     2025    wgetu(ctx, m_dropCount, t1)
     2026    wgetu(ctx, m_keepCount, t2)
     2027
     2028    dropKeep(t0, t1, t2)
     2029
     2030    dispatch(ctx)
     2031end)
  • trunk/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp

    r251978 r252800  
    286286    PartialResult WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&, const Stack& expressionStack = { });
    287287
     288    PartialResult WARN_UNUSED_RETURN endTopLevel(BlockSignature, const Stack&) { return { }; }
     289
    288290    // Calls
    289291    PartialResult WARN_UNUSED_RETURN addCall(uint32_t calleeIndex, const Signature&, Vector<ExpressionType>& args, Vector<ExpressionType, 1>& results);
     
    300302    void setParser(FunctionParser<AirIRGenerator>* parser) { m_parser = parser; };
    301303    void didFinishParsingLocals() { }
     304    void didPopValueFromStack() { }
    302305
    303306    static Vector<Tmp> toTmpVector(const Vector<TypedTmp>& vector)
  • trunk/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp

    r252306 r252800  
    236236    PartialResult WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&, const Stack& = { });
    237237
     238    PartialResult WARN_UNUSED_RETURN endTopLevel(BlockSignature, const Stack&) { return { }; }
     239
    238240    // Calls
    239241    PartialResult WARN_UNUSED_RETURN addCall(uint32_t calleeIndex, const Signature&, Vector<ExpressionType>& args, ResultList& results);
     
    245247    void setParser(FunctionParser<B3IRGenerator>* parser) { m_parser = parser; };
    246248    void didFinishParsingLocals() { }
     249    void didPopValueFromStack() { }
    247250
    248251    Value* constant(B3::Type, uint64_t bits, Optional<Origin> = WTF::nullopt);
  • trunk/Source/JavaScriptCore/wasm/WasmFunctionCodeBlock.cpp

    r252310 r252800  
    7676auto FunctionCodeBlock::addJumpTable(size_t numberOfEntries) -> JumpTable&
    7777{
    78 #if !ASSERT_DISABLED
    79     m_jumpTables.append(JumpTable(numberOfEntries, 0));
    80 #else
    8178    m_jumpTables.append(JumpTable(numberOfEntries));
    82 #endif
    8379    return m_jumpTables.last();
    8480}
  • trunk/Source/JavaScriptCore/wasm/WasmFunctionCodeBlock.h

    r252310 r252800  
    106106    const Signature& signature(unsigned index) const;
    107107
    108     using JumpTable = Vector<InstructionStream::Offset>;
     108    struct JumpTableEntry {
     109        int target { 0 };
     110        unsigned startOffset;
     111        unsigned dropCount;
     112        unsigned keepCount;
     113    };
     114
     115    using JumpTable = Vector<JumpTableEntry>;
    109116    JumpTable& addJumpTable(size_t numberOfEntries);
    110117    const JumpTable& jumpTable(unsigned tableIndex) const;
  • trunk/Source/JavaScriptCore/wasm/WasmFunctionParser.h

    r251978 r252800  
    7878
    7979    Vector<ControlEntry>& controlStack() { return m_controlStack; }
     80    Stack& expressionStack() { return m_expressionStack; }
    8081
    8182private:
     
    9091        WASM_PARSER_FAIL_IF(m_expressionStack.isEmpty(), "can't pop empty stack in " what); \
    9192        result = m_expressionStack.takeLast();                                              \
     93        m_context.didPopValueFromStack();                                                   \
    9294    } while (0)
    9395
     
    177179        }
    178180    }
     181    WASM_FAIL_IF_HELPER_FAILS(m_context.endTopLevel(&m_signature, m_expressionStack));
    179182
    180183    ASSERT(op == OpType::End);
     
    439442        Vector<ExpressionType> args;
    440443        WASM_PARSER_FAIL_IF(!args.tryReserveCapacity(calleeSignature.argumentCount()), "can't allocate enough memory for call's ", calleeSignature.argumentCount(), " arguments");
    441         for (size_t i = firstArgumentIndex; i < m_expressionStack.size(); ++i)
     444        for (size_t i = firstArgumentIndex; i < m_expressionStack.size(); ++i) {
    442445            args.uncheckedAppend(m_expressionStack.at(i));
     446            m_context.didPopValueFromStack();
     447        }
    443448        m_expressionStack.shrink(firstArgumentIndex);
    444449
     
    468473        WASM_PARSER_FAIL_IF(!args.tryReserveCapacity(argumentCount), "can't allocate enough memory for ", argumentCount, " call_indirect arguments");
    469474        size_t firstArgumentIndex = m_expressionStack.size() - argumentCount;
    470         for (size_t i = firstArgumentIndex; i < m_expressionStack.size(); ++i)
     475        for (size_t i = firstArgumentIndex; i < m_expressionStack.size(); ++i) {
    471476            args.uncheckedAppend(m_expressionStack.at(i));
     477            m_context.didPopValueFromStack();
     478        }
    472479        m_expressionStack.shrink(firstArgumentIndex);
    473480
     
    609616        WASM_PARSER_FAIL_IF(!m_expressionStack.size(), "can't drop on empty stack");
    610617        m_expressionStack.takeLast();
     618        m_context.didPopValueFromStack();
    611619        return { };
    612620    }
  • trunk/Source/JavaScriptCore/wasm/WasmLLIntGenerator.cpp

    r252310 r252800  
    3333#include "InstructionStream.h"
    3434#include "Label.h"
    35 #include "RegisterID.h"
    3635#include "WasmCallingConvention.h"
    3736#include "WasmContextInlines.h"
     
    4039#include "WasmGeneratorTraits.h"
    4140#include "WasmThunks.h"
     41#include <wtf/CompletionHandler.h>
    4242#include <wtf/RefPtr.h>
    4343#include <wtf/StdUnorderedMap.h>
     
    4848class LLIntGenerator : public BytecodeGeneratorBase<GeneratorTraits> {
    4949public:
    50     using ExpressionType = RefPtr<RegisterID>;
     50    using ExpressionType = VirtualRegister;
    5151    using ExpressionList = Vector<ExpressionType, 1>;
    52     using Stack = ExpressionList;
     52    using Stack = Vector<ExpressionType, 16, UnsafeVectorOverflow>;
    5353
    5454    struct ControlLoop  {
     
    7474        }
    7575
    76         static ControlType loop(BlockSignature signature, const ExpressionList& results, Ref<Label> body, RefPtr<Label> continuation)
     76        static ControlType loop(BlockSignature signature, unsigned stackSize, Ref<Label>&& body, RefPtr<Label>&& continuation)
    7777        {
    78             return ControlType(signature, results, WTFMove(continuation), ControlLoop { WTFMove(body) });
    79         }
    80 
    81         static ControlType topLevel(BlockSignature signature, const ExpressionList& results, RefPtr<Label> continuation)
     78            return ControlType(signature, stackSize - signature->argumentCount(), WTFMove(continuation), ControlLoop { WTFMove(body) });
     79        }
     80
     81        static ControlType topLevel(BlockSignature signature, unsigned stackSize, RefPtr<Label>&& continuation)
    8282        {
    83             return ControlType(signature, results, WTFMove(continuation), ControlTopLevel { });
    84         }
    85 
    86         static ControlType block(BlockSignature signature, const ExpressionList& results, RefPtr<Label> continuation)
     83            return ControlType(signature, stackSize, WTFMove(continuation), ControlTopLevel { });
     84        }
     85
     86        static ControlType block(BlockSignature signature, unsigned stackSize, RefPtr<Label>&& continuation)
    8787        {
    88             return ControlType(signature, results, WTFMove(continuation), ControlBlock { });
    89         }
    90 
    91         static ControlType if_(BlockSignature signature, const ExpressionList& results, Ref<Label> alternate, RefPtr<Label> continuation)
     88            return ControlType(signature, stackSize - signature->argumentCount(), WTFMove(continuation), ControlBlock { });
     89        }
     90
     91        static ControlType if_(BlockSignature signature, unsigned stackSize, Ref<Label>&& alternate, RefPtr<Label>&& continuation)
    9292        {
    93             return ControlType(signature, results, WTFMove(continuation), ControlIf { WTFMove(alternate) });
     93            return ControlType(signature, stackSize - signature->argumentCount(), WTFMove(continuation), ControlIf { WTFMove(alternate) });
    9494        }
    9595
     
    101101        }
    102102
     103        unsigned targetArity() const
     104        {
     105            if (WTF::holds_alternative<ControlLoop>(*this))
     106                return m_signature->argumentCount();
     107            return m_signature->returnCount();
     108        }
     109
     110        unsigned stackSize() const { return m_stackSize; }
     111
    103112        BlockSignature m_signature;
    104         ExpressionList m_results;
     113        unsigned m_stackSize;
    105114        RefPtr<Label> m_continuation;
    106115
    107116    private:
    108117        template<typename T>
    109         ControlType(BlockSignature signature, const ExpressionList& results, RefPtr<Label> continuation, T t)
     118        ControlType(BlockSignature signature, unsigned stackSize, RefPtr<Label>&& continuation, T&& t)
    110119            : Base(WTFMove(t))
    111120            , m_signature(signature)
    112             , m_results(results)
     121            , m_stackSize(stackSize)
    113122            , m_continuation(WTFMove(continuation))
    114123        {
     
    138147    {
    139148        ASSERT(destinations.size() <= values.size());
     149        auto offset = values.size() - destinations.size();
    140150        for (size_t i = 0; i < destinations.size(); ++i)
    141             WasmMov::emit(this, destinations[destinations.size() - i - 1], values[values.size() - i - 1]);
    142     }
    143 
    144 
    145     static ExpressionType emptyExpression() { return nullptr; };
     151            WasmMov::emit(this, destinations[i], values[offset + i]);
     152    }
     153
     154    enum NoConsistencyCheckTag { NoConsistencyCheck };
     155    ExpressionType push(NoConsistencyCheckTag)
     156    {
     157        m_maxStackSize = std::max(m_maxStackSize, ++m_stackSize);
     158        return virtualRegisterForLocal(m_stackSize - 1);
     159    }
     160
     161    ExpressionType push()
     162    {
     163        checkConsistency();
     164        return push(NoConsistencyCheck);
     165    }
     166
     167    void didPopValueFromStack() { --m_stackSize; }
     168
     169    static ExpressionType emptyExpression() { return VirtualRegister { }; };
    146170    Stack createStack() { return Stack(); }
    147171    bool isControlTypeIf(const ControlType& control) { return WTF::holds_alternative<ControlIf>(control); }
     
    149173    PartialResult WARN_UNUSED_RETURN addArguments(const Signature&);
    150174    PartialResult WARN_UNUSED_RETURN addLocal(Type, uint32_t);
    151     ExpressionType addConstant(Type, uint64_t);
     175    ExpressionType addConstant(Type, int64_t);
    152176
    153177    // References
     
    188212    PartialResult WARN_UNUSED_RETURN addLoop(BlockSignature, Stack& enclosingStack, ControlType& block, Stack& newStack, uint32_t loopIndex);
    189213    PartialResult WARN_UNUSED_RETURN addIf(ExpressionType condition, BlockSignature, Stack& enclosingStack, ControlType& result, Stack& newStack);
    190     PartialResult WARN_UNUSED_RETURN addElse(ControlType&, const ExpressionList&);
     214    PartialResult WARN_UNUSED_RETURN addElse(ControlType&, Stack&);
    191215    PartialResult WARN_UNUSED_RETURN addElseToUnreachable(ControlType&);
    192216
    193     PartialResult WARN_UNUSED_RETURN addReturn(const ControlType&, const ExpressionList& returnValues);
    194     PartialResult WARN_UNUSED_RETURN addBranch(ControlType&, ExpressionType condition, const ExpressionList& returnValues);
    195     PartialResult WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlType*>& targets, ControlType& defaultTargets, const ExpressionList& expressionStack);
    196     PartialResult WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack);
    197     PartialResult WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&, const Stack& expressionStack = { });
     217    PartialResult WARN_UNUSED_RETURN addReturn(const ControlType&, Stack& returnValues);
     218    PartialResult WARN_UNUSED_RETURN addBranch(ControlType&, ExpressionType condition, Stack& returnValues);
     219    PartialResult WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlType*>& targets, ControlType& defaultTargets, Stack& expressionStack);
     220    PartialResult WARN_UNUSED_RETURN endBlock(ControlEntry&, Stack& expressionStack);
     221    PartialResult WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&, const Stack& expressionStack = { }, bool unreachable = true);
     222    PartialResult WARN_UNUSED_RETURN endTopLevel(BlockSignature, const Stack&);
    198223
    199224    // Calls
     
    206231    void setParser(FunctionParser<LLIntGenerator>* parser) { m_parser = parser; };
    207232
    208     void dump(const Vector<ControlEntry>&, const ExpressionList*) { }
     233    void dump(const Vector<ControlEntry>&, const Stack*) { }
    209234
    210235private:
     
    215240        unsigned numberOfStackArguments;
    216241        ExpressionList arguments;
    217         ExpressionList results;
     242        CompletionHandler<void(ExpressionList&)> commitResults;
    218243    };
    219244
    220     LLIntCallInformation callInformationFor(const Signature&, CallRole = CallRole::Caller);
    221 
    222     VirtualRegister virtualRegisterForLocal(uint32_t index)
     245    LLIntCallInformation callInformationForCaller(const Signature&);
     246    Vector<VirtualRegister, 2> callInformationForCallee(const Signature&);
     247
     248    VirtualRegister virtualRegisterForWasmLocal(uint32_t index)
    223249    {
    224250        if (index < m_codeBlock->m_numArguments)
     
    228254        const uint32_t gprCount = callingConvention.gprArgs.size();
    229255        const uint32_t fprCount = callingConvention.fprArgs.size();
    230         return ::JSC::virtualRegisterForLocal(index - m_codeBlock->m_numArguments + gprCount + fprCount + numberOfLLIntCalleeSaveRegisters);
    231     }
    232 
    233     ExpressionList tmpsForSignature(BlockSignature signature)
    234     {
    235         ExpressionList result(signature->returnCount());
    236         for (unsigned i = 0; i < signature->returnCount(); ++i)
    237             result[i] = newTemporary();
     256        return virtualRegisterForLocal(index - m_codeBlock->m_numArguments + gprCount + fprCount + numberOfLLIntCalleeSaveRegisters);
     257    }
     258
     259    ExpressionType jsNullConstant()
     260    {
     261        if (UNLIKELY(!m_jsNullConstant.isValid())) {
     262            m_jsNullConstant = VirtualRegister(FirstConstantRegisterIndex + m_codeBlock->m_constants.size());
     263            m_codeBlock->m_constants.append(JSValue::encode(jsNull()));
     264            if (UNLIKELY(Options::dumpGeneratedWasmBytecodes()))
     265                m_codeBlock->m_constantTypes.append(Type::Anyref);
     266        }
     267        return m_jsNullConstant;
     268    }
     269
     270    ExpressionType zeroConstant()
     271    {
     272        if (UNLIKELY(!m_zeroConstant.isValid())) {
     273            m_zeroConstant = VirtualRegister(FirstConstantRegisterIndex + m_codeBlock->m_constants.size());
     274            m_codeBlock->m_constants.append(0);
     275            if (UNLIKELY(Options::dumpGeneratedWasmBytecodes()))
     276                m_codeBlock->m_constantTypes.append(Type::I32);
     277        }
     278        return m_zeroConstant;
     279    }
     280
     281    void getDropKeepCount(const ControlType& target, unsigned& startOffset, unsigned& drop, unsigned& keep)
     282    {
     283        startOffset = target.stackSize() + 1;
     284        keep = target.targetArity();
     285        drop = m_stackSize - target.stackSize() - target.targetArity();
     286    }
     287
     288    void dropKeep(Stack& values, const ControlType& target, bool dropValues)
     289    {
     290        unsigned startOffset;
     291        unsigned keep;
     292        unsigned drop;
     293
     294        getDropKeepCount(target, startOffset, drop, keep);
     295
     296        if (dropValues)
     297            values.shrink(keep);
     298
     299        if (!drop)
     300            return;
     301
     302        if (keep)
     303            WasmDropKeep::emit(this, startOffset, drop, keep);
     304    }
     305
     306    template<typename Functor>
     307    void walkExpressionStack(Stack& expressionStack, unsigned stackSize, const Functor& functor)
     308    {
     309        for (unsigned i = expressionStack.size(); i > 0; --i) {
     310            VirtualRegister slot = virtualRegisterForLocal(stackSize - i);
     311            functor(expressionStack[expressionStack.size() - i], slot);
     312        }
     313    }
     314
     315    template<typename Functor>
     316    void walkExpressionStack(Stack& expressionStack, const Functor& functor)
     317    {
     318        walkExpressionStack(expressionStack, m_stackSize, functor);
     319    }
     320
     321    template<typename Functor>
     322    void walkExpressionStack(ControlEntry& entry, const Functor& functor)
     323    {
     324        walkExpressionStack(entry.enclosedExpressionStack, entry.controlData.stackSize(), functor);
     325    }
     326
     327    void checkConsistency()
     328    {
     329#if !ASSERT_DISABLED
     330        // The rules for locals and constants in the stack are:
     331        // 1) Locals have to be materialized whenever a control entry is pushed to the control stack (i.e. every time we splitStack)
     332        //    NOTE: This is a trade-off so that set_local does not have to walk up the control stack looking for delayed get_locals
     333        // 2) If the control entry is a loop, we also need to materialize constants in the newStack, since those slots will be written
     334        //    to from loop back edges
     335        // 3) Both locals and constants have to be materialized before branches, since multiple branches might share the same target,
     336        //    we can't make any assumptions about the stack state at that point, so we materialize the stack.
     337        for (ControlEntry& controlEntry : m_parser->controlStack()) {
     338            walkExpressionStack(controlEntry, [&](VirtualRegister expression, VirtualRegister slot) {
     339                ASSERT(expression == slot || expression.isConstant());
     340            });
     341        }
     342        walkExpressionStack(m_parser->expressionStack(), [&](VirtualRegister expression, VirtualRegister slot) {
     343            ASSERT(expression == slot || expression.isConstant() || expression.isArgument() || expression.toLocal() < m_codeBlock->m_numVars);
     344        });
     345#endif
     346    }
     347
     348    void materializeConstantsAndLocals(Stack& expressionStack)
     349    {
     350        if (expressionStack.isEmpty())
     351            return;
     352
     353        checkConsistency();
     354        walkExpressionStack(expressionStack, [&](VirtualRegister& expression, VirtualRegister slot) {
     355            ASSERT(expression == slot || expression.isConstant() || expression.isArgument() || expression.toLocal() < m_codeBlock->m_numVars);
     356            if (expression == slot)
     357                return;
     358            WasmMov::emit(this, slot, expression);
     359            expression = slot;
     360        });
     361        checkConsistency();
     362    }
     363
     364    Stack splitStack(BlockSignature signature, Stack& stack)
     365    {
     366        Stack result = JSC::Wasm::splitStack(signature, stack);
     367
     368        m_stackSize -= result.size();
     369        checkConsistency();
     370        walkExpressionStack(stack, [&](VirtualRegister& expression, VirtualRegister slot) {
     371            ASSERT(expression == slot || expression.isConstant() || expression.isArgument() || expression.toLocal() < m_codeBlock->m_numVars);
     372            if (expression == slot || expression.isConstant())
     373                return;
     374            WasmMov::emit(this, slot, expression);
     375            expression = slot;
     376        });
     377        checkConsistency();
     378        m_stackSize += result.size();
    238379        return result;
    239     }
    240 
    241     ExpressionType jsNullConstant()
    242     {
    243         if (!m_jsNullConstant)
    244             m_jsNullConstant = addConstant(Type::Anyref, JSValue::encode(jsNull()));
    245         return m_jsNullConstant;
    246380    }
    247381
    248382    struct SwitchEntry {
    249383        InstructionStream::Offset offset;
    250         InstructionStream::Offset* jumpTarget;
     384        int* jumpTarget;
     385    };
     386
     387    struct ConstantMapHashTraits : WTF::GenericHashTraits<EncodedJSValue> {
     388        static constexpr bool emptyValueIsZero = true;
     389        static void constructDeletedValue(EncodedJSValue& slot) { slot = JSValue::encode(jsNull()); }
     390        static bool isDeletedValue(EncodedJSValue value) { return value == JSValue::encode(jsNull()); }
    251391    };
    252392
     
    257397    HashMap<Label*, Vector<SwitchEntry>> m_switches;
    258398    ExpressionType m_jsNullConstant;
     399    ExpressionType m_zeroConstant;
    259400    ExpressionList m_unitializedLocals;
    260     StdUnorderedMap<uint64_t, VirtualRegister> m_constantMap;
     401    HashMap<EncodedJSValue, VirtualRegister, WTF::IntHash<EncodedJSValue>, ConstantMapHashTraits> m_constantMap;
     402    Vector<VirtualRegister, 2> m_results;
     403    unsigned m_stackSize { 0 };
     404    unsigned m_maxStackSize { 0 };
    261405};
    262406
     
    271415
    272416LLIntGenerator::LLIntGenerator(const ModuleInformation& info, unsigned functionIndex, ThrowWasmException throwWasmException, const Signature&)
    273     : BytecodeGeneratorBase(makeUnique<FunctionCodeBlock>(functionIndex), numberOfLLIntCalleeSaveRegisters)
     417    : BytecodeGeneratorBase(makeUnique<FunctionCodeBlock>(functionIndex), 0)
    274418    , m_info(info)
    275419    , m_functionIndex(functionIndex)
    276420{
     421    m_codeBlock->m_numVars = numberOfLLIntCalleeSaveRegisters;
     422    m_stackSize = numberOfLLIntCalleeSaveRegisters;
     423    m_maxStackSize = numberOfLLIntCalleeSaveRegisters;
     424
    277425    if (throwWasmException)
    278426        Thunks::singleton().setThrowWasmException(throwWasmException);
     
    284432{
    285433    RELEASE_ASSERT(m_codeBlock);
     434    m_codeBlock->m_numCalleeLocals = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), m_maxStackSize);
    286435    m_codeBlock->setInstructions(m_writer.finalize());
    287436    return WTFMove(m_codeBlock);
     
    291440#include "WasmLLIntGeneratorInlines.h"
    292441
    293 auto LLIntGenerator::callInformationFor(const Signature& signature, CallRole role) -> LLIntCallInformation
    294 {
     442auto LLIntGenerator::callInformationForCaller(const Signature& signature) -> LLIntCallInformation
     443{
     444    // This function sets up the stack layout for calls. The desired stack layout is:
     445
     446    // FPRn
     447    // ...
     448    // FPR1
     449    // FPR0
     450    // ---
     451    // GPRn
     452    // ...
     453    // GPR1
     454    // GPR0
     455    // ----
     456    // stackN
     457    // ...
     458    // stack1
     459    // stack0
     460    // ---
     461    // call frame header
     462
     463    // We need to allocate at least space for all GPRs and FPRs.
     464    // Return values use the same allocation layout.
     465
     466    const auto initialStackSize = m_stackSize;
     467
    295468    const auto& callingConvention = wasmCallingConvention();
    296469    const uint32_t gprCount = callingConvention.gprArgs.size();
     
    300473    uint32_t gprIndex = 0;
    301474    uint32_t fprIndex = 0;
    302 
    303     Vector<RefPtr<RegisterID>, 16> registers;
    304 
    305     ExpressionList arguments(signature.argumentCount());
    306     ExpressionList results(signature.returnCount());
     475    uint32_t stackIndex = 0;
    307476
    308477    auto allocateStackRegister = [&](Type type) {
    309         ASSERT(role == CallRole::Caller);
    310478        switch (type) {
    311479        case Type::I32:
     
    315483            if (gprIndex < gprCount)
    316484                ++gprIndex;
    317             else {
    318                 registers.append(newTemporary());
     485            else if (stackIndex++ >= stackCount)
    319486                ++stackCount;
    320             }
    321487            break;
    322488        case Type::F32:
     
    324490            if (fprIndex < fprCount)
    325491                ++fprIndex;
    326             else {
    327                 registers.append(newTemporary());
     492            else if (stackIndex++ >= stackCount)
    328493                ++stackCount;
    329             }
    330494            break;
    331495        case Void:
     
    336500
    337501
    338     if (role == CallRole::Callee) {
    339         // Reuse the slots we allocated to spill the registers in addArguments
    340         for (uint32_t i = gprCount + fprCount; i--;)
    341             registers.append(new RegisterID(::JSC::virtualRegisterForLocal(numberOfLLIntCalleeSaveRegisters + i)));
    342     } else {
    343         for (uint32_t i = 0; i < gprCount; i++)
    344             registers.append(newTemporary());
    345         for (uint32_t i = 0; i < fprCount; i++)
    346             registers.append(newTemporary());
    347 
    348         for (uint32_t i = 0; i < signature.argumentCount(); i++)
    349             allocateStackRegister(signature.argument(i));
    350         gprIndex = 0;
    351         fprIndex = 0;
    352         for (uint32_t i = 0; i < signature.returnCount(); i++)
    353             allocateStackRegister(signature.returnType(i));
    354     }
    355 
    356     unsigned stackOffset;
    357     if (role == CallRole::Callee)
    358         stackOffset = static_cast<unsigned>(-registers.last()->index());
    359     else {
    360         // Align the stack
    361         auto computeStackOffset = [&] {
    362             return static_cast<unsigned>(-registers.last()->index()) + CallFrame::headerSizeInRegisters;
    363         };
    364         while (computeStackOffset() % stackAlignmentRegisters())
    365             registers.append(newTemporary());
    366         stackOffset = computeStackOffset();
    367     }
    368 
    369     ASSERT(role == CallRole::Caller || !stackCount);
    370     const uint32_t maxGPRIndex = stackCount + gprCount;
     502    for (uint32_t i = 0; i < signature.argumentCount(); i++)
     503        allocateStackRegister(signature.argument(i));
     504
     505    gprIndex = 0;
     506    fprIndex = 0;
     507    stackIndex = 0;
     508    for (uint32_t i = 0; i < signature.returnCount(); i++)
     509        allocateStackRegister(signature.returnType(i));
     510
     511    // FIXME: we are allocating the extra space for the argument/return count in order to avoid interference, but we could do better
     512    // NOTE: We increase arg count by 1 for the case of indirect calls
     513    m_stackSize += std::max(signature.argumentCount() + 1, signature.returnCount()) + gprCount + fprCount + stackCount + CallFrame::headerSizeInRegisters;
     514    if (m_stackSize % stackAlignmentRegisters())
     515        ++m_stackSize;
     516    if (m_maxStackSize < m_stackSize)
     517        m_maxStackSize = m_stackSize;
     518
     519
     520    ExpressionList arguments(signature.argumentCount());
     521    ExpressionList temporaryResults(signature.returnCount());
     522
     523    const unsigned stackOffset = m_stackSize;
     524    const unsigned base = stackOffset - CallFrame::headerSizeInRegisters;
     525
     526    const uint32_t gprLimit = base - stackCount - gprCount;
     527    const uint32_t fprLimit = gprLimit - fprCount;
     528
     529    stackIndex = base;
     530    gprIndex = base - stackCount;
     531    fprIndex = gprIndex - gprCount;
     532    for (uint32_t i = 0; i < signature.argumentCount(); i++) {
     533        switch (signature.argument(i)) {
     534        case Type::I32:
     535        case Type::I64:
     536        case Type::Anyref:
     537        case Type::Funcref:
     538            if (gprIndex > gprLimit)
     539                arguments[i] = virtualRegisterForLocal(--gprIndex);
     540            else
     541                arguments[i] = virtualRegisterForLocal(--stackIndex);
     542            break;
     543        case Type::F32:
     544        case Type::F64:
     545            if (fprIndex > fprLimit)
     546                arguments[i] = virtualRegisterForLocal(--fprIndex);
     547            else
     548                arguments[i] = virtualRegisterForLocal(--stackIndex);
     549            break;
     550        case Void:
     551        case Func:
     552            RELEASE_ASSERT_NOT_REACHED();
     553        }
     554    }
     555
     556    stackIndex = base;
     557    gprIndex = base - stackCount;
     558    fprIndex = gprIndex - gprCount;
     559    for (uint32_t i = 0; i < signature.returnCount(); i++) {
     560        switch (signature.returnType(i)) {
     561        case Type::I32:
     562        case Type::I64:
     563        case Type::Anyref:
     564        case Type::Funcref:
     565            if (gprIndex > gprLimit)
     566                temporaryResults[i] = virtualRegisterForLocal(--gprIndex);
     567            else
     568                temporaryResults[i] = virtualRegisterForLocal(--stackIndex);
     569            break;
     570        case Type::F32:
     571        case Type::F64:
     572            if (fprIndex > fprLimit)
     573                temporaryResults[i] = virtualRegisterForLocal(--fprIndex);
     574            else
     575                temporaryResults[i] = virtualRegisterForLocal(--stackIndex);
     576            break;
     577        case Void:
     578        case Func:
     579            RELEASE_ASSERT_NOT_REACHED();
     580        }
     581    }
     582
     583    m_stackSize = initialStackSize;
     584
     585    auto commitResults = [this, temporaryResults = WTFMove(temporaryResults)](ExpressionList& results) {
     586        for (auto temporaryResult : temporaryResults) {
     587            ExpressionType result = push();
     588            WasmMov::emit(this, result, temporaryResult);
     589            results.append(result);
     590        }
     591    };
     592
     593    return LLIntCallInformation { stackOffset, stackCount, WTFMove(arguments), WTFMove(commitResults) };
     594}
     595
     596auto LLIntGenerator::callInformationForCallee(const Signature& signature) -> Vector<VirtualRegister, 2>
     597{
     598    if (m_results.size())
     599        return m_results;
     600
     601    m_results.reserveInitialCapacity(signature.returnCount());
     602
     603    const auto& callingConvention = wasmCallingConvention();
     604    const uint32_t gprCount = callingConvention.gprArgs.size();
     605    const uint32_t fprCount = callingConvention.fprArgs.size();
     606
     607    uint32_t gprIndex = 0;
     608    uint32_t fprIndex = gprCount;
     609    uint32_t stackIndex = 0;
     610    const uint32_t maxGPRIndex = gprCount;
    371611    const uint32_t maxFPRIndex = maxGPRIndex + fprCount;
    372     uint32_t stackIndex = 0;
    373     auto appendForType = [&](Type type, unsigned index, auto& vector) {
    374         switch (type) {
     612
     613    for (uint32_t i = 0; i < signature.returnCount(); i++) {
     614        switch (signature.returnType(i)) {
    375615        case Type::I32:
    376616        case Type::I64:
     
    378618        case Type::Funcref:
    379619            if (gprIndex < maxGPRIndex)
    380                 vector[index] = registers[registers.size() - gprIndex++ - 1];
    381             else {
    382                 if (role == CallRole::Caller)
    383                     vector[index] = registers[registers.size() - stackIndex++ - 1];
    384                 else
    385                     vector[index] = new RegisterID(virtualRegisterForArgument(stackIndex++));
    386             }
     620                m_results.append(virtualRegisterForLocal(numberOfLLIntCalleeSaveRegisters + gprIndex++));
     621            else
     622                m_results.append(virtualRegisterForArgument(stackIndex++));
    387623            break;
    388624        case Type::F32:
    389625        case Type::F64:
    390626            if (fprIndex < maxFPRIndex)
    391                 vector[index] = registers[registers.size() - fprIndex++ - 1];
    392             else {
    393                 if (role == CallRole::Caller)
    394                     vector[index] = registers[registers.size() - stackIndex++ - 1];
    395                 else
    396                     vector[index] = new RegisterID(virtualRegisterForArgument(stackIndex++));
    397             }
     627                m_results.append(virtualRegisterForLocal(numberOfLLIntCalleeSaveRegisters + fprIndex++));
     628            else
     629                m_results.append(virtualRegisterForArgument(stackIndex++));
    398630            break;
    399631        case Void:
     
    401633            RELEASE_ASSERT_NOT_REACHED();
    402634        }
    403     };
    404 
    405     gprIndex = stackCount;
    406     fprIndex = maxGPRIndex;
    407     for (uint32_t i = 0; i < signature.argumentCount(); i++)
    408         appendForType(signature.argument(i), i, arguments);
    409     gprIndex = stackCount;
    410     fprIndex = maxGPRIndex;
    411     for (uint32_t i = 0; i < signature.returnCount(); i++)
    412         appendForType(signature.returnType(i), i, results);
    413 
    414     if (role == CallRole::Caller) {
    415         // Reserve space for call frame.
    416         Vector<RefPtr<RegisterID>, CallFrame::headerSizeInRegisters + 2, UnsafeVectorOverflow> callFrame;
    417         for (int i = 0; i < CallFrame::headerSizeInRegisters; ++i)
    418             callFrame.append(newTemporary());
    419     }
    420 
    421     return LLIntCallInformation { stackOffset, stackCount, WTFMove(arguments), WTFMove(results) };
     635    }
     636
     637    return m_results;
    422638}
    423639
     
    436652    uint32_t stackIndex = 0;
    437653
    438     Vector<RefPtr<RegisterID>> registerArguments(gprCount + fprCount);
     654    Vector<VirtualRegister> registerArguments(gprCount + fprCount);
    439655    for (uint32_t i = 0; i < gprCount + fprCount; i++)
    440         registerArguments[i] = addVar();
     656        registerArguments[i] = push();
    441657
    442658    const auto addArgument = [&](uint32_t index, uint32_t& count, uint32_t max) {
     
    465681    }
    466682
     683    m_codeBlock->m_numVars += gprCount + fprCount;
     684
    467685    return { };
    468686}
     
    470688auto LLIntGenerator::addLocal(Type type, uint32_t count) -> PartialResult
    471689{
     690    m_codeBlock->m_numVars += count;
    472691    while (count--) {
    473         auto local = addVar();
     692        auto local = push();
    474693        switch (type) {
    475694        case Type::Anyref:
     
    486705void LLIntGenerator::didFinishParsingLocals()
    487706{
     707    if (m_unitializedLocals.isEmpty())
     708        return;
     709
    488710    auto null = jsNullConstant();
    489711    for (auto local : m_unitializedLocals)
     
    492714}
    493715
    494 auto LLIntGenerator::addConstant(Type type, uint64_t value) -> ExpressionType
    495 {
    496     VirtualRegister source(FirstConstantRegisterIndex + m_codeBlock->m_constants.size());
    497     auto result = m_constantMap.emplace(value, source);
    498     if (result.second) {
     716auto LLIntGenerator::addConstant(Type type, int64_t value) -> ExpressionType
     717{
     718    auto constant = [&] {
     719        if (!value)
     720            return zeroConstant();
     721
     722        if (value == JSValue::encode(jsNull()))
     723            return jsNullConstant();
     724
     725        VirtualRegister source(FirstConstantRegisterIndex + m_codeBlock->m_constants.size());
     726        auto result = m_constantMap.add(value, source);
     727        if (!result.isNewEntry)
     728            return result.iterator->value;
    499729        m_codeBlock->m_constants.append(value);
    500730        if (UNLIKELY(Options::dumpGeneratedWasmBytecodes()))
    501731            m_codeBlock->m_constantTypes.append(type);
    502     } else
    503         source = result.first->second;
    504     auto target = newTemporary();
    505     WasmMov::emit(this, target, source);
    506     return target;
     732        return source;
     733    };
     734    // leave a hole if we need to materialize the constant
     735    push();
     736    return constant();
    507737}
    508738
    509739auto LLIntGenerator::getLocal(uint32_t index, ExpressionType& result) -> PartialResult
    510740{
    511     // FIXME: Remove unnecessary moves
    512     // https://bugs.webkit.org/show_bug.cgi?id=203657
    513     result = newTemporary();
    514     WasmMov::emit(this, result, virtualRegisterForLocal(index));
     741    // leave a hole if we need to materialize the local
     742    push();
     743    result = virtualRegisterForWasmLocal(index);
    515744    return { };
    516745}
     
    518747auto LLIntGenerator::setLocal(uint32_t index, ExpressionType value) -> PartialResult
    519748{
    520     WasmMov::emit(this, virtualRegisterForLocal(index), value);
     749    VirtualRegister target = virtualRegisterForWasmLocal(index);
     750
     751    // If this local is currently on the stack we need to materialize it, otherwise it'll see the new value instead of the old one
     752    walkExpressionStack(m_parser->expressionStack(), [&](VirtualRegister& expression, VirtualRegister slot) {
     753        if (expression != target)
     754            return;
     755        WasmMov::emit(this, slot, expression);
     756        expression = slot;
     757    });
     758
     759    WasmMov::emit(this, target, value);
     760
    521761    return { };
    522762}
     
    524764auto LLIntGenerator::getGlobal(uint32_t index, ExpressionType& result) -> PartialResult
    525765{
    526     result = newTemporary();
     766    result = push();
    527767    WasmGetGlobal::emit(this, result, index);
    528768    return { };
     
    541781auto LLIntGenerator::addLoop(BlockSignature signature, Stack& enclosingStack, ControlType& block, Stack& newStack, uint32_t loopIndex) -> PartialResult
    542782{
     783    newStack = splitStack(signature, enclosingStack);
     784    materializeConstantsAndLocals(newStack);
     785
    543786    Ref<Label> body = newEmittedLabel();
    544787    Ref<Label> continuation = newLabel();
    545788
    546     newStack = splitStack(signature, enclosingStack);
    547     block = ControlType::loop(signature, newStack, WTFMove(body), WTFMove(continuation));
     789    block = ControlType::loop(signature, m_stackSize, WTFMove(body), WTFMove(continuation));
    548790
    549791    Vector<VirtualRegister> osrEntryData;
     
    555797    const uint32_t fprCount = callingConvention.fprArgs.size();
    556798    for (int32_t i = gprCount + fprCount + numberOfLLIntCalleeSaveRegisters; i < m_codeBlock->m_numVars; i++)
    557         osrEntryData.append(::JSC::virtualRegisterForLocal(i));
     799        osrEntryData.append(virtualRegisterForLocal(i));
    558800    for (unsigned controlIndex = 0; controlIndex < m_parser->controlStack().size(); ++controlIndex) {
    559         ExpressionList& expressionStack = m_parser->controlStack()[controlIndex].enclosedExpressionStack;
     801        Stack& expressionStack = m_parser->controlStack()[controlIndex].enclosedExpressionStack;
    560802        for (auto& expression : expressionStack)
    561             osrEntryData.append(expression->virtualRegister());
     803            osrEntryData.append(expression);
    562804    }
    563805
     
    571813auto LLIntGenerator::addTopLevel(BlockSignature signature) -> ControlType
    572814{
    573     return ControlType::topLevel(signature, tmpsForSignature(signature), newLabel());
     815    return ControlType::topLevel(signature, m_stackSize, newLabel());
    574816}
    575817
     
    577819{
    578820    newStack = splitStack(signature, enclosingStack);
    579     newBlock = ControlType::block(signature, tmpsForSignature(signature), newLabel());
     821    newBlock = ControlType::block(signature, m_stackSize, newLabel());
    580822    return { };
    581823}
     
    586828    Ref<Label> continuation = newLabel();
    587829
     830    newStack = splitStack(signature, enclosingStack);
     831
    588832    WasmJfalse::emit(this, condition, alternate->bind(this));
    589833
    590     newStack = splitStack(signature, enclosingStack);
    591     result = ControlType::if_(signature, tmpsForSignature(signature), WTFMove(alternate), WTFMove(continuation));
    592     return { };
    593 }
    594 
    595 auto LLIntGenerator::addElse(ControlType& data, const ExpressionList& currentStack) -> PartialResult
     834    result = ControlType::if_(signature, m_stackSize, WTFMove(alternate), WTFMove(continuation));
     835    return { };
     836}
     837
     838auto LLIntGenerator::addElse(ControlType& data, Stack& expressionStack) -> PartialResult
    596839{
    597840    ASSERT(WTF::holds_alternative<ControlIf>(data));
    598     unifyValuesWithBlock(data.m_results, currentStack);
     841    materializeConstantsAndLocals(expressionStack);
    599842    WasmJmp::emit(this, data.m_continuation->bind(this));
    600843    return addElseToUnreachable(data);
     
    603846auto LLIntGenerator::addElseToUnreachable(ControlType& data) -> PartialResult
    604847{
     848    m_stackSize = data.stackSize() + data.m_signature->argumentCount();
     849
    605850    ControlIf& control = WTF::get<ControlIf>(data);
    606851    emitLabel(control.m_alternate.get());
    607     data = ControlType::block(data.m_signature, WTFMove(data.m_results), WTFMove(data.m_continuation));
    608     return { };
    609 }
    610 
    611 auto LLIntGenerator::addReturn(const ControlType& data, const ExpressionList& returnValues) -> PartialResult
     852    data = ControlType::block(data.m_signature, m_stackSize, WTFMove(data.m_continuation));
     853    return { };
     854}
     855
     856auto LLIntGenerator::addReturn(const ControlType& data, Stack& returnValues) -> PartialResult
    612857{
    613858    if (!data.m_signature->returnCount()) {
     
    616861    }
    617862
    618     LLIntCallInformation info = callInformationFor(*data.m_signature, CallRole::Callee);
    619     unifyValuesWithBlock(info.results, returnValues);
     863    // no need to drop keep here, since we have to move anyway
     864    unifyValuesWithBlock(callInformationForCallee(*data.m_signature), returnValues);
    620865    WasmRet::emit(this);
    621866
     
    623868}
    624869
    625 auto LLIntGenerator::addBranch(ControlType& data, ExpressionType condition, const ExpressionList& returnValues) -> PartialResult
    626 {
    627     unifyValuesWithBlock(data.m_results, returnValues);
    628 
     870auto LLIntGenerator::addBranch(ControlType& data, ExpressionType condition, Stack& returnValues) -> PartialResult
     871{
    629872    RefPtr<Label> target = data.targetLabelForBranch();
    630     if (condition)
    631         WasmJtrue::emit(this, condition, target->bind(this));
    632     else
    633         WasmJmp::emit(this, target->bind(this));
    634 
    635     return { };
    636 }
    637 
    638 auto LLIntGenerator::addSwitch(ExpressionType condition, const Vector<ControlType*>& targets, ControlType& defaultTarget, const ExpressionList& expressionStack) -> PartialResult
    639 {
     873    RefPtr<Label> skip = nullptr;
     874
     875    materializeConstantsAndLocals(returnValues);
     876
     877    if (condition.isValid()) {
     878        skip = newLabel();
     879        WasmJfalse::emit(this, condition, skip->bind(this));
     880    }
     881
     882    dropKeep(returnValues, data, !skip);
     883    WasmJmp::emit(this, target->bind(this));
     884
     885    if (skip)
     886        emitLabel(*skip);
     887
     888    return { };
     889}
     890
     891auto LLIntGenerator::addSwitch(ExpressionType condition, const Vector<ControlType*>& targets, ControlType& defaultTarget, Stack& expressionStack) -> PartialResult
     892{
     893    materializeConstantsAndLocals(expressionStack);
     894
    640895    unsigned tableIndex = m_codeBlock->numberOfJumpTables();
    641     FunctionCodeBlock::JumpTable& jumpTable = m_codeBlock->addJumpTable(targets.size());
    642 
    643     for (const auto& target : targets)
    644         unifyValuesWithBlock(target->m_results, expressionStack);
    645     unifyValuesWithBlock(defaultTarget.m_results, expressionStack);
    646 
    647     WasmSwitch::emit(this, condition, tableIndex, defaultTarget.targetLabelForBranch()->bind(this));
     896    FunctionCodeBlock::JumpTable& jumpTable = m_codeBlock->addJumpTable(targets.size() + 1);
     897
     898    WasmSwitch::emit(this, condition, tableIndex);
    648899
    649900    unsigned index = 0;
    650901    InstructionStream::Offset offset = m_lastInstruction.offset();
    651     for (const auto& target : targets) {
    652         RefPtr<Label> targetLabel = target->targetLabelForBranch();
     902
     903    auto addTarget = [&](ControlType& target) {
     904        RefPtr<Label> targetLabel = target.targetLabelForBranch();
     905
     906        getDropKeepCount(target, jumpTable[index].startOffset, jumpTable[index].dropCount, jumpTable[index].keepCount);
     907
    653908        if (targetLabel->isForward()) {
    654909            auto result = m_switches.add(targetLabel.get(), Vector<SwitchEntry>());
    655             ASSERT(!jumpTable[index]);
    656             result.iterator->value.append({ offset, &jumpTable[index++] });
     910            ASSERT(!jumpTable[index].target);
     911            result.iterator->value.append(SwitchEntry { offset, &jumpTable[index++].target });
    657912        } else {
    658             int jumpTarget = targetLabel->bind(this).target();
     913            int jumpTarget = targetLabel->location() - offset;
    659914            ASSERT(jumpTarget);
    660             jumpTable[index++] = jumpTarget;
    661         }
    662     }
    663 
    664 
    665     return { };
    666 }
    667 
    668 auto LLIntGenerator::endBlock(ControlEntry& entry, ExpressionList& expressionStack) -> PartialResult
     915            jumpTable[index++].target = jumpTarget;
     916        }
     917    };
     918
     919    for (const auto& target : targets)
     920        addTarget(*target);
     921    addTarget(defaultTarget);
     922
     923    return { };
     924}
     925
     926auto LLIntGenerator::endBlock(ControlEntry& entry, Stack& expressionStack) -> PartialResult
     927{
     928    // FIXME: We only need to materialize constants here if there exists a jump to this label
     929    // https://bugs.webkit.org/show_bug.cgi?id=203657
     930    materializeConstantsAndLocals(expressionStack);
     931    return addEndToUnreachable(entry, expressionStack, false);
     932}
     933
     934
     935auto LLIntGenerator::addEndToUnreachable(ControlEntry& entry, const Stack& expressionStack, bool unreachable) -> PartialResult
    669936{
    670937    ControlType& data = entry.controlData;
    671938
    672     if (!WTF::holds_alternative<ControlLoop>(data))
    673         unifyValuesWithBlock(data.m_results, expressionStack);
    674 
    675     return addEndToUnreachable(entry, expressionStack);
    676 }
    677 
    678 
    679 auto LLIntGenerator::addEndToUnreachable(ControlEntry& entry, const Stack& expressionStack) -> PartialResult
    680 {
    681     ControlType& data = entry.controlData;
    682 
    683     emitLabel(*data.m_continuation);
    684 
    685     if (!WTF::holds_alternative<ControlLoop>(data))
    686         entry.enclosedExpressionStack.appendVector(data.m_results);
    687     else {
    688         for (unsigned i = 0; i < data.m_signature->returnCount(); ++i) {
    689             if (i < expressionStack.size())
    690                 entry.enclosedExpressionStack.append(expressionStack[i]);
    691             else
    692                 entry.enclosedExpressionStack.append(newTemporary());
    693         }
    694     }
    695 
    696     // TopLevel does not have any code after this so we need to make sure we emit a return here.
    697     if (WTF::holds_alternative<ControlTopLevel>(data))
    698         return addReturn(data, entry.enclosedExpressionStack);
     939    RELEASE_ASSERT(unreachable || m_stackSize == data.stackSize() + data.m_signature->returnCount());
     940
     941    m_stackSize = data.stackSize();
     942
     943    for (unsigned i = 0; i < data.m_signature->returnCount(); ++i) {
     944        // We don't want to do a consistency check here because we just reset the stack size
     945        // are pushing new values, while we already have the same values in the stack.
     946        // The only reason we do things this way is so that it also works for unreachable blocks,
     947        // since they might not have the right number of values in the expression stack.
     948        // Instead, we do a stricter consistency check below.
     949        auto tmp = push(NoConsistencyCheck);
     950        ASSERT_UNUSED(expressionStack, unreachable || tmp == expressionStack[i]);
     951        entry.enclosedExpressionStack.append(tmp);
     952    }
     953
     954    if (m_lastOpcodeID == wasm_jmp && data.m_continuation->unresolvedJumps().size() == 1 && data.m_continuation->unresolvedJumps()[0] == static_cast<int>(m_lastInstruction.offset())) {
     955        m_lastOpcodeID = wasm_unreachable;
     956        m_writer.rewind(m_lastInstruction);
     957    } else
     958        emitLabel(*data.m_continuation);
     959
     960    return { };
     961}
     962
     963auto LLIntGenerator::endTopLevel(BlockSignature signature, const Stack& expressionStack) -> PartialResult
     964{
     965    RELEASE_ASSERT(expressionStack.size() == signature->returnCount());
     966
     967    if (!signature->returnCount()) {
     968        WasmRetVoid::emit(this);
     969        return { };
     970    }
     971
     972    checkConsistency();
     973    unifyValuesWithBlock(callInformationForCallee(*signature), expressionStack);
     974    WasmRet::emit(this);
    699975
    700976    return { };
     
    704980{
    705981    ASSERT(signature.argumentCount() == args.size());
    706     LLIntCallInformation info = callInformationFor(signature);
     982    LLIntCallInformation info = callInformationForCaller(signature);
    707983    unifyValuesWithBlock(info.arguments, args);
    708     results = WTFMove(info.results);
    709984    if (Context::useFastTLS())
    710985        WasmCall::emit(this, functionIndex, info.stackOffset, info.numberOfStackArguments);
    711986    else
    712987        WasmCallNoTls::emit(this, functionIndex, info.stackOffset, info.numberOfStackArguments);
     988    info.commitResults(results);
    713989
    714990    return { };
     
    723999    ASSERT(m_info.tables[tableIndex].type() == TableElementType::Funcref);
    7241000
    725     LLIntCallInformation info = callInformationFor(signature);
     1001    LLIntCallInformation info = callInformationForCaller(signature);
    7261002    unifyValuesWithBlock(info.arguments, args);
    727     results = WTFMove(info.results);
    7281003    if (Context::useFastTLS())
    7291004        WasmCallIndirect::emit(this, calleeIndex, m_codeBlock->addSignature(signature), info.stackOffset, info.numberOfStackArguments, tableIndex);
    7301005    else
    7311006        WasmCallIndirectNoTls::emit(this, calleeIndex, m_codeBlock->addSignature(signature), info.stackOffset, info.numberOfStackArguments, tableIndex);
     1007    info.commitResults(results);
    7321008
    7331009    return { };
     
    7361012auto LLIntGenerator::addRefIsNull(ExpressionType value, ExpressionType& result) -> PartialResult
    7371013{
    738     result = value;
     1014    result = push();
    7391015    WasmRefIsNull::emit(this, result, value);
    7401016
     
    7441020auto LLIntGenerator::addRefFunc(uint32_t index, ExpressionType& result) -> PartialResult
    7451021{
    746     result = newTemporary();
     1022    result = push();
    7471023    WasmRefFunc::emit(this, result, index);
    7481024
     
    7521028auto LLIntGenerator::addTableGet(unsigned tableIndex, ExpressionType index, ExpressionType& result) -> PartialResult
    7531029{
    754     result = index;
     1030    result = push();
    7551031    WasmTableGet::emit(this, result, index, tableIndex);
    7561032
     
    7671043auto LLIntGenerator::addTableSize(unsigned tableIndex, ExpressionType& result) -> PartialResult
    7681044{
    769     result = newTemporary();
     1045    result = push();
    7701046    WasmTableSize::emit(this, result, tableIndex);
    7711047
     
    7751051auto LLIntGenerator::addTableGrow(unsigned tableIndex, ExpressionType fill, ExpressionType delta, ExpressionType& result) -> PartialResult
    7761052{
    777     result = fill;
     1053    result = push();
    7781054    WasmTableGrow::emit(this, result, fill, delta, tableIndex);
    7791055
     
    7971073auto LLIntGenerator::addCurrentMemory(ExpressionType& result) -> PartialResult
    7981074{
    799     result = newTemporary();
     1075    result = push();
    8001076    WasmCurrentMemory::emit(this, result);
    8011077
     
    8051081auto LLIntGenerator::addGrowMemory(ExpressionType delta, ExpressionType& result) -> PartialResult
    8061082{
    807     result = delta;
     1083    result = push();
    8081084    WasmGrowMemory::emit(this, result, delta);
    8091085
     
    8131089auto LLIntGenerator::addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result) -> PartialResult
    8141090{
    815     result = condition;
     1091    result = push();
    8161092    WasmSelect::emit(this, result, condition, nonZero, zero);
    8171093
     
    8211097auto LLIntGenerator::load(LoadOpType op, ExpressionType pointer, ExpressionType& result, uint32_t offset) -> PartialResult
    8221098{
    823     result = pointer;
     1099    result = push();
    8241100    switch (op) {
    8251101    case LoadOpType::I32Load8S:
     
    9331209        CASE(WasmJtrue)
    9341210        CASE(WasmJfalse)
    935         case WasmSwitch::opcodeID: {
    936             ASSERT((!instruction->as<WasmSwitch, WasmOpcodeTraits>().m_defaultTarget));
    937             instruction->cast<WasmSwitch, WasmOpcodeTraits>()->setDefaultTarget(BoundLabel(target), [&]() {
    938                 generator.m_codeBlock->addOutOfLineJumpTarget(instruction.offset(), target);
    939                 return BoundLabel();
    940             });
    941             break;
    942         }
    9431211        default:
    944             ASSERT_NOT_REACHED();
     1212            RELEASE_ASSERT_NOT_REACHED();
    9451213        }
    9461214#undef CASE
  • trunk/Source/JavaScriptCore/wasm/WasmLLIntPlan.cpp

    r252310 r252800  
    8181
    8282    if (m_exportedFunctionIndices.contains(functionIndex) || m_moduleInformation->referencedFunctions().contains(functionIndex)) {
    83         auto locker = holdLock(m_lock);
    8483        EmbederToWasmFunction entry;
    8584        entry.jit = makeUnique<CCallHelpers>();
    8685        entry.function = m_createEmbedderWrapper(*entry.jit, signature, &m_unlinkedWasmToWasmCalls[functionIndex], m_moduleInformation.get(), m_mode, functionIndex);
     86        auto locker = holdLock(m_lock);
    8787        auto result = m_embedderToWasmInternalFunctions.add(functionIndex, WTFMove(entry));
    8888        ASSERT_UNUSED(result, result.isNewEntry);
  • trunk/Source/JavaScriptCore/wasm/WasmValidate.cpp

    r251978 r252800  
    162162
    163163    Result WARN_UNUSED_RETURN addUnreachable() { return { }; }
     164    Result WARN_UNUSED_RETURN endTopLevel(BlockSignature, const Stack&) { return { }; }
    164165
    165166    // Calls
     
    177178    void setParser(FunctionParser<Validate>*) { }
    178179    void didFinishParsingLocals() { }
     180    void didPopValueFromStack() { }
    179181
    180182private:
Note: See TracChangeset for help on using the changeset viewer.