Enhance MacroAssembler::probe() to allow the probe function to resize the stack frame...
authormark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 21 Aug 2017 04:26:40 +0000 (04:26 +0000)
committermark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 21 Aug 2017 04:26:40 +0000 (04:26 +0000)
https://bugs.webkit.org/show_bug.cgi?id=175688
<rdar://problem/33436870>

Reviewed by JF Bastien.

With this patch, the clients of the MacroAssembler::probe() can now change
stack values without having to worry about whether there is enough room in the
current stack frame for it or not.  This is done using the Probe::Context's stack
member like so:

    jit.probe([] (Probe::Context& context) {
        auto cpu = context.cpu;
        auto stack = context.stack();
        uintptr_t* currentSP = cpu.sp<uintptr_t*>();

        // Get a value at the current stack pointer location.
        auto value = stack.get<uintptr_t>(currentSP);

        // Set a value above the current stack pointer (within current frame).
        stack.set<uintptr_t>(currentSP + 10, value);

        // Set a value below the current stack pointer (out of current frame).
        stack.set<uintptr_t>(currentSP - 10, value);

        // Set the new stack pointer.
        cpu.sp() = currentSP - 20;
    });

What happens behind the scene:

1. the generated JIT probe code will now call Probe::executeProbe(), and
   Probe::executeProbe() will in turn call the client's probe function.

   Probe::executeProbe() receives the Probe::State on the machine stack passed
   to it by the probe trampoline.  Probe::executeProbe() will instantiate a
   Probe::Context to be passed to the client's probe function.  The client will
   no longer see the Probe::State directly.

2. The Probe::Context comes with a Probe::Stack which serves as a manager of
   stack pages.  Currently, each page is 1K in size.
   Probe::Context::stack() returns a reference to an instance of Probe::Stack.

3. Invoking get() of set() on Probe::Stack with an address will lead to the
   following:

   a. the address will be decoded to a baseAddress that points to the 1K page
      that contains that address.

   b. the Probe::Stack will check if it already has a cached 1K page for that baseAddress.
      If so, go to step (f).  Else, continue with step (c).

   c. the Probe::Stack will malloc a 1K mirror page, and memcpy the 1K stack page
      for that specified baseAddress to this mirror page.

   d. the mirror page will be added to the ProbeStack's m_pages HashMap,
      keyed on the baseAddress.

   e. the ProbeStack will also cache the last baseAddress and its corresponding
      mirror page in use.  With memory accesses tending to be localized, this
      will save us from having to look up the page in the HashMap.

   f. get() will map the requested address to a physical address in the mirror
      page, and return the value at that location.

   g. set() will map the requested address to a physical address in the mirror
      page, and set the value at that location in the mirror page.

      set() will also set a dirty bit corresponding to the "cache line" that
      was modified in the mirror page.

4. When the client's probe function returns, Probe::executeProbe() will check if
   there are stack changes that need to be applied.  If stack changes are needed:

   a. Probe::executeProbe() will adjust the stack pointer to ensure enough stack
      space is available to flush the dirty stack pages.  It will also register a
      flushStackDirtyPages callback function in the Probe::State.  Thereafter,
      Probe::executeProbe() returns to the probe trampoline.

   b. the probe trampoline adjusts the stack pointer, moves the Probe::State to
      a safe place if needed, and then calls the flushStackDirtyPages callback
      if needed.

   c. the flushStackDirtyPages() callback iterates the Probe::Stack's m_pages
      HashMap and flush all dirty "cache lines" to the machine stack.
      Thereafter, flushStackDirtyPages() returns to the probe trampoline.

   d. lastly, the probe trampoline will restore all register values and return
      to the pc set in the Probe::State.

To make this patch work, I also had to do the following work:

5. Refactor MacroAssembler::CPUState into Probe::CPUState.
   Mainly, this means moving the code over to ProbeContext.h.
   I also added some convenience accessor methods for spr registers.

   Moved Probe::Context over to its own file ProbeContext.h/cpp.

6. Fix all probe trampolines to pass the address of Probe::executeProbe in
   addition to the client's probe function and arg.

   I also took this opportunity to optimize the generated JIT probe code to
   minimize the amount of memory stores needed.

7. Simplified the ARM64 probe trampoline.  The ARM64 probe only supports changing
   either lr or pc (or neither), but not both at in the same probe invocation.
   The ARM64 probe trampoline used to have to check for this invariant in the
   assembly trampoline code.  With the introduction of Probe::executeProbe(),
   we can now do it there and simplify the trampoline.

8. Fix a bug in the old  ARM64 probe trampoline for the case where the client
   changes lr.  That code path never worked before, but has now been fixed.

9. Removed trustedImm32FromPtr() helper functions in MacroAssemblerARM and
   MacroAssemblerARMv7.

   We can now use move() with TrustedImmPtr, and it does the same thing but in a
   more generic way.

       10. ARMv7's move() emitter may encode a T1 move instruction, which happens to have
   the same semantics as movs (according to the Thumb spec).  This means these
   instructions may trash the APSR flags before we have a chance to preserve them.

   This patch changes MacroAssemblerARMv7's probe() to preserve the APSR register
   early on.  This entails adding support for the mrs instruction in the
   ARMv7Assembler.

       10. Change testmasm's testProbeModifiesStackValues() to now modify stack values
   the easy way.

   Also fixed testmasm tests which check flag registers to only compare the
   portions that are modifiable by the client i.e. some masking is applied.

This patch has passed the testmasm tests on x86, x86_64, arm64, and armv7.

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* assembler/ARMv7Assembler.h:
(JSC::ARMv7Assembler::mrs):
* assembler/AbstractMacroAssembler.h:
* assembler/MacroAssembler.cpp:
(JSC::stdFunctionCallback):
(JSC::MacroAssembler::probe):
* assembler/MacroAssembler.h:
(JSC::MacroAssembler::CPUState::gprName): Deleted.
(JSC::MacroAssembler::CPUState::sprName): Deleted.
(JSC::MacroAssembler::CPUState::fprName): Deleted.
(JSC::MacroAssembler::CPUState::gpr): Deleted.
(JSC::MacroAssembler::CPUState::spr): Deleted.
(JSC::MacroAssembler::CPUState::fpr): Deleted.
(JSC:: const): Deleted.
(JSC::MacroAssembler::CPUState::fpr const): Deleted.
(JSC::MacroAssembler::CPUState::pc): Deleted.
(JSC::MacroAssembler::CPUState::fp): Deleted.
(JSC::MacroAssembler::CPUState::sp): Deleted.
(JSC::MacroAssembler::CPUState::pc const): Deleted.
(JSC::MacroAssembler::CPUState::fp const): Deleted.
(JSC::MacroAssembler::CPUState::sp const): Deleted.
(JSC::Probe::State::gpr): Deleted.
(JSC::Probe::State::spr): Deleted.
(JSC::Probe::State::fpr): Deleted.
(JSC::Probe::State::gprName): Deleted.
(JSC::Probe::State::sprName): Deleted.
(JSC::Probe::State::fprName): Deleted.
(JSC::Probe::State::pc): Deleted.
(JSC::Probe::State::fp): Deleted.
(JSC::Probe::State::sp): Deleted.
* assembler/MacroAssemblerARM.cpp:
(JSC::MacroAssembler::probe):
* assembler/MacroAssemblerARM.h:
(JSC::MacroAssemblerARM::trustedImm32FromPtr): Deleted.
* assembler/MacroAssemblerARM64.cpp:
(JSC::MacroAssembler::probe):
(JSC::arm64ProbeError): Deleted.
* assembler/MacroAssemblerARMv7.cpp:
(JSC::MacroAssembler::probe):
* assembler/MacroAssemblerARMv7.h:
(JSC::MacroAssemblerARMv7::armV7Condition):
(JSC::MacroAssemblerARMv7::trustedImm32FromPtr): Deleted.
* assembler/MacroAssemblerPrinter.cpp:
(JSC::Printer::printCallback):
* assembler/MacroAssemblerPrinter.h:
* assembler/MacroAssemblerX86Common.cpp:
(JSC::ctiMasmProbeTrampoline):
(JSC::MacroAssembler::probe):
* assembler/Printer.h:
(JSC::Printer::Context::Context):
* assembler/ProbeContext.cpp: Added.
(JSC::Probe::executeProbe):
(JSC::Probe::handleProbeStackInitialization):
(JSC::Probe::probeStateForContext):
* assembler/ProbeContext.h: Added.
(JSC::Probe::CPUState::gprName):
(JSC::Probe::CPUState::sprName):
(JSC::Probe::CPUState::fprName):
(JSC::Probe::CPUState::gpr):
(JSC::Probe::CPUState::spr):
(JSC::Probe::CPUState::fpr):
(JSC::Probe:: const):
(JSC::Probe::CPUState::fpr const):
(JSC::Probe::CPUState::pc):
(JSC::Probe::CPUState::fp):
(JSC::Probe::CPUState::sp):
(JSC::Probe::CPUState::pc const):
(JSC::Probe::CPUState::fp const):
(JSC::Probe::CPUState::sp const):
(JSC::Probe::Context::Context):
(JSC::Probe::Context::gpr):
(JSC::Probe::Context::spr):
(JSC::Probe::Context::fpr):
(JSC::Probe::Context::gprName):
(JSC::Probe::Context::sprName):
(JSC::Probe::Context::fprName):
(JSC::Probe::Context::pc):
(JSC::Probe::Context::fp):
(JSC::Probe::Context::sp):
(JSC::Probe::Context::stack):
(JSC::Probe::Context::hasWritesToFlush):
(JSC::Probe::Context::releaseStack):
* assembler/ProbeStack.cpp: Added.
(JSC::Probe::Page::Page):
(JSC::Probe::Page::flushWrites):
(JSC::Probe::Stack::Stack):
(JSC::Probe::Stack::hasWritesToFlush):
(JSC::Probe::Stack::flushWrites):
(JSC::Probe::Stack::ensurePageFor):
* assembler/ProbeStack.h: Added.
(JSC::Probe::Page::baseAddressFor):
(JSC::Probe::Page::chunkAddressFor):
(JSC::Probe::Page::baseAddress):
(JSC::Probe::Page::get):
(JSC::Probe::Page::set):
(JSC::Probe::Page::hasWritesToFlush const):
(JSC::Probe::Page::flushWritesIfNeeded):
(JSC::Probe::Page::dirtyBitFor):
(JSC::Probe::Page::physicalAddressFor):
(JSC::Probe::Stack::Stack):
(JSC::Probe::Stack::lowWatermark):
(JSC::Probe::Stack::get):
(JSC::Probe::Stack::set):
(JSC::Probe::Stack::newStackPointer const):
(JSC::Probe::Stack::setNewStackPointer):
(JSC::Probe::Stack::isValid):
(JSC::Probe::Stack::pageFor):
* assembler/testmasm.cpp:
(JSC::testProbeReadsArgumentRegisters):
(JSC::testProbeWritesArgumentRegisters):
(JSC::testProbePreservesGPRS):
(JSC::testProbeModifiesStackPointer):
(JSC::testProbeModifiesStackPointerToInsideProbeStateOnStack):
(JSC::testProbeModifiesStackPointerToNBytesBelowSP):
(JSC::testProbeModifiesProgramCounter):
(JSC::testProbeModifiesStackValues):
(JSC::run):
(): Deleted.
(JSC::fillStack): Deleted.
(JSC::testProbeModifiesStackWithCallback): Deleted.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@220958 268f45cc-cd09-0410-ab3c-d52691b4dbfc

21 files changed:
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/assembler/ARMv7Assembler.h
Source/JavaScriptCore/assembler/AbstractMacroAssembler.h
Source/JavaScriptCore/assembler/MacroAssembler.cpp
Source/JavaScriptCore/assembler/MacroAssembler.h
Source/JavaScriptCore/assembler/MacroAssemblerARM.cpp
Source/JavaScriptCore/assembler/MacroAssemblerARM.h
Source/JavaScriptCore/assembler/MacroAssemblerARM64.cpp
Source/JavaScriptCore/assembler/MacroAssemblerARMv7.cpp
Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h
Source/JavaScriptCore/assembler/MacroAssemblerPrinter.cpp
Source/JavaScriptCore/assembler/MacroAssemblerPrinter.h
Source/JavaScriptCore/assembler/MacroAssemblerX86Common.cpp
Source/JavaScriptCore/assembler/Printer.h
Source/JavaScriptCore/assembler/ProbeContext.cpp [new file with mode: 0644]
Source/JavaScriptCore/assembler/ProbeContext.h [new file with mode: 0644]
Source/JavaScriptCore/assembler/ProbeStack.cpp [new file with mode: 0644]
Source/JavaScriptCore/assembler/ProbeStack.h [new file with mode: 0644]
Source/JavaScriptCore/assembler/testmasm.cpp

