Create regression tests for the JIT probe.
authormark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 23 Jul 2017 20:35:02 +0000 (20:35 +0000)
committermark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 23 Jul 2017 20:35:02 +0000 (20:35 +0000)
https://bugs.webkit.org/show_bug.cgi?id=174696
<rdar://problem/33436922>

Reviewed by Saam Barati.

The new testmasm will test the following:
1. the probe is able to read the value of CPU registers.
2. the probe is able to write the value of CPU registers.
3. the probe is able to preserve all CPU registers.
4. special case of (2): the probe is able to change the value of the stack pointer.
5. special case of (2): the probe is able to change the value of the program counter
   i.e. the probe can change where the code continues executing upon returning from
   the probe.

Currently, the x86, x86_64, and ARMv7 ports passes the test.  ARM64 does not
because it does not support changing the sp and pc yet.  The ARM64 probe
implementation will be fixed in https://bugs.webkit.org/show_bug.cgi?id=174697
later.

* Configurations/ToolExecutable.xcconfig:
* JavaScriptCore.xcodeproj/project.pbxproj:
* assembler/MacroAssembler.h:
(JSC::MacroAssembler::CPUState::pc):
(JSC::MacroAssembler::CPUState::fp):
(JSC::MacroAssembler::CPUState::sp):
(JSC::ProbeContext::pc):
(JSC::ProbeContext::fp):
(JSC::ProbeContext::sp):
* assembler/MacroAssemblerARM64.cpp:
(JSC::arm64ProbeTrampoline):
* assembler/MacroAssemblerPrinter.cpp:
(JSC::Printer::printPCRegister):
* assembler/testmasm.cpp: Added.
(hiddenTruthBecauseNoReturnIsStupid):
(usage):
(JSC::nextID):
(JSC::isPC):
(JSC::isSP):
(JSC::isFP):
(JSC::compile):
(JSC::invoke):
(JSC::compileAndRun):
(JSC::testSimple):
(JSC::testProbeReadsArgumentRegisters):
(JSC::testProbeWritesArgumentRegisters):
(JSC::testFunctionToTrashRegisters):
(JSC::testProbePreservesGPRS):
(JSC::testProbeModifiesStackPointer):
(JSC::testProbeModifiesProgramCounter):
(JSC::run):
(run):
(main):
* b3/air/testair.cpp:
(usage):
* shell/CMakeLists.txt:

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

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/Configurations/ToolExecutable.xcconfig
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/assembler/MacroAssembler.h
Source/JavaScriptCore/assembler/MacroAssemblerARM64.cpp
Source/JavaScriptCore/assembler/MacroAssemblerPrinter.cpp
Source/JavaScriptCore/assembler/testmasm.cpp [new file with mode: 0644]
Source/JavaScriptCore/b3/air/testair.cpp
Source/JavaScriptCore/shell/CMakeLists.txt

index d6a929b..3572345 100644 (file)
@@ -1,3 +1,62 @@
+2017-07-23  Mark Lam  <mark.lam@apple.com>
+
+        Create regression tests for the JIT probe.
+        https://bugs.webkit.org/show_bug.cgi?id=174696
+        <rdar://problem/33436922>
+
+        Reviewed by Saam Barati.
+
+        The new testmasm will test the following:
+        1. the probe is able to read the value of CPU registers.
+        2. the probe is able to write the value of CPU registers.
+        3. the probe is able to preserve all CPU registers.
+        4. special case of (2): the probe is able to change the value of the stack pointer.
+        5. special case of (2): the probe is able to change the value of the program counter
+           i.e. the probe can change where the code continues executing upon returning from
+           the probe.
+
+        Currently, the x86, x86_64, and ARMv7 ports passes the test.  ARM64 does not
+        because it does not support changing the sp and pc yet.  The ARM64 probe
+        implementation will be fixed in https://bugs.webkit.org/show_bug.cgi?id=174697
+        later.
+
+        * Configurations/ToolExecutable.xcconfig:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * assembler/MacroAssembler.h:
+        (JSC::MacroAssembler::CPUState::pc):
+        (JSC::MacroAssembler::CPUState::fp):
+        (JSC::MacroAssembler::CPUState::sp):
+        (JSC::ProbeContext::pc):
+        (JSC::ProbeContext::fp):
+        (JSC::ProbeContext::sp):
+        * assembler/MacroAssemblerARM64.cpp:
+        (JSC::arm64ProbeTrampoline):
+        * assembler/MacroAssemblerPrinter.cpp:
+        (JSC::Printer::printPCRegister):
+        * assembler/testmasm.cpp: Added.
+        (hiddenTruthBecauseNoReturnIsStupid):
+        (usage):
+        (JSC::nextID):
+        (JSC::isPC):
+        (JSC::isSP):
+        (JSC::isFP):
+        (JSC::compile):
+        (JSC::invoke):
+        (JSC::compileAndRun):
+        (JSC::testSimple):
+        (JSC::testProbeReadsArgumentRegisters):
+        (JSC::testProbeWritesArgumentRegisters):
+        (JSC::testFunctionToTrashRegisters):
+        (JSC::testProbePreservesGPRS):
+        (JSC::testProbeModifiesStackPointer):
+        (JSC::testProbeModifiesProgramCounter):
+        (JSC::run):
+        (run):
+        (main):
+        * b3/air/testair.cpp:
+        (usage):
+        * shell/CMakeLists.txt:
+
 2017-07-14  Filip Pizlo  <fpizlo@apple.com>
 
         It should be easy to decide how WebKit yields
