Changeset 38322 in webkit for trunk/JavaScriptCore/VM/Machine.cpp


Ignore:
Timestamp:
Nov 11, 2008, 4:32:38 PM (17 years ago)
Author:
[email protected]
Message:

2008-11-11 Geoffrey Garen <[email protected]>

Reviewed by Darin Adler.


Fixed https://bugs.webkit.org/show_bug.cgi?id=22174
Simplified op_call by nixing its responsibility for moving the value of
"this" into the first argument slot.

Instead, the caller emits an explicit load or mov instruction, or relies
on implicit knowledge that "this" is already in the first argument slot.
As a result, two operands to op_call are gone: firstArg and thisVal.


SunSpider and v8 tests show no change in bytecode or CTI.

  • VM/CTI.cpp: (JSC::CTI::compileOpCallSetupArgs): (JSC::CTI::compileOpCallEvalSetupArgs): (JSC::CTI::compileOpConstructSetupArgs): Split apart these three versions of setting up arguments to op_call, because they're more different than they are the same -- even more so with this patch.

(JSC::CTI::compileOpCall): Updated for the fact that op_construct doesn't
match op_call anymore.

(JSC::CTI::privateCompileMainPass):
(JSC::CTI::privateCompileSlowCases): Merged a few call cases. Updated
for changes mentioned above.

  • VM/CTI.h:
  • VM/CodeBlock.cpp: (JSC::CodeBlock::dump): Updated for new bytecode format of call / construct.
  • VM/Machine.cpp: (JSC::Machine::callEval): Updated for new bytecode format of call / construct.

(JSC::Machine::dumpCallFrame):
(JSC::Machine::dumpRegisters): Simplified these debugging functions,
taking advantage of the new call frame layout.

(JSC::Machine::execute): Fixed up the eval version of execute to be
friendlier to calls in the new format.

(JSC::Machine::privateExecute): Implemented the new call format in
bytecode.

(JSC::Machine::cti_op_call_NotJSFunction):
(JSC::Machine::cti_op_construct_JSConstruct):
(JSC::Machine::cti_op_construct_NotJSConstruct):
(JSC::Machine::cti_op_call_eval): Updated CTI helpers to match the new
call format.


Fixed a latent bug in stack overflow checking that is now hit because
the register layout has changed a bit -- namely: when throwing a stack
overflow exception inside an op_call helper, we need to account for the
fact that the current call frame is only half-constructed, and use the
parent call frame instead.

  • VM/Machine.h:
  • bytecompiler/CodeGenerator.cpp: (JSC::CodeGenerator::emitCall): (JSC::CodeGenerator::emitCallEval): (JSC::CodeGenerator::emitConstruct):
  • bytecompiler/CodeGenerator.h: Updated codegen to match the new call format.
  • parser/Nodes.cpp: (JSC::EvalFunctionCallNode::emitCode): (JSC::FunctionCallValueNode::emitCode): (JSC::FunctionCallResolveNode::emitCode): (JSC::FunctionCallBracketNode::emitCode): (JSC::FunctionCallDotNode::emitCode):
  • parser/Nodes.h: (JSC::ScopeNode::neededConstants): ditto

2008-11-10 Geoffrey Garen <[email protected]>

Reviewed by Darin Adler.