index 05f5673..0aeb44c 100644 (file)
@@ -76,6 +76,8 @@ set(JavaScriptCore_SOURCES
     assembler/MacroAssemblerPrinter.cpp
     assembler/MacroAssemblerX86Common.cpp
     assembler/Printer.cpp
+    assembler/ProbeContext.cpp
+    assembler/ProbeStack.cpp
 
     b3/air/AirAllocateRegistersAndStackByLinearScan.cpp
     b3/air/AirAllocateRegistersByGraphColoring.cpp
index 5e59a9d..057e6b1 100644 (file)
@@ -1,3 +1,263 @@
+2017-08-20  Mark Lam  <mark.lam@apple.com>
+
+        Enhance MacroAssembler::probe() to allow the probe function to resize the stack frame and alter stack data in one pass.
+        https://bugs.webkit.org/show_bug.cgi?id=175688
+        <rdar://problem/33436870>
+
+        Reviewed by JF Bastien.
+
+        With this patch, the clients of the MacroAssembler::probe() can now change
+        stack values without having to worry about whether there is enough room in the
+        current stack frame for it or not.  This is done using the Probe::Context's stack
+        member like so:
+
+            jit.probe([] (Probe::Context& context) {
+                auto cpu = context.cpu;
+                auto stack = context.stack();
+                uintptr_t* currentSP = cpu.sp<uintptr_t*>();
+
+                // Get a value at the current stack pointer location.
+                auto value = stack.get<uintptr_t>(currentSP);
+
+                // Set a value above the current stack pointer (within current frame).
+                stack.set<uintptr_t>(currentSP + 10, value);
+
+                // Set a value below the current stack pointer (out of current frame).
+                stack.set<uintptr_t>(currentSP - 10, value);
+
+                // Set the new stack pointer.
+                cpu.sp() = currentSP - 20;
+            });
+
+        What happens behind the scene:
+
+        1. the generated JIT probe code will now call Probe::executeProbe(), and
+           Probe::executeProbe() will in turn call the client's probe function.
+
+           Probe::executeProbe() receives the Probe::State on the machine stack passed
+           to it by the probe trampoline.  Probe::executeProbe() will instantiate a
+           Probe::Context to be passed to the client's probe function.  The client will
+           no longer see the Probe::State directly.
+
+        2. The Probe::Context comes with a Probe::Stack which serves as a manager of
+           stack pages.  Currently, each page is 1K in size.
+           Probe::Context::stack() returns a reference to an instance of Probe::Stack.
+
+        3. Invoking get() of set() on Probe::Stack with an address will lead to the
+           following:
+
+           a. the address will be decoded to a baseAddress that points to the 1K page
+              that contains that address.
+
+           b. the Probe::Stack will check if it already has a cached 1K page for that baseAddress.
+              If so, go to step (f).  Else, continue with step (c).
+
+           c. the Probe::Stack will malloc a 1K mirror page, and memcpy the 1K stack page
+              for that specified baseAddress to this mirror page.
+
+           d. the mirror page will be added to the ProbeStack's m_pages HashMap,
+              keyed on the baseAddress.
+
+           e. the ProbeStack will also cache the last baseAddress and its corresponding
+              mirror page in use.  With memory accesses tending to be localized, this
+              will save us from having to look up the page in the HashMap.
+
+           f. get() will map the requested address to a physical address in the mirror
+              page, and return the value at that location.
+
+           g. set() will map the requested address to a physical address in the mirror
+              page, and set the value at that location in the mirror page.
+
+              set() will also set a dirty bit corresponding to the "cache line" that
+              was modified in the mirror page.
+
+        4. When the client's probe function returns, Probe::executeProbe() will check if
+           there are stack changes that need to be applied.  If stack changes are needed:
+
+           a. Probe::executeProbe() will adjust the stack pointer to ensure enough stack
+              space is available to flush the dirty stack pages.  It will also register a
+              flushStackDirtyPages callback function in the Probe::State.  Thereafter,
+              Probe::executeProbe() returns to the probe trampoline.
+
+           b. the probe trampoline adjusts the stack pointer, moves the Probe::State to
+              a safe place if needed, and then calls the flushStackDirtyPages callback
+              if needed.
+
+           c. the flushStackDirtyPages() callback iterates the Probe::Stack's m_pages
+              HashMap and flush all dirty "cache lines" to the machine stack.
+              Thereafter, flushStackDirtyPages() returns to the probe trampoline.
+
+           d. lastly, the probe trampoline will restore all register values and return
+              to the pc set in the Probe::State.
+
+        To make this patch work, I also had to do the following work:
+
+        5. Refactor MacroAssembler::CPUState into Probe::CPUState.
+           Mainly, this means moving the code over to ProbeContext.h.
+           I also added some convenience accessor methods for spr registers. 
+
+           Moved Probe::Context over to its own file ProbeContext.h/cpp.
+
+        6. Fix all probe trampolines to pass the address of Probe::executeProbe in
+           addition to the client's probe function and arg.
+
+           I also took this opportunity to optimize the generated JIT probe code to
+           minimize the amount of memory stores needed. 
+
+        7. Simplified the ARM64 probe trampoline.  The ARM64 probe only supports changing
+           either lr or pc (or neither), but not both at in the same probe invocation.
+           The ARM64 probe trampoline used to have to check for this invariant in the
+           assembly trampoline code.  With the introduction of Probe::executeProbe(),
+           we can now do it there and simplify the trampoline.
+
+        8. Fix a bug in the old  ARM64 probe trampoline for the case where the client
+           changes lr.  That code path never worked before, but has now been fixed.
+
+        9. Removed trustedImm32FromPtr() helper functions in MacroAssemblerARM and
+           MacroAssemblerARMv7.
+
+           We can now use move() with TrustedImmPtr, and it does the same thing but in a
+           more generic way.
+
+       10. ARMv7's move() emitter may encode a T1 move instruction, which happens to have
+           the same semantics as movs (according to the Thumb spec).  This means these
+           instructions may trash the APSR flags before we have a chance to preserve them.
+
+           This patch changes MacroAssemblerARMv7's probe() to preserve the APSR register
+           early on.  This entails adding support for the mrs instruction in the
+           ARMv7Assembler.
+
+       10. Change testmasm's testProbeModifiesStackValues() to now modify stack values
+           the easy way.
+
+           Also fixed testmasm tests which check flag registers to only compare the
+           portions that are modifiable by the client i.e. some masking is applied.
+
+        This patch has passed the testmasm tests on x86, x86_64, arm64, and armv7.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * assembler/ARMv7Assembler.h:
+        (JSC::ARMv7Assembler::mrs):
+        * assembler/AbstractMacroAssembler.h:
+        * assembler/MacroAssembler.cpp:
+        (JSC::stdFunctionCallback):
+        (JSC::MacroAssembler::probe):
+        * assembler/MacroAssembler.h:
+        (JSC::MacroAssembler::CPUState::gprName): Deleted.
+        (JSC::MacroAssembler::CPUState::sprName): Deleted.
+        (JSC::MacroAssembler::CPUState::fprName): Deleted.
+        (JSC::MacroAssembler::CPUState::gpr): Deleted.
+        (JSC::MacroAssembler::CPUState::spr): Deleted.
+        (JSC::MacroAssembler::CPUState::fpr): Deleted.
+        (JSC:: const): Deleted.
+        (JSC::MacroAssembler::CPUState::fpr const): Deleted.
+        (JSC::MacroAssembler::CPUState::pc): Deleted.
+        (JSC::MacroAssembler::CPUState::fp): Deleted.
+        (JSC::MacroAssembler::CPUState::sp): Deleted.
+        (JSC::MacroAssembler::CPUState::pc const): Deleted.
+        (JSC::MacroAssembler::CPUState::fp const): Deleted.
+        (JSC::MacroAssembler::CPUState::sp const): Deleted.
+        (JSC::Probe::State::gpr): Deleted.
+        (JSC::Probe::State::spr): Deleted.
+        (JSC::Probe::State::fpr): Deleted.
+        (JSC::Probe::State::gprName): Deleted.
+        (JSC::Probe::State::sprName): Deleted.
+        (JSC::Probe::State::fprName): Deleted.
+        (JSC::Probe::State::pc): Deleted.
+        (JSC::Probe::State::fp): Deleted.
+        (JSC::Probe::State::sp): Deleted.
+        * assembler/MacroAssemblerARM.cpp:
+        (JSC::MacroAssembler::probe):
+        * assembler/MacroAssemblerARM.h:
+        (JSC::MacroAssemblerARM::trustedImm32FromPtr): Deleted.
+        * assembler/MacroAssemblerARM64.cpp:
+        (JSC::MacroAssembler::probe):
+        (JSC::arm64ProbeError): Deleted.
+        * assembler/MacroAssemblerARMv7.cpp:
+        (JSC::MacroAssembler::probe):
+        * assembler/MacroAssemblerARMv7.h:
+        (JSC::MacroAssemblerARMv7::armV7Condition):
+        (JSC::MacroAssemblerARMv7::trustedImm32FromPtr): Deleted.
+        * assembler/MacroAssemblerPrinter.cpp:
+        (JSC::Printer::printCallback):
+        * assembler/MacroAssemblerPrinter.h:
+        * assembler/MacroAssemblerX86Common.cpp:
+        (JSC::ctiMasmProbeTrampoline):
+        (JSC::MacroAssembler::probe):
+        * assembler/Printer.h:
+        (JSC::Printer::Context::Context):
+        * assembler/ProbeContext.cpp: Added.
+        (JSC::Probe::executeProbe):
+        (JSC::Probe::handleProbeStackInitialization):
+        (JSC::Probe::probeStateForContext):
+        * assembler/ProbeContext.h: Added.
+        (JSC::Probe::CPUState::gprName):
+        (JSC::Probe::CPUState::sprName):
+        (JSC::Probe::CPUState::fprName):
+        (JSC::Probe::CPUState::gpr):
+        (JSC::Probe::CPUState::spr):
+        (JSC::Probe::CPUState::fpr):
+        (JSC::Probe:: const):
+        (JSC::Probe::CPUState::fpr const):
+        (JSC::Probe::CPUState::pc):
+        (JSC::Probe::CPUState::fp):
+        (JSC::Probe::CPUState::sp):
+        (JSC::Probe::CPUState::pc const):
+        (JSC::Probe::CPUState::fp const):
+        (JSC::Probe::CPUState::sp const):
+        (JSC::Probe::Context::Context):
+        (JSC::Probe::Context::gpr):
+        (JSC::Probe::Context::spr):
+        (JSC::Probe::Context::fpr):
+        (JSC::Probe::Context::gprName):
+        (JSC::Probe::Context::sprName):
+        (JSC::Probe::Context::fprName):
+        (JSC::Probe::Context::pc):
+        (JSC::Probe::Context::fp):
+        (JSC::Probe::Context::sp):
+        (JSC::Probe::Context::stack):
+        (JSC::Probe::Context::hasWritesToFlush):
+        (JSC::Probe::Context::releaseStack):
+        * assembler/ProbeStack.cpp: Added.
+        (JSC::Probe::Page::Page):
+        (JSC::Probe::Page::flushWrites):
+        (JSC::Probe::Stack::Stack):
+        (JSC::Probe::Stack::hasWritesToFlush):
+        (JSC::Probe::Stack::flushWrites):
+        (JSC::Probe::Stack::ensurePageFor):
+        * assembler/ProbeStack.h: Added.
+        (JSC::Probe::Page::baseAddressFor):
+        (JSC::Probe::Page::chunkAddressFor):
+        (JSC::Probe::Page::baseAddress):
+        (JSC::Probe::Page::get):
+        (JSC::Probe::Page::set):
+        (JSC::Probe::Page::hasWritesToFlush const):
+        (JSC::Probe::Page::flushWritesIfNeeded):
+        (JSC::Probe::Page::dirtyBitFor):
+        (JSC::Probe::Page::physicalAddressFor):
+        (JSC::Probe::Stack::Stack):
+        (JSC::Probe::Stack::lowWatermark):
+        (JSC::Probe::Stack::get):
+        (JSC::Probe::Stack::set):
+        (JSC::Probe::Stack::newStackPointer const):
+        (JSC::Probe::Stack::setNewStackPointer):
+        (JSC::Probe::Stack::isValid):
+        (JSC::Probe::Stack::pageFor):
+        * assembler/testmasm.cpp:
+        (JSC::testProbeReadsArgumentRegisters):
+        (JSC::testProbeWritesArgumentRegisters):
+        (JSC::testProbePreservesGPRS):
+        (JSC::testProbeModifiesStackPointer):
+        (JSC::testProbeModifiesStackPointerToInsideProbeStateOnStack):
+        (JSC::testProbeModifiesStackPointerToNBytesBelowSP):
+        (JSC::testProbeModifiesProgramCounter):
+        (JSC::testProbeModifiesStackValues):
+        (JSC::run):
+        (): Deleted.
+        (JSC::fillStack): Deleted.
+        (JSC::testProbeModifiesStackWithCallback): Deleted.
+
 2017-08-19  Andy Estes  <aestes@apple.com>
 
         [Payment Request] Add interface stubs
index 16d3f2c..00b5824 100644 (file)
                FA3AB211C8494524AB390267 /* JSSourceCode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F73926918DC64330AFCDF0D7 /* JSSourceCode.cpp */; };
                FE0D4A061AB8DD0A002F54BF /* ExecutionTimeLimitTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE0D4A041AB8DD0A002F54BF /* ExecutionTimeLimitTest.cpp */; };
                FE0D4A091ABA2437002F54BF /* GlobalContextWithFinalizerTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE0D4A071ABA2437002F54BF /* GlobalContextWithFinalizerTest.cpp */; };
+               FE10AAEB1F44D528009DEDC5 /* ProbeStack.h in Headers */ = {isa = PBXBuildFile; fileRef = FE10AAEA1F44D512009DEDC5 /* ProbeStack.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               FE10AAEC1F44D545009DEDC5 /* ProbeStack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE10AAE91F44D510009DEDC5 /* ProbeStack.cpp */; };
+               FE10AAEE1F44D954009DEDC5 /* ProbeContext.h in Headers */ = {isa = PBXBuildFile; fileRef = FE10AAED1F44D946009DEDC5 /* ProbeContext.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               FE10AAF41F468396009DEDC5 /* ProbeContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE10AAF31F46826D009DEDC5 /* ProbeContext.cpp */; };
                FE1220271BE7F58C0039E6F2 /* JITAddGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = FE1220261BE7F5640039E6F2 /* JITAddGenerator.h */; };
                FE1220281BE7F5910039E6F2 /* JITAddGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE1220251BE7F5640039E6F2 /* JITAddGenerator.cpp */; };
                FE187A011BFBE55E0038BBCA /* JITMulGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE1879FF1BFBC73C0038BBCA /* JITMulGenerator.cpp */; };
                FE0D4A051AB8DD0A002F54BF /* ExecutionTimeLimitTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ExecutionTimeLimitTest.h; path = API/tests/ExecutionTimeLimitTest.h; sourceTree = "<group>"; };
                FE0D4A071ABA2437002F54BF /* GlobalContextWithFinalizerTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GlobalContextWithFinalizerTest.cpp; path = API/tests/GlobalContextWithFinalizerTest.cpp; sourceTree = "<group>"; };
                FE0D4A081ABA2437002F54BF /* GlobalContextWithFinalizerTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GlobalContextWithFinalizerTest.h; path = API/tests/GlobalContextWithFinalizerTest.h; sourceTree = "<group>"; };
+               FE10AAE91F44D510009DEDC5 /* ProbeStack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProbeStack.cpp; sourceTree = "<group>"; };
+               FE10AAEA1F44D512009DEDC5 /* ProbeStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProbeStack.h; sourceTree = "<group>"; };
+               FE10AAED1F44D946009DEDC5 /* ProbeContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProbeContext.h; sourceTree = "<group>"; };
+               FE10AAF31F46826D009DEDC5 /* ProbeContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProbeContext.cpp; sourceTree = "<group>"; };
                FE1220251BE7F5640039E6F2 /* JITAddGenerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JITAddGenerator.cpp; sourceTree = "<group>"; };
                FE1220261BE7F5640039E6F2 /* JITAddGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JITAddGenerator.h; sourceTree = "<group>"; };
                FE1879FF1BFBC73C0038BBCA /* JITMulGenerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JITMulGenerator.cpp; sourceTree = "<group>"; };
                                86C568DF11A213EE0007F7F0 /* MIPSAssembler.h */,
                                FE63DD551EA9BC5D00103A69 /* Printer.cpp */,
                                FE63DD531EA9B60E00103A69 /* Printer.h */,
+                               FE10AAF31F46826D009DEDC5 /* ProbeContext.cpp */,
+                               FE10AAED1F44D946009DEDC5 /* ProbeContext.h */,
+                               FE10AAE91F44D510009DEDC5 /* ProbeStack.cpp */,
+                               FE10AAEA1F44D512009DEDC5 /* ProbeStack.h */,
                                FE533CA01F217C310016A1FE /* testmasm.cpp */,
                                9688CB140ED12B4E001D649F /* X86Assembler.h */,
                        );
                                0FBF158D19B7A53100695DD0 /* DFGBlockSetInlines.h in Headers */,
                                0FC3CD0019ADA410006AC72A /* DFGBlockWorklist.h in Headers */,
                                0F8364B7164B0C110053329A /* DFGBranchDirection.h in Headers */,
+                               FE10AAEE1F44D954009DEDC5 /* ProbeContext.h in Headers */,
                                86EC9DC51328DF82002B2AD7 /* DFGByteCodeParser.h in Headers */,
                                0F256C361627B0AD007F2783 /* DFGCallArrayAllocatorSlowPathGenerator.h in Headers */,
                                0FBDB9AD1AB0FBC6000B57E5 /* DFGCallCreateDirectArgumentsSlowPathGenerator.h in Headers */,
                                AD4937C81DDD0AAE0077C807 /* WebAssemblyModuleRecord.h in Headers */,
                                AD2FCC2D1DB838FD00B3E736 /* WebAssemblyPrototype.h in Headers */,
                                AD2FCBF91DB58DAD00B3E736 /* WebAssemblyRuntimeErrorConstructor.h in Headers */,
+                               FE10AAEB1F44D528009DEDC5 /* ProbeStack.h in Headers */,
                                AD2FCC1E1DB59CB200B3E736 /* WebAssemblyRuntimeErrorConstructor.lut.h in Headers */,
                                AD2FCBFB1DB58DAD00B3E736 /* WebAssemblyRuntimeErrorPrototype.h in Headers */,
                                AD2FCC1F1DB59CB200B3E736 /* WebAssemblyRuntimeErrorPrototype.lut.h in Headers */,
                                0F69CC88193AC60A0045759E /* DFGFrozenValue.cpp in Sources */,
                                86EC9DC71328DF82002B2AD7 /* DFGGraph.cpp in Sources */,
                                0F2FCCF918A60070001A27F8 /* DFGGraphSafepoint.cpp in Sources */,
+                               FE10AAF41F468396009DEDC5 /* ProbeContext.cpp in Sources */,
                                0FB17660196B8F9E0091052A /* DFGHeapLocation.cpp in Sources */,
                                0FC841681BA8C3210061837D /* DFGInferredTypeCheck.cpp in Sources */,
                                A704D90517A0BAA8006BA554 /* DFGInPlaceAbstractState.cpp in Sources */,
                                147F39D6107EC37600427A48 /* JSCJSValue.cpp in Sources */,
                                1440FCE40A51E46B0005F061 /* JSClassRef.cpp in Sources */,
                                86E3C616167BABEE006D760A /* JSContext.mm in Sources */,
+                               FE10AAEC1F44D545009DEDC5 /* ProbeStack.cpp in Sources */,
                                14BD5A300A3E91F600BAF59C /* JSContextRef.cpp in Sources */,
                                A72028B61797601E0098028C /* JSCTestRunnerUtils.cpp in Sources */,
                                72AAF7CD1D0D31B3005E60BE /* JSCustomGetterSetterFunction.cpp in Sources */,
index 080afc0..3bade3f 100644 (file)
@@ -735,6 +735,7 @@ private:
         OP_SDIV_T1      = 0xFB90,
         OP_UDIV_T1      = 0xFBB0,
 #endif
+        OP_MRS_T1       = 0xF3EF,
     } OpcodeID1;
 
     typedef enum {
@@ -1433,6 +1434,15 @@ public:
             mvn(rd, rm, ShiftTypeAndAmount());
     }
 