index b1b2e79..ccfe1b4 100644 (file)
@@ -31,6 +31,7 @@ CODE_SIGN_ENTITLEMENTS_ios_minidom = entitlements.plist;
 CODE_SIGN_ENTITLEMENTS_ios_testair = entitlements.plist;
 CODE_SIGN_ENTITLEMENTS_ios_testapi = entitlements.plist;
 CODE_SIGN_ENTITLEMENTS_ios_testb3 = entitlements.plist;
+CODE_SIGN_ENTITLEMENTS_ios_testmasm = entitlements.plist;
 CODE_SIGN_ENTITLEMENTS_ios_testWasm = entitlements.plist;
 CODE_SIGN_ENTITLEMENTS_ios_testRegExp = entitlements.plist;
 
index f1bfc10..49344f0 100644 (file)
@@ -25,6 +25,7 @@
                        buildPhases = (
                        );
                        dependencies = (
+                               FE533CAF1F217EC60016A1FE /* PBXTargetDependency */,
                                0F6183471C45F67A0072450B /* PBXTargetDependency */,
                                0F93275D1C20BF3A00CF6564 /* PBXTargetDependency */,
                                0FEC85B11BDB5D8F0080FF74 /* PBXTargetDependency */,
                FE4D55B81AE716CA0052E459 /* IterationStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = FE4D55B71AE716CA0052E459 /* IterationStatus.h */; settings = {ATTRIBUTES = (Private, ); }; };
                FE5068651AE246390009DAB7 /* DeferredSourceDump.h in Headers */ = {isa = PBXBuildFile; fileRef = FE5068641AE246390009DAB7 /* DeferredSourceDump.h */; settings = {ATTRIBUTES = (Private, ); }; };
                FE5068671AE25E280009DAB7 /* DeferredSourceDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE5068661AE25E280009DAB7 /* DeferredSourceDump.cpp */; };
+               FE533CA51F217DB30016A1FE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51F0EB6105C86C6B00E6DF1B /* Foundation.framework */; };
+               FE533CA61F217DB30016A1FE /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 932F5BD90822A1C700736975 /* JavaScriptCore.framework */; };
+               FE533CAD1F217EA50016A1FE /* testmasm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE533CA01F217C310016A1FE /* testmasm.cpp */; };
                FE54DEFB1E8C6D8800A892C5 /* DisallowVMReentry.h in Headers */ = {isa = PBXBuildFile; fileRef = FE54DEFA1E8C6D7200A892C5 /* DisallowVMReentry.h */; settings = {ATTRIBUTES = (Private, ); }; };
                FE54DEFD1E8C6E3700A892C5 /* DisallowVMReentry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE54DEFC1E8C6DFF00A892C5 /* DisallowVMReentry.cpp */; };
                FE54DEFF1E8D76FA00A892C5 /* DisallowScope.h in Headers */ = {isa = PBXBuildFile; fileRef = FE54DEFE1E8D742800A892C5 /* DisallowScope.h */; settings = {ATTRIBUTES = (Private, ); }; };
                        remoteGlobalIDString = 932F5B3E0822A1C700736975;
                        remoteInfo = "JavaScriptCore (Upgraded)";
                };
