Changeset 185920 in webkit


Ignore:
Timestamp:
Jun 24, 2015 12:13:54 PM (9 years ago)
Author:
commit-queue@webkit.org
Message:

Strict Equality on objects should only check that one of the two sides is an object.
https://bugs.webkit.org/show_bug.cgi?id=145992

Source/JavaScriptCore:

This patch adds a new optimization for checking strict equality on objects.
If we speculate that a strict equality comparison has an object on one side
we only need to type check that side. Equality is then determined by a pointer
comparison between the two values (although in the 32-bit case we must also check
that the other side is a cell). Once LICM hoists type checks out of a loop we
can be cleverer about how we choose the operand we type check if both are
speculated to be objects.

For testing I added the addressOf function, which returns the address
of a Cell to the runtime.

Patch by Keith Miller <keith_miller@apple.com> on 2015-06-24
Reviewed by Mark Lam.

  • dfg/DFGFixupPhase.cpp:

(JSC::DFG::FixupPhase::fixupNode):

  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::compileStrictEq):

  • dfg/DFGSpeculativeJIT.h:
  • dfg/DFGSpeculativeJIT32_64.cpp:

(JSC::DFG::SpeculativeJIT::compileObjectStrictEquality):
(JSC::DFG::SpeculativeJIT::compilePeepHoleObjectStrictEquality):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::compileObjectStrictEquality):
(JSC::DFG::SpeculativeJIT::compilePeepHoleObjectStrictEquality):

  • ftl/FTLCapabilities.cpp:

(JSC::FTL::canCompile):

  • ftl/FTLLowerDFGToLLVM.cpp:

(JSC::FTL::DFG::LowerDFGToLLVM::compileCompareStrictEq):

  • jsc.cpp:

(GlobalObject::finishCreation):
(functionAddressOf):

  • tests/stress/equality-type-checking.js: Added.

(Foo):
(checkStrictEq):
(checkStrictEqOther):

LayoutTests:

Patch by Keith Miller <keith_miller@apple.com> on 2015-06-24
Reviewed by Mark Lam.

Adds a test that checks if strict equality checks with objects properly exit out of DFG code when
dealing with document.all, which is an object that masquerades as undefined.

  • js/dom/document-all-strict-eq-expected.txt: Added.
  • js/dom/document-all-strict-eq.html: Added.
  • js/dom/script-tests/document-all-strict-eq.js: Added.

(f):