+    ALWAYS_INLINE void mrs(RegisterID rd, SPRegisterID specReg)
+    {
+        ASSERT(specReg == ARMRegisters::apsr);
+        ASSERT(!BadReg(rd));
+        unsigned short specialRegisterBit = (specReg == ARMRegisters::apsr) ? 0 : (1 << 4);
+        OpcodeID1 mrsOp = static_cast<OpcodeID1>(OP_MRS_T1 | specialRegisterBit);
+        m_formatter.twoWordOp16FourFours(mrsOp, FourFours(0x8, rd, 0, 0));
+    }
+
     ALWAYS_INLINE void neg(RegisterID rd, RegisterID rm)
     {
         ARMThumbImmediate zero = ARMThumbImmediate::makeUInt12(0);
index aa4b79a..067f14e 100644 (file)
@@ -43,13 +43,6 @@ namespace JSC {
 
 #if ENABLE(ASSEMBLER)
 
-#if ENABLE(MASM_PROBE)
-namespace Probe {
-struct State;
-typedef void (*Function)(struct State*);
-} // namespace Probe
-#endif
-    
 class AllowMacroScratchRegisterUsage;
 class DisallowMacroScratchRegisterUsage;
 class LinkBuffer;
index d91a180..d19b6c5 100644 (file)
@@ -28,6 +28,7 @@
 
 #if ENABLE(ASSEMBLER)
 
+#include "ProbeContext.h"
 #include <wtf/PrintStream.h>
 
 namespace JSC {
@@ -35,15 +36,15 @@ namespace JSC {
 const double MacroAssembler::twoToThe32 = (double)0x100000000ull;
 
 #if ENABLE(MASM_PROBE)
-static void stdFunctionCallback(Probe::State* state)
+static void stdFunctionCallback(Probe::Context& context)
 {
-    auto func = static_cast<const std::function<void(Probe::State*)>*>(state->arg);
-    (*func)(state);
+    auto func = static_cast<const std::function<void(Probe::Context&)>*>(context.arg);
+    (*func)(context);
 }
     
-void MacroAssembler::probe(std::function<void(Probe::State*)> func)
+void MacroAssembler::probe(std::function<void(Probe::Context&)> func)
 {
-    probe(stdFunctionCallback, new std::function<void(Probe::State*)>(func));
+    probe(stdFunctionCallback, new std::function<void(Probe::Context&)>(func));
 }
 #endif // ENABLE(MASM_PROBE)
 
index f92c297..cf41ce8 100644 (file)
@@ -63,12 +63,21 @@ namespace JSC { typedef MacroAssemblerX86_64 MacroAssemblerBase; };
 
 namespace JSC {
 
+#if ENABLE(MASM_PROBE)
+namespace Probe {
+
+class Context;
+typedef void (*Function)(Context&);
+
+} // namespace Probe
+#endif // ENABLE(MASM_PROBE)
+
 namespace Printer {
 
 struct PrintRecord;
 typedef Vector<PrintRecord> PrintRecordList;
 
-}
+} // namespace Printer
 
 class MacroAssembler : public MacroAssemblerBase {
 public:
@@ -1825,8 +1834,6 @@ public:
     }
 
 #if ENABLE(MASM_PROBE)
-    struct CPUState;
-
     // This function emits code to preserve the CPUState (e.g. registers),
     // call a user supplied probe function, and restore the CPUState before
     // continuing with other JIT generated code.
@@ -1878,7 +1885,7 @@ public:
     // MacroAssembler.
     void probe(Probe::Function, void* arg);
 
-    JS_EXPORT_PRIVATE void probe(std::function<void(Probe::State*)>);
+    JS_EXPORT_PRIVATE void probe(std::function<void(Probe::Context&)>);
 
     // Let's you print from your JIT generated code.
     // See comments in MacroAssemblerPrinter.h for examples of how to use this.
@@ -1889,173 +1896,6 @@ public:
 #endif // ENABLE(MASM_PROBE)
 };
 
-#if ENABLE(MASM_PROBE)
-struct MacroAssembler::CPUState {
-    static inline const char* gprName(RegisterID id) { return MacroAssembler::gprName(id); }
-    static inline const char* sprName(SPRegisterID id) { return MacroAssembler::sprName(id); }
-    static inline const char* fprName(FPRegisterID id) { return MacroAssembler::fprName(id); }
-    inline uintptr_t& gpr(RegisterID);
-    inline uintptr_t& spr(SPRegisterID);
-    inline double& fpr(FPRegisterID);
-
-    template<typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
-    T gpr(RegisterID) const;
-    template<typename T, typename std::enable_if<std::is_pointer<T>::value>::type* = nullptr>
-    T gpr(RegisterID) const;
-    template<typename T> T fpr(FPRegisterID) const;
-
-    void*& pc();
-    void*& fp();
-    void*& sp();
-    template<typename T> T pc() const;
-    template<typename T> T fp() const;
-    template<typename T> T sp() const;
-
-    uintptr_t gprs[MacroAssembler::numberOfRegisters()];
-    uintptr_t sprs[MacroAssembler::numberOfSPRegisters()];
-    double fprs[MacroAssembler::numberOfFPRegisters()];
-};
-
-inline uintptr_t& MacroAssembler::CPUState::gpr(RegisterID id)
-{
-    ASSERT(id >= MacroAssembler::firstRegister() && id <= MacroAssembler::lastRegister());
-    return gprs[id];
-}
-
-inline uintptr_t& MacroAssembler::CPUState::spr(SPRegisterID id)
-{
-    ASSERT(id >= MacroAssembler::firstSPRegister() && id <= MacroAssembler::lastSPRegister());
-    return sprs[id];
-}
-
-inline double& MacroAssembler::CPUState::fpr(FPRegisterID id)
-{
-    ASSERT(id >= MacroAssembler::firstFPRegister() && id <= MacroAssembler::lastFPRegister());
-    return fprs[id];
-}
-
-template<typename T, typename std::enable_if<std::is_integral<T>::value>::type*>
-T MacroAssembler::CPUState::gpr(RegisterID id) const
-{
-    CPUState* cpu = const_cast<CPUState*>(this);
-    return static_cast<T>(cpu->gpr(id));
-}
-
-template<typename T, typename std::enable_if<std::is_pointer<T>::value>::type*>
-T MacroAssembler::CPUState::gpr(RegisterID id) const
-{
-    CPUState* cpu = const_cast<CPUState*>(this);
-    return reinterpret_cast<T>(cpu->gpr(id));
-}
-
-template<typename T>
-T MacroAssembler::CPUState::fpr(FPRegisterID id) const
-{
-    CPUState* cpu = const_cast<CPUState*>(this);
-    return bitwise_cast<T>(cpu->fpr(id));
-}
-
-inline void*& MacroAssembler::CPUState::pc()
-{
-#if CPU(X86) || CPU(X86_64)
-    return *reinterpret_cast<void**>(&spr(X86Registers::eip));
-#elif CPU(ARM64)
-    return *reinterpret_cast<void**>(&spr(ARM64Registers::pc));
-#elif CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL)
-    return *reinterpret_cast<void**>(&gpr(ARMRegisters::pc));
-#elif CPU(MIPS)
-    RELEASE_ASSERT_NOT_REACHED();
-#else
-#error "Unsupported CPU"
-#endif
-}
-
-inline void*& MacroAssembler::CPUState::fp()
-{
-#if CPU(X86) || CPU(X86_64)
-    return *reinterpret_cast<void**>(&gpr(X86Registers::ebp));
-#elif CPU(ARM64)
-    return *reinterpret_cast<void**>(&gpr(ARM64Registers::fp));
-#elif CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL)
-    return *reinterpret_cast<void**>(&gpr(ARMRegisters::fp));
-#elif CPU(MIPS)
-    return *reinterpret_cast<void**>(&gpr(MIPSRegisters::fp));
-#else
-#error "Unsupported CPU"
-#endif
-}
-
-inline void*& MacroAssembler::CPUState::sp()
-{
-#if CPU(X86) || CPU(X86_64)
-    return *reinterpret_cast<void**>(&gpr(X86Registers::esp));
-#elif CPU(ARM64)
-    return *reinterpret_cast<void**>(&gpr(ARM64Registers::sp));
-#elif CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL)
-    return *reinterpret_cast<void**>(&gpr(ARMRegisters::sp));
-#elif CPU(MIPS)
-    return *reinterpret_cast<void**>(&gpr(MIPSRegisters::sp));
-#else
-#error "Unsupported CPU"
-#endif
-}
-
-template<typename T>
-T MacroAssembler::CPUState::pc() const
-{
-    CPUState* cpu = const_cast<CPUState*>(this);
-    return reinterpret_cast<T>(cpu->pc());
-}
-
-template<typename T>
-T MacroAssembler::CPUState::fp() const
-{
-    CPUState* cpu = const_cast<CPUState*>(this);
-    return reinterpret_cast<T>(cpu->fp());
-}
-
-template<typename T>
-T MacroAssembler::CPUState::sp() const
-{
-    CPUState* cpu = const_cast<CPUState*>(this);
-    return reinterpret_cast<T>(cpu->sp());
-}
-
-namespace Probe {
-
-struct State {
-    using CPUState = MacroAssembler::CPUState;
-    using RegisterID = MacroAssembler::RegisterID;
-    using SPRegisterID = MacroAssembler::SPRegisterID;
-    using FPRegisterID = MacroAssembler::FPRegisterID;
-
-    Function probeFunction;
-    void* arg;
-    Function initializeStackFunction;
-    void* initializeStackArg;
-    CPUState cpu;
-
-    // Convenience methods:
-    uintptr_t& gpr(RegisterID id) { return cpu.gpr(id); }
-    uintptr_t& spr(SPRegisterID id) { return cpu.spr(id); }
-    double& fpr(FPRegisterID id) { return cpu.fpr(id); }
-    const char* gprName(RegisterID id) { return cpu.gprName(id); }
-    const char* sprName(SPRegisterID id) { return cpu.sprName(id); }
-    const char* fprName(FPRegisterID id) { return cpu.fprName(id); }
-
-    void*& pc() { return cpu.pc(); }
-    void*& fp() { return cpu.fp(); }
-    void*& sp() { return cpu.sp(); }
-
-    template<typename T> T pc() { return cpu.pc<T>(); }
-    template<typename T> T fp() { return cpu.fp<T>(); }
-    template<typename T> T sp() { return cpu.sp<T>(); }
-};
-
-} // namespace Probe
-
-#endif // ENABLE(MASM_PROBE)
-
 } // namespace JSC
 
 namespace WTF {
index 2a9bc96..2992c5f 100644 (file)
@@ -30,6 +30,7 @@
 #if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL)
 #include "MacroAssembler.h"
 
+#include "ProbeContext.h"
 #include <wtf/InlineASM.h>
 
 #if OS(LINUX)
@@ -100,6 +101,8 @@ void MacroAssemblerARM::load32WithUnalignedHalfWords(BaseIndex address, Register
 
 extern "C" void ctiMasmProbeTrampoline();
 
+using namespace ARMRegisters;
+
 #if COMPILER(GCC_OR_CLANG)
 
 // The following are offsets for Probe::State fields accessed
@@ -250,6 +253,31 @@ COMPILE_ASSERT(PROBE_OFFSETOF(cpu.fprs[ARMRegisters::d31]) == PROBE_CPU_D31_OFFS
 COMPILE_ASSERT(sizeof(Probe::State) == PROBE_SIZE, ProbeState_size_matches_ctiMasmProbeTrampoline);
 #undef PROBE_OFFSETOF
 
+struct IncomingRecord {
+    uintptr_t lr;
+    uintptr_t ip;
+    uintptr_t r6;
+    uintptr_t r0;
+    uintptr_t r1;
+    uintptr_t r2;
+};
+
+#define IN_LR_OFFSET (0 * PTR_SIZE)
+#define IN_IP_OFFSET (1 * PTR_SIZE)
+#define IN_R6_OFFSET (2 * PTR_SIZE)
+#define IN_R0_OFFSET (3 * PTR_SIZE)
+#define IN_R1_OFFSET (4 * PTR_SIZE)
+#define IN_R2_OFFSET (5 * PTR_SIZE)
+#define IN_SIZE      (6 * PTR_SIZE)
+
+static_assert(IN_LR_OFFSET == offsetof(IncomingRecord, lr), "IN_LR_OFFSET is incorrect");
+static_assert(IN_IP_OFFSET == offsetof(IncomingRecord, ip), "IN_IP_OFFSET is incorrect");
+static_assert(IN_R6_OFFSET == offsetof(IncomingRecord, r6), "IN_R6_OFFSET is incorrect");
+static_assert(IN_R0_OFFSET == offsetof(IncomingRecord, r0), "IN_R0_OFFSET is incorrect");
+static_assert(IN_R1_OFFSET == offsetof(IncomingRecord, r1), "IN_R1_OFFSET is incorrect");
+static_assert(IN_R2_OFFSET == offsetof(IncomingRecord, r2), "IN_R2_OFFSET is incorrect");
+static_assert(IN_SIZE == sizeof(IncomingRecord), "IN_SIZE is incorrect");
+
 asm (
     ".text" "\n"
     ".globl " SYMBOL_STRING(ctiMasmProbeTrampoline) "\n"
@@ -257,66 +285,72 @@ asm (
     INLINE_ARM_FUNCTION(ctiMasmProbeTrampoline) "\n"
     SYMBOL_STRING(ctiMasmProbeTrampoline) ":" "\n"
 
-    // MacroAssemblerARM::probe() has already generated code to store some values.
-    // The top of stack now looks like this:
-    //     esp[0 * ptrSize]: probe handler function
-    //     esp[1 * ptrSize]: probe arg
-    //     esp[2 * ptrSize]: saved r3 / S0
-    //     esp[3 * ptrSize]: saved ip
-    //     esp[4 * ptrSize]: saved lr
-    //     esp[5 * ptrSize]: saved sp
+    // MacroAssemblerARMv7::probe() has already generated code to store some values.
+    // The top of stack now contains the IncomingRecord.
+    //
+    // Incoming register values:
+    //     r0: probe function
+    //     r1: probe arg
+    //     r2: Probe::executeProbe
+    //     r6: scratch
+    //     ip: scratch, was ctiMasmProbeTrampoline
+    //     lr: return address
 
     "mov       ip, sp" "\n"
-    "mov       r3, sp" "\n"
-    "sub       r3, r3, #" STRINGIZE_VALUE_OF(PROBE_SIZE + OUT_SIZE) "\n"
+    "mov       r6, sp" "\n"
+    "sub       r6, r6, #" STRINGIZE_VALUE_OF(PROBE_SIZE + OUT_SIZE) "\n"
 
     // The ARM EABI specifies that the stack needs to be 16 byte aligned.
-    "bic       r3, r3, #0xf" "\n"
-    "mov       sp, r3" "\n" // Set the sp to protect the Probe::State from interrupts before we initialize it.
+    "bic       r6, r6, #0xf" "\n"
+    "mov       sp, r6" "\n" // Set the sp to protect the Probe::State from interrupts before we initialize it.
 
+    "str       r0, [sp, #" STRINGIZE_VALUE_OF(PROBE_PROBE_FUNCTION_OFFSET) "]" "\n"
+    "str       r1, [sp, #" STRINGIZE_VALUE_OF(PROBE_ARG_OFFSET) "]" "\n"
     "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_PC_OFFSET) "]" "\n"
-    "add       lr, sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_R0_OFFSET) "\n"
-    "stmia     lr, { r0-r11 }" "\n"
+
+    "add       r0, ip, #" STRINGIZE_VALUE_OF(IN_SIZE) "\n"
+    "str       r0, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n"
+
+    "add       lr, sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_R3_OFFSET) "\n"
+    "stmia     lr, { r3-r5 }" "\n"
+    "add       lr, sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_R7_OFFSET) "\n"
+    "stmia     lr, { r7-r11 }" "\n"
     "mrs       lr, APSR" "\n"
     "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_APSR_OFFSET) "]" "\n"
     "vmrs      lr, FPSCR" "\n"
     "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_FPSCR_OFFSET) "]" "\n"
 
-    "ldr       lr, [ip, #0 * " STRINGIZE_VALUE_OF(PTR_SIZE) "]" "\n"
-    "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_PROBE_FUNCTION_OFFSET) "]" "\n"
-    "ldr       lr, [ip, #1 * " STRINGIZE_VALUE_OF(PTR_SIZE) "]" "\n"
-    "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_ARG_OFFSET) "]" "\n"
-    "ldr       lr, [ip, #2 * " STRINGIZE_VALUE_OF(PTR_SIZE) "]" "\n"
-    "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_R3_OFFSET) "]" "\n"
-    "ldr       lr, [ip, #3 * " STRINGIZE_VALUE_OF(PTR_SIZE) "]" "\n"
-    "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_IP_OFFSET) "]" "\n"
-    "ldr       lr, [ip, #4 * " STRINGIZE_VALUE_OF(PTR_SIZE) "]" "\n"
-    "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_LR_OFFSET) "]" "\n"
-    "ldr       lr, [ip, #5 * " STRINGIZE_VALUE_OF(PTR_SIZE) "]" "\n"
-    "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n"
-
-    "ldr       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_PC_OFFSET) "]" "\n"
+    "ldr       r4, [ip, #" STRINGIZE_VALUE_OF(IN_LR_OFFSET) "]" "\n"
+    "ldr       r5, [ip, #" STRINGIZE_VALUE_OF(IN_IP_OFFSET) "]" "\n"
+    "ldr       r6, [ip, #" STRINGIZE_VALUE_OF(IN_R6_OFFSET) "]" "\n"
+    "ldr       r7, [ip, #" STRINGIZE_VALUE_OF(IN_R0_OFFSET) "]" "\n"
+    "ldr       r8, [ip, #" STRINGIZE_VALUE_OF(IN_R1_OFFSET) "]" "\n"
+    "ldr       r9, [ip, #" STRINGIZE_VALUE_OF(IN_R2_OFFSET) "]" "\n"
+    "str       r4, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_LR_OFFSET) "]" "\n"
+    "str       r5, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_IP_OFFSET) "]" "\n"
+    "str       r6, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_R6_OFFSET) "]" "\n"
+    "str       r7, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_R0_OFFSET) "]" "\n"
+    "str       r8, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_R1_OFFSET) "]" "\n"
+    "str       r9, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_R2_OFFSET) "]" "\n"
 
     "add       ip, sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_D0_OFFSET) "\n"
     "vstmia.64 ip!, { d0-d15 }" "\n"
 #if CPU(ARM_NEON) || CPU(ARM_VFP_V3_D32)
     "vstmia.64 ip!, { d16-d31 }" "\n"
 #endif
-    "mov       fp, sp" "\n" // Save the Probe::State*.
 
-    // Initialize Probe::State::initializeStackFunction to zero.
-    "mov       r0, #0" "\n"
-    "str       r0, [fp, #" STRINGIZE_VALUE_OF(PROBE_INIT_STACK_FUNCTION_OFFSET) "]" "\n"
+    // r5 is a callee saved register. We'll use it for preserving the Probe::State*.
+    // https://stackoverflow.com/questions/261419/arm-to-c-calling-convention-registers-to-save#261496
+    "mov       r5, sp" "\n"
 
-    "ldr       ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_PROBE_FUNCTION_OFFSET) "]" "\n"
     "mov       r0, sp" "\n" // the Probe::State* arg.
-    "blx       ip" "\n"
+    "blx       r2" "\n" // Call Probe::executeProbe.
 
     // Make sure the Probe::State is entirely below the result stack pointer so
     // that register values are still preserved when we call the initializeStack
     // function.
-    "ldr       r1, [fp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n" // Result sp.
-    "add       r2, fp, #" STRINGIZE_VALUE_OF(PROBE_SIZE + OUT_SIZE) "\n" // End of ProveContext + buffer.
+    "ldr       r1, [r5, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n" // Result sp.
+    "add       r2, r5, #" STRINGIZE_VALUE_OF(PROBE_SIZE + OUT_SIZE) "\n" // End of ProveContext + buffer.
     "cmp       r1, r2" "\n"
     "bge     " LOCAL_LABEL_STRING(ctiMasmProbeTrampolineProbeStateIsSafe) "\n"
 