+               FE533CAE1F217EC60016A1FE /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = FE533CA11F217DB30016A1FE;
+                       remoteInfo = testmasm;
+               };
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXCopyFilesBuildPhase section */
                FE4D55B71AE716CA0052E459 /* IterationStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IterationStatus.h; sourceTree = "<group>"; };
                FE5068641AE246390009DAB7 /* DeferredSourceDump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DeferredSourceDump.h; sourceTree = "<group>"; };
                FE5068661AE25E280009DAB7 /* DeferredSourceDump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DeferredSourceDump.cpp; sourceTree = "<group>"; };
+               FE533CA01F217C310016A1FE /* testmasm.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = testmasm.cpp; sourceTree = "<group>"; };
+               FE533CAC1F217DB40016A1FE /* testmasm */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testmasm; sourceTree = BUILT_PRODUCTS_DIR; };
                FE54DEFA1E8C6D7200A892C5 /* DisallowVMReentry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DisallowVMReentry.h; sourceTree = "<group>"; };
                FE54DEFC1E8C6DFF00A892C5 /* DisallowVMReentry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DisallowVMReentry.cpp; sourceTree = "<group>"; };
                FE54DEFE1E8D742800A892C5 /* DisallowScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DisallowScope.h; sourceTree = "<group>"; };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               FE533CA41F217DB30016A1FE /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               FE533CA51F217DB30016A1FE /* Foundation.framework in Frameworks */,
+                               FE533CA61F217DB30016A1FE /* JavaScriptCore.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
                                0FEC85AD1BDB5CF10080FF74 /* testb3 */,
                                6511230514046A4C002B101D /* testRegExp */,
                                932F5BD90822A1C700736975 /* JavaScriptCore.framework */,
+                               FE533CAC1F217DB40016A1FE /* testmasm */,
                        );
                        name = Products;
                        sourceTree = "<group>";
                                FE63DD551EA9BC5D00103A69 /* Printer.cpp */,
                                FE63DD531EA9B60E00103A69 /* Printer.h */,
                                9688CB140ED12B4E001D649F /* X86Assembler.h */,
+                               FE533CA01F217C310016A1FE /* testmasm.cpp */,
                        );
                        path = assembler;
                        sourceTree = "<group>";
                        productReference = 932F5BE10822A1C700736975 /* jsc */;
                        productType = "com.apple.product-type.tool";
                };
+               FE533CA11F217DB30016A1FE /* testmasm */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = FE533CA71F217DB30016A1FE /* Build configuration list for PBXNativeTarget "testmasm" */;
+                       buildPhases = (
+                               FE533CA21F217DB30016A1FE /* Sources */,
+                               FE533CA41F217DB30016A1FE /* Frameworks */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = testmasm;
+                       productName = testapi;
+                       productReference = FE533CAC1F217DB40016A1FE /* testmasm */;
+                       productType = "com.apple.product-type.tool";
+               };
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
                                5D6B2A47152B9E17005231DE /* Test Tools */,
                                0F93274E1C20BCBA00CF6564 /* dynbench */,
                                0F6183381C45F62A0072450B /* testair */,
+                               FE533CA11F217DB30016A1FE /* testmasm */,
                        );
                };
 /* End PBXProject section */
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               FE533CA21F217DB30016A1FE /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               FE533CAD1F217EA50016A1FE /* testmasm.cpp in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
                        target = 932F5B3E0822A1C700736975 /* JavaScriptCore */;
                        targetProxy = 932F5BE60822A1C700736975 /* PBXContainerItemProxy */;
                };
+               FE533CAF1F217EC60016A1FE /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = FE533CA11F217DB30016A1FE /* testmasm */;
+                       targetProxy = FE533CAE1F217EC60016A1FE /* PBXContainerItemProxy */;
+               };
 /* End PBXTargetDependency section */
 
 /* Begin XCBuildConfiguration section */
                        };
                        name = Profiling;
                };
+               FE533CA81F217DB30016A1FE /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = BC021BF2136900C300FC5467 /* ToolExecutable.xcconfig */;
+                       buildSettings = {
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                       };
+                       name = Debug;
+               };
+               FE533CA91F217DB30016A1FE /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = BC021BF2136900C300FC5467 /* ToolExecutable.xcconfig */;
+                       buildSettings = {
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                       };
+                       name = Release;
+               };
+               FE533CAA1F217DB30016A1FE /* Profiling */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = BC021BF2136900C300FC5467 /* ToolExecutable.xcconfig */;
+                       buildSettings = {
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                       };
+                       name = Profiling;
+               };
+               FE533CAB1F217DB30016A1FE /* Production */ = {
+                       isa = XCBuildConfiguration;
+                       baseConfigurationReference = BC021BF2136900C300FC5467 /* ToolExecutable.xcconfig */;
+                       buildSettings = {
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                       };
+                       name = Production;
+               };
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Production;
                };
+               FE533CA71F217DB30016A1FE /* Build configuration list for PBXNativeTarget "testmasm" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               FE533CA81F217DB30016A1FE /* Debug */,
+                               FE533CA91F217DB30016A1FE /* Release */,
+                               FE533CAA1F217DB30016A1FE /* Profiling */,
+                               FE533CAB1F217DB30016A1FE /* Production */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Production;
+               };
 /* End XCConfigurationList section */
        };
        rootObject = 0867D690FE84028FC02AAC07 /* Project object */;
