Add testing tool to lie to the DFG about profiles
authorkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 15 Aug 2017 04:18:56 +0000 (04:18 +0000)
committerkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 15 Aug 2017 04:18:56 +0000 (04:18 +0000)
https://bugs.webkit.org/show_bug.cgi?id=175487

Reviewed by Saam Barati.

JSTests:

* stress/compare-eq-incomplete-profile.js: Added.
(const.test.createBuiltin):

Source/JavaScriptCore:

This patch adds a new bytecode identity_with_profile that lets
us lie to the DFG about what profiles it has seen as the input to
another bytecode. Previously, there was no reliable way to force
a given profile when we tired up.

* bytecode/BytecodeDumper.cpp:
(JSC::BytecodeDumper<Block>::dumpBytecode):
* bytecode/BytecodeIntrinsicRegistry.h:
* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/SpeculatedType.cpp:
(JSC::speculationFromString):
* bytecode/SpeculatedType.h:
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitIdWithProfile):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::BytecodeIntrinsicNode::emit_intrinsic_idWithProfile):
* 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/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGMayExit.cpp:
* dfg/DFGNode.h:
(JSC::DFG::Node::getForcedPrediction):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGValidate.cpp:
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_identity_with_profile):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_identity_with_profile):
* llint/LowLevelInterpreter.asm:

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

31 files changed:
JSTests/ChangeLog
JSTests/stress/compare-eq-incomplete-profile.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/BytecodeDumper.cpp
Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h
Source/JavaScriptCore/bytecode/BytecodeList.json
Source/JavaScriptCore/bytecode/BytecodeUseDef.h
Source/JavaScriptCore/bytecode/SpeculatedType.cpp
Source/JavaScriptCore/bytecode/SpeculatedType.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
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/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGMayExit.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGValidate.cpp
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter.asm

index 94c3364..3cf3738 100644 (file)
@@ -1,3 +1,13 @@
+2017-08-14  Keith Miller  <keith_miller@apple.com>
+
+        Add testing tool to lie to the DFG about profiles
+        https://bugs.webkit.org/show_bug.cgi?id=175487
+
+        Reviewed by Saam Barati.
+
+        * stress/compare-eq-incomplete-profile.js: Added.
+        (const.test.createBuiltin):
+
 2017-08-14  Robin Morisset  <rmorisset@apple.com>
 
         Support the with keyword in DFG
diff --git a/JSTests/stress/compare-eq-incomplete-profile.js b/JSTests/stress/compare-eq-incomplete-profile.js
new file mode 100644 (file)
index 0000000..86fdd5f
--- /dev/null
@@ -0,0 +1,10 @@
+const test = createBuiltin(`(function (arg) {
+    let other = @undefined;
+    @idWithProfile(other, "SpecObject");
+    return arg == other;
+})`);
+
+for (let i = 0; i < 10000; i++) {
+    test({});
+    test(null);
+}
index 77aa093..64a01f9 100644 (file)
@@ -1,3 +1,63 @@
+2017-08-14  Keith Miller  <keith_miller@apple.com>
+
+        Add testing tool to lie to the DFG about profiles
+        https://bugs.webkit.org/show_bug.cgi?id=175487
+
+        Reviewed by Saam Barati.
+
+        This patch adds a new bytecode identity_with_profile that lets
+        us lie to the DFG about what profiles it has seen as the input to
+        another bytecode. Previously, there was no reliable way to force
+        a given profile when we tired up.
+
+        * bytecode/BytecodeDumper.cpp:
+        (JSC::BytecodeDumper<Block>::dumpBytecode):
+        * bytecode/BytecodeIntrinsicRegistry.h:
+        * bytecode/BytecodeList.json:
+        * bytecode/BytecodeUseDef.h:
+        (JSC::computeUsesForBytecodeOffset):
+        (JSC::computeDefsForBytecodeOffset):
+        * bytecode/SpeculatedType.cpp:
+        (JSC::speculationFromString):
+        * bytecode/SpeculatedType.h:
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitIdWithProfile):
+        * bytecompiler/BytecodeGenerator.h:
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::BytecodeIntrinsicNode::emit_intrinsic_idWithProfile):
+        * 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/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGMayExit.cpp:
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::getForcedPrediction):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGValidate.cpp:
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        * jit/JIT.h:
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_identity_with_profile):
+        * jit/JITOpcodes32_64.cpp:
+        (JSC::JIT::emit_op_identity_with_profile):
+        * llint/LowLevelInterpreter.asm:
+
 2017-08-14  Simon Fraser  <simon.fraser@apple.com>
 
         Remove Proximity Events and related code