@@ -328,32 +362,30 @@ asm (
     // Copy the Probe::State to the safe place.
     // Note: we have to copy from low address to higher address because we're moving the
     // Probe::State to a lower address.
-    "mov       r5, fp" "\n"
-    "mov       r6, r1" "\n"
-    "add       r7, fp, #" STRINGIZE_VALUE_OF(PROBE_SIZE) "\n"
+    "add       r7, r5, #" STRINGIZE_VALUE_OF(PROBE_SIZE) "\n"
 
     LOCAL_LABEL_STRING(ctiMasmProbeTrampolineCopyLoop) ":" "\n"
     "ldr       r3, [r5], #4" "\n"
     "ldr       r4, [r5], #4" "\n"
-    "str       r3, [r6], #4" "\n"
-    "str       r4, [r6], #4" "\n"
+    "str       r3, [r1], #4" "\n"
+    "str       r4, [r1], #4" "\n"
     "cmp       r5, r7" "\n"
     "blt     " LOCAL_LABEL_STRING(ctiMasmProbeTrampolineCopyLoop) "\n"
 
-    "mov       fp, r1" "\n"
+    "mov       r5, sp" "\n"
 
     // Call initializeStackFunction if present.
     LOCAL_LABEL_STRING(ctiMasmProbeTrampolineProbeStateIsSafe) ":" "\n"
-    "ldr       r2, [fp, #" STRINGIZE_VALUE_OF(PROBE_INIT_STACK_FUNCTION_OFFSET) "]" "\n"
+    "ldr       r2, [r5, #" STRINGIZE_VALUE_OF(PROBE_INIT_STACK_FUNCTION_OFFSET) "]" "\n"
     "cmp       r2, #0" "\n"
     "beq     " LOCAL_LABEL_STRING(ctiMasmProbeTrampolineRestoreRegisters) "\n"
 
-    "mov       r0, fp" "\n" // Set the Probe::State* arg.
+    "mov       r0, r5" "\n" // Set the Probe::State* arg.
     "blx       r2" "\n" // Call the initializeStackFunction (loaded into r2 above).
 
     LOCAL_LABEL_STRING(ctiMasmProbeTrampolineRestoreRegisters) ":" "\n"
 
-    "mov       sp, fp" "\n"
+    "mov       sp, r5" "\n" // Ensure that sp points to the Probe::State*.
 
     // To enable probes to modify register state, we copy all registers
     // out of the Probe::State before returning.
@@ -399,17 +431,20 @@ asm (
 
 void MacroAssembler::probe(Probe::Function function, void* arg)
 {
-    push(RegisterID::sp);
-    push(RegisterID::lr);
-    push(RegisterID::ip);
-    push(RegisterID::S0);
-    // The following uses RegisterID::S0. So, they must come after we push S0 above.
-    push(trustedImm32FromPtr(arg));
-    push(trustedImm32FromPtr(function));
-
-    move(trustedImm32FromPtr(ctiMasmProbeTrampoline), RegisterID::S0);
-    m_assembler.blx(RegisterID::S0);
-
+    sub32(TrustedImm32(sizeof(IncomingRecord)), sp);
+
+    store32(lr, Address(sp, offsetof(IncomingRecord, lr)));
+    store32(ip, Address(sp, offsetof(IncomingRecord, ip)));
+    store32(r6, Address(sp, offsetof(IncomingRecord, r6))); // S0 == r6.
+    store32(r0, Address(sp, offsetof(IncomingRecord, r0)));
+    store32(r1, Address(sp, offsetof(IncomingRecord, r1)));
+    store32(r2, Address(sp, offsetof(IncomingRecord, r2)));
+    // The following uses RegisterID::S0. So, they must come after we preserve S0 above.
+    move(TrustedImmPtr(reinterpret_cast<void*>(function)), r0);
+    move(TrustedImmPtr(arg), r1);
+    move(TrustedImmPtr(reinterpret_cast<void*>(Probe::executeProbe)), r2);
+    move(TrustedImmPtr(reinterpret_cast<void*>(ctiMasmProbeTrampoline)), ip);
+    m_assembler.blx(ip);
 }
 #endif // ENABLE(MASM_PROBE)
 
index 280f095..a3a1156 100644 (file)
@@ -1586,23 +1586,6 @@ protected:
         m_assembler.blx(ARMRegisters::S1);
     }
 
-#if ENABLE(MASM_PROBE)
-    inline TrustedImm32 trustedImm32FromPtr(void* ptr)
-    {
-        return TrustedImm32(TrustedImmPtr(ptr));
-    }
-
-    inline TrustedImm32 trustedImm32FromPtr(Probe::Function function)
-    {
-        return TrustedImm32(TrustedImmPtr(reinterpret_cast<void*>(function)));
-    }
-
-    inline TrustedImm32 trustedImm32FromPtr(void (*function)())
-    {
-        return TrustedImm32(TrustedImmPtr(reinterpret_cast<void*>(function)));
-    }
-#endif // ENABLE(MASM_PROBE)
-
 private:
     friend class LinkBuffer;
 
index f6d793c..3bc927b 100644 (file)
@@ -28,6 +28,7 @@
 #if ENABLE(ASSEMBLER) && CPU(ARM64)
 #include "MacroAssembler.h"
 
+#include "ProbeContext.h"
 #include <wtf/InlineASM.h>
 
 namespace JSC {
@@ -126,8 +127,6 @@ using namespace ARM64Registers;
 #define PROBE_SIZE (PROBE_FIRST_FPREG_OFFSET + (32 * FPREG_SIZE))
 
 #define SAVED_PROBE_RETURN_PC_OFFSET        (PROBE_SIZE + (0 * PTR_SIZE))
-#define SAVED_PROBE_LR_OFFSET               (PROBE_SIZE + (1 * PTR_SIZE))
-#define SAVED_PROBE_ERROR_FUNCTION_OFFSET   (PROBE_SIZE + (2 * PTR_SIZE))
 #define PROBE_SIZE_PLUS_EXTRAS              (PROBE_SIZE + (3 * PTR_SIZE))
 
 // These ASSERTs remind you that if you change the layout of Probe::State,
@@ -225,31 +224,29 @@ static_assert(!(PROBE_SIZE_PLUS_EXTRAS & 0xf), "PROBE_SIZE_PLUS_EXTRAS should be
 #define FPR_OFFSET(fpr) (PROBE_CPU_##fpr##_OFFSET - PROBE_CPU_Q0_OFFSET)
 
 struct IncomingProbeRecord {
-    uintptr_t probeHandlerFunction;
-    uintptr_t probeArg;
+    uintptr_t x24;
+    uintptr_t x25;
     uintptr_t x26;
     uintptr_t x27;
-    uintptr_t lr;
-    uintptr_t sp;
-    uintptr_t probeErrorFunction;
-    uintptr_t unused; // Padding for alignment.
+    uintptr_t x28;
+    uintptr_t x30; // lr
 };
 
-#define IN_HANDLER_FUNCTION_OFFSET (0 * PTR_SIZE)
-#define IN_ARG_OFFSET              (1 * PTR_SIZE)
-#define IN_X26_OFFSET              (2 * PTR_SIZE)
-#define IN_X27_OFFSET              (3 * PTR_SIZE)
-#define IN_LR_OFFSET               (4 * PTR_SIZE)
-#define IN_SP_OFFSET               (5 * PTR_SIZE)
-#define IN_ERROR_FUNCTION_OFFSET   (6 * PTR_SIZE)
+#define IN_X24_OFFSET (0 * PTR_SIZE)
+#define IN_X25_OFFSET (1 * PTR_SIZE)
+#define IN_X26_OFFSET (2 * PTR_SIZE)
+#define IN_X27_OFFSET (3 * PTR_SIZE)
+#define IN_X28_OFFSET (4 * PTR_SIZE)
+#define IN_X30_OFFSET (5 * PTR_SIZE)
+#define IN_SIZE       (6 * PTR_SIZE)
 
-static_assert(IN_HANDLER_FUNCTION_OFFSET == offsetof(IncomingProbeRecord, probeHandlerFunction), "IN_HANDLER_FUNCTION_OFFSET is incorrect");
-static_assert(IN_ARG_OFFSET == offsetof(IncomingProbeRecord, probeArg), "IN_ARG_OFFSET is incorrect");
+static_assert(IN_X24_OFFSET == offsetof(IncomingProbeRecord, x24), "IN_X24_OFFSET is incorrect");
+static_assert(IN_X25_OFFSET == offsetof(IncomingProbeRecord, x25), "IN_X25_OFFSET is incorrect");
 static_assert(IN_X26_OFFSET == offsetof(IncomingProbeRecord, x26), "IN_X26_OFFSET is incorrect");
 static_assert(IN_X27_OFFSET == offsetof(IncomingProbeRecord, x27), "IN_X27_OFFSET is incorrect");
-static_assert(IN_LR_OFFSET == offsetof(IncomingProbeRecord, lr), "IN_LR_OFFSET is incorrect");
-static_assert(IN_SP_OFFSET == offsetof(IncomingProbeRecord, sp), "IN_SP_OFFSET is incorrect");
-static_assert(IN_ERROR_FUNCTION_OFFSET == offsetof(IncomingProbeRecord, probeErrorFunction), "IN_ERROR_FUNCTION_OFFSET is incorrect");
+static_assert(IN_X28_OFFSET == offsetof(IncomingProbeRecord, x28), "IN_X22_OFFSET is incorrect");
+static_assert(IN_X30_OFFSET == offsetof(IncomingProbeRecord, x30), "IN_X23_OFFSET is incorrect");
+static_assert(IN_SIZE == sizeof(IncomingProbeRecord), "IN_SIZE is incorrect");
 static_assert(!(sizeof(IncomingProbeRecord) & 0xf), "IncomingProbeStack must be 16-byte aligned");
 
 struct OutgoingProbeRecord {
@@ -278,9 +275,17 @@ static_assert(OUT_LR_OFFSET == offsetof(OutgoingProbeRecord, lr), "OUT_LR_OFFSET
 static_assert(OUT_SIZE == sizeof(OutgoingProbeRecord), "OUT_SIZE is incorrect");
 static_assert(!(sizeof(OutgoingProbeRecord) & 0xf), "OutgoingProbeStack must be 16-byte aligned");
 
-#define STATE_PC_NOT_CHANGED 0
-#define STATE_PC_CHANGED 1
-static_assert(STATE_PC_NOT_CHANGED != STATE_PC_CHANGED, "STATE_PC_NOT_CHANGED and STATE_PC_CHANGED should not be equal");
+struct LRRestorationRecord {
+    uintptr_t lr;
+    uintptr_t unusedDummyToEnsureSizeIs16ByteAligned;
+};
+
+#define LR_RESTORATION_LR_OFFSET (0 * PTR_SIZE)
+#define LR_RESTORATION_SIZE      (2 * PTR_SIZE)
+
+static_assert(LR_RESTORATION_LR_OFFSET == offsetof(LRRestorationRecord, lr), "LR_RESTORATION_LR_OFFSET is incorrect");
+static_assert(LR_RESTORATION_SIZE == sizeof(LRRestorationRecord), "LR_RESTORATION_SIZE is incorrect");
+static_assert(!(sizeof(LRRestorationRecord) & 0xf), "LRRestorationRecord must be 16-byte aligned");
 
 // We use x29 and x30 instead of fp and lr because GCC's inline assembler does not recognize fp and lr.
 // See https://bugs.webkit.org/show_bug.cgi?id=175512 for details.
@@ -293,6 +298,14 @@ asm (
 
     // MacroAssemblerARM64::probe() has already generated code to store some values in an
     // IncomingProbeRecord. sp points to the IncomingProbeRecord.
+    //
+    // Incoming register values:
+    //     x24: probe function
+    //     x25: probe arg
+    //     x26: scratch, was ctiMasmProbeTrampoline
+    //     x27: scratch
+    //     x28: Probe::executeProbe
+    //     x30: return address
 
     "mov       x26, sp" "\n"
     "mov       x27, sp" "\n"
@@ -301,6 +314,8 @@ asm (
     "bic       x27, x27, #0xf" "\n" // The ARM EABI specifies that the stack needs to be 16 byte aligned.
     "mov       sp, x27" "\n" // Set the sp to protect the Probe::State from interrupts before we initialize it.
 
+    "stp       x24, x25, [sp, #" STRINGIZE_VALUE_OF(PROBE_PROBE_FUNCTION_OFFSET) "]" "\n" // Store the probe handler function and arg (preloaded into x24 and x25
+
     "stp       x0, x1, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_X0_OFFSET) "]" "\n"
     "mrs       x0, nzcv" "\n" // Preload nzcv.
     "stp       x2, x3, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_X2_OFFSET) "]" "\n"
@@ -309,10 +324,10 @@ asm (
     "stp       x6, x7, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_X6_OFFSET) "]" "\n"
     "stp       x8, x9, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_X8_OFFSET) "]" "\n"
 
-    "ldp       x2, x3, [x26, #" STRINGIZE_VALUE_OF(IN_HANDLER_FUNCTION_OFFSET) "]" "\n" // Preload probe handler function and probe arg.
-    "ldp       x4, x5, [x26, #" STRINGIZE_VALUE_OF(IN_X26_OFFSET) "]" "\n" // Preload saved r26 and r27.
-    "ldp       x6, x7, [x26, #" STRINGIZE_VALUE_OF(IN_LR_OFFSET) "]" "\n" // Preload saved lr and sp.
-    "ldr       x8, [x26, #" STRINGIZE_VALUE_OF(IN_ERROR_FUNCTION_OFFSET) "]" "\n" // Preload probe error function.
+    "ldp       x2, x3, [x26, #" STRINGIZE_VALUE_OF(IN_X24_OFFSET) "]" "\n" // Preload saved x24 and x25.
+    "ldp       x4, x5, [x26, #" STRINGIZE_VALUE_OF(IN_X26_OFFSET) "]" "\n" // Preload saved x26 and x27.
+    "ldp       x6, x7, [x26, #" STRINGIZE_VALUE_OF(IN_X28_OFFSET) "]" "\n" // Preload saved x28 and lr.
+    "add       x26, x26, #" STRINGIZE_VALUE_OF(IN_SIZE) "\n" // Compute the sp before the probe.
 
     "stp       x10, x11, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_X10_OFFSET) "]" "\n"
     "stp       x12, x13, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_X12_OFFSET) "]" "\n"
@@ -321,12 +336,11 @@ asm (
     "stp       x18, x19, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_X18_OFFSET) "]" "\n"
     "stp       x20, x21, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_X20_OFFSET) "]" "\n"
     "stp       x22, x23, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_X22_OFFSET) "]" "\n"
-    "stp       x24, x25, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_X24_OFFSET) "]" "\n"
+    "stp       x2, x3, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_X24_OFFSET) "]" "\n" // Store saved r24 and r25 (preloaded into x2 and x3 above).
     "stp       x4, x5, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_X26_OFFSET) "]" "\n" // Store saved r26 and r27 (preloaded into x4 and x5 above).
-    "stp       x28, x29, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_X28_OFFSET) "]" "\n"
-    "stp       x6, x7, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_LR_OFFSET) "]" "\n" // Save values lr and sp (preloaded into x6 and x7 above).
+    "stp       x6, x29, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_X28_OFFSET) "]" "\n"
+    "stp       x7, x26, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_LR_OFFSET) "]" "\n" // Save values lr and sp (original sp value computed into x26 above).
 
-    "str       x6, [sp, #" STRINGIZE_VALUE_OF(SAVED_PROBE_LR_OFFSET) "]" "\n" // Save a duplicate copy of lr (in x6).
     "str       x30, [sp, #" STRINGIZE_VALUE_OF(SAVED_PROBE_RETURN_PC_OFFSET) "]" "\n" // Save a duplicate copy of return pc (in lr).
 
     "add       x30, x30, #" STRINGIZE_VALUE_OF(2 * PTR_SIZE) "\n" // The PC after the probe is at 2 instructions past the return point.
@@ -334,9 +348,6 @@ asm (
 
     "stp       x0, x1, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_NZCV_OFFSET) "]" "\n" // Store nzcv and fpsr (preloaded into x0 and x1 above).
 
-    "stp       x2, x3, [sp, #" STRINGIZE_VALUE_OF(PROBE_PROBE_FUNCTION_OFFSET) "]" "\n" // Store the probe handler function and arg (preloaded into x2 and x3 above).
-    "str       x8, [sp, #" STRINGIZE_VALUE_OF(SAVED_PROBE_ERROR_FUNCTION_OFFSET) "]" "\n" // Store the probe handler function and arg (preloaded into x8 above).
-
     "add       x9, sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_Q0_OFFSET) "\n"
     "stp       d0, d1, [x9, #" STRINGIZE_VALUE_OF(FPR_OFFSET(Q0)) "]" "\n"
     "stp       d2, d3, [x9, #" STRINGIZE_VALUE_OF(FPR_OFFSET(Q2)) "]" "\n"
@@ -357,13 +368,10 @@ asm (
 
     "mov       x27, sp" "\n" // Save the Probe::State* in a callee saved register.
 
-    // Initialize Probe::State::initializeStackFunction to zero.
-    "str       xzr, [x27, #" STRINGIZE_VALUE_OF(PROBE_INIT_STACK_FUNCTION_OFFSET) "]" "\n"
-
     // Note: we haven't changed the value of fp. Hence, it is still pointing to the frame of
     // the caller of the probe (which is what we want in order to play nice with debuggers e.g. lldb).
     "mov       x0, sp" "\n" // Set the Probe::State* arg.
-    "blr       x2" "\n" // Call the probe handler function (loaded into x2 above).
+    "blr       x28" "\n" // Call the probe handler.
 
     // Make sure the Probe::State is entirely below the result stack pointer so
     // that register values are still preserved when we call the initializeStack
@@ -446,63 +454,37 @@ asm (
 
     // Remaining registers to restore are: fpsr, nzcv, x27, x28, fp, lr, sp, and pc.
 
-    "mov       x30, #" STRINGIZE_VALUE_OF(STATE_PC_NOT_CHANGED) "\n"
-
     // The only way to set the pc on ARM64 (from user space) is via an indirect branch
     // or a ret, which means we'll need a free register to do so. For our purposes, lr
     // happens to be available in applications of the probe where we may want to
     // continue executing at a different location (i.e. change the pc) after the probe
     // returns. So, the ARM64 probe implementation will allow the probe handler to
     // either modify lr or pc, but not both in the same probe invocation. The probe
-    // mechanism ensures that we never try to modify both lr and pc, else it will
-    // fail with a RELEASE_ASSERT_NOT_REACHED in arm64ProbeError().
+    // mechanism ensures that we never try to modify both lr and pc with a RELEASE_ASSERT
+    // in Probe::executeProbe().
 
     // Determine if the probe handler changed the pc.
+    "ldr       x30, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n" // preload the target sp.
     "ldr       x27, [sp, #" STRINGIZE_VALUE_OF(SAVED_PROBE_RETURN_PC_OFFSET) "]" "\n"
     "ldr       x28, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_PC_OFFSET) "]" "\n"
     "add       x27, x27, #" STRINGIZE_VALUE_OF(2 * PTR_SIZE) "\n"
     "cmp       x27, x28" "\n"
-    "beq     " LOCAL_LABEL_STRING(ctiMasmProbeTrampolinePrepareOutgoingRecords) "\n"
-
-    // pc was changed. Determine if the probe handler also changed lr.
-    "ldr       x27, [sp, #" STRINGIZE_VALUE_OF(SAVED_PROBE_LR_OFFSET) "]" "\n"
-    "ldr       x28, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_LR_OFFSET) "]" "\n"
-    "cmp       x27, x28" "\n"
-    "bne     " LOCAL_LABEL_STRING(ctiMasmProbeTrampolineError) "\n"
-
-    "mov       x30, #" STRINGIZE_VALUE_OF(STATE_PC_CHANGED) "\n"
-
-    LOCAL_LABEL_STRING(ctiMasmProbeTrampolinePrepareOutgoingRecords) ":" "\n"
+    "bne     " LOCAL_LABEL_STRING(ctiMasmProbeTrampolineEnd) "\n"
 
-    "ldr       x29, [sp, #" STRINGIZE_VALUE_OF(SAVED_PROBE_RETURN_PC_OFFSET) "]" "\n" // Preload the probe return site pc.
-
-    LOCAL_LABEL_STRING(ctiMasmProbeTrampolineFillOutgoingProbeRecords) ":" "\n"
-
-    "cbnz       x30, " LOCAL_LABEL_STRING(ctiMasmProbeTrampolineEnd) "\n" // Skip lr restoration setup if state (in lr) == STATE_PC_CHANGED.
-
-    // In order to restore lr, we need to do the restoration at the probe return site.
-    // The probe return site expects sp to be pointing at an OutgoingProbeRecord such that
-    // popping the OutgoingProbeRecord will yield the desired sp. The probe return site
-    // also expects the lr value to be restored is stashed in the OutgoingProbeRecord.
-    // We can make this happen by pushing 2 OutgoingProbeRecords instead of 1:
-    // 1 for the probe return site, and 1 at ctiMasmProbeTrampolineEnd for returning from
-    // this probe.
-
-    // Fill in the OutgoingProbeStack for the probe return site.
-    "ldr       x30, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n"
-    "sub       x30, x30, #" STRINGIZE_VALUE_OF(OUT_SIZE) "\n"
+     // We didn't change the PC. So, let's prepare for setting a potentially new lr value.
 
+     // 1. Make room for the LRRestorationRecord. The probe site will pop this off later.
+    "sub       x30, x30, #" STRINGIZE_VALUE_OF(LR_RESTORATION_SIZE) "\n"
+     // 2. Store the lp value to restore at the probe return site.
     "ldr       x27, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_LR_OFFSET) "]" "\n"
-    "str       x27, [x30, #" STRINGIZE_VALUE_OF(OUT_LR_OFFSET) "]" "\n"
-
-    // Set up the sp and pc values so that ctiMasmProbeTrampolineEnd will return to the probe return site.
-    "str       x30, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n"
-    "str       x29, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_PC_OFFSET) "]" "\n" // Store the probe return site pc (preloaded into fp above).
+    "str       x27, [x30, #" STRINGIZE_VALUE_OF(LR_RESTORATION_LR_OFFSET) "]" "\n"
+     // 3. Force the return ramp to return to the probe return site.
+    "ldr       x27, [sp, #" STRINGIZE_VALUE_OF(SAVED_PROBE_RETURN_PC_OFFSET) "]" "\n"
+    "str       x27, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_PC_OFFSET) "]" "\n"
 
     LOCAL_LABEL_STRING(ctiMasmProbeTrampolineEnd) ":" "\n"
 
-    // Fill in the OutgoingProbeStack.
-    "ldr       x30, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n"
+    // Fill in the OutgoingProbeRecord.
     "sub       x30, x30, #" STRINGIZE_VALUE_OF(OUT_SIZE) "\n"
 
     "ldp       x27, x28, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_NZCV_OFFSET) "]" "\n"