Updated a test after fixing https://bugs.webkit.org/show_bug.cgi?id=22174
Simplified op_call by nixing its responsibility for moving the value of
"this" into the first argument slot.

  • fast/js/global-recursion-on-full-stack-expected.txt: This test passes a little differently now, because the register layout has changed. Specifically, the stack overflow now happens in the call to f() instead of the initiation of the <script> tag, so it is caught, and it does not log an exception to the console.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/VM/Machine.cpp

    r38311 r38322  
    581581}
    582582
    583 NEVER_INLINE JSValue* Machine::callEval(CallFrame* callFrame, JSObject* thisObj, ScopeChainNode* scopeChain, RegisterFile* registerFile, int argv, int argc, JSValue*& exceptionValue)
     583NEVER_INLINE JSValue* Machine::callEval(CallFrame* callFrame, RegisterFile* registerFile, Register* argv, int argc, int registerOffset, JSValue*& exceptionValue)
    584584{
    585585    if (argc < 2)
    586586        return jsUndefined();
    587587
    588     JSValue* program = callFrame[argv + 1].jsValue(callFrame);
     588    JSValue* program = argv[1].jsValue(callFrame);
    589589
    590590    if (!program->isString())
     
    593593    UString programSource = asString(program)->value();
    594594
     595    ScopeChainNode* scopeChain = callFrame->scopeChain();
    595596    CodeBlock* codeBlock = callFrame->codeBlock();
    596597    RefPtr<EvalNode> evalNode = codeBlock->evalCodeCache.get(callFrame, programSource, scopeChain, exceptionValue);
     
    598599    JSValue* result = jsUndefined();
    599600    if (evalNode)
    600         result = callFrame->globalData().machine->execute(evalNode.get(), callFrame, thisObj, callFrame->registers() - registerFile->start() + argv + 1 + RegisterFile::CallFrameHeaderSize, scopeChain, &exceptionValue);
     601        result = callFrame->globalData().machine->execute(evalNode.get(), callFrame, callFrame->thisValue()->toThisObject(callFrame), callFrame->registers() - registerFile->start() + registerOffset, scopeChain, &exceptionValue);
    601602
    602603    return result;
     
    656657#ifndef NDEBUG
    657658
    658 void Machine::dumpCallFrame(const RegisterFile* registerFile, CallFrame* callFrame)
    659 {
    660     JSGlobalObject* globalObject = callFrame->scopeChain()->globalObject();
    661 
    662     CodeBlock* codeBlock = callFrame->codeBlock();
    663     codeBlock->dump(globalObject->globalExec());
    664 
    665     dumpRegisters(registerFile, callFrame);
    666 }
    667 
    668 void Machine::dumpRegisters(const RegisterFile* registerFile, CallFrame* callFrame)
     659void Machine::dumpCallFrame(CallFrame* callFrame)
     660{
     661    callFrame->codeBlock()->dump(callFrame);
     662    dumpRegisters(callFrame);
     663}
     664
     665void Machine::dumpRegisters(CallFrame* callFrame)
    669666{
    670667    printf("Register frame: \n\n");
     
    674671
    675672    CodeBlock* codeBlock = callFrame->codeBlock();
     673    RegisterFile* registerFile = &callFrame->scopeChain()->globalObject()->globalData()->machine->registerFile();
    676674    const Register* it;
    677675    const Register* end;
     
    10241022}
    10251023
    1026 JSValue* Machine::execute(EvalNode* evalNode, CallFrame* callFrame, JSObject* thisObj, int registerOffset, ScopeChainNode* scopeChain, JSValue** exception)
     1024JSValue* Machine::execute(EvalNode* evalNode, CallFrame* callFrame, JSObject* thisObj, int globalRegisterOffset, ScopeChainNode* scopeChain, JSValue** exception)
    10271025{
    10281026    ASSERT(!scopeChain->globalData->exception);
     
    10701068
    10711069    Register* oldEnd = m_registerFile.end();
    1072     Register* newEnd = m_registerFile.start() + registerOffset + codeBlock->numCalleeRegisters;
     1070    Register* newEnd = m_registerFile.start() + globalRegisterOffset + codeBlock->numCalleeRegisters;
    10731071    if (!m_registerFile.grow(newEnd)) {
    10741072        *exception = createStackOverflowError(callFrame);
     
    10761074    }
    10771075
    1078     CallFrame* newCallFrame = CallFrame::create(m_registerFile.start() + registerOffset);
     1076    CallFrame* newCallFrame = CallFrame::create(m_registerFile.start() + globalRegisterOffset);
    10791077
    10801078    // a 0 codeBlock indicates a built-in caller
     
    32653263    }
    32663264    BEGIN_OPCODE(op_call_eval) {
    3267         /* call_eval dst(r) func(r) thisVal(r) firstArg(r) argCount(n)
     3265        /* call_eval dst(r) func(r) argCount(n) registerOffset(n)
    32683266
    32693267           Call a function named "eval" with no explicit "this" value
     
    32783276        int dst = vPC[1].u.operand;
    32793277        int func = vPC[2].u.operand;
    3280         int thisVal = vPC[3].u.operand;
    3281         int firstArg = vPC[4].u.operand;
    3282         int argCount = vPC[5].u.operand;
     3278        int argCount = vPC[3].u.operand;
     3279        int registerOffset = vPC[4].u.operand;
    32833280
    32843281        JSValue* funcVal = callFrame[func].jsValue(callFrame);
    3285         JSValue* baseVal = callFrame[thisVal].jsValue(callFrame);
    3286 
    3287         ScopeChainNode* scopeChain = callFrame->scopeChain();
    3288         if (baseVal == scopeChain->globalObject() && funcVal == scopeChain->globalObject()->evalFunction()) {
    3289             JSObject* thisObject = asObject(callFrame[callFrame->codeBlock()->thisRegister].jsValue(callFrame));
    3290             JSValue* result = callEval(callFrame, thisObject, scopeChain, registerFile, firstArg, argCount, exceptionValue);
     3282
     3283        Register* newCallFrame = callFrame->registers() + registerOffset;
     3284        Register* argv = newCallFrame - RegisterFile::CallFrameHeaderSize - argCount;
     3285        JSValue* thisValue = argv[0].jsValue(callFrame);
     3286        JSGlobalObject* globalObject = callFrame->scopeChain()->globalObject();
     3287
     3288        if (thisValue == globalObject && funcVal == globalObject->evalFunction()) {
     3289            JSValue* result = callEval(callFrame, registerFile, argv, argCount, registerOffset, exceptionValue);
    32913290            if (exceptionValue)
    32923291                goto vm_throw;
    3293 
    32943292            callFrame[dst] = result;
    32953293
    3296             vPC += 7;
     3294            vPC += 5;
    32973295            NEXT_OPCODE;
    32983296        }
    32993297
    3300         // We didn't find the blessed version of eval, so reset vPC and process
    3301         // this instruction as a normal function call, supplying the proper 'this'
    3302         // value.
    3303         callFrame[thisVal] = baseVal->toThisObject(callFrame);
     3298        // We didn't find the blessed version of eval, so process this
     3299        // instruction as a normal function call.
    33043300
    33053301#if HAVE(COMPUTED_GOTO)
     
    33123308    }
    33133309    BEGIN_OPCODE(op_call) {
    3314         /* call dst(r) func(r) thisVal(r) firstArg(r) argCount(n) registerOffset(n)
    3315 
    3316            Perform a function call. Specifically, call register func
    3317            with a "this" value of register thisVal, and put the result
    3318            in register dst.
    3319 
    3320            The arguments start at register firstArg and go up to
    3321            argCount, but the "this" value is considered an implicit
    3322            first argument, so the argCount should be one greater than
    3323            the number of explicit arguments passed, and the register
    3324            after firstArg should contain the actual first
    3325            argument. This opcode will copy from the thisVal register
    3326            to the firstArg register, unless the register index of
    3327            thisVal is the special missing this object marker, which is
    3328            2^31-1; in that case, the global object will be used as the
    3329            "this" value.
    3330 
    3331            If func is a native code function, then this opcode calls
    3332            it and returns the value immediately.
    3333 
    3334            But if it is a JS function, then the current scope chain
    3335            and code block is set to the function's, and we slide the
    3336            register window so that the arguments would form the first
    3337            few local registers of the called function's register
    3338            window. In addition, a call frame header is written
    3339            immediately before the arguments; see the call frame
    3340            documentation for an explanation of how many registers a
    3341            call frame takes and what they contain. That many registers
    3342            before the firstArg register will be overwritten by the
    3343            call. In addition, any registers higher than firstArg +
    3344            argCount may be overwritten. Once this setup is complete,
    3345            execution continues from the called function's first
    3346            argument, and does not return until a "ret" opcode is
    3347            encountered.
     3310        /* call dst(r) func(r) argCount(n) registerOffset(n)
     3311
     3312           Perform a function call.
     3313           
     3314           registerOffset is the distance the callFrame pointer should move
     3315           before the VM initializes the new call frame's header.
     3316           
     3317           dst is where op_ret should store its result.
    33483318         */
    33493319
    33503320        int dst = vPC[1].u.operand;
    33513321        int func = vPC[2].u.operand;
    3352         int thisVal = vPC[3].u.operand;
    3353         int firstArg = vPC[4].u.operand;
    3354         int argCount = vPC[5].u.operand;
    3355         int registerOffset = vPC[6].u.operand;
     3322        int argCount = vPC[3].u.operand;
     3323        int registerOffset = vPC[4].u.operand;
    33563324
    33573325        JSValue* v = callFrame[func].jsValue(callFrame);
     
    33653333            CodeBlock* newCodeBlock = &functionBodyNode->byteCode(callDataScopeChain);
    33663334
    3367             callFrame[firstArg] = thisVal == missingThisObjectMarker() ? callFrame->globalThisValue() : callFrame[thisVal].jsValue(callFrame);
    3368            
    33693335            CallFrame* previousCallFrame = callFrame;
    33703336
     
    33763342            }
    33773343
    3378             callFrame->init(newCodeBlock, vPC + 7, callDataScopeChain, previousCallFrame, dst, argCount, asFunction(v));
     3344            callFrame->init(newCodeBlock, vPC + 5, callDataScopeChain, previousCallFrame, dst, argCount, asFunction(v));
    33793345            vPC = newCodeBlock->instructions.begin();
    33803346
     
    33873353
    33883354        if (callType == CallTypeHost) {
    3389             JSValue* thisValue = thisVal == missingThisObjectMarker() ? callFrame->globalThisValue() : callFrame[thisVal].jsValue(callFrame);
    3390             ArgList args(callFrame->registers() + firstArg + 1, argCount - 1);
    3391 
    33923355            ScopeChainNode* scopeChain = callFrame->scopeChain();
    33933356            CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + registerOffset);
    3394             newCallFrame->init(0, vPC + 7, scopeChain, callFrame, dst, argCount, 0);
     3357            newCallFrame->init(0, vPC + 5, scopeChain, callFrame, dst, argCount, 0);
     3358
     3359            Register* thisRegister = newCallFrame->registers() - RegisterFile::CallFrameHeaderSize - argCount;
     3360            ArgList args(thisRegister + 1, argCount - 1);
     3361
     3362            // FIXME: All host methods should be calling toThisObject, but this is not presently the case.
     3363            JSValue* thisValue = thisRegister->jsValue(callFrame);
     3364            if (thisValue == jsNull())
     3365                thisValue = callFrame->globalThisValue();
    33953366
    33963367            JSValue* returnValue;
     
    34033374            callFrame[dst] = returnValue;
    34043375
    3405             vPC += 7;
     3376            vPC += 5;
    34063377            NEXT_OPCODE;
    34073378        }
     
    35733544    }
    35743545    BEGIN_OPCODE(op_construct) {
    3575         /* construct dst(r) constr(r) constrProto(r) firstArg(r) argCount(n) registerOffset(n)
    3576 
    3577            Invoke register "constr" as a constructor. For JS
     3546        /* construct dst(r) func(r) argCount(n) registerOffset(n) proto(r) thisRegister(r)
     3547
     3548           Invoke register "func" as a constructor. For JS
    35783549           functions, the calling convention is exactly as for the
    35793550           "call" opcode, except that the "this" value is a newly
    3580            created Object. For native constructors, a null "this"
    3581            value is passed. In either case, the firstArg and argCount
     3551           created Object. For native constructors, no "this"
     3552           value is passed. In either case, the argCount and registerOffset
    35823553           registers are interpreted as for the "call" opcode.
    35833554
    3584            Register constrProto must contain the prototype property of
    3585            register constsr. This is to enable polymorphic inline
     3555           Register proto must contain the prototype property of
     3556           register func. This is to enable polymorphic inline
    35863557           caching of this lookup.
    35873558        */
    35883559
    35893560        int dst = vPC[1].u.operand;
    3590         int constr = vPC[2].u.operand;
    3591         int constrProto = vPC[3].u.operand;
    3592         int firstArg = vPC[4].u.operand;
    3593         int argCount = vPC[5].u.operand;
    3594         int registerOffset = vPC[6].u.operand;
    3595 
    3596         JSValue* v = callFrame[constr].jsValue(callFrame);
     3561        int func = vPC[2].u.operand;
     3562        int argCount = vPC[3].u.operand;
     3563        int registerOffset = vPC[4].u.operand;
     3564        int proto = vPC[5].u.operand;
     3565        int thisRegister = vPC[6].u.operand;
     3566
     3567        JSValue* v = callFrame[func].jsValue(callFrame);
    35973568
    35983569        ConstructData constructData;
     
    36053576
    36063577            StructureID* structure;
    3607             JSValue* prototype = callFrame[constrProto].jsValue(callFrame);
     3578            JSValue* prototype = callFrame[proto].jsValue(callFrame);
    36083579            if (prototype->isObject())
    36093580                structure = asObject(prototype)->inheritorID();
     
    36123583            JSObject* newObject = new (globalData) JSObject(structure);
    36133584
    3614             callFrame[firstArg] = newObject; // "this" value
     3585            callFrame[thisRegister] = newObject; // "this" value
    36153586
    36163587            CallFrame* previousCallFrame = callFrame;
     
    36343605
    36353606        if (constructType == ConstructTypeHost) {
    3636             ArgList args(callFrame->registers() + firstArg + 1, argCount - 1);
     3607            ArgList args(callFrame->registers() + thisRegister + 1, argCount - 1);
    36373608
    36383609            ScopeChainNode* scopeChain = callFrame->scopeChain();
     
    43654336    } while (0)
    43664337
     4338// This macro rewinds to the previous call frame because CTI functions that
     4339// throw stack overflow exceptions execute after the call frame has
     4340// optimistically moved forward.
     4341#define CTI_THROW_STACK_OVERFLOW() do { \
     4342    CallFrame* oldCallFrame = ARG_callFrame->callerFrame(); \
     4343    JSGlobalData* globalData = ARG_globalData; \
     4344    globalData->exception = createStackOverflowError(oldCallFrame); \
     4345    globalData->throwReturnAddress = CTI_RETURN_ADDRESS; \
     4346    ARG_setCallFrame(oldCallFrame); \
     4347    CTI_SET_RETURN_ADDRESS(reinterpret_cast<void*>(ctiVMThrowTrampoline)); \
     4348} while (0);
     4349
    43674350JSObject* Machine::cti_op_convert_this(CTI_ARGS)
    43684351{
     
    44534436}
    44544437
    4455 NEVER_INLINE void Machine::throwStackOverflowPreviousFrame(CallFrame* callFrame, JSGlobalData* globalData, void*& returnAddress)
    4456 {
    4457     globalData->exception = createStackOverflowError(callFrame->callerFrame());
    4458     globalData->throwReturnAddress = callFrame->returnPC();
    4459     ctiSetReturnAddress(&returnAddress, reinterpret_cast<void*>(ctiVMThrowTrampoline));
    4460 }
    4461 
    44624438void Machine::cti_register_file_check(CTI_ARGS)
    44634439{
     
    44674443        return;
    44684444
    4469     ARG_setCallFrame(ARG_callFrame->callerFrame());
    4470     throwStackOverflowPreviousFrame(ARG_callFrame, ARG_globalData, CTI_RETURN_ADDRESS);
     4445    CTI_THROW_STACK_OVERFLOW();
    44714446}
    44724447
     
    47454720        Register* newEnd = r + newCodeBlock->numCalleeRegisters;
    47464721        if (!ARG_registerFile->grow(newEnd)) {
    4747             ARG_globalData->exception = createStackOverflowError(oldCallFrame);
    4748             VM_THROW_EXCEPTION_2();
     4722            CTI_THROW_STACK_OVERFLOW();
     4723            VoidPtrPairValue pair = {{ 0, 0 }};
     4724            return pair.i;
    47494725        }
    47504726
     
    48114787            SamplingTool::HostCallRecord callRecord(CTI_SAMPLER);
    48124788
    4813             // All host methods should be calling toThisObject, but this is not presently the case.
     4789            // FIXME: All host methods should be calling toThisObject, but this is not presently the case.
    48144790            JSValue* thisValue = argv[0].jsValue(callFrame);
    48154791            if (thisValue == jsNull())
     
    49354911
    49364912    StructureID* structure;
    4937     if (ARG_src5->isObject())
    4938         structure = asObject(ARG_src5)->inheritorID();
     4913    if (ARG_src4->isObject())
     4914        structure = asObject(ARG_src4)->inheritorID();
    49394915    else
    49404916        structure = asFunction(ARG_src1)->m_scopeChain.node()->globalObject()->emptyObjectStructure();
     
    49504926    JSValue* constrVal = ARG_src1;
    49514927    int argCount = ARG_int3;
    4952     int firstArg = ARG_int6;
     4928    int thisRegister = ARG_int5;
    49534929
    49544930    ConstructData constructData;
     
    49564932
    49574933    if (constructType == ConstructTypeHost) {
    4958         ArgList argList(callFrame->registers() + firstArg + 1, argCount - 1);
     4934        ArgList argList(callFrame->registers() + thisRegister + 1, argCount - 1);
    49594935
    49604936        JSValue* returnValue;
     
    49704946    ASSERT(constructType == ConstructTypeNone);
    49714947
    4972     ARG_globalData->exception = createNotAConstructorError(callFrame, constrVal, ARG_instr4, callFrame->codeBlock());
     4948    ARG_globalData->exception = createNotAConstructorError(callFrame, constrVal, ARG_instr6, callFrame->codeBlock());
    49734949    VM_THROW_EXCEPTION();
    49744950}
     
    55695545    CallFrame* callFrame = ARG_callFrame;
    55705546    RegisterFile* registerFile = ARG_registerFile;
    5571     CodeBlock* codeBlock = callFrame->codeBlock();
    5572     ScopeChainNode* scopeChain = callFrame->scopeChain();
    55735547
    55745548    Machine* machine = ARG_globalData->machine;
     
    55775551    int registerOffset = ARG_int2;
    55785552    int argCount = ARG_int3;
    5579     JSValue* baseVal = ARG_src5;
    5580 
    5581     if (baseVal == scopeChain->globalObject() && funcVal == scopeChain->globalObject()->evalFunction()) {
    5582         JSObject* thisObject = callFrame[codeBlock->thisRegister].jsValue(callFrame)->toThisObject(callFrame);
     5553
     5554    Register* newCallFrame = callFrame->registers() + registerOffset;
     5555    Register* argv = newCallFrame - RegisterFile::CallFrameHeaderSize - argCount;
     5556    JSValue* thisValue = argv[0].jsValue(callFrame);
     5557    JSGlobalObject* globalObject = callFrame->scopeChain()->globalObject();
     5558
     5559    if (thisValue == globalObject && funcVal == globalObject->evalFunction()) {
    55835560        JSValue* exceptionValue = noValue();
    5584         JSValue* result = machine->callEval(callFrame, thisObject, scopeChain, registerFile, registerOffset - RegisterFile::CallFrameHeaderSize - argCount, argCount, exceptionValue);
     5561        JSValue* result = machine->callEval(callFrame, registerFile, argv, argCount, registerOffset, exceptionValue);
    55855562        if (UNLIKELY(exceptionValue != noValue())) {
    55865563            ARG_globalData->exception = exceptionValue;
     
    59455922    CallFrame* callFrame = ARG_callFrame;
    59465923    CodeBlock* codeBlock = callFrame->codeBlock();
    5947 
    5948     ASSERT(codeBlock->ctiReturnAddressVPCMap.contains(ARG_globalData->throwReturnAddress));
    5949     unsigned vPCIndex = codeBlock->ctiReturnAddressVPCMap.get(ARG_globalData->throwReturnAddress);
    5950 
    5951     JSValue* exceptionValue = ARG_globalData->exception;
     5924    JSGlobalData* globalData = ARG_globalData;
     5925
     5926    ASSERT(codeBlock->ctiReturnAddressVPCMap.contains(globalData->throwReturnAddress));
     5927    unsigned vPCIndex = codeBlock->ctiReturnAddressVPCMap.get(globalData->throwReturnAddress);
     5928
     5929    JSValue* exceptionValue = globalData->exception;
    59525930    ASSERT(exceptionValue);
    5953     ARG_globalData->exception = noValue();
    5954 
    5955     Instruction* handlerVPC = ARG_globalData->machine->throwException(callFrame, exceptionValue, codeBlock->instructions.begin() + vPCIndex, false);
     5931    globalData->exception = noValue();
     5932
     5933    Instruction* handlerVPC = globalData->machine->throwException(callFrame, exceptionValue, codeBlock->instructions.begin() + vPCIndex, false);
    59565934
    59575935    if (!handlerVPC) {
Note: See TracChangeset for help on using the changeset viewer.