⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 181835 in webkit


Ignore:
Timestamp:
Mar 22, 2015, 12:35:26 PM (11 years ago)
Author:
fpizlo@apple.com
Message:

DFG OSR exit shouldn't assume that the frame count for exit is greater than the frame count in DFG
https://bugs.webkit.org/show_bug.cgi?id=142948

Reviewed by Sam Weinig.

It's necessary to ensure that the stack pointer accounts for the extent of our stack usage
since a signal may clobber the area below the stack pointer. When the DFG is executing,
the stack pointer accounts for the DFG's worst-case stack usage. When we OSR exit back to
baseline, we will use a different amount of stack. This is because baseline is a different
compiler. It will make different decisions. So it will use a different amount of stack.

This gets tricky when we are in the process of doing an OSR exit, because we are sort of
incrementally transforming the stack from how it looked in the DFG to how it will look in
baseline. The most conservative approach would be to set the stack pointer to the max of
DFG and baseline.

When this code was written, a reckless assumption was made: that the stack usage in
baseline is always at least as large as the stack usage in DFG. Based on this incorrect
assumption, the code first adjusts the stack pointer to account for the baseline stack
usage. This sort of usually works, because usually baseline does happen to use more stack.
But that's not an invariant. Nobody guarantees this. We will never make any changes that
would make this be guaranteed, because that would be antithetical to how optimizing
compilers work. The DFG should be allowed to use however much stack it decides that it
should use in order to get good performance, and it shouldn't try to guarantee that it
always uses less stack than baseline.

As such, we must always assume that the frame size for DFG execution (i.e.
frameRegisterCount) and the frame size in baseline once we exit (i.e.
requiredRegisterCountForExit) are two independent quantities and they have no
relationship.

Fortunately, though, this code can be made correct by just moving the stack adjustment to
just before we do conversions. This is because we have since changed the OSR exit
algorithm to first lift up all state from the DFG state into a scratch buffer, and then to
drop it out of the scratch buffer and into the stack according to the baseline layout. The
point just before conversions is the point where we have finished reading the DFG frame
and will not read it anymore, and we haven't started writing the baseline frame. So, at
this point it is safe to set the stack pointer to account for the frame size at exit.

This is benign because baseline happens to create larger frames than DFG.

  • dfg/DFGOSRExitCompiler32_64.cpp:

(JSC::DFG::OSRExitCompiler::compileExit):

  • dfg/DFGOSRExitCompiler64.cpp:

(JSC::DFG::OSRExitCompiler::compileExit):

  • dfg/DFGOSRExitCompilerCommon.cpp:

(JSC::DFG::adjustAndJumpToTarget):