@@ -510,52 +492,36 @@ asm (
     "ldp       x27, x28, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_X27_OFFSET) "]" "\n"
     "stp       x27, x28, [x30, #" STRINGIZE_VALUE_OF(OUT_X27_OFFSET) "]" "\n"
     "ldr       x27, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_FP_OFFSET) "]" "\n"
-    "ldr       x28, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_PC_OFFSET) "]" "\n"
+    "ldr       x28, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_PC_OFFSET) "]" "\n" // Set up the outgoing record so that we'll jump to the new PC.
     "stp       x27, x28, [x30, #" STRINGIZE_VALUE_OF(OUT_FP_OFFSET) "]" "\n"
     "mov       sp, x30" "\n"
 
-    // Restore the remaining registers and pop the OutgoingProbeStack.
+    // Restore the remaining registers and pop the OutgoingProbeRecord.
     "ldp       x27, x28, [sp], #" STRINGIZE_VALUE_OF(2 * PTR_SIZE) "\n"
     "msr       nzcv, x27" "\n"
     "msr       fpsr, x28" "\n"
     "ldp       x27, x28, [sp], #" STRINGIZE_VALUE_OF(2 * PTR_SIZE) "\n"
     "ldp       x29, x30, [sp], #" STRINGIZE_VALUE_OF(2 * PTR_SIZE) "\n"
     "ret" "\n"
-
-    LOCAL_LABEL_STRING(ctiMasmProbeTrampolineError) ":" "\n"
-    // The probe handler changed both lr and pc. This is not supported for ARM64.
-    "ldr       x1, [sp, #" STRINGIZE_VALUE_OF(SAVED_PROBE_ERROR_FUNCTION_OFFSET) "]" "\n"
-    "mov       x0, sp" "\n" // Set the Probe::State* arg.
-    "blr       x1" "\n"
-    "brk       #0x1000" // Should never return here.
 );
 #endif // COMPILER(GCC_OR_CLANG)
 
-static NO_RETURN_DUE_TO_CRASH void arm64ProbeError(Probe::State*)
-{
-    dataLog("MacroAssembler probe ERROR: ARM64 does not support the probe changing both LR and PC.\n");
-    RELEASE_ASSERT_NOT_REACHED();
-}
-
 void MacroAssembler::probe(Probe::Function function, void* arg)
 {
     sub64(TrustedImm32(sizeof(IncomingProbeRecord)), sp);
 
+    storePair64(x24, x25, sp, TrustedImm32(offsetof(IncomingProbeRecord, x24)));
     storePair64(x26, x27, sp, TrustedImm32(offsetof(IncomingProbeRecord, x26)));
-    add64(TrustedImm32(sizeof(IncomingProbeRecord)), sp, x26);
-    storePair64(lr, x26, sp, TrustedImm32(offsetof(IncomingProbeRecord, lr))); // Save lr and original sp value.
-    move(TrustedImmPtr(reinterpret_cast<void*>(function)), x26);
-    move(TrustedImmPtr(arg), x27);
-    storePair64(x26, x27, sp, TrustedImm32(offsetof(IncomingProbeRecord, probeHandlerFunction)));
-    move(TrustedImmPtr(reinterpret_cast<void*>(arm64ProbeError)), x27);
-    store64(x27, Address(sp, offsetof(IncomingProbeRecord, probeErrorFunction)));
-
+    storePair64(x28, x30, sp, TrustedImm32(offsetof(IncomingProbeRecord, x28))); // Note: x30 is lr.
     move(TrustedImmPtr(reinterpret_cast<void*>(ctiMasmProbeTrampoline)), x26);
+    move(TrustedImmPtr(reinterpret_cast<void*>(Probe::executeProbe)), x28);
+    move(TrustedImmPtr(reinterpret_cast<void*>(function)), x24);
+    move(TrustedImmPtr(arg), x25);
     m_assembler.blr(x26);
 
     // ctiMasmProbeTrampoline should have restored every register except for lr and the sp.
-    load64(Address(sp, offsetof(OutgoingProbeRecord, lr)), lr);
-    add64(TrustedImm32(sizeof(OutgoingProbeRecord)), sp);
+    load64(Address(sp, offsetof(LRRestorationRecord, lr)), lr);
+    add64(TrustedImm32(sizeof(LRRestorationRecord)), sp);
 }
 
 #endif // ENABLE(MASM_PROBE)
index c7f55f0..7c682a2 100644 (file)
@@ -28,6 +28,7 @@
 #if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2)
 #include "MacroAssembler.h"
 
+#include "ProbeContext.h"
 #include <wtf/InlineASM.h>
 
 namespace JSC {
@@ -36,6 +37,8 @@ namespace JSC {
 
 extern "C" void ctiMasmProbeTrampoline();
 
+using namespace ARMRegisters;
+
 #if COMPILER(GCC_OR_CLANG)
 
 // The following are offsets for Probe::State fields accessed
@@ -186,6 +189,31 @@ COMPILE_ASSERT(PROBE_OFFSETOF(cpu.fprs[ARMRegisters::d31]) == PROBE_CPU_D31_OFFS
 COMPILE_ASSERT(sizeof(Probe::State) == PROBE_SIZE, ProbeState_size_matches_ctiMasmProbeTrampoline);
 #undef PROBE_OFFSETOF
 
+struct IncomingRecord {
+    uintptr_t lr;
+    uintptr_t ip;
+    uintptr_t apsr;
+    uintptr_t r0;
+    uintptr_t r1;
+    uintptr_t r2;
+};
+
+#define IN_LR_OFFSET (0 * PTR_SIZE)
+#define IN_IP_OFFSET (1 * PTR_SIZE)
+#define IN_APSR_OFFSET (2 * PTR_SIZE)
+#define IN_R0_OFFSET (3 * PTR_SIZE)
+#define IN_R1_OFFSET (4 * PTR_SIZE)
+#define IN_R2_OFFSET (5 * PTR_SIZE)
+#define IN_SIZE      (6 * PTR_SIZE)
+
+static_assert(IN_LR_OFFSET == offsetof(IncomingRecord, lr), "IN_LR_OFFSET is incorrect");
+static_assert(IN_IP_OFFSET == offsetof(IncomingRecord, ip), "IN_IP_OFFSET is incorrect");
+static_assert(IN_APSR_OFFSET == offsetof(IncomingRecord, apsr), "IN_APSR_OFFSET is incorrect");
+static_assert(IN_R0_OFFSET == offsetof(IncomingRecord, r0), "IN_R0_OFFSET is incorrect");
+static_assert(IN_R1_OFFSET == offsetof(IncomingRecord, r1), "IN_R1_OFFSET is incorrect");
+static_assert(IN_R2_OFFSET == offsetof(IncomingRecord, r2), "IN_R2_OFFSET is incorrect");
+static_assert(IN_SIZE == sizeof(IncomingRecord), "IN_SIZE is incorrect");
+
 asm (
     ".text" "\n"
     ".align 2" "\n"
@@ -196,65 +224,70 @@ asm (
     SYMBOL_STRING(ctiMasmProbeTrampoline) ":" "\n"
 
     // MacroAssemblerARMv7::probe() has already generated code to store some values.
-    // The top of stack now looks like this:
-    //     esp[0 * ptrSize]: probe handler function
-    //     esp[1 * ptrSize]: probe arg
-    //     esp[2 * ptrSize]: saved r0
-    //     esp[3 * ptrSize]: saved ip
-    //     esp[4 * ptrSize]: saved lr
-    //     esp[5 * ptrSize]: saved sp
+    // The top of stack now contains the IncomingRecord.
+    //
+    // Incoming register values:
+    //     r0: probe function
+    //     r1: probe arg
+    //     r2: Probe::executeProbe
+    //     ip: scratch, was ctiMasmProbeTrampoline
+    //     lr: return address
 
     "mov       ip, sp" "\n"
-    "mov       r0, sp" "\n"
-    "sub       r0, r0, #" STRINGIZE_VALUE_OF(PROBE_SIZE + OUT_SIZE) "\n"
+    "str       r2, [ip, #-" STRINGIZE_VALUE_OF(PTR_SIZE) "]" "\n" // Stash Probe::executeProbe.
+
+    "mov       r2, sp" "\n"
+    "sub       r2, r2, #" STRINGIZE_VALUE_OF(PROBE_SIZE + OUT_SIZE) "\n"
 
     // The ARM EABI specifies that the stack needs to be 16 byte aligned.
-    "bic       r0, r0, #0xf" "\n"
-    "mov       sp, r0" "\n" // Set the sp to protect the Probe::State from interrupts before we initialize it.
+    "bic       r2, r2, #0xf" "\n"
+    "mov       sp, r2" "\n" // Set the sp to protect the Probe::State from interrupts before we initialize it.
+    "ldr       r2, [ip, #-" STRINGIZE_VALUE_OF(PTR_SIZE) "]" "\n" // Reload Probe::executeProbe.
 
+    "str       r0, [sp, #" STRINGIZE_VALUE_OF(PROBE_PROBE_FUNCTION_OFFSET) "]" "\n"
+    "str       r1, [sp, #" STRINGIZE_VALUE_OF(PROBE_ARG_OFFSET) "]" "\n"
     "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_PC_OFFSET) "]" "\n"
-    "add       lr, sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_R1_OFFSET) "\n"
-    "stmia     lr, { r1-r11 }" "\n"
-    "mrs       lr, APSR" "\n"
-    "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_APSR_OFFSET) "]" "\n"
+
+    "add       r0, ip, #" STRINGIZE_VALUE_OF(IN_SIZE) "\n"
+    "str       r0, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n"
+
+    "add       lr, sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_R3_OFFSET) "\n"
+    "stmia     lr, { r3-r11 }" "\n"
+
     "vmrs      lr, FPSCR" "\n"
     "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_FPSCR_OFFSET) "]" "\n"
 
-    "ldr       lr, [ip, #0 * " STRINGIZE_VALUE_OF(PTR_SIZE) "]" "\n"
-    "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_PROBE_FUNCTION_OFFSET) "]" "\n"
-    "ldr       lr, [ip, #1 * " STRINGIZE_VALUE_OF(PTR_SIZE) "]" "\n"
-    "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_ARG_OFFSET) "]" "\n"
-    "ldr       lr, [ip, #2 * " STRINGIZE_VALUE_OF(PTR_SIZE) "]" "\n"
-    "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_R0_OFFSET) "]" "\n"
-    "ldr       lr, [ip, #3 * " STRINGIZE_VALUE_OF(PTR_SIZE) "]" "\n"
-    "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_IP_OFFSET) "]" "\n"
-    "ldr       lr, [ip, #4 * " STRINGIZE_VALUE_OF(PTR_SIZE) "]" "\n"
-    "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_LR_OFFSET) "]" "\n"
-    "ldr       lr, [ip, #5 * " STRINGIZE_VALUE_OF(PTR_SIZE) "]" "\n"
-    "str       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n"
-
-    "ldr       lr, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_PC_OFFSET) "]" "\n"
+    "ldr       r4, [ip, #" STRINGIZE_VALUE_OF(IN_LR_OFFSET) "]" "\n"
+    "ldr       r5, [ip, #" STRINGIZE_VALUE_OF(IN_IP_OFFSET) "]" "\n"
+    "ldr       r6, [ip, #" STRINGIZE_VALUE_OF(IN_APSR_OFFSET) "]" "\n"
+    "ldr       r7, [ip, #" STRINGIZE_VALUE_OF(IN_R0_OFFSET) "]" "\n"
+    "ldr       r8, [ip, #" STRINGIZE_VALUE_OF(IN_R1_OFFSET) "]" "\n"
+    "ldr       r9, [ip, #" STRINGIZE_VALUE_OF(IN_R2_OFFSET) "]" "\n"
+    "str       r4, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_LR_OFFSET) "]" "\n"
+    "str       r5, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_IP_OFFSET) "]" "\n"
+    "str       r6, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_APSR_OFFSET) "]" "\n"
+    "str       r7, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_R0_OFFSET) "]" "\n"
+    "str       r8, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_R1_OFFSET) "]" "\n"
+    "str       r9, [sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_R2_OFFSET) "]" "\n"
 
     "add       ip, sp, #" STRINGIZE_VALUE_OF(PROBE_CPU_D0_OFFSET) "\n"
     "vstmia.64 ip!, { d0-d15 }" "\n"
 #if CPU(ARM_NEON) || CPU(ARM_VFP_V3_D32)
     "vstmia.64 ip!, { d16-d31 }" "\n"
 #endif
-    "mov       fp, sp" "\n" // Save the Probe::State*.
 
-    // Initialize Probe::State::initializeStackFunction to zero.
-    "mov       r0, #0" "\n"
-    "str       r0, [fp, #" STRINGIZE_VALUE_OF(PROBE_INIT_STACK_FUNCTION_OFFSET) "]" "\n"
+    // r5 is a callee saved register. We'll use it for preserving the Probe::State*.
+    // https://stackoverflow.com/questions/261419/arm-to-c-calling-convention-registers-to-save#261496
+    "mov       r5, sp" "\n"
 
-    "ldr       ip, [sp, #" STRINGIZE_VALUE_OF(PROBE_PROBE_FUNCTION_OFFSET) "]" "\n"
     "mov       r0, sp" "\n" // the Probe::State* arg.
-    "blx       ip" "\n"
+    "blx       r2" "\n" // Call Probe::executeProbe.
 
     // Make sure the Probe::State is entirely below the result stack pointer so
     // that register values are still preserved when we call the initializeStack
     // function.
-    "ldr       r1, [fp, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n" // Result sp.
-    "add       r2, fp, #" STRINGIZE_VALUE_OF(PROBE_SIZE + OUT_SIZE) "\n" // End of ProveContext + buffer.
+    "ldr       r1, [r5, #" STRINGIZE_VALUE_OF(PROBE_CPU_SP_OFFSET) "]" "\n" // Result sp.
+    "add       r2, r5, #" STRINGIZE_VALUE_OF(PROBE_SIZE + OUT_SIZE) "\n" // End of ProveContext + buffer.
     "cmp       r1, r2" "\n"
     "it        ge" "\n"
     "bge     " LOCAL_LABEL_STRING(ctiMasmProbeTrampolineProbeStateIsSafe) "\n"