index 07b0655..1264b70 100644 (file)
@@ -1569,6 +1569,14 @@ void BytecodeDumper<Block>::dumpBytecode(PrintStream& out, const typename Block:
         out.printf("%s, %d", registerName(condition).data(), line);
         break;
     }
+    case op_identity_with_profile: {
+        int r0 = (++it)->u.operand;
+        ++it; // Profile top half
+        ++it; // Profile bottom half
+        printLocationAndOp(out, location, it, "identity_with_profile");
+        out.printf("%s", registerName(r0).data());
+        break;
+    }
     case op_unreachable: {
         printLocationAndOp(out, location, it, "unreachable");
         break;
index 3b61181..d9749f9 100644 (file)
@@ -41,6 +41,7 @@ class Identifier;
     macro(argument) \
     macro(argumentCount) \
     macro(assert) \
+    macro(idWithProfile) \
     macro(isObject) \
     macro(isJSArray) \
     macro(isProxyObject) \
index 29f2333..781e8f6 100644 (file)
@@ -51,6 +51,7 @@
             { "name" : "op_bitxor", "length" : 5 },
             { "name" : "op_bitor", "length" : 5 },
             { "name" : "op_overrides_has_instance", "length" : 4 },
+            { "name" : "op_identity_with_profile", "length" : 4 },
             { "name" : "op_instanceof", "length" : 4 },
             { "name" : "op_instanceof_custom", "length" : 5 },
             { "name" : "op_typeof", "length" : 3 },
index 809c21e..cdaea9e 100644 (file)
@@ -60,6 +60,7 @@ void computeUsesForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, Instructi
     case op_get_scope:
     case op_to_this:
     case op_check_tdz:
+    case op_identity_with_profile:
     case op_profile_type:
     case op_throw:
     case op_end:
@@ -423,6 +424,7 @@ void computeDefsForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, Instructi
     case op_instanceof_custom:
     case op_get_by_val:
     case op_typeof:
+    case op_identity_with_profile:
     case op_is_empty:
     case op_is_undefined:
     case op_is_boolean:
index 20a3a66..ca25947 100644 (file)
@@ -659,5 +659,122 @@ SpeculatedType typeOfDoubleUnaryOp(SpeculatedType value)
     return polluteDouble(value);
 }
 