index d933b11..dfafe82 100644 (file)
@@ -1847,7 +1847,7 @@ public:
     // MacroAssembler.
     void probe(ProbeFunction, void* arg);
 
-    void probe(std::function<void(ProbeContext*)>);
+    JS_EXPORT_PRIVATE void probe(std::function<void(ProbeContext*)>);
 #endif // ENABLE(MASM_PROBE)
 
     // Let's you print from your JIT generated code.
@@ -1869,9 +1869,9 @@ struct MacroAssembler::CPUState {
     inline uintptr_t& spr(SPRegisterID);
     inline double& fpr(FPRegisterID);
     
-    inline uintptr_t& pc();
-    inline uintptr_t& fp();
-    inline uintptr_t& sp();
+    inline void*& pc();
+    inline void*& fp();
+    inline void*& sp();
     
     uintptr_t gprs[MacroAssembler::numberOfRegisters()];
     uintptr_t sprs[MacroAssembler::numberOfSPRegisters()];
@@ -1896,14 +1896,14 @@ inline double& MacroAssembler::CPUState::fpr(FPRegisterID id)
     return fprs[id];
 }
 
-inline uintptr_t& MacroAssembler::CPUState::pc()
+inline void*& MacroAssembler::CPUState::pc()
 {
 #if CPU(X86) || CPU(X86_64)
-    return spr(X86Registers::eip);
+    return *reinterpret_cast<void**>(&spr(X86Registers::eip));
 #elif CPU(ARM64)
-    return spr(ARM64Registers::pc);
+    return *reinterpret_cast<void**>(&spr(ARM64Registers::pc));
 #elif CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL)
-    return gpr(ARMRegisters::pc);
+    return *reinterpret_cast<void**>(&gpr(ARMRegisters::pc));
 #elif CPU(MIPS)
     RELEASE_ASSERT_NOT_REACHED();
 #else
@@ -1911,31 +1911,31 @@ inline uintptr_t& MacroAssembler::CPUState::pc()
 #endif
 }
 
-inline uintptr_t& MacroAssembler::CPUState::fp()
+inline void*& MacroAssembler::CPUState::fp()
 {
 #if CPU(X86) || CPU(X86_64)
-    return gpr(X86Registers::ebp);
+    return *reinterpret_cast<void**>(&gpr(X86Registers::ebp));
 #elif CPU(ARM64)
-    return gpr(ARM64Registers::fp);
+    return *reinterpret_cast<void**>(&gpr(ARM64Registers::fp));
 #elif CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL)
-    return gpr(ARMRegisters::fp);
+    return *reinterpret_cast<void**>(&gpr(ARMRegisters::fp));
 #elif CPU(MIPS)
-    return gpr(MIPSRegisters::fp);
+    return *reinterpret_cast<void**>(&gpr(MIPSRegisters::fp));
 #else
 #error "Unsupported CPU"
 #endif
 }
 
-inline uintptr_t& MacroAssembler::CPUState::sp()
+inline void*& MacroAssembler::CPUState::sp()
 {
 #if CPU(X86) || CPU(X86_64)
-    return gpr(X86Registers::esp);
+    return *reinterpret_cast<void**>(&gpr(X86Registers::esp));
 #elif CPU(ARM64)
-    return gpr(ARM64Registers::sp);
+    return *reinterpret_cast<void**>(&gpr(ARM64Registers::sp));
 #elif CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL)
-    return gpr(ARMRegisters::sp);
+    return *reinterpret_cast<void**>(&gpr(ARMRegisters::sp));
 #elif CPU(MIPS)
-    return gpr(MIPSRegisters::sp);
+    return *reinterpret_cast<void**>(&gpr(MIPSRegisters::sp));
 #else
 #error "Unsupported CPU"
 #endif
@@ -1958,6 +1958,10 @@ struct ProbeContext {
     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(); }
 };
 #endif // ENABLE(MASM_PROBE)
     