Location:
trunk
Files:
4 added
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r185918 r185920  
     12015-06-24  Keith Miller  <keith_miller@apple.com>
     2
     3        Strict Equality on objects should only check that one of the two sides is an object.
     4        https://bugs.webkit.org/show_bug.cgi?id=145992
     5
     6        Reviewed by Mark Lam.
     7
     8        Adds a test that checks if strict equality checks with objects properly exit out of DFG code when
     9        dealing with document.all, which is an object that masquerades as undefined.
     10
     11        * js/dom/document-all-strict-eq-expected.txt: Added.
     12        * js/dom/document-all-strict-eq.html: Added.
     13        * js/dom/script-tests/document-all-strict-eq.js: Added.
     14        (f):
     15
    1162015-06-24  Myles C. Maxfield  <mmaxfield@apple.com>
    217
  • trunk/Source/JavaScriptCore/ChangeLog

    r185909 r185920  
     12015-06-24  Keith Miller  <keith_miller@apple.com>
     2
     3        Strict Equality on objects should only check that one of the two sides is an object.
     4        https://bugs.webkit.org/show_bug.cgi?id=145992
     5
     6        This patch adds a new optimization for checking strict equality on objects.
     7        If we speculate that a strict equality comparison has an object on one side
     8        we only need to type check that side. Equality is then determined by a pointer
     9        comparison between the two values (although in the 32-bit case we must also check
     10        that the other side is a cell). Once LICM hoists type checks out of a loop we
     11        can be cleverer about how we choose the operand we type check if both are
     12        speculated to be objects.
     13
     14        For testing I added the addressOf function, which returns the address
     15        of a Cell to the runtime.
     16
     17        Reviewed by Mark Lam.
     18
     19        * dfg/DFGFixupPhase.cpp:
     20        (JSC::DFG::FixupPhase::fixupNode):
     21        * dfg/DFGSpeculativeJIT.cpp:
     22        (JSC::DFG::SpeculativeJIT::compileStrictEq):
     23        * dfg/DFGSpeculativeJIT.h:
     24        * dfg/DFGSpeculativeJIT32_64.cpp:
     25        (JSC::DFG::SpeculativeJIT::compileObjectStrictEquality):
     26        (JSC::DFG::SpeculativeJIT::compilePeepHoleObjectStrictEquality):
     27        * dfg/DFGSpeculativeJIT64.cpp:
     28        (JSC::DFG::SpeculativeJIT::compileObjectStrictEquality):
     29        (JSC::DFG::SpeculativeJIT::compilePeepHoleObjectStrictEquality):
     30        * ftl/FTLCapabilities.cpp:
     31        (JSC::FTL::canCompile):
     32        * ftl/FTLLowerDFGToLLVM.cpp:
     33        (JSC::FTL::DFG::LowerDFGToLLVM::compileCompareStrictEq):
     34        * jsc.cpp:
     35        (GlobalObject::finishCreation):
     36        (functionAddressOf):
     37        * tests/stress/equality-type-checking.js: Added.
     38        (Foo):
     39        (checkStrictEq):
     40        (checkStrictEqOther):
     41
    1422015-06-24  Mark Lam  <mark.lam@apple.com>
    243
  • trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp

    r185728 r185920  
    472472                break;
    473473            }
    474             if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObject()) {
     474            WatchpointSet* masqueradesAsUndefinedWatchpoint = m_graph.globalObjectFor(node->origin.semantic)->masqueradesAsUndefinedWatchpoint();
     475            if (masqueradesAsUndefinedWatchpoint->isStillValid()) {
     476               
     477                if (node->child1()->shouldSpeculateObject()) {
     478                    m_graph.watchpoints().addLazily(masqueradesAsUndefinedWatchpoint);
     479                    fixEdge<ObjectUse>(node->child1());
     480                    break;
     481                }
     482                if (node->child2()->shouldSpeculateObject()) {
     483                    m_graph.watchpoints().addLazily(masqueradesAsUndefinedWatchpoint);
     484                    fixEdge<ObjectUse>(node->child2());
     485                    break;
     486                }
     487               
     488            } else if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObject()) {
    475489                fixEdge<ObjectUse>(node->child1());
    476490                fixEdge<ObjectUse>(node->child2());
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp

    r185267 r185920  
    39163916    }
    39173917
     3918    if (node->isBinaryUseKind(ObjectUse, UntypedUse)) {
     3919        unsigned branchIndexInBlock = detectPeepHoleBranch();
     3920        if (branchIndexInBlock != UINT_MAX) {
     3921            Node* branchNode = m_block->at(branchIndexInBlock);
     3922            compilePeepHoleObjectStrictEquality(node->child1(), node->child2(), branchNode);
     3923            use(node->child1());
     3924            use(node->child2());
     3925            m_indexInBlock = branchIndexInBlock;
     3926            m_currentNode = branchNode;
     3927            return true;
     3928        }
     3929        compileObjectStrictEquality(node->child1(), node->child2());
     3930        return false;
     3931    }
     3932   
     3933    if (node->isBinaryUseKind(UntypedUse, ObjectUse)) {
     3934        unsigned branchIndexInBlock = detectPeepHoleBranch();
     3935        if (branchIndexInBlock != UINT_MAX) {
     3936            Node* branchNode = m_block->at(branchIndexInBlock);
     3937            compilePeepHoleObjectStrictEquality(node->child2(), node->child1(), branchNode);
     3938            use(node->child1());
     3939            use(node->child2());
     3940            m_indexInBlock = branchIndexInBlock;
     3941            m_currentNode = branchNode;
     3942            return true;
     3943        }
     3944        compileObjectStrictEquality(node->child2(), node->child1());
     3945        return false;
     3946    }
     3947
    39183948    if (node->isBinaryUseKind(ObjectUse)) {
    39193949        unsigned branchIndexInBlock = detectPeepHoleBranch();
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h

    r185239 r185920  
    20892089    void compilePeepHoleDoubleBranch(Node*, Node* branchNode, JITCompiler::DoubleCondition);
    20902090    void compilePeepHoleObjectEquality(Node*, Node* branchNode);
     2091    void compilePeepHoleObjectStrictEquality(Edge objectChild, Edge otherChild, Node* branchNode);
    20912092    void compilePeepHoleObjectToObjectOrOtherEquality(Edge leftChild, Edge rightChild, Node* branchNode);
    20922093    void compileObjectEquality(Node*);
     2094    void compileObjectStrictEquality(Edge objectChild, Edge otherChild);
    20932095    void compileObjectToObjectOrOtherEquality(Edge leftChild, Edge rightChild);
    20942096    void compileObjectOrOtherLogicalNot(Edge value);
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp

    r184542 r185920  
    11831183
    11841184    booleanResult(resultPayloadGPR, node);
     1185}
     1186
     1187void SpeculativeJIT::compileObjectStrictEquality(Edge objectChild, Edge otherChild)
     1188{
     1189    SpeculateCellOperand op1(this, objectChild);
     1190    JSValueOperand op2(this, otherChild);
     1191
     1192    GPRReg op1GPR = op1.gpr();
     1193    GPRReg op2GPR = op2.payloadGPR();
     1194
     1195    DFG_TYPE_CHECK(JSValueSource::unboxedCell(op1GPR), objectChild, SpecObject, m_jit.branchIfNotObject(op1GPR));
     1196
     1197    GPRTemporary resultPayload(this, Reuse, op1);
     1198    GPRReg resultPayloadGPR = resultPayload.gpr();
     1199   
     1200    MacroAssembler::Jump op2CellJump = m_jit.branchIfCell(op2.jsValueRegs());
     1201   
     1202    m_jit.move(TrustedImm32(0), resultPayloadGPR);
     1203    MacroAssembler::Jump op2NotCellJump = m_jit.jump();
     1204   
     1205    // At this point we know that we can perform a straight-forward equality comparison on pointer
     1206    // values because we are doing strict equality.
     1207    op2CellJump.link(&m_jit);
     1208    m_jit.compare32(MacroAssembler::Equal, op1GPR, op2GPR, resultPayloadGPR);
     1209   
     1210    op2NotCellJump.link(&m_jit);
     1211    booleanResult(resultPayloadGPR, m_currentNode);
     1212}
     1213   
     1214void SpeculativeJIT::compilePeepHoleObjectStrictEquality(Edge objectChild, Edge otherChild, Node* branchNode)
     1215{
     1216    BasicBlock* taken = branchNode->branchData()->taken.block;
     1217    BasicBlock* notTaken = branchNode->branchData()->notTaken.block;
     1218
     1219    SpeculateCellOperand op1(this, objectChild);
     1220    JSValueOperand op2(this, otherChild);
     1221   
     1222    GPRReg op1GPR = op1.gpr();
     1223    GPRReg op2GPR = op2.payloadGPR();
     1224
     1225    DFG_TYPE_CHECK(JSValueSource::unboxedCell(op1GPR), objectChild, SpecObject, m_jit.branchIfNotObject(op1GPR));
     1226
     1227    branch32(MacroAssembler::NotEqual, op2.tagGPR(), TrustedImm32(JSValue::CellTag), notTaken);
     1228   
     1229    if (taken == nextBlock()) {
     1230        branch32(MacroAssembler::NotEqual, op1GPR, op2GPR, notTaken);
     1231        jump(taken);
     1232    } else {
     1233        branch32(MacroAssembler::Equal, op1GPR, op2GPR, taken);
     1234        jump(notTaken);
     1235    }
    11851236}
    11861237
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp

    r184542 r185920  
    12951295
    12961296    jsValueResult(resultGPR, m_currentNode, DataFormatJSBoolean);
     1297}
     1298
     1299void SpeculativeJIT::compileObjectStrictEquality(Edge objectChild, Edge otherChild)
     1300{
     1301    SpeculateCellOperand op1(this, objectChild);
     1302    JSValueOperand op2(this, otherChild);
     1303    GPRTemporary result(this);
     1304
     1305    GPRReg op1GPR = op1.gpr();
     1306    GPRReg op2GPR = op2.gpr();
     1307    GPRReg resultGPR = result.gpr();
     1308
     1309    DFG_TYPE_CHECK(JSValueSource::unboxedCell(op1GPR), objectChild, SpecObject, m_jit.branchIfNotObject(op1GPR));
     1310
     1311    // At this point we know that we can perform a straight-forward equality comparison on pointer
     1312    // values because we are doing strict equality.
     1313    m_jit.compare64(MacroAssembler::Equal, op1GPR, op2GPR, resultGPR);
     1314    m_jit.or32(TrustedImm32(ValueFalse), resultGPR);
     1315    jsValueResult(resultGPR, m_currentNode, DataFormatJSBoolean);
     1316}
     1317   
     1318void SpeculativeJIT::compilePeepHoleObjectStrictEquality(Edge objectChild, Edge otherChild, Node* branchNode)
     1319{
     1320    BasicBlock* taken = branchNode->branchData()->taken.block;
     1321    BasicBlock* notTaken = branchNode->branchData()->notTaken.block;
     1322   
     1323    SpeculateCellOperand op1(this, objectChild);
     1324    JSValueOperand op2(this, otherChild);
     1325   
     1326    GPRReg op1GPR = op1.gpr();
     1327    GPRReg op2GPR = op2.gpr();
     1328   
     1329    DFG_TYPE_CHECK(JSValueSource::unboxedCell(op1GPR), objectChild, SpecObject, m_jit.branchIfNotObject(op1GPR));
     1330
     1331    if (taken == nextBlock()) {
     1332        branchPtr(MacroAssembler::NotEqual, op1GPR, op2GPR, notTaken);
     1333        jump(taken);
     1334    } else {
     1335        branchPtr(MacroAssembler::Equal, op1GPR, op2GPR, taken);
     1336        jump(notTaken);
     1337    }
    12971338}
    12981339
  • trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp

    r185239 r185920  
    330330        if (node->isBinaryUseKind(StringIdentUse))
    331331            break;
     332        if (node->isBinaryUseKind(ObjectUse, UntypedUse))
     333            break;
     334        if (node->isBinaryUseKind(UntypedUse, ObjectUse))
     335            break;
    332336        if (node->isBinaryUseKind(ObjectUse))
    333337            break;
  • trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp

    r185600 r185920  
    41584158            return;
    41594159        }
    4160        
     4160
     4161        if (m_node->isBinaryUseKind(ObjectUse, UntypedUse)) {
     4162            setBoolean(
     4163                m_out.equal(
     4164                    lowNonNullObject(m_node->child1()),
     4165                    lowJSValue(m_node->child2())));
     4166            return;
     4167        }
     4168       
     4169        if (m_node->isBinaryUseKind(UntypedUse, ObjectUse)) {
     4170            setBoolean(
     4171                m_out.equal(
     4172                    lowNonNullObject(m_node->child2()),
     4173                    lowJSValue(m_node->child1())));
     4174            return;
     4175        }
     4176
    41614177        if (m_node->isBinaryUseKind(ObjectUse)) {
    41624178            setBoolean(
  • trunk/Source/JavaScriptCore/jsc.cpp

    r185608 r185920  
    459459static EncodedJSValue JSC_HOST_CALL functionHeapSize(ExecState*);
    460460static EncodedJSValue JSC_HOST_CALL functionDeleteAllCompiledCode(ExecState*);
     461static EncodedJSValue JSC_HOST_CALL functionAddressOf(ExecState*);
    461462#ifndef NDEBUG
    462463static EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState*);
     
    600601        addFunction(vm, "gcHeapSize", functionHeapSize, 0);
    601602        addFunction(vm, "deleteAllCompiledCode", functionDeleteAllCompiledCode, 0);
     603        addFunction(vm, "addressOf", functionAddressOf, 1);
    602604#ifndef NDEBUG
    603605        addFunction(vm, "dumpCallFrame", functionDumpCallFrame, 0);
     
    877879    return JSValue::encode(jsUndefined());
    878880}
     881
     882// This function is not generally very helpful in 64-bit code as the tag and payload
     883// share a register. But in 32-bit JITed code the tag may not be checked if an
     884// optimization removes type checking requirements, such as in ===.
     885EncodedJSValue JSC_HOST_CALL functionAddressOf(ExecState* exec)
     886{
     887    JSValue value = exec->argument(0);
     888    if (!value.isCell())
     889        return JSValue::encode(jsUndefined());
     890    // Need to cast to uint64_t so bitwise_cast will play along.
     891    uint64_t asNumber = reinterpret_cast<uint64_t>(value.asCell());
     892    EncodedJSValue returnValue = JSValue::encode(jsNumber(bitwise_cast<double>(asNumber)));
     893    return returnValue;
     894}
     895
    879896
    880897#ifndef NDEBUG
Note: See TracChangeset for help on using the changeset viewer.