+SpeculatedType speculationFromString(const char* speculation)
+{
+    if (!strncmp(speculation, "SpecNone", strlen("SpecNone")))
+        return SpecNone;
+    if (!strncmp(speculation, "SpecFinalObject", strlen("SpecFinalObject")))
+        return SpecFinalObject;
+    if (!strncmp(speculation, "SpecArray", strlen("SpecArray")))
+        return SpecArray;
+    if (!strncmp(speculation, "SpecFunction", strlen("SpecFunction")))
+        return SpecFunction;
+    if (!strncmp(speculation, "SpecInt8Array", strlen("SpecInt8Array")))
+        return SpecInt8Array;
+    if (!strncmp(speculation, "SpecInt16Array", strlen("SpecInt16Array")))
+        return SpecInt16Array;
+    if (!strncmp(speculation, "SpecInt32Array", strlen("SpecInt32Array")))
+        return SpecInt32Array;
+    if (!strncmp(speculation, "SpecUint8Array", strlen("SpecUint8Array")))
+        return SpecUint8Array;
+    if (!strncmp(speculation, "SpecUint8ClampedArray", strlen("SpecUint8ClampedArray")))
+        return SpecUint8ClampedArray;
+    if (!strncmp(speculation, "SpecUint16Array", strlen("SpecUint16Array")))
+        return SpecUint16Array;
+    if (!strncmp(speculation, "SpecUint32Array", strlen("SpecUint32Array")))
+        return SpecUint32Array;
+    if (!strncmp(speculation, "SpecFloat32Array", strlen("SpecFloat32Array")))
+        return SpecFloat32Array;
+    if (!strncmp(speculation, "SpecFloat64Array", strlen("SpecFloat64Array")))
+        return SpecFloat64Array;
+    if (!strncmp(speculation, "SpecTypedArrayView", strlen("SpecTypedArrayView")))
+        return SpecTypedArrayView;
+    if (!strncmp(speculation, "SpecDirectArguments", strlen("SpecDirectArguments")))
+        return SpecDirectArguments;
+    if (!strncmp(speculation, "SpecScopedArguments", strlen("SpecScopedArguments")))
+        return SpecScopedArguments;
+    if (!strncmp(speculation, "SpecStringObject", strlen("SpecStringObject")))
+        return SpecStringObject;
+    if (!strncmp(speculation, "SpecRegExpObject", strlen("SpecRegExpObject")))
+        return SpecRegExpObject;
+    if (!strncmp(speculation, "SpecMapObject", strlen("SpecMapObject")))
+        return SpecMapObject;
+    if (!strncmp(speculation, "SpecSetObject", strlen("SpecSetObject")))
+        return SpecSetObject;
+    if (!strncmp(speculation, "SpecProxyObject", strlen("SpecProxyObject")))
+        return SpecProxyObject;
+    if (!strncmp(speculation, "SpecDerivedArray", strlen("SpecDerivedArray")))
+        return SpecDerivedArray;
+    if (!strncmp(speculation, "SpecObjectOther", strlen("SpecObjectOther")))
+        return SpecObjectOther;
+    if (!strncmp(speculation, "SpecObject", strlen("SpecObject")))
+        return SpecObject;
+    if (!strncmp(speculation, "SpecStringIdent", strlen("SpecStringIdent")))
+        return SpecStringIdent;
+    if (!strncmp(speculation, "SpecStringVar", strlen("SpecStringVar")))
+        return SpecStringVar;
+    if (!strncmp(speculation, "SpecString", strlen("SpecString")))
+        return SpecString;
+    if (!strncmp(speculation, "SpecSymbol", strlen("SpecSymbol")))
+        return SpecSymbol;
+    if (!strncmp(speculation, "SpecCellOther", strlen("SpecCellOther")))
+        return SpecCellOther;
+    if (!strncmp(speculation, "SpecCell", strlen("SpecCell")))
+        return SpecCell;
+    if (!strncmp(speculation, "SpecBoolInt32", strlen("SpecBoolInt32")))
+        return SpecBoolInt32;
+    if (!strncmp(speculation, "SpecNonBoolInt32", strlen("SpecNonBoolInt32")))
+        return SpecNonBoolInt32;
+    if (!strncmp(speculation, "SpecInt32Only", strlen("SpecInt32Only")))
+        return SpecInt32Only;
+    if (!strncmp(speculation, "SpecInt52Only", strlen("SpecInt52Only")))
+        return SpecInt52Only;
+    if (!strncmp(speculation, "SpecAnyInt", strlen("SpecAnyInt")))
+        return SpecAnyInt;
+    if (!strncmp(speculation, "SpecAnyIntAsDouble", strlen("SpecAnyIntAsDouble")))
+        return SpecAnyIntAsDouble;
+    if (!strncmp(speculation, "SpecNonIntAsDouble", strlen("SpecNonIntAsDouble")))
+        return SpecNonIntAsDouble;
+    if (!strncmp(speculation, "SpecDoubleReal", strlen("SpecDoubleReal")))
+        return SpecDoubleReal;
+    if (!strncmp(speculation, "SpecDoublePureNaN", strlen("SpecDoublePureNaN")))
+        return SpecDoublePureNaN;
+    if (!strncmp(speculation, "SpecDoubleImpureNaN", strlen("SpecDoubleImpureNaN")))
+        return SpecDoubleImpureNaN;
+    if (!strncmp(speculation, "SpecDoubleNaN", strlen("SpecDoubleNaN")))
+        return SpecDoubleNaN;
+    if (!strncmp(speculation, "SpecBytecodeDouble", strlen("SpecBytecodeDouble")))
+        return SpecBytecodeDouble;
+    if (!strncmp(speculation, "SpecFullDouble", strlen("SpecFullDouble")))
+        return SpecFullDouble;
+    if (!strncmp(speculation, "SpecBytecodeRealNumber", strlen("SpecBytecodeRealNumber")))
+        return SpecBytecodeRealNumber;
+    if (!strncmp(speculation, "SpecFullRealNumber", strlen("SpecFullRealNumber")))
+        return SpecFullRealNumber;
+    if (!strncmp(speculation, "SpecBytecodeNumber", strlen("SpecBytecodeNumber")))
+        return SpecBytecodeNumber;
+    if (!strncmp(speculation, "SpecFullNumber", strlen("SpecFullNumber")))
+        return SpecFullNumber;
+    if (!strncmp(speculation, "SpecBoolean", strlen("SpecBoolean")))
+        return SpecBoolean;
+    if (!strncmp(speculation, "SpecOther", strlen("SpecOther")))
+        return SpecOther;
+    if (!strncmp(speculation, "SpecMisc", strlen("SpecMisc")))
+        return SpecMisc;
+    if (!strncmp(speculation, "SpecHeapTop", strlen("SpecHeapTop")))
+        return SpecHeapTop;
+    if (!strncmp(speculation, "SpecPrimitive", strlen("SpecPrimitive")))
+        return SpecPrimitive;
+    if (!strncmp(speculation, "SpecEmpty", strlen("SpecEmpty")))
+        return SpecEmpty;
+    if (!strncmp(speculation, "SpecBytecodeTop", strlen("SpecBytecodeTop")))
+        return SpecBytecodeTop;
+    if (!strncmp(speculation, "SpecFullTop", strlen("SpecFullTop")))
+        return SpecFullTop;
+    if (!strncmp(speculation, "SpecCellCheck", strlen("SpecCellCheck")))
+        return SpecCellCheck;
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
 } // namespace JSC
 