index 7d2de66..0a8b820 100644 (file)
@@ -457,8 +457,8 @@ asm (
 
 static void arm64ProbeTrampoline(ProbeContext* context)
 {
-    uintptr_t origSP = context->cpu.sp();
-    uintptr_t origPC = context->cpu.pc();
+    void* origSP = context->cpu.sp();
+    void* origPC = context->cpu.pc();
     
     context->probeFunction(context);
     
index 0618795..1c74ee5 100644 (file)
@@ -82,8 +82,8 @@ void printAllRegisters(PrintStream& out, Context& context)
 void printPCRegister(PrintStream& out, Context& context)
 {
     auto cpu = context.probeContext.cpu;
-    intptr_t value = cpu.pc();
-    out.printf("pc:<%p %ld>", bitwise_cast<void*>(value), value);
+    void* value = cpu.pc();
+    out.printf("pc:<%p %ld>", value, bitwise_cast<intptr_t>(value));
 }
 
 void printRegisterID(PrintStream& out, Context& context)
diff --git a/Source/JavaScriptCore/assembler/testmasm.cpp b/Source/JavaScriptCore/assembler/testmasm.cpp
new file mode 100644 (file)
index 0000000..bc3905a
--- /dev/null
@@ -0,0 +1,452 @@
+/*
+ * 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 "CCallHelpers.h"
+#include "CPU.h"
+#include "FPRInfo.h"
+#include "GPRInfo.h"
+#include "InitializeThreading.h"
+#include "LinkBuffer.h"
+#include <wtf/Compiler.h>
+#include <wtf/Lock.h>
+#include <wtf/NumberOfCores.h>
+#include <wtf/Threading.h>
+
+// We don't have a NO_RETURN_DUE_TO_EXIT, nor should we. That's ridiculous.
+static bool hiddenTruthBecauseNoReturnIsStupid() { return true; }
+
+static void usage()
+{
+    dataLog("Usage: testmasm [<filter>]\n");
+    if (hiddenTruthBecauseNoReturnIsStupid())
+        exit(1);
+}
+
+#if ENABLE(JIT)
+
+using namespace JSC;
+
+namespace {
+
+StaticLock crashLock;
+
+typedef std::function<void(CCallHelpers&)> Generator;
+
+template<typename T> T nextID(T id) { return static_cast<T>(id + 1); }
+
+#define TESTWORD64 0x0c0defefebeef000
+#define TESTWORD32 0x0beef000
+
+#define testWord32(x) (TESTWORD32 + static_cast<uint32_t>(x))
+#define testWord64(x) (TESTWORD64 + static_cast<uint64_t>(x))
+
+#if ENABLE(JSVALUE64)
+#define testWord(x) testWord64(x)
+#else
+#define testWord(x) testWord32(x)
+#endif
+
+// Nothing fancy for now; we just use the existing WTF assertion machinery.
+#define CHECK(x) do {                                                   \
+        if (!!(x))                                                      \
+            break;                                                      \
+        crashLock.lock();                                               \
+        WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #x); \
+        CRASH();                                                        \
+    } while (false)
+
+#if ENABLE(MASM_PROBE)
+bool isPC(MacroAssembler::RegisterID id)
+{
+#if CPU(ARM_THUMB2) || CPU(ARM_TRADITIONAL)
+    return id == ARMRegisters::pc;
+#else
+    UNUSED_PARAM(id);
+    return false;
+#endif
+}
+
+bool isSP(MacroAssembler::RegisterID id)
+{
+    return id == MacroAssembler::stackPointerRegister;
+}
+
+bool isFP(MacroAssembler::RegisterID id)
+{
+    return id == MacroAssembler::framePointerRegister;
+}
+#endif // ENABLE(MASM_PROBE)
+
+MacroAssemblerCodeRef compile(Generator generate)
+{
+    CCallHelpers jit;
+    generate(jit);
+    LinkBuffer linkBuffer(jit, nullptr);
+    return FINALIZE_CODE(linkBuffer, ("testmasm compilation"));
+}
+
+template<typename T, typename... Arguments>
+T invoke(MacroAssemblerCodeRef code, Arguments... arguments)
+{
+    T (*function)(Arguments...) = bitwise_cast<T(*)(Arguments...)>(code.code().executableAddress());
+    return function(arguments...);
+}
+
+template<typename T, typename... Arguments>
+T compileAndRun(Generator generator, Arguments... arguments)
+{
+    return invoke<T>(compile(generator), arguments...);
+}
+
+void testSimple()
+{
+    CHECK(compileAndRun<int>([] (CCallHelpers& jit) {
+        jit.emitFunctionPrologue();
+        jit.move(CCallHelpers::TrustedImm32(42), GPRInfo::returnValueGPR);
+        jit.emitFunctionEpilogue();
+        jit.ret();
+    }) == 42);
+}
+
+#if ENABLE(MASM_PROBE)
+void testProbeReadsArgumentRegisters()
+{
+    bool success = true;
+    compileAndRun<void>([&] (CCallHelpers& jit) {
+        jit.emitFunctionPrologue();
+
+        jit.move(CCallHelpers::TrustedImm32(testWord(0)), GPRInfo::argumentGPR0);
+        jit.convertInt32ToDouble(GPRInfo::argumentGPR0, FPRInfo::fpRegT0);
+        jit.move(CCallHelpers::TrustedImm32(testWord(1)), GPRInfo::argumentGPR0);
+        jit.convertInt32ToDouble(GPRInfo::argumentGPR0, FPRInfo::fpRegT1);
+#if ENABLE(JSVALUE64)
+        jit.move(CCallHelpers::TrustedImm64(testWord(0)), GPRInfo::argumentGPR0);
+        jit.move(CCallHelpers::TrustedImm64(testWord(1)), GPRInfo::argumentGPR1);
+        jit.move(CCallHelpers::TrustedImm64(testWord(2)), GPRInfo::argumentGPR2);
+        jit.move(CCallHelpers::TrustedImm64(testWord(3)), GPRInfo::argumentGPR3);
+#else
+        jit.move(CCallHelpers::TrustedImm32(testWord(0)), GPRInfo::argumentGPR0);
+        jit.move(CCallHelpers::TrustedImm32(testWord(1)), GPRInfo::argumentGPR1);
+        jit.move(CCallHelpers::TrustedImm32(testWord(2)), GPRInfo::argumentGPR2);
+        jit.move(CCallHelpers::TrustedImm32(testWord(3)), GPRInfo::argumentGPR3);
+#endif
+
+        jit.probe([&] (ProbeContext* context) {
+            success = success && context->gpr(GPRInfo::argumentGPR0) == testWord(0);
+            success = success && context->gpr(GPRInfo::argumentGPR1) == testWord(1);
+            success = success && context->gpr(GPRInfo::argumentGPR2) == testWord(2);
+            success = success && context->gpr(GPRInfo::argumentGPR3) == testWord(3);
+
+            success = success && context->fpr(FPRInfo::fpRegT0) == testWord32(0);
+            success = success && context->fpr(FPRInfo::fpRegT1) == testWord32(1);
+        });
+        jit.emitFunctionEpilogue();
+        jit.ret();
+    });
+    CHECK(success);
+}
+
+void testProbeWritesArgumentRegisters()
+{
+    // This test relies on testProbeReadsArgumentRegisters() having already validated
+    // that we can read from argument registers. We'll use that ability to validate
+    // that our writes did take effect.
+    bool success = true;
+    compileAndRun<void>([&] (CCallHelpers& jit) {
+        jit.emitFunctionPrologue();
+
+        // Pre-initialize with non-expected values.
+#if ENABLE(JSVALUE64)
+        jit.move(CCallHelpers::TrustedImm64(0), GPRInfo::argumentGPR0);
+        jit.move(CCallHelpers::TrustedImm64(0), GPRInfo::argumentGPR1);
+        jit.move(CCallHelpers::TrustedImm64(0), GPRInfo::argumentGPR2);
+        jit.move(CCallHelpers::TrustedImm64(0), GPRInfo::argumentGPR3);
+#else
+        jit.move(CCallHelpers::TrustedImm32(0), GPRInfo::argumentGPR0);
+        jit.move(CCallHelpers::TrustedImm32(0), GPRInfo::argumentGPR1);
+        jit.move(CCallHelpers::TrustedImm32(0), GPRInfo::argumentGPR2);
+        jit.move(CCallHelpers::TrustedImm32(0), GPRInfo::argumentGPR3);
+#endif
+        jit.convertInt32ToDouble(GPRInfo::argumentGPR0, FPRInfo::fpRegT0);
+        jit.convertInt32ToDouble(GPRInfo::argumentGPR0, FPRInfo::fpRegT1);
+
+        // Write expected values.
+        jit.probe([] (ProbeContext* context) {
+            context->gpr(GPRInfo::argumentGPR0) = testWord(0);
+            context->gpr(GPRInfo::argumentGPR1) = testWord(1);
+            context->gpr(GPRInfo::argumentGPR2) = testWord(2);
+            context->gpr(GPRInfo::argumentGPR3) = testWord(3);
+            
+            context->fpr(FPRInfo::fpRegT0) = testWord32(0);
+            context->fpr(FPRInfo::fpRegT1) = testWord32(1);
+        });
+
+        // Validate that expected values were written.
+        jit.probe([&] (ProbeContext* context) {
+            success = success && context->gpr(GPRInfo::argumentGPR0) == testWord(0);
+            success = success && context->gpr(GPRInfo::argumentGPR1) == testWord(1);
+            success = success && context->gpr(GPRInfo::argumentGPR2) == testWord(2);
+            success = success && context->gpr(GPRInfo::argumentGPR3) == testWord(3);
+
+            success = success && context->fpr(FPRInfo::fpRegT0) == testWord32(0);
+            success = success && context->fpr(FPRInfo::fpRegT1) == testWord32(1);
+        });
+
+        jit.emitFunctionEpilogue();
+        jit.ret();
+    });
+    CHECK(success);
+}
+
+static NEVER_INLINE NOT_TAIL_CALLED int testFunctionToTrashGPRs(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j)
+{
+    if (j > 0)
+        return testFunctionToTrashGPRs(a + 1, b + a, c + b, d + 5, e - a, f * 1.5, g ^ a, h - b, i, j - 1);
+    return a + 1;
+}
+static NEVER_INLINE NOT_TAIL_CALLED double testFunctionToTrashFPRs(double a, double b, double c, double d, double e, double f, double g, double h, double i, double j)
+{
+    if (j > 0)
+        return testFunctionToTrashFPRs(a + 1, b + a, c + b, d + 5, e - a, f * 1.5, pow(g, a), h - b, i, j - 1);
+    return a + 1;
+}
+
+void testProbePreservesGPRS()
+{
+    // This test relies on testProbeReadsArgumentRegisters() and testProbeWritesArgumentRegisters()
+    // having already validated that we can read and write from registers. We'll use these abilities
+    // to validate that the probe preserves register values.
+    bool success = true;
+    MacroAssembler::CPUState originalState;
+
+    compileAndRun<void>([&] (CCallHelpers& jit) {
+        jit.emitFunctionPrologue();
+
+        // Write expected values into the registers (except for sp, fp, and pc).
+        jit.probe([&] (ProbeContext* context) {
+            for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
+                originalState.gpr(id) = context->gpr(id);
+                if (isPC(id) || isSP(id) || isFP(id))
+                    continue;
+                context->gpr(id) = testWord(static_cast<int>(id));
+            }
+            for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id)) {
+                originalState.fpr(id) = context->fpr(id);
+                context->fpr(id) = testWord(id);
+            }
+        });
+
+        // Invoke the probe to call a lot of functions and trash register values.
+        jit.probe([&] (ProbeContext*) {
+            success = success && (testFunctionToTrashGPRs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) == 10);
+            success = success && (testFunctionToTrashFPRs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) == 10);
+        });
+
+        // Validate that the registers have the expected values.
+        jit.probe([&] (ProbeContext* context) {
+            for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
+                if (isPC(id))
+                    continue;
+                if (isSP(id) || isFP(id)) {
+                    success = success && context->gpr(id) == originalState.gpr(id);
+                    continue;
+                }
+                success = success && context->gpr(id) == testWord(id);
+            }
+            for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
+                success = success && context->fpr(id) == testWord(id);
+        });
+
+        // Restore the original state.
+        jit.probe([&] (ProbeContext* context) {
+            for (auto id = CCallHelpers::firstRegister(); id <= CCallHelpers::lastRegister(); id = nextID(id)) {
+                if (isPC(id) || isSP(id) || isFP(id))
+                    continue;
+                context->gpr(id) = originalState.gpr(id);
+            }
+            for (auto id = CCallHelpers::firstFPRegister(); id <= CCallHelpers::lastFPRegister(); id = nextID(id))
+                context->fpr(id) = originalState.fpr(id);
+        });
+
+        jit.emitFunctionEpilogue();
+        jit.ret();
+    });
+    CHECK(success);
+}
+
+void testProbeModifiesStackPointer()
+{
+    bool success = true;
+    uint8_t* originalSP;
+
+    compileAndRun<void>([&] (CCallHelpers& jit) {
+        jit.emitFunctionPrologue();
+
+        // Preserve original stack pointer and modify the sp.
+        jit.probe([&] (ProbeContext* context) {
+            originalSP = reinterpret_cast<uint8_t*>(context->sp());
+            context->sp() = originalSP - 1 * KB;
+        });
+
+        // Validate that the stack pointer has the expected value, and restore the original.
+        jit.probe([&] (ProbeContext* context) {
+            success = (reinterpret_cast<uint8_t*>(context->sp()) == (originalSP - 1 * KB));
+            context->sp() = originalSP;
+        });
+
+        // Validate that the original stack pointer was restored.
+        jit.probe([&] (ProbeContext* context) {
+            success = (context->sp() == originalSP);
+        });
+
+        jit.emitFunctionEpilogue();
+        jit.ret();
+    });
+    CHECK(success);
+}
+
+void testProbeModifiesProgramCounter()
+{
+    // This test relies on testProbeReadsArgumentRegisters() and testProbeWritesArgumentRegisters()
+    // having already validated that we can read and write from registers. We'll use these abilities
+    // to validate that the probe preserves register values.
+    bool success = false;
+
+    MacroAssemblerCodeRef continuation = compile([&] (CCallHelpers& jit) {
+        // Validate that we reached the continuation.
+        jit.probe([&] (ProbeContext*) {
+            success = true;
+        });
+
+        jit.emitFunctionEpilogue();
+        jit.ret();
+    });
+
+    compileAndRun<void>([&] (CCallHelpers& jit) {
+        jit.emitFunctionPrologue();
+
+        // Write expected values into the registers.
+        jit.probe([&] (ProbeContext* context) {
+            context->pc() = continuation.code().executableAddress();
+        });
+
+        jit.breakpoint(); // We should never get here.
+    });
+    CHECK(success);
+}
+#endif // ENABLE(MASM_PROBE)
+
+#define RUN(test) do {                          \
+        if (!shouldRun(#test))                  \
+            break;                              \
+        tasks.append(                           \
+            createSharedTask<void()>(           \
+                [&] () {                        \
+                    dataLog(#test "...\n");     \
+                    test;                       \
+                    dataLog(#test ": OK!\n");   \
+                }));                            \
+    } while (false);
+
+void run(const char* filter)
+{
+    JSC::initializeThreading();
+
+    Deque<RefPtr<SharedTask<void()>>> tasks;
+
+    auto shouldRun = [&] (const char* testName) -> bool {
+        return !filter || !!strcasestr(testName, filter);
+    };
+
+    RUN(testSimple());
+
+#if ENABLE(MASM_PROBE)
+    RUN(testProbeReadsArgumentRegisters());
+    RUN(testProbeWritesArgumentRegisters());
+    RUN(testProbePreservesGPRS());
+    RUN(testProbeModifiesStackPointer());
+    RUN(testProbeModifiesProgramCounter());
+#endif
+
+    if (tasks.isEmpty())
+        usage();
+
+    Lock lock;
+
+    Vector<RefPtr<Thread>> threads;
+    for (unsigned i = filter ? 1 : WTF::numberOfProcessorCores(); i--;) {
+        threads.append(
+            Thread::create(
+                "testmasm thread",
+                [&] () {
+                    for (;;) {
+                        RefPtr<SharedTask<void()>> task;
+                        {
+                            LockHolder locker(lock);
+                            if (tasks.isEmpty())
+                                return;
+                            task = tasks.takeFirst();
+                        }
+
+                        task->run();
+                    }
+                }));
+    }
+
+    for (RefPtr<Thread> thread : threads)
+        thread->waitForCompletion();
+    crashLock.lock();
+}
+
+} // anonymous namespace
+
+#else // not ENABLE(JIT)
+
+static void run(const char*)
+{
+    dataLog("JIT is not enabled.\n");
+}
+
+#endif // ENABLE(JIT)
+
+int main(int argc, char** argv)
+{
+    const char* filter = nullptr;
+    switch (argc) {
+    case 1:
+        break;
+    case 2:
+        filter = argv[1];
+        break;
+    default:
+        usage();
+        break;
+    }
+
+    run(filter);
+    return 0;
+}
index 8548504..8bf7678 100644 (file)
@@ -51,7 +51,7 @@ static bool hiddenTruthBecauseNoReturnIsStupid() { return true; }
 
 static void usage()
 {
-    dataLog("Usage: testb3 [<filter>]\n");
+    dataLog("Usage: testair [<filter>]\n");
     if (hiddenTruthBecauseNoReturnIsStupid())
         exit(1);
 }
@@ -2029,7 +2029,7 @@ void run(const char* filter)
     for (unsigned i = filter ? 1 : WTF::numberOfProcessorCores(); i--;) {
         threads.append(
             Thread::create(
-                "testb3 thread",
+                "testair thread",
                 [&] () {
                     for (;;) {
                         RefPtr<SharedTask<void()>> task;
index 8d33ab0..bc37dd3 100644 (file)
@@ -56,6 +56,10 @@ if (TARGET jscLib)
 endif ()
 
 if (NOT WIN32)
+    set(TESTMASM_SOURCES
+        ../assembler/testmasm.cpp
+    )
+
     set(TESTB3_SOURCES
         ../b3/testb3.cpp
     )
@@ -64,6 +68,9 @@ if (NOT WIN32)
         ../b3/air/testair.cpp
     )
 
+    add_executable(testmasm ${TESTMASM_SOURCES})
+    target_link_libraries(testmasm ${JSC_LIBRARIES})
+
     add_executable(testb3 ${TESTB3_SOURCES})
     target_link_libraries(testb3 ${JSC_LIBRARIES})