Location:
trunk/Source/JavaScriptCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r181834 r181835  
     12015-03-22  Filip Pizlo  <fpizlo@apple.com>
     2
     3        DFG OSR exit shouldn't assume that the frame count for exit is greater than the frame count in DFG
     4        https://bugs.webkit.org/show_bug.cgi?id=142948
     5
     6        Reviewed by Sam Weinig.
     7       
     8        It's necessary to ensure that the stack pointer accounts for the extent of our stack usage
     9        since a signal may clobber the area below the stack pointer. When the DFG is executing,
     10        the stack pointer accounts for the DFG's worst-case stack usage. When we OSR exit back to
     11        baseline, we will use a different amount of stack. This is because baseline is a different
     12        compiler. It will make different decisions. So it will use a different amount of stack.
     13       
     14        This gets tricky when we are in the process of doing an OSR exit, because we are sort of
     15        incrementally transforming the stack from how it looked in the DFG to how it will look in
     16        baseline. The most conservative approach would be to set the stack pointer to the max of
     17        DFG and baseline.
     18       
     19        When this code was written, a reckless assumption was made: that the stack usage in
     20        baseline is always at least as large as the stack usage in DFG. Based on this incorrect
     21        assumption, the code first adjusts the stack pointer to account for the baseline stack
     22        usage. This sort of usually works, because usually baseline does happen to use more stack.
     23        But that's not an invariant. Nobody guarantees this. We will never make any changes that
     24        would make this be guaranteed, because that would be antithetical to how optimizing
     25        compilers work. The DFG should be allowed to use however much stack it decides that it
     26        should use in order to get good performance, and it shouldn't try to guarantee that it
     27        always uses less stack than baseline.
     28       
     29        As such, we must always assume that the frame size for DFG execution (i.e.
     30        frameRegisterCount) and the frame size in baseline once we exit (i.e.
     31        requiredRegisterCountForExit) are two independent quantities and they have no
     32        relationship.
     33       
     34        Fortunately, though, this code can be made correct by just moving the stack adjustment to
     35        just before we do conversions. This is because we have since changed the OSR exit
     36        algorithm to first lift up all state from the DFG state into a scratch buffer, and then to
     37        drop it out of the scratch buffer and into the stack according to the baseline layout. The
     38        point just before conversions is the point where we have finished reading the DFG frame
     39        and will not read it anymore, and we haven't started writing the baseline frame. So, at
     40        this point it is safe to set the stack pointer to account for the frame size at exit.
     41       
     42        This is benign because baseline happens to create larger frames than DFG.
     43
     44        * dfg/DFGOSRExitCompiler32_64.cpp:
     45        (JSC::DFG::OSRExitCompiler::compileExit):
     46        * dfg/DFGOSRExitCompiler64.cpp:
     47        (JSC::DFG::OSRExitCompiler::compileExit):
     48        * dfg/DFGOSRExitCompilerCommon.cpp:
     49        (JSC::DFG::adjustAndJumpToTarget):
     50
    1512015-03-22  Filip Pizlo  <fpizlo@apple.com>
    252
  • trunk/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp

    r168535 r181835  
    4949    }
    5050   
    51     // Need to ensure that the stack pointer accounts for the worst-case stack usage at exit.
    52     m_jit.addPtr(
    53         CCallHelpers::TrustedImm32(
    54             -m_jit.codeBlock()->jitCode()->dfgCommon()->requiredRegisterCountForExit * sizeof(Register)),
    55         CCallHelpers::framePointerRegister, CCallHelpers::stackPointerRegister);
    56    
    5751    // 2) Perform speculation recovery. This only comes into play when an operation
    5852    //    starts mutating state before verifying the speculation it has already made.
     
    259253        }
    260254    }
     255   
     256    // Need to ensure that the stack pointer accounts for the worst-case stack usage at exit. This
     257    // could toast some stack that the DFG used. We need to do it before storing to stack offsets
     258    // used by baseline.
     259    m_jit.addPtr(
     260        CCallHelpers::TrustedImm32(
     261            -m_jit.codeBlock()->jitCode()->dfgCommon()->requiredRegisterCountForExit * sizeof(Register)),
     262        CCallHelpers::framePointerRegister, CCallHelpers::stackPointerRegister);
    261263   
    262264    // 7) Do all data format conversions and store the results into the stack.
  • trunk/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp

    r168535 r181835  
    11/*
    2  * Copyright (C) 2011, 2013, 2014 Apple Inc. All rights reserved.
     2 * Copyright (C) 2011, 2013-2015 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    5353    }
    5454   
    55     // Need to ensure that the stack pointer accounts for the worst-case stack usage at exit.
    56     m_jit.addPtr(
    57         CCallHelpers::TrustedImm32(
    58             -m_jit.codeBlock()->jitCode()->dfgCommon()->requiredRegisterCountForExit * sizeof(Register)),
    59         CCallHelpers::framePointerRegister, CCallHelpers::stackPointerRegister);
    60    
    6155    // 2) Perform speculation recovery. This only comes into play when an operation
    6256    //    starts mutating state before verifying the speculation it has already made.
     
    252246    }
    253247   
     248    // Need to ensure that the stack pointer accounts for the worst-case stack usage at exit. This
     249    // could toast some stack that the DFG used. We need to do it before storing to stack offsets
     250    // used by baseline.
     251    m_jit.addPtr(
     252        CCallHelpers::TrustedImm32(
     253            -m_jit.codeBlock()->jitCode()->dfgCommon()->requiredRegisterCountForExit * sizeof(Register)),
     254        CCallHelpers::framePointerRegister, CCallHelpers::stackPointerRegister);
     255   
    254256    // 7) Do all data format conversions and store the results into the stack.
    255257   
  • trunk/Source/JavaScriptCore/dfg/DFGOSRExitCompilerCommon.cpp

    r180279 r181835  
    267267{
    268268#if ENABLE(GGC)
    269     // 11) Write barrier the owner executables because we're jumping into a different block.
    270269    jit.move(AssemblyHelpers::TrustedImmPtr(jit.codeBlock()->ownerExecutable()), GPRInfo::nonArgGPR0);
    271270    osrWriteBarrier(jit, GPRInfo::nonArgGPR0, GPRInfo::nonArgGPR1);
Note: See TracChangeset for help on using the changeset viewer.