index bcda24c..7a6bb46 100644 (file)
@@ -498,4 +498,7 @@ SpeculatedType typeOfDoublePow(SpeculatedType, SpeculatedType);
 SpeculatedType typeOfDoubleBinaryOp(SpeculatedType, SpeculatedType);
 SpeculatedType typeOfDoubleUnaryOp(SpeculatedType);
 
+// This is mostly for debugging so we can fill profiles from strings.
+SpeculatedType speculationFromString(const char*);
+
 } // namespace JSC
index 82c191f..cc65dd5 100644 (file)
@@ -2974,6 +2974,15 @@ RegisterID* BytecodeGenerator::emitAssert(RegisterID* condition, int line)
     return condition;
 }
 
+RegisterID* BytecodeGenerator::emitIdWithProfile(RegisterID* src, SpeculatedType profile)
+{
+    emitOpcode(op_identity_with_profile);
+    instructions().append(src->index());
+    instructions().append(static_cast<uint32_t>(profile >> 32));
+    instructions().append(static_cast<uint32_t>(profile));
+    return src;
+}
+
 void BytecodeGenerator::emitUnreachable()
 {
     emitOpcode(op_unreachable);
index 1a4b279..50fa768 100644 (file)
@@ -686,6 +686,7 @@ namespace JSC {
         RegisterID* emitPutByIndex(RegisterID* base, unsigned index, RegisterID* value);
 
         RegisterID* emitAssert(RegisterID* condition, int line);
+        RegisterID* emitIdWithProfile(RegisterID* src, SpeculatedType profile);
         void emitUnreachable();
 
         void emitPutGetterById(RegisterID* base, const Identifier& property, unsigned propertyDescriptorOptions, RegisterID* getter);
index 6f39a1e..8f4c3d0 100644 (file)
@@ -1023,6 +1023,22 @@ RegisterID* BytecodeIntrinsicNode::emit_intrinsic_toString(BytecodeGenerator& ge
 
     return generator.moveToDestinationIfNeeded(dst, generator.emitToString(generator.tempDestination(dst), src.get()));
 }
+
+RegisterID* BytecodeIntrinsicNode::emit_intrinsic_idWithProfile(BytecodeGenerator& generator, RegisterID* dst)
+{
+
+    ArgumentListNode* node = m_args->m_listNode;
+    RefPtr<RegisterID> idValue = generator.emitNode(node);
+    SpeculatedType speculation = SpecNone;
+    while (node->m_next) {
+        node = node->m_next;
+        ASSERT(node->m_expr->isString());
+        const Identifier& ident = static_cast<StringNode*>(node->m_expr)->value();
+        speculation |= speculationFromString(ident.utf8().data());
+    }
+
+    return generator.moveToDestinationIfNeeded(dst, generator.emitIdWithProfile(idValue.get(), speculation));
+}
     
 RegisterID* BytecodeIntrinsicNode::emit_intrinsic_isJSArray(JSC::BytecodeGenerator& generator, JSC::RegisterID* dst)
 {
index 51f3d7e..8a2986b 100644 (file)
@@ -199,7 +199,8 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         }
         break;
     }
-        
+
+    case IdentityWithProfile:
     case Identity: {
         forNode(node) = forNode(node->child1());
         if (forNode(node).value())
index 95ebe86..c7b075e 100644 (file)
@@ -4459,6 +4459,13 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             NEXT_OPCODE(op_overrides_has_instance);
         }
 
+        case op_identity_with_profile: {
+            Node* src = get(VirtualRegister(currentInstruction[1].u.operand));
+            SpeculatedType speculation = static_cast<SpeculatedType>(currentInstruction[2].u.operand) << 32 | static_cast<SpeculatedType>(currentInstruction[3].u.operand);
+            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(IdentityWithProfile, OpInfo(speculation), src));
+            NEXT_OPCODE(op_identity_with_profile);
+        }
+
         case op_instanceof: {
             Node* value = get(VirtualRegister(currentInstruction[2].u.operand));
             Node* prototype = get(VirtualRegister(currentInstruction[3].u.operand));
index bc5563d..049810f 100644 (file)
@@ -135,6 +135,7 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
     case op_profile_control_flow:
     case op_mov:
     case op_overrides_has_instance:
+    case op_identity_with_profile:
     case op_instanceof:
     case op_instanceof_custom:
     case op_is_empty:
index 70e9708..3feab35 100644 (file)
@@ -137,6 +137,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         return;
 
     case Identity:
+    case IdentityWithProfile:
     case Phantom:
     case Check:
     case ExtractOSREntryLocal:
index 3a25d87..c5edb55 100644 (file)
@@ -49,6 +49,7 @@ bool doesGC(Graph& graph, Node* node)
     case Int52Constant:
     case LazyJSConstant:
     case Identity:
+    case IdentityWithProfile:
     case GetCallee:
     case GetArgumentCountIncludingThis:
     case GetRestLength:
index 7bee93d..ff89d0a 100644 (file)
@@ -1573,7 +1573,7 @@ private:
             // fixup rules for them.
             DFG_CRASH(m_graph, node, "Unexpected node during fixup");
             break;
-        
+
         case PutGlobalVariable: {
             fixEdge<CellUse>(node->child1());
             speculateForBarrier(node->child2());
@@ -1929,6 +1929,11 @@ private:
             break;
         }
 
+        case IdentityWithProfile: {
+            node->clearFlags(NodeMustGenerate);
+            break;
+        }
+
 #if !ASSERT_DISABLED
         // Have these no-op cases here to ensure that nobody forgets to add handlers for new opcodes.
         case SetArgument:
index 4627905..4bdf3ad 100644 (file)
@@ -58,6 +58,7 @@ ExitMode mayExitImpl(Graph& graph, Node* node, StateType& state)
     case Phantom:
     case Check:
     case Identity:
+    case IdentityWithProfile:
     case GetLocal:
     case LoopHint:
     case Phi:
index 3f78619..0f191a0 100644 (file)
@@ -1535,6 +1535,12 @@ public:
         ASSERT(hasHeapPrediction());
         m_opInfo2 = prediction;
     }
