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
+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
--- /dev/null
+const test = createBuiltin(`(function (arg) {
+ let other = @undefined;
+ @idWithProfile(other, "SpecObject");
+ return arg == other;
+})`);
+
+for (let i = 0; i < 10000; i++) {
+ test({});
+ test(null);
+}
+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
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;
macro(argument) \
macro(argumentCount) \
macro(assert) \
+ macro(idWithProfile) \
macro(isObject) \
macro(isJSArray) \
macro(isProxyObject) \
{ "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 },
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:
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:
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
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
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);
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);
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)
{
}
break;
}
-
+
+ case IdentityWithProfile:
case Identity: {
forNode(node) = forNode(node->child1());
if (forNode(node).value())
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));
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:
return;
case Identity:
+ case IdentityWithProfile:
case Phantom:
case Check:
case ExtractOSREntryLocal:
case Int52Constant:
case LazyJSConstant:
case Identity:
+ case IdentityWithProfile:
case GetCallee:
case GetArgumentCountIncludingThis:
case GetRestLength:
// fixup rules for them.
DFG_CRASH(m_graph, node, "Unexpected node during fixup");
break;
-
+
case PutGlobalVariable: {
fixEdge<CellUse>(node->child1());
speculateForBarrier(node->child2());
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:
case Phantom:
case Check:
case Identity:
+ case IdentityWithProfile:
case GetLocal:
case LoopHint:
case Phi:
ASSERT(hasHeapPrediction());
m_opInfo2 = prediction;
}
+
+ SpeculatedType getForcedPrediction()
+ {
+ ASSERT(op() == IdentityWithProfile);
+ return m_opInfo.as<SpeculatedType>();
+ }
bool hasCellOperand()
{
/* 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) \
break;
}
+ case IdentityWithProfile: {
+ setPrediction(m_currentNode->getForcedPrediction());
+ break;
+ }
+
case GetLocal:
case SetLocal:
case UInt32ToNumber:
case Int52Constant:
case LazyJSConstant:
case Identity:
+ case IdentityWithProfile:
case ToThis:
case CreateThis:
case GetCallee:
case AtomicsStore:
case AtomicsSub:
case AtomicsXor:
+ case IdentityWithProfile:
DFG_CRASH(m_jit.graph(), node, "unexpected node in DFG backend");
break;
}
case PhantomCreateRest:
case PhantomSpread:
case PhantomNewArrayWithSpread:
+ case IdentityWithProfile:
DFG_CRASH(m_jit.graph(), node, "Unexpected node");
break;
}
switch (node->op()) {
case Identity:
+ case IdentityWithProfile:
VALIDATE((node), canonicalResultRepresentation(node->result()) == canonicalResultRepresentation(node->child1()->result()));
break;
case SetLocal:
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)
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*);
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);
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);
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)