Support the type profiler in the DFG
authorsaambarati1@gmail.com <saambarati1@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 1 Oct 2014 20:47:51 +0000 (20:47 +0000)
committersaambarati1@gmail.com <saambarati1@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 1 Oct 2014 20:47:51 +0000 (20:47 +0000)
https://bugs.webkit.org/show_bug.cgi?id=136712

Reviewed by Filip Pizlo.

This patch implements op_profile_type inside the DFG as the node: ProfileType.
The DFG will convert the ProfileType node into a Check node in the cases where
passing a type check is equivalent to writing to the TypeProfilerLog. This
gives the DFG the potential to optimize out multiple ProfileType nodes into
a single Check node.

When the DFG doesn't convert ProfileType into a Check node, it will generate
the same inline code as the baseline JIT does for writing an entry to the
TypeProfilerLog.

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGDriver.cpp:
(JSC::DFG::compileImpl):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::typeLocation):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* runtime/TypeProfiler.cpp:
(JSC::TypeProfiler::logTypesForTypeLocation):
* runtime/TypeSet.cpp:
(JSC::TypeSet::dumpTypes):
(JSC::TypeSet::doesTypeConformTo):
Make this method public so others can reason about the types a TypeSet has seen.
(JSC::TypeSet::seenTypes): Deleted.
(JSC::TypeSet::dumpSeenTypes): Deleted.
Renamed to dumpTypes so the method seenTypes can be used as a public getter.
* runtime/TypeSet.h:
(JSC::TypeSet::seenTypes):
* tests/typeProfiler/dfg-jit-optimizations.js: Added.
(tierUpToDFG):
(funcs):
(.return):

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

21 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGDriver.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/runtime/TypeProfiler.cpp
Source/JavaScriptCore/runtime/TypeSet.cpp
Source/JavaScriptCore/runtime/TypeSet.h
Source/JavaScriptCore/tests/typeProfiler/dfg-jit-optimizations.js [new file with mode: 0644]

index 0cd0502..10d0eee 100644 (file)
@@ -1,3 +1,65 @@
+2014-10-01  Saam Barati  <saambarati1@gmail.com>
+
+        Support the type profiler in the DFG
+        https://bugs.webkit.org/show_bug.cgi?id=136712
+
+        Reviewed by Filip Pizlo.
+
+        This patch implements op_profile_type inside the DFG as the node: ProfileType.
+        The DFG will convert the ProfileType node into a Check node in the cases where
+        passing a type check is equivalent to writing to the TypeProfilerLog. This
+        gives the DFG the potential to optimize out multiple ProfileType nodes into
+        a single Check node.
+
+        When the DFG doesn't convert ProfileType into a Check node, it will generate
+        the same inline code as the baseline JIT does for writing an entry to the
+        TypeProfilerLog.
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGCapabilities.cpp:
+        (JSC::DFG::capabilityLevel):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGDriver.cpp:
+        (JSC::DFG::compileImpl):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::typeLocation):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::callOperation):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * runtime/TypeProfiler.cpp:
+        (JSC::TypeProfiler::logTypesForTypeLocation):
+        * runtime/TypeSet.cpp:
+        (JSC::TypeSet::dumpTypes):
+        (JSC::TypeSet::doesTypeConformTo):
+        Make this method public so others can reason about the types a TypeSet has seen.
+        (JSC::TypeSet::seenTypes): Deleted.
+        (JSC::TypeSet::dumpSeenTypes): Deleted.
+        Renamed to dumpTypes so the method seenTypes can be used as a public getter.
+        * runtime/TypeSet.h:
+        (JSC::TypeSet::seenTypes):
+        * tests/typeProfiler/dfg-jit-optimizations.js: Added.
+        (tierUpToDFG):
+        (funcs):
+        (.return):
+
 2014-10-01  Filip Pizlo  <fpizlo@apple.com>
 
         Unreviewed, fix 32-bit.
index 68a9e5b..842630e 100644 (file)
@@ -1992,6 +1992,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     case Breakpoint:
     case ProfileWillCall:
     case ProfileDidCall:
+    case ProfileType:
     case Phantom:
     case HardPhantom:
     case CountExecution:
index 9612fea..a1d939e 100644 (file)
@@ -2893,6 +2893,12 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             NEXT_OPCODE(op_init_global_const);
         }
 
+        case op_profile_type: {
+            Node* valueToProfile = get(VirtualRegister(currentInstruction[1].u.operand));
+            addToGraph(ProfileType, OpInfo(currentInstruction[2].u.location), valueToProfile);
+            NEXT_OPCODE(op_profile_type);
+        }
+
         // === Block terminators. ===
 
         case op_jmp: {
index 27dd6cc..220a1e3 100644 (file)
@@ -120,6 +120,7 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
     case op_debug:
     case op_profile_will_call:
     case op_profile_did_call:
+    case op_profile_type:
     case op_mov:
     case op_captured_mov:
     case op_check_has_instance:
index 387f0f1..60f884f 100644 (file)
@@ -273,6 +273,7 @@ void clobberize(Graph& graph, Node* node, ReadFunctor& read, WriteFunctor& write
     case Breakpoint:
     case ProfileWillCall:
     case ProfileDidCall:
+    case ProfileType:
     case StoreBarrier:
     case StoreBarrierWithNullCheck:
     case PutByOffsetHint:
index d81978b..fd17065 100644 (file)
@@ -124,6 +124,7 @@ bool doesGC(Graph& graph, Node* node)
     case Breakpoint:
     case ProfileWillCall:
     case ProfileDidCall:
+    case ProfileType:
     case CheckHasInstance:
     case InstanceOf:
     case IsUndefined:
index 2601735..e6c8e8b 100644 (file)
@@ -38,6 +38,7 @@
 #include "JSCInlines.h"
 #include "Options.h"
 #include "SamplingTool.h"
+#include "TypeProfilerLog.h"
 #include <wtf/Atomics.h>
 
 #if ENABLE(FTL_JIT)
@@ -91,6 +92,9 @@ static CompilationResult compileImpl(
     
     if (CallEdgeLog::isEnabled())
         vm.ensureCallEdgeLog().processLog();
+
+    if (vm.typeProfiler())
+        vm.typeProfilerLog()->processLogEntries(ASCIILiteral("Preparing for DFG compilation."));
     
     RefPtr<Plan> plan = adoptRef(
         new Plan(codeBlock, profiledDFGCodeBlock, mode, osrEntryBytecodeIndex, mustHandleValues));
index 498ef09..0b9501a 100644 (file)
@@ -34,6 +34,7 @@
 #include "DFGPredictionPropagationPhase.h"
 #include "DFGVariableAccessDataDump.h"
 #include "JSCInlines.h"
+#include "TypeLocation.h"
 
 namespace JSC { namespace DFG {
 
@@ -1114,6 +1115,38 @@ private:
             fixEdge<KnownInt32Use>(node->child1());
             break;
         }
+        case ProfileType: {
+            // We want to insert type checks based on the instructionTypeSet of the TypeLocation, not the globalTypeSet.
+            // Because the instructionTypeSet is contained in globalTypeSet, if we produce a type check for
+            // type T for the instructionTypeSet, the global type set must also have information for type T.
+            // So if it the type check succeeds for type T in the instructionTypeSet, a type check for type T 
+            // in the globalTypeSet would've also succeeded.
+            // (The other direction does not hold in general).
+
+            RefPtr<TypeSet> typeSet = node->typeLocation()->m_instructionTypeSet;
+            uint32_t seenTypes = typeSet->seenTypes();
+            if (typeSet->doesTypeConformTo(TypeMachineInt)) {
+                node->convertToCheck();
+                if (node->child1()->shouldSpeculateInt32())
+                    fixEdge<Int32Use>(node->child1());
+                else
+                    fixEdge<MachineIntUse>(node->child1());
+            } else if (typeSet->doesTypeConformTo(TypeNumber | TypeMachineInt)) {
+                node->convertToCheck();
+                fixEdge<NumberUse>(node->child1());
+            } else if (typeSet->doesTypeConformTo(TypeString)) {
+                node->convertToCheck();
+                fixEdge<StringUse>(node->child1());
+            } else if (typeSet->doesTypeConformTo(TypeBoolean)) {
+                node->convertToCheck();
+                fixEdge<BooleanUse>(node->child1());
+            } else if (typeSet->doesTypeConformTo(TypeUndefined | TypeNull) && (seenTypes & TypeUndefined) && (seenTypes & TypeNull)) {
+                node->convertToCheck();
+                fixEdge<OtherUse>(node->child1());
+            }
+
+            break;
+        }
             
 #if !ASSERT_DISABLED
         // Have these no-op cases here to ensure that nobody forgets to add handlers for new opcodes.
index f1153d0..d2ce0f3 100644 (file)
@@ -48,6 +48,7 @@
 #include "PutByIdVariant.h"
 #include "SpeculatedType.h"
 #include "StructureSet.h"
+#include "TypeLocation.h"
 #include "ValueProfile.h"
 #include <wtf/ListDump.h>
 
@@ -1776,6 +1777,11 @@ struct Node {
     {
         return canSpeculateInt52(sourceFor(pass));
     }
+
+    TypeLocation* typeLocation()
+    {
+        return reinterpret_cast<TypeLocation*>(m_opInfo);
+    }
     
     void dumpChildren(PrintStream& out)
     {
index 5c20524..fb5c6fa 100644 (file)
@@ -257,6 +257,7 @@ namespace JSC { namespace DFG {
     macro(NewStringObject, NodeResultJS) \
     macro(MakeRope, NodeResultJS) \
     macro(In, NodeResultBoolean | NodeMustGenerate | NodeClobbersWorld) \
+    macro(ProfileType, NodeMustGenerate) \
     \
     /* Nodes used for activations. Activation support works by having it anchored at */\
     /* epilgoues via TearOffActivation, and all CreateActivation nodes kept alive by */\
index 5d45fc5..b7ac22d 100644 (file)
@@ -53,6 +53,7 @@
 #include "JSCInlines.h"
 #include "Repatch.h"
 #include "StringConstructor.h"
+#include "TypeProfilerLog.h"
 #include "TypedArrayInlines.h"
 #include <wtf/InlineASM.h>
 
@@ -1062,6 +1063,11 @@ int64_t JIT_OPERATION operationConvertDoubleToInt52(double value)
     return tryConvertToInt52(value);
 }
 
+void JIT_OPERATION operationProcessTypeProfilerLogDFG(ExecState* exec) 
+{
+    exec->vm().typeProfilerLog()->processLogEntries(ASCIILiteral("Log Full, called from inside DFG."));
+}
+
 size_t JIT_OPERATION dfgConvertJSValueToInt32(ExecState* exec, EncodedJSValue value)
 {
     VM* vm = &exec->vm();
index f78c9b0..cc45167 100644 (file)
@@ -130,6 +130,8 @@ void JIT_OPERATION operationNotifyWrite(ExecState*, VariableWatchpointSet*, Enco
 int64_t JIT_OPERATION operationConvertBoxedDoubleToInt52(EncodedJSValue);
 int64_t JIT_OPERATION operationConvertDoubleToInt52(double);
 
+void JIT_OPERATION operationProcessTypeProfilerLogDFG(ExecState*) WTF_INTERNAL;
+
 // These operations implement the implicitly called ToInt32 and ToBoolean conversions from ES5.
 // This conversion returns an int32_t within a size_t such that the value is zero extended to fill the register.
 size_t JIT_OPERATION dfgConvertJSValueToInt32(ExecState*, EncodedJSValue) WTF_INTERNAL;
index ee2d297..6b5f043 100644 (file)
@@ -647,6 +647,7 @@ private:
         case Breakpoint:
         case ProfileWillCall:
         case ProfileDidCall:
+        case ProfileType:
         case CheckHasInstance:
         case ThrowReferenceError:
         case ForceOSRExit:
index 2e25d89..f4e7cb7 100644 (file)
@@ -200,6 +200,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case Breakpoint:
     case ProfileWillCall:
     case ProfileDidCall:
+    case ProfileType:
     case CheckHasInstance:
     case InstanceOf:
     case IsUndefined:
index e4dbdb1..ed65922 100644 (file)
@@ -944,6 +944,11 @@ public:
     // machine registers, and delegate the calling convention specific
     // decision as to how to fill the regsiters to setupArguments* methods.
 
+    JITCompiler::Call callOperation(V_JITOperation_E operation)
+    {
+        m_jit.setupArgumentsExecState();
+        return appendCall(operation);
+    }
     JITCompiler::Call callOperation(P_JITOperation_E operation, GPRReg result)
     {
         m_jit.setupArgumentsExecState();
@@ -1148,6 +1153,11 @@ public:
         return appendCallWithExceptionCheckSetResult(operation, result);
     }
 
+    template<typename FunctionType>
+    JITCompiler::Call callOperation(FunctionType operation, NoResultTag)
+    {
+        return callOperation(operation);
+    }
     template<typename FunctionType, typename ArgumentType1>
     JITCompiler::Call callOperation(FunctionType operation, NoResultTag, ArgumentType1 arg1)
     {
index 3e3baec..7fc4068 100644 (file)
@@ -40,6 +40,7 @@
 #include "JSPropertyNameEnumerator.h"
 #include "ObjectPrototype.h"
 #include "JSCInlines.h"
+#include "TypeProfilerLog.h"
 
 namespace JSC { namespace DFG {
 
@@ -4856,6 +4857,51 @@ void SpeculativeJIT::compile(Node* node)
         cellResult(resultGPR, node);
         break;
     }
+    case ProfileType: {
+        JSValueOperand value(this, node->child1());
+        GPRTemporary scratch1(this);
+        GPRTemporary scratch2(this);
+        GPRTemporary scratch3(this);
+
+        GPRReg scratch1GPR = scratch1.gpr();
+        GPRReg scratch2GPR = scratch2.gpr();
+        GPRReg scratch3GPR = scratch3.gpr();
+
+        // Load the TypeProfilerLog into Scratch2.
+        TypeProfilerLog* cachedTypeProfilerLog = m_jit.vm()->typeProfilerLog();
+        m_jit.move(TrustedImmPtr(cachedTypeProfilerLog), scratch2GPR);
+
+        // Load the next LogEntry into Scratch1.
+        m_jit.loadPtr(MacroAssembler::Address(scratch2GPR, TypeProfilerLog::currentLogEntryOffset()), scratch1GPR);
+
+        // Store the JSValue onto the log entry.
+        m_jit.store32(value.tagGPR(), MacroAssembler::Address(scratch1GPR, TypeProfilerLog::LogEntry::valueOffset() + OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
+        m_jit.store32(value.payloadGPR(), MacroAssembler::Address(scratch1GPR, TypeProfilerLog::LogEntry::valueOffset() + OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
+
+        // Store the structureID of the cell if valueGPR is a cell, otherwise, store 0 on the log entry.
+        MacroAssembler::Jump isNotCell = branchNotCell(value.jsValueRegs());
+        m_jit.load32(MacroAssembler::Address(value.payloadGPR(), JSCell::structureIDOffset()), scratch3GPR);
+        m_jit.store32(scratch3GPR, MacroAssembler::Address(scratch1GPR, TypeProfilerLog::LogEntry::structureIDOffset()));
+        MacroAssembler::Jump skipIsCell = m_jit.jump();
+        isNotCell.link(&m_jit);
+        m_jit.store32(TrustedImm32(0), MacroAssembler::Address(scratch1GPR, TypeProfilerLog::LogEntry::structureIDOffset()));
+        skipIsCell.link(&m_jit);
+
+        // Store the typeLocation on the log entry.
+        TypeLocation* cachedTypeLocation = node->typeLocation();
+        m_jit.move(TrustedImmPtr(cachedTypeLocation), scratch3GPR);
+        m_jit.storePtr(scratch3GPR, MacroAssembler::Address(scratch1GPR, TypeProfilerLog::LogEntry::locationOffset()));
+
+        // Increment the current log entry.
+        m_jit.addPtr(TrustedImm32(sizeof(TypeProfilerLog::LogEntry)), scratch1GPR);
+        m_jit.storePtr(scratch1GPR, MacroAssembler::Address(scratch2GPR, TypeProfilerLog::currentLogEntryOffset()));
+        MacroAssembler::Jump clearLog = m_jit.branchPtr(MacroAssembler::Equal, scratch1GPR, TrustedImmPtr(cachedTypeProfilerLog->logEndPtr()));
+        addSlowPathGenerator(
+            slowPathCall(clearLog, this, operationProcessTypeProfilerLogDFG, NoResult));
+
+        noResult(node);
+        break;
+    }
 
     case ForceOSRExit: {
         terminateSpeculativeExecution(InadequateCoverage, JSValueRegs(), 0);
index fca0273..047bae8 100644 (file)
@@ -40,6 +40,7 @@
 #include "JSPropertyNameEnumerator.h"
 #include "ObjectPrototype.h"
 #include "SpillRegistersMode.h"
+#include "TypeProfilerLog.h"
 
 namespace JSC { namespace DFG {
 
@@ -4902,6 +4903,75 @@ void SpeculativeJIT::compile(Node* node)
         cellResult(resultGPR, node);
         break;
     }
+    case ProfileType: {
+        JSValueOperand value(this, node->child1());
+        GPRTemporary scratch1(this);
+        GPRTemporary scratch2(this);
+        GPRTemporary scratch3(this);
+
+        GPRReg scratch1GPR = scratch1.gpr();
+        GPRReg scratch2GPR = scratch2.gpr();
+        GPRReg scratch3GPR = scratch3.gpr();
+        GPRReg valueGPR = value.gpr();
+
+        MacroAssembler::JumpList jumpToEnd;
+
+        TypeLocation* cachedTypeLocation = node->typeLocation();
+        // Compile in a predictive type check, if possible, to see if we can skip writing to the log.
+        // These typechecks are inlined to match those of the 64-bit JSValue type checks.
+        if (cachedTypeLocation->m_lastSeenType == TypeUndefined)
+            jumpToEnd.append(m_jit.branch64(MacroAssembler::Equal, valueGPR, MacroAssembler::TrustedImm64(JSValue::encode(jsUndefined()))));
+        else if (cachedTypeLocation->m_lastSeenType == TypeNull)
+            jumpToEnd.append(m_jit.branch64(MacroAssembler::Equal, valueGPR, MacroAssembler::TrustedImm64(JSValue::encode(jsNull()))));
+        else if (cachedTypeLocation->m_lastSeenType == TypeBoolean) {
+            m_jit.move(valueGPR, scratch2GPR);
+            m_jit.and64(TrustedImm32(~1), scratch2GPR);
+            jumpToEnd.append(m_jit.branch64(MacroAssembler::Equal, scratch2GPR, MacroAssembler::TrustedImm64(ValueFalse)));
+        } else if (cachedTypeLocation->m_lastSeenType == TypeMachineInt)
+            jumpToEnd.append(m_jit.branch64(MacroAssembler::AboveOrEqual, valueGPR, GPRInfo::tagTypeNumberRegister));
+        else if (cachedTypeLocation->m_lastSeenType == TypeNumber)
+            jumpToEnd.append(m_jit.branchTest64(MacroAssembler::NonZero, valueGPR, GPRInfo::tagTypeNumberRegister));
+        else if (cachedTypeLocation->m_lastSeenType == TypeString) {
+            MacroAssembler::Jump isNotCell = branchNotCell(JSValueRegs(valueGPR));
+            jumpToEnd.append(m_jit.branch8(MacroAssembler::Equal, MacroAssembler::Address(valueGPR, JSCell::typeInfoTypeOffset()), TrustedImm32(StringType)));
+            isNotCell.link(&m_jit);
+        }
+
+        // Load the TypeProfilerLog into Scratch2.
+        TypeProfilerLog* cachedTypeProfilerLog = m_jit.vm()->typeProfilerLog();
+        m_jit.move(TrustedImmPtr(cachedTypeProfilerLog), scratch2GPR);
+
+        // Load the next LogEntry into Scratch1.
+        m_jit.loadPtr(MacroAssembler::Address(scratch2GPR, TypeProfilerLog::currentLogEntryOffset()), scratch1GPR);
+
+        // Store the JSValue onto the log entry.
+        m_jit.store64(valueGPR, MacroAssembler::Address(scratch1GPR, TypeProfilerLog::LogEntry::valueOffset()));
+
+        // Store the structureID of the cell if valueGPR is a cell, otherwise, store 0 on the log entry.
+        MacroAssembler::Jump isNotCell = branchNotCell(JSValueRegs(valueGPR));
+        m_jit.load32(MacroAssembler::Address(valueGPR, JSCell::structureIDOffset()), scratch3GPR);
+        m_jit.store32(scratch3GPR, MacroAssembler::Address(scratch1GPR, TypeProfilerLog::LogEntry::structureIDOffset()));
+        MacroAssembler::Jump skipIsCell = m_jit.jump();
+        isNotCell.link(&m_jit);
+        m_jit.store32(TrustedImm32(0), MacroAssembler::Address(scratch1GPR, TypeProfilerLog::LogEntry::structureIDOffset()));
+        skipIsCell.link(&m_jit);
+
+        // Store the typeLocation on the log entry.
+        m_jit.move(TrustedImmPtr(cachedTypeLocation), scratch3GPR);
+        m_jit.storePtr(scratch3GPR, MacroAssembler::Address(scratch1GPR, TypeProfilerLog::LogEntry::locationOffset()));
+
+        // Increment the current log entry.
+        m_jit.addPtr(TrustedImm32(sizeof(TypeProfilerLog::LogEntry)), scratch1GPR);
+        m_jit.storePtr(scratch1GPR, MacroAssembler::Address(scratch2GPR, TypeProfilerLog::currentLogEntryOffset()));
+        MacroAssembler::Jump clearLog = m_jit.branchPtr(MacroAssembler::Equal, scratch1GPR, TrustedImmPtr(cachedTypeProfilerLog->logEndPtr()));
+        addSlowPathGenerator(
+            slowPathCall(clearLog, this, operationProcessTypeProfilerLogDFG, NoResult));
+
+        jumpToEnd.link(&m_jit);
+
+        noResult(node);
+        break;
+    }
 
 #if ENABLE(FTL_JIT)        
     case CheckTierUpInLoop: {
index 421abaf..5922965 100644 (file)
@@ -46,9 +46,9 @@ void TypeProfiler::logTypesForTypeLocation(TypeLocation* location)
 
     dataLog("\t\t", location->m_globalVariableID == TypeProfilerReturnStatement ? "[Return Statement]" : "[Normal Statement]", "\n");
 
-    dataLog("\t\t#Local#\n\t\t", location->m_instructionTypeSet->seenTypes().replace("\n", "\n\t\t"), "\n");
+    dataLog("\t\t#Local#\n\t\t", location->m_instructionTypeSet->dumpTypes().replace("\n", "\n\t\t"), "\n");
     if (location->m_globalTypeSet)
-        dataLog("\t\t#Global#\n\t\t", location->m_globalTypeSet->seenTypes().replace("\n", "\n\t\t"), "\n");
+        dataLog("\t\t#Global#\n\t\t", location->m_globalTypeSet->dumpTypes().replace("\n", "\n\t\t"), "\n");
 }
 
 void TypeProfiler::insertNewLocation(TypeLocation* location)
index af62744..b193005 100644 (file)
@@ -110,7 +110,7 @@ void TypeSet::invalidateCache()
     m_structureIDCache.clear();
 }
 
-String TypeSet::seenTypes() const
+String TypeSet::dumpTypes() const
 {
     if (m_seenTypes == TypeNothing)
         return ASCIILiteral("(Unreached Statement)");
@@ -174,7 +174,7 @@ bool TypeSet::doesTypeConformTo(uint32_t test) const
     // ------ (AND)
     // 0b0010 != seen
 
-    return (m_seenTypes & test) == m_seenTypes;
+    return m_seenTypes != TypeNothing && (m_seenTypes & test) == m_seenTypes;
 }
 
 String TypeSet::displayName() const
@@ -353,11 +353,6 @@ String TypeSet::toJSONString() const
     return json.toString();
 }
 
-void TypeSet::dumpSeenTypes()
-{
-    dataLog(seenTypes(), "\n");
-}
-
 StructureShape::StructureShape()
     : m_proto(nullptr)
     , m_propertyHash(nullptr)
index f1ebf45..d58bff7 100644 (file)
@@ -100,7 +100,7 @@ public:
     void addTypeInformation(RuntimeType, PassRefPtr<StructureShape>, StructureID);
     static RuntimeType getRuntimeTypeForValue(JSValue);
     void invalidateCache();
-    JS_EXPORT_PRIVATE String seenTypes() const;
+    String dumpTypes() const;
     String displayName() const;
     PassRefPtr<Inspector::Protocol::Array<String>> allPrimitiveTypeNames() const;
     PassRefPtr<Inspector::Protocol::Array<Inspector::Protocol::Runtime::StructureDescription>> allStructureRepresentations() const;
@@ -109,10 +109,10 @@ public:
     String leastCommonAncestor() const;
     PassRefPtr<Inspector::Protocol::Runtime::TypeSet> inspectorTypeSet() const;
     bool isEmpty() const { return m_seenTypes == TypeNothing; }
+    bool doesTypeConformTo(uint32_t test) const;
+    uint32_t seenTypes() const { return m_seenTypes; }
 
 private:
-    void dumpSeenTypes();
-    bool doesTypeConformTo(uint32_t test) const;
 
     uint32_t m_seenTypes;
     bool m_isOverflown;
diff --git a/Source/JavaScriptCore/tests/typeProfiler/dfg-jit-optimizations.js b/Source/JavaScriptCore/tests/typeProfiler/dfg-jit-optimizations.js
new file mode 100644 (file)
index 0000000..b24ebb9
--- /dev/null
@@ -0,0 +1,38 @@
+load("./driver/driver.js");
+
+function tierUpToDFG(func, arg) 
+{
+    for (var i = 0; i < 1000; i++)
+        func(arg);
+}
+
+var types = [T.Boolean, T.Integer, T.Null, T.Number, T.String, T.Undefined];
+var values = [true, 1, null, 1.1, "hello", undefined];
+var funcs = [function(x) { return x; }, function(x) { return x; }, function(x) { return x; }, 
+    function(x) { return x; }, function(x) { return x; }, function(x) { return x; }]
+
+assert(types.length === values.length && values.length === funcs.length);
+
+// Make sure baseline DFG type check optimizations work.
+// Tier up each function on a different type, then call it with all other primitive types to make sure they're recorded.
+for (var i = 0; i < types.length; i++) {
+    var func = funcs[i];
+    var type = types[i];
+    var value = values[i];
+    noInline(func);
+    tierUpToDFG(func, value);
+    var typeInfo = findTypeForExpression(func, "x"); 
+    assert(typeInfo.instructionTypeSet.primitiveTypeNames.length === 1, "Only one primitive type should have been seen so far.");
+    assert(typeInfo.instructionTypeSet.primitiveTypeNames.indexOf(type) !== -1, "Should have been optimized on type: " + type);
+    for (var j = 0; j < types.length; j++) {
+        if (type === T.Number && types[j] === T.Integer) {
+            // If we optimize on type Number, but we pass in an Integer, our typecheck will still pass, so we wont directly get
+            // type Integer because T.Integer is implied to be contained in T.Number.
+            continue; 
+        }
+
+        func(values[j]);
+        typeInfo = findTypeForExpression(func, "x");
+        assert(typeInfo.instructionTypeSet.primitiveTypeNames.indexOf(types[j]) !== -1, "Should have seen type: " + types[j] + " " + i + "," + j);
+    }
+}