+
+    SpeculatedType getForcedPrediction()
+    {
+        ASSERT(op() == IdentityWithProfile);
+        return m_opInfo.as<SpeculatedType>();
+    }
     
     bool hasCellOperand()
     {
index 48abc53..69ade8f 100644 (file)
@@ -47,6 +47,8 @@ namespace JSC { namespace DFG {
     /* is to make one node alias another. CSE will later usually eliminate this node, */\
     /* though it may choose not to if it would corrupt predictions (very rare). */\
     macro(Identity, NodeResultJS) \
+    /* Used for debugging to force a profile to appear as anything we want. */ \
+    macro(IdentityWithProfile, NodeResultJS | NodeMustGenerate) \
     \
     /* Nodes for handling functions (both as call and as construct). */\
     macro(ToThis, NodeResultJS) \
index e3e36c7..2514e95 100644 (file)
@@ -988,6 +988,11 @@ private:
             break;
         }
 
+        case IdentityWithProfile: {
+            setPrediction(m_currentNode->getForcedPrediction());
+            break;
+        }
+
         case GetLocal:
         case SetLocal:
         case UInt32ToNumber:
index 4df484f..55c8c94 100644 (file)
@@ -143,6 +143,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case Int52Constant:
     case LazyJSConstant:
     case Identity:
+    case IdentityWithProfile:
     case ToThis:
     case CreateThis:
     case GetCallee:
index 07bd0d0..06a58a3 100644 (file)
@@ -5680,6 +5680,7 @@ void SpeculativeJIT::compile(Node* node)
     case AtomicsStore:
     case AtomicsSub:
     case AtomicsXor:
+    case IdentityWithProfile:
         DFG_CRASH(m_jit.graph(), node, "unexpected node in DFG backend");
         break;
     }
index 72414e7..6ef8bc8 100644 (file)
@@ -6121,6 +6121,7 @@ void SpeculativeJIT::compile(Node* node)
     case PhantomCreateRest:
     case PhantomSpread:
     case PhantomNewArrayWithSpread:
+    case IdentityWithProfile:
         DFG_CRASH(m_jit.graph(), node, "Unexpected node");
         break;
     }