@@ -267,32 +300,30 @@ asm (
     // Copy the Probe::State to the safe place.
     // Note: we have to copy from low address to higher address because we're moving the
     // Probe::State to a lower address.
-    "mov       r5, fp" "\n"
-    "mov       r6, r1" "\n"
-    "add       r7, fp, #" STRINGIZE_VALUE_OF(PROBE_SIZE) "\n"
+    "add       r7, r5, #" STRINGIZE_VALUE_OF(PROBE_SIZE) "\n"
 
     LOCAL_LABEL_STRING(ctiMasmProbeTrampolineCopyLoop) ":" "\n"
     "ldr       r3, [r5], #4" "\n"
     "ldr       r4, [r5], #4" "\n"
-    "str       r3, [r6], #4" "\n"
-    "str       r4, [r6], #4" "\n"
+    "str       r3, [r1], #4" "\n"
+    "str       r4, [r1], #4" "\n"
     "cmp       r5, r7" "\n"
     "it        lt" "\n"
     "blt     " LOCAL_LABEL_STRING(ctiMasmProbeTrampolineCopyLoop) "\n"
 
-    "mov       fp, r1" "\n"
+    "mov       r5, sp" "\n"
 
     // Call initializeStackFunction if present.
     LOCAL_LABEL_STRING(ctiMasmProbeTrampolineProbeStateIsSafe) ":" "\n"
-    "ldr       r2, [fp, #" STRINGIZE_VALUE_OF(PROBE_INIT_STACK_FUNCTION_OFFSET) "]" "\n"
+    "ldr       r2, [r5, #" STRINGIZE_VALUE_OF(PROBE_INIT_STACK_FUNCTION_OFFSET) "]" "\n"
     "cbz       r2, " LOCAL_LABEL_STRING(ctiMasmProbeTrampolineRestoreRegisters) "\n"
 
-    "mov       r0, fp" "\n" // Set the Probe::State* arg.
+    "mov       r0, r5" "\n" // Set the Probe::State* arg.
     "blx       r2" "\n" // Call the initializeStackFunction (loaded into r2 above).
 
     LOCAL_LABEL_STRING(ctiMasmProbeTrampolineRestoreRegisters) ":" "\n"
 
-    "mov       sp, fp" "\n"
+    "mov       sp, r5" "\n" // Ensure that sp points to the Probe::State*.
 
     // To enable probes to modify register state, we copy all registers
     // out of the Probe::State before returning.
@@ -338,22 +369,26 @@ asm (
 
 void MacroAssembler::probe(Probe::Function function, void* arg)
 {
-    push(RegisterID::lr);
-    push(RegisterID::lr);
-    add32(TrustedImm32(8), RegisterID::sp, RegisterID::lr);
-    store32(RegisterID::lr, ArmAddress(RegisterID::sp, 4));
-    push(RegisterID::ip);
-    push(RegisterID::r0);
-    // The following uses RegisterID::ip. So, they must come after we push ip above.
-    push(trustedImm32FromPtr(arg));
-    push(trustedImm32FromPtr(function));
-
-    move(trustedImm32FromPtr(ctiMasmProbeTrampoline), RegisterID::ip);
-    m_assembler.blx(RegisterID::ip);
+    sub32(TrustedImm32(sizeof(IncomingRecord)), sp);
+
+    store32(lr, Address(sp, offsetof(IncomingRecord, lr)));
+    store32(ip, Address(sp, offsetof(IncomingRecord, ip)));
+    m_assembler.mrs(ip, apsr);
+    store32(ip, Address(sp, offsetof(IncomingRecord, apsr)));
+    store32(r0, Address(sp, offsetof(IncomingRecord, r0)));
+    store32(r1, Address(sp, offsetof(IncomingRecord, r1)));
+    store32(r2, Address(sp, offsetof(IncomingRecord, r2)));
+
+    // The following may emit a T1 mov instruction, which is effectively a movs.
+    // This means we must first preserve the apsr flags above first.
+    move(TrustedImmPtr(reinterpret_cast<void*>(function)), r0);
+    move(TrustedImmPtr(arg), r1);
+    move(TrustedImmPtr(reinterpret_cast<void*>(Probe::executeProbe)), r2);
+    move(TrustedImmPtr(reinterpret_cast<void*>(ctiMasmProbeTrampoline)), ip);
+    m_assembler.blx(ip);
 }
 #endif // ENABLE(MASM_PROBE)
 
 } // namespace JSC
 
-#endif // ENABLE(ASSEMBLER)
-
+#endif // ENABLE(ASSEMBLER) && CPU(ARM_THUMB2)
index 372f8b0..7368eb4 100644 (file)
@@ -2106,23 +2106,6 @@ protected:
     {
         return static_cast<ARMv7Assembler::Condition>(cond);
     }
-    
-#if ENABLE(MASM_PROBE)
-    inline TrustedImm32 trustedImm32FromPtr(void* ptr)
-    {
-        return TrustedImm32(TrustedImmPtr(ptr));
-    }
-
-    inline TrustedImm32 trustedImm32FromPtr(Probe::Function function)
-    {
-        return TrustedImm32(TrustedImmPtr(reinterpret_cast<void*>(function)));
-    }
-
-    inline TrustedImm32 trustedImm32FromPtr(void (*function)())
-    {
-        return TrustedImm32(TrustedImmPtr(reinterpret_cast<void*>(function)));
-    }
-#endif // ENABLE(MASM_PROBE)
 
 private:
     friend class LinkBuffer;
index 429fbb7..443f77f 100644 (file)
 #include <inttypes.h>
 
 namespace JSC {
+
 namespace Printer {
 
-using CPUState = MacroAssembler::CPUState;
+using CPUState = Probe::CPUState;
 using RegisterID = MacroAssembler::RegisterID;
 using FPRegisterID = MacroAssembler::FPRegisterID;
 
@@ -171,13 +172,13 @@ void printMemory(PrintStream& out, Context& context)
         out.print("\n");
 }
 
-void printCallback(Probe::State* probeContext)
+void printCallback(Probe::Context& probeContext)
 {
     auto& out = WTF::dataFile();
-    PrintRecordList& list = *reinterpret_cast<PrintRecordList*>(probeContext->arg);
+    PrintRecordList& list = *reinterpret_cast<PrintRecordList*>(probeContext.arg);
     for (size_t i = 0; i < list.size(); i++) {
         auto& record = list[i];
-        Context context(*probeContext, record.data);
+        Context context(probeContext, record.data);
         record.printer(out, context);
     }
 }
index f89c277..0e8cd5b 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "MacroAssembler.h"
 #include "Printer.h"
+#include "ProbeContext.h"
 
 namespace JSC {
 
@@ -223,7 +224,7 @@ struct Printer<MemWord<IntType>> : public Printer<Memory> {
     { }
 };
 
-void printCallback(Probe::State*);
+void printCallback(Probe::Context&);
 
 } // namespace Printer
 
index bd5449e..369a1cb 100644 (file)
@@ -28,6 +28,7 @@
 #if ENABLE(ASSEMBLER) && (CPU(X86) || CPU(X86_64))
 #include "MacroAssembler.h"
 
+#include "ProbeContext.h"
 #include <wtf/InlineASM.h>
 
 namespace JSC {
@@ -101,6 +102,8 @@ extern "C" void ctiMasmProbeTrampoline();
 #define PROBE_SIZE (PROBE_CPU_XMM15_OFFSET + XMM_SIZE)
 #endif // CPU(X86_64)
 
+#define PROBE_EXECUTOR_OFFSET PROBE_SIZE // Stash the executeProbe function pointer at the end of the ProbeContext.
+
 // The outgoing record to be popped off the stack at the end consists of:
 // eflags, eax, ecx, ebp, eip.
 #define OUT_SIZE        (5 * PTR_SIZE)
@@ -159,6 +162,7 @@ COMPILE_ASSERT(PROBE_OFFSETOF_REG(cpu.fprs, X86Registers::xmm15) == PROBE_CPU_XM
 #endif // CPU(X86_64)
 
 COMPILE_ASSERT(sizeof(Probe::State) == PROBE_SIZE, ProbeState_size_matches_ctiMasmProbeTrampoline);
+COMPILE_ASSERT((PROBE_EXECUTOR_OFFSET + PTR_SIZE) <= (PROBE_SIZE + OUT_SIZE), Must_have_room_after_ProbeContext_to_stash_the_probe_handler);
 
 #undef PROBE_OFFSETOF
 
@@ -176,10 +180,16 @@ asm (
     // this:
     //     esp[0 * ptrSize]: eflags
     //     esp[1 * ptrSize]: return address / saved eip
-    //     esp[2 * ptrSize]: probe handler function
-    //     esp[3 * ptrSize]: probe arg
-    //     esp[4 * ptrSize]: saved eax
-    //     esp[5 * ptrSize]: saved esp
+    //     esp[2 * ptrSize]: saved ebx
+    //     esp[3 * ptrSize]: saved edx
+    //     esp[4 * ptrSize]: saved ecx
+    //     esp[5 * ptrSize]: saved eax
+    //
+    // Incoming registers contain:
+    //     ecx: Probe::executeProbe
+    //     edx: probe function
+    //     ebx: probe arg
+    //     eax: scratch (was ctiMasmProbeTrampoline)
 
     "movl %esp, %eax" "\n"
     "subl $" STRINGIZE_VALUE_OF(PROBE_SIZE + OUT_SIZE) ", %esp" "\n"
@@ -190,9 +200,9 @@ asm (
     "movl %ebp, " STRINGIZE_VALUE_OF(PROBE_CPU_EBP_OFFSET) "(%esp)" "\n"
     "movl %esp, %ebp" "\n" // Save the Probe::State*.
 
-    "movl %ecx, " STRINGIZE_VALUE_OF(PROBE_CPU_ECX_OFFSET) "(%ebp)" "\n"
-    "movl %edx, " STRINGIZE_VALUE_OF(PROBE_CPU_EDX_OFFSET) "(%ebp)" "\n"
-    "movl %ebx, " STRINGIZE_VALUE_OF(PROBE_CPU_EBX_OFFSET) "(%ebp)" "\n"
+    "movl %ecx, " STRINGIZE_VALUE_OF(PROBE_EXECUTOR_OFFSET) "(%ebp)" "\n"
+    "movl %edx, " STRINGIZE_VALUE_OF(PROBE_PROBE_FUNCTION_OFFSET) "(%ebp)" "\n"
+    "movl %ebx, " STRINGIZE_VALUE_OF(PROBE_ARG_OFFSET) "(%ebp)" "\n"
     "movl %esi, " STRINGIZE_VALUE_OF(PROBE_CPU_ESI_OFFSET) "(%ebp)" "\n"
     "movl %edi, " STRINGIZE_VALUE_OF(PROBE_CPU_EDI_OFFSET) "(%ebp)" "\n"
 
@@ -201,12 +211,16 @@ asm (
     "movl 1 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%eax), %ecx" "\n"
     "movl %ecx, " STRINGIZE_VALUE_OF(PROBE_CPU_EIP_OFFSET) "(%ebp)" "\n"
     "movl 2 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%eax), %ecx" "\n"
-    "movl %ecx, " STRINGIZE_VALUE_OF(PROBE_PROBE_FUNCTION_OFFSET) "(%ebp)" "\n"
+    "movl %ecx, " STRINGIZE_VALUE_OF(PROBE_CPU_EBX_OFFSET) "(%ebp)" "\n"
     "movl 3 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%eax), %ecx" "\n"
-    "movl %ecx, " STRINGIZE_VALUE_OF(PROBE_ARG_OFFSET) "(%ebp)" "\n"
+    "movl %ecx, " STRINGIZE_VALUE_OF(PROBE_CPU_EDX_OFFSET) "(%ebp)" "\n"
     "movl 4 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%eax), %ecx" "\n"
-    "movl %ecx, " STRINGIZE_VALUE_OF(PROBE_CPU_EAX_OFFSET) "(%ebp)" "\n"
+    "movl %ecx, " STRINGIZE_VALUE_OF(PROBE_CPU_ECX_OFFSET) "(%ebp)" "\n"
     "movl 5 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%eax), %ecx" "\n"
+    "movl %ecx, " STRINGIZE_VALUE_OF(PROBE_CPU_EAX_OFFSET) "(%ebp)" "\n"
+
+    "movl %eax, %ecx" "\n"
+    "addl $" STRINGIZE_VALUE_OF(6 * PTR_SIZE) ", %ecx" "\n"
     "movl %ecx, " STRINGIZE_VALUE_OF(PROBE_CPU_ESP_OFFSET) "(%ebp)" "\n"
 
     "movq %xmm0, " STRINGIZE_VALUE_OF(PROBE_CPU_XMM0_OFFSET) "(%ebp)" "\n"
@@ -218,15 +232,12 @@ asm (
     "movq %xmm6, " STRINGIZE_VALUE_OF(PROBE_CPU_XMM6_OFFSET) "(%ebp)" "\n"
     "movq %xmm7, " STRINGIZE_VALUE_OF(PROBE_CPU_XMM7_OFFSET) "(%ebp)" "\n"
 
-    "xorl %eax, %eax" "\n"
-    "movl %eax, " STRINGIZE_VALUE_OF(PROBE_INIT_STACK_FUNCTION_OFFSET) "(%ebp)" "\n"
-
     // Reserve stack space for the arg while maintaining the required stack
     // pointer 32 byte alignment:
     "subl $0x20, %esp" "\n"
     "movl %ebp, 0(%esp)" "\n" // the Probe::State* arg.
 
-    "call *" STRINGIZE_VALUE_OF(PROBE_PROBE_FUNCTION_OFFSET) "(%ebp)" "\n"
+    "call *" STRINGIZE_VALUE_OF(PROBE_EXECUTOR_OFFSET) "(%ebp)" "\n"
 
     // Make sure the Probe::State is entirely below the result stack pointer so
     // that register values are still preserved when we call the initializeStack
@@ -336,10 +347,16 @@ extern "C" __declspec(naked) void ctiMasmProbeTrampoline()
         // this:
         //     esp[0 * ptrSize]: eflags
         //     esp[1 * ptrSize]: return address / saved eip
-        //     esp[2 * ptrSize]: probe handler function
-        //     esp[3 * ptrSize]: probe arg
-        //     esp[4 * ptrSize]: saved eax
-        //     esp[5 * ptrSize]: saved esp
+        //     esp[2 * ptrSize]: saved ebx
+        //     esp[3 * ptrSize]: saved edx
+        //     esp[4 * ptrSize]: saved ecx
+        //     esp[5 * ptrSize]: saved eax
+        //
+        // Incoming registers contain:
+        //     ecx: Probe::executeProbe
+        //     edx: probe function
+        //     ebx: probe arg
+        //     eax: scratch (was ctiMasmProbeTrampoline)
 
         mov eax, esp
         sub esp, PROBE_SIZE + OUT_SIZE
@@ -350,9 +367,9 @@ extern "C" __declspec(naked) void ctiMasmProbeTrampoline()
         mov [PROBE_CPU_EBP_OFFSET + esp], ebp
         mov ebp, esp // Save the ProbeContext*.
 
-        mov [PROBE_CPU_ECX_OFFSET + ebp], ecx
-        mov [PROBE_CPU_EDX_OFFSET + ebp], edx
-        mov [PROBE_CPU_EBX_OFFSET + ebp], ebx
+        mov [PROBE_EXECUTOR_OFFSET + ebp], ecx
+        mov [PROBE_PROBE_FUNCTION_OFFSET + ebp], edx
+        mov [PROBE_ARG_OFFSET + ebp], ebx
         mov [PROBE_CPU_ESI_OFFSET + ebp], esi
         mov [PROBE_CPU_EDI_OFFSET + ebp], edi
 
@@ -361,12 +378,16 @@ extern "C" __declspec(naked) void ctiMasmProbeTrampoline()
         mov ecx, [1 * PTR_SIZE + eax]
         mov [PROBE_CPU_EIP_OFFSET + ebp], ecx
         mov ecx, [2 * PTR_SIZE + eax]
-        mov [PROBE_PROBE_FUNCTION_OFFSET + ebp], ecx
+        mov [PROBE_CPU_EBX_OFFSET + ebp], ecx
         mov ecx, [3 * PTR_SIZE + eax]
-        mov [PROBE_ARG_OFFSET + ebp], ecx
+        mov [PROBE_CPU_EDX_OFFSET + ebp], ecx
         mov ecx, [4 * PTR_SIZE + eax]
-        mov [PROBE_CPU_EAX_OFFSET + ebp], ecx
+        mov [PROBE_CPU_ECX_OFFSET + ebp], ecx
         mov ecx, [5 * PTR_SIZE + eax]
+        mov [PROBE_CPU_EAX_OFFSET + ebp], ecx
+
+        mov ecx, eax
+        add ecx, 6 * PTR_SIZE
         mov [PROBE_CPU_ESP_OFFSET + ebp], ecx
 
         movq qword ptr[PROBE_CPU_XMM0_OFFSET + ebp], xmm0
@@ -378,15 +399,12 @@ extern "C" __declspec(naked) void ctiMasmProbeTrampoline()
         movq qword ptr[PROBE_CPU_XMM6_OFFSET + ebp], xmm6
         movq qword ptr[PROBE_CPU_XMM7_OFFSET + ebp], xmm7
 
-        xor eax, eax
-        mov [PROBE_INIT_STACK_FUNCTION_OFFSET + ebp], eax
-
         // Reserve stack space for the arg while maintaining the required stack
         // pointer 32 byte alignment:
         sub esp, 0x20
         mov [0 + esp], ebp // the ProbeContext* arg.
 
-        call [PROBE_PROBE_FUNCTION_OFFSET + ebp]
+        call [PROBE_EXECUTOR_OFFSET + ebp]
 
         // Make sure the ProbeContext is entirely below the result stack pointer so
         // that register values are still preserved when we call the initializeStack
@@ -484,7 +502,7 @@ extern "C" __declspec(naked) void ctiMasmProbeTrampoline()
         ret
     }
 }
-#endif
+#endif // COMPILER(MSVC)
 
 #endif // CPU(X86)
 
@@ -498,14 +516,19 @@ asm (
     "pushfq" "\n"
 
     // MacroAssemblerX86Common::probe() has already generated code to store some values.
-    // Together with the rflags pushed above, the top of stack now looks like
-    // this:
-    //     esp[0 * ptrSize]: rflags
-    //     esp[1 * ptrSize]: return address / saved rip
-    //     esp[2 * ptrSize]: probe handler function
-    //     esp[3 * ptrSize]: probe arg
-    //     esp[4 * ptrSize]: saved rax
-    //     esp[5 * ptrSize]: saved rsp
+    // Together with the rflags pushed above, the top of stack now looks like this:
+    //     rsp[0 * ptrSize]: rflags
+    //     rsp[1 * ptrSize]: return address / saved rip
+    //     rsp[2 * ptrSize]: saved rbx
+    //     rsp[3 * ptrSize]: saved rdx
+    //     rsp[4 * ptrSize]: saved rcx
+    //     rsp[5 * ptrSize]: saved rax
+    //
+    // Incoming registers contain:
+    //     rcx: Probe::executeProbe
+    //     rdx: probe function
+    //     rbx: probe arg
+    //     rax: scratch (was ctiMasmProbeTrampoline)
 
     "movq %rsp, %rax" "\n"
     "subq $" STRINGIZE_VALUE_OF(PROBE_SIZE + OUT_SIZE) ", %rsp" "\n"
@@ -517,9 +540,9 @@ asm (
     "movq %rbp, " STRINGIZE_VALUE_OF(PROBE_CPU_EBP_OFFSET) "(%rsp)" "\n"
     "movq %rsp, %rbp" "\n" // Save the Probe::State*.
 
-    "movq %rcx, " STRINGIZE_VALUE_OF(PROBE_CPU_ECX_OFFSET) "(%rbp)" "\n"
-    "movq %rdx, " STRINGIZE_VALUE_OF(PROBE_CPU_EDX_OFFSET) "(%rbp)" "\n"
-    "movq %rbx, " STRINGIZE_VALUE_OF(PROBE_CPU_EBX_OFFSET) "(%rbp)" "\n"
+    "movq %rcx, " STRINGIZE_VALUE_OF(PROBE_EXECUTOR_OFFSET) "(%rbp)" "\n"
+    "movq %rdx, " STRINGIZE_VALUE_OF(PROBE_PROBE_FUNCTION_OFFSET) "(%rbp)" "\n"
+    "movq %rbx, " STRINGIZE_VALUE_OF(PROBE_ARG_OFFSET) "(%rbp)" "\n"
     "movq %rsi, " STRINGIZE_VALUE_OF(PROBE_CPU_ESI_OFFSET) "(%rbp)" "\n"
     "movq %rdi, " STRINGIZE_VALUE_OF(PROBE_CPU_EDI_OFFSET) "(%rbp)" "\n"
 
@@ -528,12 +551,16 @@ asm (
     "movq 1 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%rax), %rcx" "\n"
     "movq %rcx, " STRINGIZE_VALUE_OF(PROBE_CPU_EIP_OFFSET) "(%rbp)" "\n"
     "movq 2 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%rax), %rcx" "\n"
-    "movq %rcx, " STRINGIZE_VALUE_OF(PROBE_PROBE_FUNCTION_OFFSET) "(%rbp)" "\n"
+    "movq %rcx, " STRINGIZE_VALUE_OF(PROBE_CPU_EBX_OFFSET) "(%rbp)" "\n"
     "movq 3 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%rax), %rcx" "\n"
-    "movq %rcx, " STRINGIZE_VALUE_OF(PROBE_ARG_OFFSET) "(%rbp)" "\n"
+    "movq %rcx, " STRINGIZE_VALUE_OF(PROBE_CPU_EDX_OFFSET) "(%rbp)" "\n"
     "movq 4 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%rax), %rcx" "\n"
-    "movq %rcx, " STRINGIZE_VALUE_OF(PROBE_CPU_EAX_OFFSET) "(%rbp)" "\n"
+    "movq %rcx, " STRINGIZE_VALUE_OF(PROBE_CPU_ECX_OFFSET) "(%rbp)" "\n"
     "movq 5 * " STRINGIZE_VALUE_OF(PTR_SIZE) "(%rax), %rcx" "\n"
+    "movq %rcx, " STRINGIZE_VALUE_OF(PROBE_CPU_EAX_OFFSET) "(%rbp)" "\n"
+
+    "movq %rax, %rcx" "\n"
+    "addq $" STRINGIZE_VALUE_OF(6 * PTR_SIZE) ", %rcx" "\n"
     "movq %rcx, " STRINGIZE_VALUE_OF(PROBE_CPU_ESP_OFFSET) "(%rbp)" "\n"
 
     "movq %r8, " STRINGIZE_VALUE_OF(PROBE_CPU_R8_OFFSET) "(%rbp)" "\n"
@@ -562,11 +589,8 @@ asm (
     "movq %xmm14, " STRINGIZE_VALUE_OF(PROBE_CPU_XMM14_OFFSET) "(%rbp)" "\n"
     "movq %xmm15, " STRINGIZE_VALUE_OF(PROBE_CPU_XMM15_OFFSET) "(%rbp)" "\n"
 
-    "xorq %rax, %rax" "\n"
-    "movq %rax, " STRINGIZE_VALUE_OF(PROBE_INIT_STACK_FUNCTION_OFFSET) "(%rbp)" "\n"
-
     "movq %rbp, %rdi" "\n" // the Probe::State* arg.
-    "call *" STRINGIZE_VALUE_OF(PROBE_PROBE_FUNCTION_OFFSET) "(%rbp)" "\n"
+    "call *" STRINGIZE_VALUE_OF(PROBE_EXECUTOR_OFFSET) "(%rbp)" "\n"
 
     // Make sure the Probe::State is entirely below the result stack pointer so
     // that register values are still preserved when we call the initializeStack
@@ -731,13 +755,14 @@ extern "C" void ctiMasmProbeTrampoline()
 
 void MacroAssembler::probe(Probe::Function function, void* arg)
 {
-    push(RegisterID::esp);
-    push(RegisterID::eax);
-    move(TrustedImmPtr(arg), RegisterID::eax);
-    push(RegisterID::eax);
-    move(TrustedImmPtr(reinterpret_cast<void*>(function)), RegisterID::eax);
     push(RegisterID::eax);
     move(TrustedImmPtr(reinterpret_cast<void*>(ctiMasmProbeTrampoline)), RegisterID::eax);
+    push(RegisterID::ecx);
+    move(TrustedImmPtr(reinterpret_cast<void*>(Probe::executeProbe)), RegisterID::ecx);
+    push(RegisterID::edx);
+    move(TrustedImmPtr(reinterpret_cast<void*>(function)), RegisterID::edx);
+    push(RegisterID::ebx);
+    move(TrustedImmPtr(arg), RegisterID::ebx);
     call(RegisterID::eax);
 }
 #endif // ENABLE(MASM_PROBE)
index 80a88cf..641deae 100644 (file)
@@ -32,7 +32,7 @@
 namespace JSC {
 
 namespace Probe {
-struct State;
+class Context;
 } // namespace Probe
 
 namespace Printer {
@@ -86,12 +86,12 @@ union Data {
 };
 
 struct Context {
-    Context(Probe::State& probeContext, Data& data)
+    Context(Probe::Context& probeContext, Data& data)
         : probeContext(probeContext)
         , data(data)
     { }
 
-    Probe::State& probeContext;
+    Probe::Context& probeContext;
     Data& data;
 };
 
diff --git a/Source/JavaScriptCore/assembler/ProbeContext.cpp b/Source/JavaScriptCore/assembler/ProbeContext.cpp
new file mode 100644 (file)
index 0000000..27a7248
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ProbeContext.h"
+
+namespace JSC {
+namespace Probe {
+
+static void flushDirtyStackPages(State*);
+
+void executeProbe(State* state)
+{
+    Context context(state);
+#if CPU(ARM64)
+    auto& cpu = context.cpu;
+    void* originalLR = cpu.gpr<void*>(ARM64Registers::lr);
+    void* originalPC = cpu.pc();
+#endif
+
+    state->initializeStackFunction = nullptr;
+    state->initializeStackArg = nullptr;
+    state->probeFunction(context);
+
+#if CPU(ARM64)
+    // The ARM64 probe trampoline does not support changing both lr and pc.
+    RELEASE_ASSERT(originalPC == cpu.pc() || originalLR == cpu.gpr<void*>(ARM64Registers::lr));
+#endif
+
+    if (context.hasWritesToFlush()) {
+        context.stack().setNewStackPointer(state->cpu.sp());
+        state->cpu.sp() = std::min(context.stack().lowWatermark(), state->cpu.sp());
+
+        state->initializeStackFunction = flushDirtyStackPages;
+        state->initializeStackArg = context.releaseStack();
+    }
+}
+
+static void flushDirtyStackPages(State* state)
+{
+    std::unique_ptr<Stack> stack(reinterpret_cast<Probe::Stack*>(state->initializeStackArg));
+    stack->flushWrites();
+    state->cpu.sp() = stack->newStackPointer();
+}
+
+// Not for general use. This should only be for writing tests.
+JS_EXPORT_PRIVATE void* probeStateForContext(Context&);
+void* probeStateForContext(Context& context)
+{
+    return context.m_state;
+}
+
+} // namespace Probe
+} // namespace JSC
diff --git a/Source/JavaScriptCore/assembler/ProbeContext.h b/Source/JavaScriptCore/assembler/ProbeContext.h
new file mode 100644 (file)
index 0000000..8453fb4
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "MacroAssembler.h"
+#include "ProbeStack.h"
+
+namespace JSC {
+namespace Probe {
+
+struct CPUState {
+    using RegisterID = MacroAssembler::RegisterID;
+    using SPRegisterID = MacroAssembler::SPRegisterID;
+    using FPRegisterID = MacroAssembler::FPRegisterID;
+
+    static inline const char* gprName(RegisterID id) { return MacroAssembler::gprName(id); }
+    static inline const char* sprName(SPRegisterID id) { return MacroAssembler::sprName(id); }
+    static inline const char* fprName(FPRegisterID id) { return MacroAssembler::fprName(id); }
+    inline uintptr_t& gpr(RegisterID);
+    inline uintptr_t& spr(SPRegisterID);
+    inline double& fpr(FPRegisterID);
+
+    template<typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+    T gpr(RegisterID) const;
+    template<typename T, typename std::enable_if<std::is_pointer<T>::value>::type* = nullptr>
+    T gpr(RegisterID) const;
+    template<typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+    T spr(SPRegisterID) const;
+    template<typename T, typename std::enable_if<std::is_pointer<T>::value>::type* = nullptr>
+    T spr(SPRegisterID) const;
+    template<typename T> T fpr(FPRegisterID) const;
+
+    void*& pc();
+    void*& fp();
+    void*& sp();
+    template<typename T> T pc() const;
+    template<typename T> T fp() const;
+    template<typename T> T sp() const;
+
+    uintptr_t gprs[MacroAssembler::numberOfRegisters()];
+    uintptr_t sprs[MacroAssembler::numberOfSPRegisters()];
+    double fprs[MacroAssembler::numberOfFPRegisters()];
+};
+
+inline uintptr_t& CPUState::gpr(RegisterID id)
+{
+    ASSERT(id >= MacroAssembler::firstRegister() && id <= MacroAssembler::lastRegister());
+    return gprs[id];
+}
+
+inline uintptr_t& CPUState::spr(SPRegisterID id)
+{
+    ASSERT(id >= MacroAssembler::firstSPRegister() && id <= MacroAssembler::lastSPRegister());
+    return sprs[id];
+}
+
+inline double& CPUState::fpr(FPRegisterID id)
+{
+    ASSERT(id >= MacroAssembler::firstFPRegister() && id <= MacroAssembler::lastFPRegister());
+    return fprs[id];
+}
+
+template<typename T, typename std::enable_if<std::is_integral<T>::value>::type*>
+T CPUState::gpr(RegisterID id) const
+{
+    CPUState* cpu = const_cast<CPUState*>(this);
+    return static_cast<T>(cpu->gpr(id));
+}
+
+template<typename T, typename std::enable_if<std::is_pointer<T>::value>::type*>
+T CPUState::gpr(RegisterID id) const
+{
+    CPUState* cpu = const_cast<CPUState*>(this);
+    return reinterpret_cast<T>(cpu->gpr(id));
+}
+
+template<typename T, typename std::enable_if<std::is_integral<T>::value>::type*>
+T CPUState::spr(SPRegisterID id) const
+{
+    CPUState* cpu = const_cast<CPUState*>(this);
+    return static_cast<T>(cpu->spr(id));
+}
+
+template<typename T, typename std::enable_if<std::is_pointer<T>::value>::type*>
+T CPUState::spr(SPRegisterID id) const
+{
+    CPUState* cpu = const_cast<CPUState*>(this);
+    return reinterpret_cast<T>(cpu->spr(id));
+}
+
+template<typename T>
+T CPUState::fpr(FPRegisterID id) const
+{
+    CPUState* cpu = const_cast<CPUState*>(this);
+    return bitwise_cast<T>(cpu->fpr(id));
+}
+
+inline void*& CPUState::pc()
+{
+#if CPU(X86) || CPU(X86_64)
+    return *reinterpret_cast<void**>(&spr(X86Registers::eip));
+#elif CPU(ARM64)
+    return *reinterpret_cast<void**>(&spr(ARM64Registers::pc));
+#elif CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL)
+    return *reinterpret_cast<void**>(&gpr(ARMRegisters::pc));
+#elif CPU(MIPS)
+    RELEASE_ASSERT_NOT_REACHED();
+#else
+#error "Unsupported CPU"
+#endif
+}
+
+inline void*& CPUState::fp()
+{
+#if CPU(X86) || CPU(X86_64)
+    return *reinterpret_cast<void**>(&gpr(X86Registers::ebp));
+#elif CPU(ARM64)
+    return *reinterpret_cast<void**>(&gpr(ARM64Registers::fp));
+#elif CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL)
+    return *reinterpret_cast<void**>(&gpr(ARMRegisters::fp));
+#elif CPU(MIPS)
+    return *reinterpret_cast<void**>(&gpr(MIPSRegisters::fp));
+#else
+#error "Unsupported CPU"
+#endif
+}
+
+inline void*& CPUState::sp()
+{
+#if CPU(X86) || CPU(X86_64)
+    return *reinterpret_cast<void**>(&gpr(X86Registers::esp));
+#elif CPU(ARM64)
+    return *reinterpret_cast<void**>(&gpr(ARM64Registers::sp));
+#elif CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL)
+    return *reinterpret_cast<void**>(&gpr(ARMRegisters::sp));
+#elif CPU(MIPS)
+    return *reinterpret_cast<void**>(&gpr(MIPSRegisters::sp));
+#else
+#error "Unsupported CPU"
+#endif
+}
+
+template<typename T>
+T CPUState::pc() const
+{
+    CPUState* cpu = const_cast<CPUState*>(this);
+    return reinterpret_cast<T>(cpu->pc());
+}
+
+template<typename T>
+T CPUState::fp() const
+{
+    CPUState* cpu = const_cast<CPUState*>(this);
+    return reinterpret_cast<T>(cpu->fp());
+}
+
+template<typename T>
+T CPUState::sp() const
+{
+    CPUState* cpu = const_cast<CPUState*>(this);
+    return reinterpret_cast<T>(cpu->sp());
+}
+
+struct State;
+typedef void (*StackInitializationFunction)(State*);
+
+struct State {
+    Probe::Function probeFunction;
+    void* arg;
+    StackInitializationFunction initializeStackFunction;
+    void* initializeStackArg;
+    CPUState cpu;
+};
+
+class Context {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    using RegisterID = MacroAssembler::RegisterID;
+    using SPRegisterID = MacroAssembler::SPRegisterID;
+    using FPRegisterID = MacroAssembler::FPRegisterID;
+
+    Context(State* state)
+        : m_state(state)
+        , arg(state->arg)
+        , cpu(state->cpu)
+    { }
+
+    uintptr_t& gpr(RegisterID id) { return m_state->cpu.gpr(id); }
+    uintptr_t& spr(SPRegisterID id) { return m_state->cpu.spr(id); }
+    double& fpr(FPRegisterID id) { return m_state->cpu.fpr(id); }
+    const char* gprName(RegisterID id) { return m_state->cpu.gprName(id); }
+    const char* sprName(SPRegisterID id) { return m_state->cpu.sprName(id); }
+    const char* fprName(FPRegisterID id) { return m_state->cpu.fprName(id); }
+
+    void*& pc() { return m_state->cpu.pc(); }
+    void*& fp() { return m_state->cpu.fp(); }
+    void*& sp() { return m_state->cpu.sp(); }
+
+    template<typename T> T pc() { return m_state->cpu.pc<T>(); }
+    template<typename T> T fp() { return m_state->cpu.fp<T>(); }
+    template<typename T> T sp() { return m_state->cpu.sp<T>(); }
+
+    Stack& stack()
+    {
+        ASSERT(m_stack.isValid());
+        return m_stack;
+    };
+
+    bool hasWritesToFlush() { return m_stack.hasWritesToFlush(); }
+    Stack* releaseStack() { return new Stack(WTFMove(m_stack)); }
+
+private:
+    State* m_state;
+public:
+    void* arg;
+    CPUState& cpu;
+
+private:
+    Stack m_stack;
+
+    friend JS_EXPORT_PRIVATE void* probeStateForContext(Context&); // Not for general use. This should only be for writing tests.
+};
+
+void executeProbe(State*);
+
+} // namespace Probe
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/assembler/ProbeStack.cpp b/Source/JavaScriptCore/assembler/ProbeStack.cpp
new file mode 100644 (file)
index 0000000..f3b2102
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ProbeStack.h"
+
+#include <memory>
+
+namespace JSC {
+namespace Probe {
+
+Page::Page(void* baseAddress)
+    : m_baseLogicalAddress(baseAddress)
+{
+    memcpy(&m_buffer, baseAddress, s_pageSize);
+}
+
+void Page::flushWrites()
+{
+    uintptr_t dirtyBits = m_dirtyBits;
+    size_t offset = 0;
+    while (dirtyBits) {
+        // Find start.
+        if (dirtyBits & 1) {
+            size_t startOffset = offset;
+            // Find end.
+            do {
+                dirtyBits = dirtyBits >> 1;
+                offset += s_chunkSize;
+            } while (dirtyBits & 1);
+
+            size_t size = offset - startOffset;
+            uint8_t* src = reinterpret_cast<uint8_t*>(&m_buffer) + startOffset;
+            uint8_t* dst = reinterpret_cast<uint8_t*>(m_baseLogicalAddress) + startOffset;
+            memcpy(dst, src, size);
+        }
+        dirtyBits = dirtyBits >> 1;
+        offset += s_chunkSize;
+    }
+    m_dirtyBits = 0;
+}
+
+Stack::Stack(Stack&& other)
+    : m_newStackPointer(other.m_newStackPointer)
+    , m_lowWatermark(other.m_lowWatermark)
+    , m_stackBounds(WTFMove(other.m_stackBounds))
+    , m_pages(WTFMove(other.m_pages))
+{
+#if !ASSERT_DISABLED
+    other.m_isValid = false;
+#endif
+}
+
+bool Stack::hasWritesToFlush()
+{
+    return std::any_of(m_pages.begin(), m_pages.end(), [] (auto& it) { return it.value->hasWritesToFlush(); });
+}
+
+void Stack::flushWrites()
+{
+    for (auto it = m_pages.begin(); it != m_pages.end(); ++it)
+        it->value->flushWritesIfNeeded();
+}
+
+Page* Stack::ensurePageFor(void* address)
+{
+    // Since the machine stack is always allocated in units of whole pages, asserting
+    // that the address is contained in the stack is sufficient to infer that its page
+    // is also contained in the stack.
+    RELEASE_ASSERT(m_stackBounds.contains(address));
+
+    // We may have gotten here because of a cache miss. So, look up the page first
+    // before allocating a new one,
+    void* baseAddress = Page::baseAddressFor(address);
+    auto it = m_pages.find(baseAddress);
+    if (LIKELY(it != m_pages.end()))
+        m_lastAccessedPage = it->value.get();
+    else {
+        std::unique_ptr<Page> page = std::make_unique<Page>(baseAddress);
+        auto result = m_pages.add(baseAddress, WTFMove(page));
+        m_lastAccessedPage = result.iterator->value.get();
+    }
+    m_lastAccessedPageBaseAddress = baseAddress;
+    return m_lastAccessedPage;
+}
+
+} // namespace Probe
+} // namespace JSC
diff --git a/Source/JavaScriptCore/assembler/ProbeStack.h b/Source/JavaScriptCore/assembler/ProbeStack.h
new file mode 100644 (file)
index 0000000..94ad7e9
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/HashMap.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/Threading.h>
+
+namespace JSC {
+
+struct ProbeContext;
+
+namespace Probe {
+
+class Page {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    Page(void* baseAddress);
+
+    static void* baseAddressFor(void* p)
+    {
+        return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(p) & ~s_pageMask);
+    }
+    static void* chunkAddressFor(void* p)
+    {
+        return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(p) & ~s_chunkMask);
+    }
+
+    void* baseAddress() { return m_baseLogicalAddress; }
+
+    template<typename T>
+    T get(void* logicalAddress)
+    {
+        return *physicalAddressFor<T*>(logicalAddress);
+    }
+
+    template<typename T>
+    void set(void* logicalAddress, T value)
+    {
+        m_dirtyBits |= dirtyBitFor(logicalAddress);
+        *physicalAddressFor<T*>(logicalAddress) = value;
+    }
+
+    bool hasWritesToFlush() const { return !!m_dirtyBits; }
+    void flushWritesIfNeeded()
+    {
+        if (m_dirtyBits)
+            flushWrites();
+    }
+
+private:
+    uintptr_t dirtyBitFor(void* logicalAddress)
+    {
+        uintptr_t offset = reinterpret_cast<uintptr_t>(logicalAddress) & s_pageMask;
+        return static_cast<uintptr_t>(1) << (offset >> s_chunkSizeShift);
+    }
+
+    template<typename T, typename = typename std::enable_if<std::is_pointer<T>::value>::type>
+    T physicalAddressFor(void* logicalAddress)
+    {
+        uintptr_t offset = reinterpret_cast<uintptr_t>(logicalAddress) & s_pageMask;
+        void* physicalAddress = reinterpret_cast<uint8_t*>(&m_buffer) + offset;
+        return reinterpret_cast<T>(physicalAddress);
+    }
+
+    void flushWrites();
+
+    void* m_baseLogicalAddress { nullptr };
+    uintptr_t m_dirtyBits { 0 };
+
+    static constexpr size_t s_pageSize = 1024;
+    static constexpr uintptr_t s_pageMask = s_pageSize - 1;
+    static constexpr size_t s_chunksPerPage = sizeof(uintptr_t) * 8; // sizeof(m_dirtyBits) in bits.
+    static constexpr size_t s_chunkSize = s_pageSize / s_chunksPerPage;
+    static constexpr uintptr_t s_chunkMask = s_chunkSize - 1;
+#if USE(JSVALUE64)
+    static constexpr size_t s_chunkSizeShift = 4;
+#else
+    static constexpr size_t s_chunkSizeShift = 5;
+#endif
+    static_assert(s_pageSize > s_chunkSize, "bad pageSize or chunkSize");
+    static_assert(s_chunkSize == (1 << s_chunkSizeShift), "bad chunkSizeShift");
+
+
+    typedef typename std::aligned_storage<s_pageSize, std::alignment_of<uintptr_t>::value>::type Buffer;
+    Buffer m_buffer;
+};
+
+class Stack {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    Stack()
+        : m_lowWatermark(reinterpret_cast<void*>(-1))
+        , m_stackBounds(Thread::current().stack())
+    { }
+    Stack(Stack&& other);
+
+    void* lowWatermark() { return m_lowWatermark; }
+
+    template<typename T>
+    typename std::enable_if<!std::is_same<double, typename std::remove_cv<T>::type>::value, T>::type get(void* address)
+    {
+        Page* page = pageFor(address);
+        return page->get<T>(address);
+    }
+
+    template<typename T, typename = typename std::enable_if<!std::is_same<double, typename std::remove_cv<T>::type>::value>::type>
+    void set(void* address, T value)
+    {
+        Page* page = pageFor(address);
+        page->set<T>(address, value);
+
+        // We use the chunkAddress for the low watermark because we'll be doing write backs
+        // to the stack in increments of chunks. Hence, we'll treat the lowest address of
+        // the chunk as the low watermark of any given set address.
+        void* chunkAddress = Page::chunkAddressFor(address);
+        if (chunkAddress < m_lowWatermark)
+            m_lowWatermark = chunkAddress;
+    }
+
+    template<typename T>
+    typename std::enable_if<std::is_same<double, typename std::remove_cv<T>::type>::value, T>::type get(void* address)
+    {
+        Page* page = pageFor(address);
+        return bitwise_cast<double>(page->get<uint64_t>(address));
+    }
+
+    template<typename T, typename = typename std::enable_if<std::is_same<double, typename std::remove_cv<T>::type>::value>::type>
+    void set(void* address, double value)
+    {
+        set<uint64_t>(address, bitwise_cast<uint64_t>(value));
+    }
+
+    JS_EXPORT_PRIVATE Page* ensurePageFor(void* address);
+
+    void* newStackPointer() const { return m_newStackPointer; };
+    void setNewStackPointer(void* sp) { m_newStackPointer = sp; };
+
+    bool hasWritesToFlush();
+    void flushWrites();
+
+#if !ASSERT_DISABLED
+    bool isValid() { return m_isValid; }
+#endif
+
+private:
+    Page* pageFor(void* address)
+    {
+        if (LIKELY(Page::baseAddressFor(address) == m_lastAccessedPageBaseAddress))
+            return m_lastAccessedPage;
+        return ensurePageFor(address);
+    }
+
+    void* m_newStackPointer { nullptr };
+    void* m_lowWatermark;
+
+    // A cache of the last accessed page details for quick access.
+    void* m_lastAccessedPageBaseAddress { nullptr };
+    Page* m_lastAccessedPage { nullptr };
+
+    StackBounds m_stackBounds;
+    HashMap<void*, std::unique_ptr<Page>> m_pages;
+
+#if !ASSERT_DISABLED
+    bool m_isValid { true };
+#endif
+};
+
+} // namespace Probe
+} // namespace JSC
index 51f48c5..31988b4 100644 (file)
@@ -31,6 +31,7 @@
 #include "GPRInfo.h"
 #include "InitializeThreading.h"
 #include "LinkBuffer.h"
+#include "ProbeContext.h"
 #include <wtf/Compiler.h>
 #include <wtf/DataLog.h>
 #include <wtf/Function.h>
@@ -61,12 +62,20 @@ static void printInternal(PrintStream& out, void* value)
 } // namespace WTF
 #endif // ENABLE(MASM_PROBE)
 
+namespace JSC {
+namespace Probe {
+
+JS_EXPORT_PRIVATE void* probeStateForContext(Probe::Context&);
+
+} // namespace Probe
+} // namespace JSC
+
 using namespace JSC;
 
 namespace {
 
 #if ENABLE(MASM_PROBE)
-using CPUState = MacroAssembler::CPUState;
+using CPUState = Probe::CPUState;
 #endif
 
 StaticLock crashLock;
@@ -184,8 +193,8 @@ void testProbeReadsArgumentRegisters()
         jit.move(CCallHelpers::TrustedImm32(testWord(3)), GPRInfo::argumentGPR3);
 #endif
 
-        jit.probe([&] (Probe::State* context) {
-            auto& cpu = context->cpu;
+        jit.probe([&] (Probe::Context& context) {
+            auto& cpu = context.cpu;
             probeWasCalled = true;
             CHECK_EQ(cpu.gpr(GPRInfo::argumentGPR0), testWord(0));
             CHECK_EQ(cpu.gpr(GPRInfo::argumentGPR1), testWord(1));
@@ -226,8 +235,8 @@ void testProbeWritesArgumentRegisters()
         jit.convertInt32ToDouble(GPRInfo::argumentGPR0, FPRInfo::fpRegT1);
 
         // Write expected values.
-        jit.probe([&] (Probe::State* context) {
-            auto& cpu = context->cpu;
+        jit.probe([&] (Probe::Context& context) {
+            auto& cpu = context.cpu;
             probeCallCount++;
             cpu.gpr(GPRInfo::argumentGPR0) = testWord(0);
             cpu.gpr(GPRInfo::argumentGPR1) = testWord(1);
@@ -239,8 +248,8 @@ void testProbeWritesArgumentRegisters()
         });
 
         // Validate that expected values were written.
-        jit.probe([&] (Probe::State* context) {
-            auto& cpu = context->cpu;
+        jit.probe([&] (Probe::Context& context) {
+            auto& cpu = context.cpu;
             probeCallCount++;
             CHECK_EQ(cpu.gpr(GPRInfo::argumentGPR0), testWord(0));
             CHECK_EQ(cpu.gpr(GPRInfo::argumentGPR1), testWord(1));
@@ -282,8 +291,8 @@ void testProbePreservesGPRS()
         jit.emitFunctionPrologue();
 
         // Write expected values into the registers (except for sp, fp, and pc).
-        jit.probe([&] (Probe::State* context) {
-            auto& cpu = context->cpu;
+        jit.probe([&] (Probe::Context& context) {
+            auto& cpu = context.cpu;
             probeCallCount++;
             for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
                 originalState.gpr(id) = cpu.gpr(id);
@@ -298,15 +307,15 @@ void testProbePreservesGPRS()
         });
 
         // Invoke the probe to call a lot of functions and trash register values.
-        jit.probe([&] (Probe::State*) {
+        jit.probe([&] (Probe::Context&) {
             probeCallCount++;
             CHECK_EQ(testFunctionToTrashGPRs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), 10);
             CHECK_EQ(testFunctionToTrashFPRs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), 10);
         });
 
         // Validate that the registers have the expected values.
-        jit.probe([&] (Probe::State* context) {
-            auto& cpu = context->cpu;
+        jit.probe([&] (Probe::Context& context) {
+            auto& cpu = context.cpu;
             probeCallCount++;
             for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
                 if (isSP(id) || isFP(id)) {
@@ -322,8 +331,8 @@ void testProbePreservesGPRS()
         });
 
         // Restore the original state.
-        jit.probe([&] (Probe::State* context) {
-            auto& cpu = context->cpu;
+        jit.probe([&] (Probe::Context& context) {
+            auto& cpu = context.cpu;
             probeCallCount++;
             for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
                 if (isSpecialGPR(id))
@@ -335,8 +344,8 @@ void testProbePreservesGPRS()
         });
 
         // Validate that the original state was restored.
-        jit.probe([&] (Probe::State* context) {
-            auto& cpu = context->cpu;
+        jit.probe([&] (Probe::Context& context) {
+            auto& cpu = context.cpu;
             probeCallCount++;
             for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
                 if (isSpecialGPR(id))
@@ -353,7 +362,7 @@ void testProbePreservesGPRS()
     CHECK_EQ(probeCallCount, 5);
 }
 
-void testProbeModifiesStackPointer(WTF::Function<void*(Probe::State*)> computeModifiedStack)
+void testProbeModifiesStackPointer(WTF::Function<void*(Probe::Context&)> computeModifiedStackPointer)
 {
     unsigned probeCallCount = 0;
     CPUState originalState;
@@ -366,7 +375,7 @@ void testProbeModifiesStackPointer(WTF::Function<void*(Probe::State*)> computeMo
     uintptr_t flagsMask = 0xc5;
 #elif CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL)
     auto flagsSPR = ARMRegisters::apsr;
-    uintptr_t flagsMask = 0xf0000000;
+    uintptr_t flagsMask = 0xf8000000;
 #elif CPU(ARM64)
     auto flagsSPR = ARM64Registers::nzcv;
     uintptr_t flagsMask = 0xf0000000;
@@ -377,8 +386,8 @@ void testProbeModifiesStackPointer(WTF::Function<void*(Probe::State*)> computeMo
 
         // Preserve original stack pointer and modify the sp, and
         // write expected values into other registers (except for fp, and pc).
-        jit.probe([&] (Probe::State* context) {
-            auto& cpu = context->cpu;
+        jit.probe([&] (Probe::Context& context) {
+            auto& cpu = context.cpu;
             probeCallCount++;
             for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
                 originalState.gpr(id) = cpu.gpr(id);
@@ -396,13 +405,13 @@ void testProbeModifiesStackPointer(WTF::Function<void*(Probe::State*)> computeMo
             cpu.spr(flagsSPR) = modifiedFlags;
 
             originalSP = cpu.sp();
-            modifiedSP = computeModifiedStack(context);
+            modifiedSP = computeModifiedStackPointer(context);
             cpu.sp() = modifiedSP;
         });
 
         // Validate that the registers have the expected values.
-        jit.probe([&] (Probe::State* context) {
-            auto& cpu = context->cpu;
+        jit.probe([&] (Probe::Context& context) {
+            auto& cpu = context.cpu;
             probeCallCount++;
             for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
                 if (isFP(id)) {
@@ -415,13 +424,13 @@ void testProbeModifiesStackPointer(WTF::Function<void*(Probe::State*)> computeMo
             }
             for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
                 CHECK_EQ(cpu.fpr<uint64_t>(id), testWord64(id));
-            CHECK_EQ(cpu.spr(flagsSPR), modifiedFlags);
+            CHECK_EQ(cpu.spr(flagsSPR) & flagsMask, modifiedFlags & flagsMask);
             CHECK_EQ(cpu.sp(), modifiedSP);
         });
 
         // Restore the original state.
-        jit.probe([&] (Probe::State* context) {
-            auto& cpu = context->cpu;
+        jit.probe([&] (Probe::Context& context) {
+            auto& cpu = context.cpu;
             probeCallCount++;
             for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
                 if (isSpecialGPR(id))
@@ -435,8 +444,8 @@ void testProbeModifiesStackPointer(WTF::Function<void*(Probe::State*)> computeMo
         });
 
         // Validate that the original state was restored.
-        jit.probe([&] (Probe::State* context) {
-            auto& cpu = context->cpu;
+        jit.probe([&] (Probe::Context& context) {
+            auto& cpu = context.cpu;
             probeCallCount++;
             for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
                 if (isSpecialGPR(id))
@@ -445,7 +454,7 @@ void testProbeModifiesStackPointer(WTF::Function<void*(Probe::State*)> computeMo
             }
             for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
                 CHECK_EQ(cpu.fpr<uint64_t>(id), originalState.fpr<uint64_t>(id));
-            CHECK_EQ(cpu.spr(flagsSPR), originalState.spr(flagsSPR));
+            CHECK_EQ(cpu.spr(flagsSPR) & flagsMask, originalState.spr(flagsSPR) & flagsMask);
             CHECK_EQ(cpu.sp(), originalSP);
         });
 
@@ -463,8 +472,9 @@ void testProbeModifiesStackPointerToInsideProbeStateOnStack()
     increment = 2 * sizeof(uintptr_t);
 #endif
     for (size_t offset = 0; offset < sizeof(Probe::State); offset += increment) {
-        testProbeModifiesStackPointer([=] (Probe::State* context) -> void* {
-            return reinterpret_cast<uint8_t*>(context) + offset;
+        testProbeModifiesStackPointer([=] (Probe::Context& context) -> void* {
+            return reinterpret_cast<uint8_t*>(probeStateForContext(context)) + offset;
+
         });
     }
 }
@@ -477,8 +487,8 @@ void testProbeModifiesStackPointerToNBytesBelowSP()
     increment = 2 * sizeof(uintptr_t);
 #endif
     for (size_t offset = 0; offset < 1 * KB; offset += increment) {
-        testProbeModifiesStackPointer([=] (Probe::State* context) -> void* {
-            return reinterpret_cast<uint8_t*>(context->cpu.sp()) - offset;
+        testProbeModifiesStackPointer([=] (Probe::Context& context) -> void* {
+            return context.cpu.sp<uint8_t*>() - offset;
         });
     }
 }
@@ -493,7 +503,7 @@ void testProbeModifiesProgramCounter()
 
     MacroAssemblerCodeRef continuation = compile([&] (CCallHelpers& jit) {
         // Validate that we reached the continuation.
-        jit.probe([&] (Probe::State*) {
+        jit.probe([&] (Probe::Context&) {
             probeCallCount++;
             continuationWasReached = true;
         });
@@ -506,9 +516,9 @@ void testProbeModifiesProgramCounter()
         jit.emitFunctionPrologue();
 
         // Write expected values into the registers.
-        jit.probe([&] (Probe::State* context) {
+        jit.probe([&] (Probe::Context& context) {
             probeCallCount++;
-            context->pc() = continuation.code().executableAddress();
+            context.cpu.pc() = continuation.code().executableAddress();
         });
 
         jit.breakpoint(); // We should never get here.
@@ -517,71 +527,23 @@ void testProbeModifiesProgramCounter()
     CHECK_EQ(continuationWasReached, true);
 }
 
-struct FillStackData {
+void testProbeModifiesStackValues()
+{
+    unsigned probeCallCount = 0;
     CPUState originalState;
     void* originalSP { nullptr };
     void* newSP { nullptr };
     uintptr_t modifiedFlags { 0 };
-    MacroAssembler::SPRegisterID flagsSPR;
-};
-
-static void fillStack(Probe::State* context)
-{
-    auto& cpu = context->cpu;
-
-    FillStackData& data = *reinterpret_cast<FillStackData*>(context->initializeStackArg);
-    CPUState& originalState = data.originalState;
-    void*& originalSP = data.originalSP;
-    void*& newSP = data.newSP;
-    uintptr_t& modifiedFlags = data.modifiedFlags;
-    MacroAssembler::SPRegisterID& flagsSPR = data.flagsSPR;
-
-    CHECK_EQ(reinterpret_cast<void*>(context->initializeStackFunction), reinterpret_cast<void*>(fillStack));
-    CHECK_EQ(cpu.sp(), newSP);
-
-    // Verify that the probe has put the Probe::State out of harm's way.
-    CHECK_EQ((reinterpret_cast<void*>(context + 1) <= cpu.sp()), true);
-
-    // Verify the CPU state.
-    for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
-        if (isFP(id)) {
-            CHECK_EQ(cpu.gpr(id), originalState.gpr(id));
-            continue;
-        }
-        if (isSpecialGPR(id))
-            continue;
-        CHECK_EQ(cpu.gpr(id), testWord(id));
-    }
-    for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
-        CHECK_EQ(cpu.fpr<uint64_t>(id), testWord64(id));
-    CHECK_EQ(cpu.spr(flagsSPR), modifiedFlags);
-
-    // Fill the stack with values.
-    uintptr_t* p = reinterpret_cast<uintptr_t*>(newSP);
-    int count = 0;
-    while (p < reinterpret_cast<uintptr_t*>(originalSP))
-        *p++ = testWord(count++);
-};
-
-void testProbeModifiesStackWithCallback()
-{
-    unsigned probeCallCount = 0;
-    FillStackData data;
-    CPUState& originalState = data.originalState;
-    void*& originalSP = data.originalSP;
-    void*& newSP = data.newSP;
-    uintptr_t& modifiedFlags = data.modifiedFlags;
-    size_t numberOfExtraEntriesToWrite = 10; // ARM64 requires that this be 2 word aligned.
-    MacroAssembler::SPRegisterID& flagsSPR = data.flagsSPR;
+    size_t numberOfExtraEntriesToWrite { 10 }; // ARM64 requires that this be 2 word aligned.
 
 #if CPU(X86) || CPU(X86_64)
-    flagsSPR = X86Registers::eflags;
+    MacroAssembler::SPRegisterID flagsSPR = X86Registers::eflags;
     uintptr_t flagsMask = 0xc5;
 #elif CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL)
-    flagsSPR = ARMRegisters::apsr;
-    uintptr_t flagsMask = 0xf0000000;
+    MacroAssembler::SPRegisterID flagsSPR = ARMRegisters::apsr;
+    uintptr_t flagsMask = 0xf8000000;
 #elif CPU(ARM64)
-    flagsSPR = ARM64Registers::nzcv;
+    MacroAssembler::SPRegisterID flagsSPR = ARM64Registers::nzcv;
     uintptr_t flagsMask = 0xf0000000;
 #endif
 
@@ -589,8 +551,9 @@ void testProbeModifiesStackWithCallback()
         jit.emitFunctionPrologue();
 
         // Write expected values into the registers.
-        jit.probe([&] (Probe::State* context) {
-            auto& cpu = context->cpu;
+        jit.probe([&] (Probe::Context& context) {
+            auto& cpu = context.cpu;
+            auto& stack = context.stack();
             probeCallCount++;
 
             // Preserve the original CPU state.
@@ -608,21 +571,25 @@ void testProbeModifiesStackWithCallback()
             modifiedFlags = originalState.spr(flagsSPR) ^ flagsMask;
             cpu.spr(flagsSPR) = modifiedFlags;
 
-            CHECK_EQ(reinterpret_cast<void*>(context->initializeStackFunction), 0);
-
-            // Prepare for initializeStack callback.
-            context->initializeStackFunction = fillStack;
-            context->initializeStackArg = &data;
-
             // Ensure that we'll be writing over the regions of the stack where the Probe::State is.
             originalSP = cpu.sp();
-            newSP = reinterpret_cast<uintptr_t*>(context) - numberOfExtraEntriesToWrite;
+            newSP = reinterpret_cast<uintptr_t*>(probeStateForContext(context)) - numberOfExtraEntriesToWrite;
             cpu.sp() = newSP;
+
+            // Fill the stack with values.
+            uintptr_t* p = reinterpret_cast<uintptr_t*>(newSP);
+            int count = 0;
+            stack.set<double>(p++, 1.23456789);
+            if (is32Bit())
+                p++; // On 32-bit targets, a double takes up 2 uintptr_t.
+            while (p < reinterpret_cast<uintptr_t*>(originalSP))
+                stack.set<uintptr_t>(p++, testWord(count++));
         });
 
         // Validate that the registers and stack have the expected values.
-        jit.probe([&] (Probe::State* context) {
-            auto& cpu = context->cpu;
+        jit.probe([&] (Probe::Context& context) {
+            auto& cpu = context.cpu;
+            auto& stack = context.stack();
             probeCallCount++;
 
             // Validate the register values.
@@ -637,19 +604,22 @@ void testProbeModifiesStackWithCallback()
             }
             for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
                 CHECK_EQ(cpu.fpr<uint64_t>(id), testWord64(id));
-            CHECK_EQ(cpu.spr(flagsSPR), modifiedFlags);
+            CHECK_EQ(cpu.spr(flagsSPR) & flagsMask, modifiedFlags & flagsMask);
             CHECK_EQ(cpu.sp(), newSP);
 
-            // Validate the stack with values.
+            // Validate the stack values.
             uintptr_t* p = reinterpret_cast<uintptr_t*>(newSP);
             int count = 0;
+            CHECK_EQ(stack.get<double>(p++), 1.23456789);
+            if (is32Bit())
+                p++; // On 32-bit targets, a double takes up 2 uintptr_t.
             while (p < reinterpret_cast<uintptr_t*>(originalSP))
-                CHECK_EQ(*p++, testWord(count++));
+                CHECK_EQ(stack.get<uintptr_t>(p++), testWord(count++));
         });
 
         // Restore the original state.
-        jit.probe([&] (Probe::State* context) {
-            auto& cpu = context->cpu;
+        jit.probe([&] (Probe::Context& context) {
+            auto& cpu = context.cpu;
             probeCallCount++;
             for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
                 if (isSpecialGPR(id))
@@ -707,7 +677,7 @@ void run(const char* filter)
     RUN(testProbeModifiesStackPointerToInsideProbeStateOnStack());
     RUN(testProbeModifiesStackPointerToNBytesBelowSP());
     RUN(testProbeModifiesProgramCounter());
-    RUN(testProbeModifiesStackWithCallback());
+    RUN(testProbeModifiesStackValues());
 #endif // ENABLE(MASM_PROBE)
 
     if (tasks.isEmpty())