index 20c9a33..77036b2 100644 (file)
@@ -222,6 +222,7 @@ public:
                  
                 switch (node->op()) {
                 case Identity:
+                case IdentityWithProfile:
                     VALIDATE((node), canonicalResultRepresentation(node->result()) == canonicalResultRepresentation(node->child1()->result()));
                     break;
                 case SetLocal:
index c64147c..7ab79e3 100644 (file)
@@ -289,6 +289,7 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_get_rest_length)
         DEFINE_OP(op_check_tdz)
         DEFINE_OP(op_assert)
+        DEFINE_OP(op_identity_with_profile)
         DEFINE_OP(op_unreachable)
         DEFINE_OP(op_debug)
         DEFINE_OP(op_del_by_id)
index cc4412f..2ed37ed 100644 (file)
@@ -488,6 +488,7 @@ namespace JSC {
         void emit_op_get_rest_length(Instruction*);
         void emit_op_check_tdz(Instruction*);
         void emit_op_assert(Instruction*);
+        void emit_op_identity_with_profile(Instruction*);
         void emit_op_unreachable(Instruction*);
         void emit_op_debug(Instruction*);
         void emit_op_del_by_id(Instruction*);
index 1876128..b015734 100644 (file)
@@ -559,6 +559,11 @@ void JIT::emit_op_assert(Instruction* currentInstruction)
     slowPathCall.call();
 }
 
+void JIT::emit_op_identity_with_profile(Instruction*)
+{
+    // We don't need to do anything here...
+}
+
 void JIT::emit_op_create_lexical_environment(Instruction* currentInstruction)
 {
     JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_create_lexical_environment);
index add4c2e..3aa0305 100644 (file)
@@ -859,6 +859,11 @@ void JIT::emit_op_assert(Instruction* currentInstruction)
     slowPathCall.call();
 }
 
+void JIT::emit_op_identity_with_profile(Instruction*)
+{
+    // We don't need to do anything here...
+}
+
 void JIT::emit_op_create_lexical_environment(Instruction* currentInstruction)
 {
     JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_create_lexical_environment);
index f540cb9..52562e5 100644 (file)
@@ -1723,6 +1723,11 @@ _llint_op_assert:
     dispatch(constexpr op_assert_length)
 
 
+_llint_op_identity_with_profile:
+    traceExecution()
+    dispatch(constexpr op_identity_with_profile_length)
+
+
 _llint_op_unreachable:
     traceExecution()
     callOpcodeSlowPath(_slow_path_unreachable)