Changeset 248426 in webkit


Ignore:
Timestamp:
Aug 8, 2019 11:23:18 AM (5 years ago)
Author:
Ross Kirsling
Message:

[JSC] Add "jump if (not) undefined or null" bytecode ops
https://bugs.webkit.org/show_bug.cgi?id=200480

Reviewed by Saam Barati.

JSTests:

  • stress/destructuring-assignment-require-object-coercible.js:
  • stress/nullish-coalescing.js:

Source/JavaScriptCore:

This patch introduces fused jumps for op_is_undefined_or_null, which ignores "masquerade as undefined" behavior.

This lets us fix a edge-case bug in RequireObjectCoercible (where ({ length } = document.all) was a TypeError)
and moreover provides a very useful optimization for the new ?. and ?? operators, which have semantics centered
around op_jundefined_or_null and op_jnundefined_or_null, respectively.

  • bytecode/BytecodeList.rb:
  • bytecode/BytecodeUseDef.h:

(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):

  • bytecode/Opcode.h:

(JSC::isBranch):

  • bytecode/PreciseJumpTargetsInlines.h:
  • bytecompiler/BytecodeGenerator.cpp:

(JSC::Label::setLocation):
(JSC::BytecodeGenerator::emitJumpIfTrue):
(JSC::BytecodeGenerator::emitJumpIfFalse):
(JSC::BytecodeGenerator::emitRequireObjectCoercible):

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::parseBlock):

  • dfg/DFGCapabilities.cpp:

(JSC::DFG::capabilityLevel):

  • jit/JIT.cpp:

(JSC::JIT::privateCompileMainPass):

  • jit/JIT.h:
  • jit/JITOpcodes.cpp:

(JSC::JIT::emit_op_jundefined_or_null): Added.
(JSC::JIT::emit_op_jnundefined_or_null): Added.

  • jit/JITOpcodes32_64.cpp:

(JSC::JIT::emit_op_jundefined_or_null): Added.
(JSC::JIT::emit_op_jnundefined_or_null): Added.

  • llint/LowLevelInterpreter32_64.asm:
  • llint/LowLevelInterpreter64.asm:
Location:
trunk
Files:
17 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r248271 r248426  
     12019-08-08  Ross Kirsling  <ross.kirsling@sony.com>
     2
     3        [JSC] Add "jump if (not) undefined or null" bytecode ops
     4        https://bugs.webkit.org/show_bug.cgi?id=200480
     5
     6        Reviewed by Saam Barati.
     7
     8        * stress/destructuring-assignment-require-object-coercible.js:
     9        * stress/nullish-coalescing.js:
     10
    1112019-08-05  Michael Saboff  <msaboff@apple.com>
    212
  • trunk/JSTests/stress/destructuring-assignment-require-object-coercible.js

    r192899 r248426  
    6767testOK(`({ a: { b } = /1/ } = { })`);
    6868testOK(`({ a: { b } } = { a: /1/ })`);
     69
     70testOK(`({ } = makeMasquerader())`);
     71testOK(`({ a } = makeMasquerader())`);
     72testOK(`({ a: { b } = makeMasquerader() } = { })`);
     73testOK(`({ a: { b } } = { a: makeMasquerader() })`);
  • trunk/JSTests/stress/nullish-coalescing.js

    r247819 r248426  
    2222}
    2323
    24 shouldBe(undefined ?? 3, 3);
    25 shouldBe(null ?? 3, 3);
    26 shouldBe(true ?? 3, true);
    27 shouldBe(false ?? 3, false);
    28 shouldBe(0 ?? 3, 0);
    29 shouldBe(1 ?? 3, 1);
    30 shouldBe('' ?? 3, '');
    31 shouldBe('hi' ?? 3, 'hi');
    32 shouldBe(({} ?? 3) instanceof Object, true);
    33 shouldBe(({ x: 'hi' } ?? 3).x, 'hi');
    34 shouldBe(([] ?? 3) instanceof Array, true);
    35 shouldBe((['hi'] ?? 3)[0], 'hi');
    36 shouldBe((makeMasquerader() ?? 3) == null, true);
     24function testBasicCases() {
     25    shouldBe(undefined ?? 3, 3);
     26    shouldBe(null ?? 3, 3);
     27    shouldBe(true ?? 3, true);
     28    shouldBe(false ?? 3, false);
     29    shouldBe(0 ?? 3, 0);
     30    shouldBe(1 ?? 3, 1);
     31    shouldBe('' ?? 3, '');
     32    shouldBe('hi' ?? 3, 'hi');
     33    shouldBe(({} ?? 3) instanceof Object, true);
     34    shouldBe(({ x: 'hi' } ?? 3).x, 'hi');
     35    shouldBe(([] ?? 3) instanceof Array, true);
     36    shouldBe((['hi'] ?? 3)[0], 'hi');
     37    shouldBe((makeMasquerader() ?? 3) == null, true);
     38}
     39noInline(testBasicCases);
     40
     41for (let i = 0; i < 1e5; i++)
     42    testBasicCases();
    3743
    3844shouldBe(1 | null ?? 3, 1);
  • trunk/Source/JavaScriptCore/ChangeLog

    r248374 r248426  
     12019-08-08  Ross Kirsling  <ross.kirsling@sony.com>
     2
     3        [JSC] Add "jump if (not) undefined or null" bytecode ops
     4        https://bugs.webkit.org/show_bug.cgi?id=200480
     5
     6        Reviewed by Saam Barati.
     7
     8        This patch introduces fused jumps for op_is_undefined_or_null, which ignores "masquerade as undefined" behavior.
     9
     10        This lets us fix a edge-case bug in RequireObjectCoercible (where `({ length } = document.all)` was a TypeError)
     11        and moreover provides a very useful optimization for the new ?. and ?? operators, which have semantics centered
     12        around op_jundefined_or_null and op_jnundefined_or_null, respectively.
     13
     14        * bytecode/BytecodeList.rb:
     15        * bytecode/BytecodeUseDef.h:
     16        (JSC::computeUsesForBytecodeOffset):
     17        (JSC::computeDefsForBytecodeOffset):
     18        * bytecode/Opcode.h:
     19        (JSC::isBranch):
     20        * bytecode/PreciseJumpTargetsInlines.h:
     21        * bytecompiler/BytecodeGenerator.cpp:
     22        (JSC::Label::setLocation):
     23        (JSC::BytecodeGenerator::emitJumpIfTrue):
     24        (JSC::BytecodeGenerator::emitJumpIfFalse):
     25        (JSC::BytecodeGenerator::emitRequireObjectCoercible):
     26        * dfg/DFGByteCodeParser.cpp:
     27        (JSC::DFG::ByteCodeParser::parseBlock):
     28        * dfg/DFGCapabilities.cpp:
     29        (JSC::DFG::capabilityLevel):
     30        * jit/JIT.cpp:
     31        (JSC::JIT::privateCompileMainPass):
     32        * jit/JIT.h:
     33        * jit/JITOpcodes.cpp:
     34        (JSC::JIT::emit_op_jundefined_or_null): Added.
     35        (JSC::JIT::emit_op_jnundefined_or_null): Added.
     36        * jit/JITOpcodes32_64.cpp:
     37        (JSC::JIT::emit_op_jundefined_or_null): Added.
     38        (JSC::JIT::emit_op_jnundefined_or_null): Added.
     39        * llint/LowLevelInterpreter32_64.asm:
     40        * llint/LowLevelInterpreter64.asm:
     41
    1422019-08-07  Devin Rousso  <drousso@apple.com>
    243
  • trunk/Source/JavaScriptCore/bytecode/BytecodeList.rb

    r248027 r248426  
    621621
    622622op :jneq_null,
     623    args: {
     624        value: VirtualRegister,
     625        targetLabel: BoundLabel,
     626    }
     627
     628op :jundefined_or_null,
     629    args: {
     630        value: VirtualRegister,
     631        targetLabel: BoundLabel,
     632    }
     633
     634op :jnundefined_or_null,
    623635    args: {
    624636        value: VirtualRegister,
  • trunk/Source/JavaScriptCore/bytecode/BytecodeUseDef.h

    r245906 r248426  
    108108    USES(OpJeqNull, value)
    109109    USES(OpJneqNull, value)
     110    USES(OpJundefinedOrNull, value)
     111    USES(OpJnundefinedOrNull, value)
    110112    USES(OpDec, srcDst)
    111113    USES(OpInc, srcDst)
     
    308310    case op_jeq_null:
    309311    case op_jneq_null:
     312    case op_jundefined_or_null:
     313    case op_jnundefined_or_null:
    310314    case op_jneq_ptr:
    311315    case op_jless:
  • trunk/Source/JavaScriptCore/bytecode/Opcode.h

    r247387 r248426  
    174174    case op_jeq_null:
    175175    case op_jneq_null:
     176    case op_jundefined_or_null:
     177    case op_jnundefined_or_null:
    176178    case op_jneq_ptr:
    177179    case op_jless:
  • trunk/Source/JavaScriptCore/bytecode/PreciseJumpTargetsInlines.h

    r240138 r248426  
    4141    CASE_OP(OpJeqNull) \
    4242    CASE_OP(OpJneqNull) \
     43    CASE_OP(OpJundefinedOrNull) \
     44    CASE_OP(OpJnundefinedOrNull) \
    4345    CASE_OP(OpJneqPtr) \
    4446    \
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp

    r246490 r248426  
    116116        CASE(OpJeqNull)
    117117        CASE(OpJneqNull)
     118        CASE(OpJundefinedOrNull)
     119        CASE(OpJnundefinedOrNull)
    118120        CASE(OpJeq)
    119121        CASE(OpJstricteq)
     
    14821484            if (fuseTestAndJmp<OpNeqNull, OpJneqNull>(cond, target))
    14831485                return;
     1486        } else if (m_lastOpcodeID == op_is_undefined_or_null && target.isForward()) {
     1487            if (fuseTestAndJmp<OpIsUndefinedOrNull, OpJundefinedOrNull>(cond, target))
     1488                return;
    14841489        }
    14851490    }
     
    15291534        } else if (m_lastOpcodeID == op_neq_null && target.isForward()) {
    15301535            if (fuseTestAndJmp<OpNeqNull, OpJeqNull>(cond, target))
     1536                return;
     1537        } else if (m_lastOpcodeID == op_is_undefined_or_null && target.isForward()) {
     1538            if (fuseTestAndJmp<OpIsUndefinedOrNull, OpJnundefinedOrNull>(cond, target))
    15311539                return;
    15321540        }
     
    44584466void BytecodeGenerator::emitRequireObjectCoercible(RegisterID* value, const String& error)
    44594467{
    4460     // FIXME: op_jneq_null treats "undetectable" objects as null/undefined. RequireObjectCoercible
    4461     // thus incorrectly throws a TypeError for interfaces like HTMLAllCollection.
    44624468    Ref<Label> target = newLabel();
    4463     OpJneqNull::emit(this, value, target->bind(this));
     4469    OpJnundefinedOrNull::emit(this, value, target->bind(this));
    44644470    emitThrowTypeError(error);
    44654471    emitLabel(target.get());
  • trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp

    r247387 r248426  
    57345734            addToGraph(Branch, OpInfo(branchData(m_currentIndex + currentInstruction->size(), m_currentIndex + relativeOffset)), condition);
    57355735            LAST_OPCODE(op_jneq_null);
     5736        }
     5737
     5738        case op_jundefined_or_null: {
     5739            auto bytecode = currentInstruction->as<OpJundefinedOrNull>();
     5740            unsigned relativeOffset = jumpTarget(bytecode.m_targetLabel);
     5741            Node* value = get(bytecode.m_value);
     5742            Node* condition = addToGraph(IsUndefinedOrNull, value);
     5743            addToGraph(Branch, OpInfo(branchData(m_currentIndex + relativeOffset, m_currentIndex + currentInstruction->size())), condition);
     5744            LAST_OPCODE(op_jundefined_or_null);
     5745        }
     5746
     5747        case op_jnundefined_or_null: {
     5748            auto bytecode = currentInstruction->as<OpJnundefinedOrNull>();
     5749            unsigned relativeOffset = jumpTarget(bytecode.m_targetLabel);
     5750            Node* value = get(bytecode.m_value);
     5751            Node* condition = addToGraph(IsUndefinedOrNull, value);
     5752            addToGraph(Branch, OpInfo(branchData(m_currentIndex + currentInstruction->size(), m_currentIndex + relativeOffset)), condition);
     5753            LAST_OPCODE(op_jnundefined_or_null);
    57365754        }
    57375755
  • trunk/Source/JavaScriptCore/dfg/DFGCapabilities.cpp

    r245906 r248426  
    189189    case op_jeq_null:
    190190    case op_jneq_null:
     191    case op_jundefined_or_null:
     192    case op_jnundefined_or_null:
    191193    case op_jless:
    192194    case op_jlesseq:
  • trunk/Source/JavaScriptCore/jit/JIT.cpp

    r244865 r248426  
    365365        DEFINE_OP(op_jmp)
    366366        DEFINE_OP(op_jneq_null)
     367        DEFINE_OP(op_jundefined_or_null)
     368        DEFINE_OP(op_jnundefined_or_null)
    367369        DEFINE_OP(op_jneq_ptr)
    368370        DEFINE_OP(op_jless)
  • trunk/Source/JavaScriptCore/jit/JIT.h

    r240893 r248426  
    555555        void emit_op_jmp(const Instruction*);
    556556        void emit_op_jneq_null(const Instruction*);
     557        void emit_op_jundefined_or_null(const Instruction*);
     558        void emit_op_jnundefined_or_null(const Instruction*);
    557559        void emit_op_jneq_ptr(const Instruction*);
    558560        void emit_op_jless(const Instruction*);
  • trunk/Source/JavaScriptCore/jit/JITOpcodes.cpp

    r245658 r248426  
    443443}
    444444
     445void JIT::emit_op_jundefined_or_null(const Instruction* currentInstruction)
     446{
     447    auto bytecode = currentInstruction->as<OpJundefinedOrNull>();
     448    int value = bytecode.m_value.offset();
     449    unsigned target = jumpTarget(currentInstruction, bytecode.m_targetLabel);
     450
     451    emitGetVirtualRegister(value, regT0);
     452
     453    and64(TrustedImm32(~TagBitUndefined), regT0);
     454    addJump(branch64(Equal, regT0, TrustedImm64(JSValue::encode(jsNull()))), target);
     455}
     456
     457void JIT::emit_op_jnundefined_or_null(const Instruction* currentInstruction)
     458{
     459    auto bytecode = currentInstruction->as<OpJnundefinedOrNull>();
     460    int value = bytecode.m_value.offset();
     461    unsigned target = jumpTarget(currentInstruction, bytecode.m_targetLabel);
     462
     463    emitGetVirtualRegister(value, regT0);
     464
     465    and64(TrustedImm32(~TagBitUndefined), regT0);
     466    addJump(branch64(NotEqual, regT0, TrustedImm64(JSValue::encode(jsNull()))), target);
     467}
     468
    445469void JIT::emit_op_jneq_ptr(const Instruction* currentInstruction)
    446470{
  • trunk/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp

    r245658 r248426  
    450450}
    451451
     452void JIT::emit_op_jundefined_or_null(const Instruction* currentInstruction)
     453{
     454    auto bytecode = currentInstruction->as<OpJundefinedOrNull>();
     455    int value = bytecode.m_value.offset();
     456    unsigned target = jumpTarget(currentInstruction, bytecode.m_targetLabel);
     457
     458    emitLoadTag(value, regT0);
     459    static_assert((JSValue::UndefinedTag + 1 == JSValue::NullTag) && (JSValue::NullTag & 0x1), "");
     460    or32(TrustedImm32(1), regT0);
     461    addJump(branchIfNull(regT0), target);
     462}
     463
     464void JIT::emit_op_jnundefined_or_null(const Instruction* currentInstruction)
     465{
     466    auto bytecode = currentInstruction->as<OpJnundefinedOrNull>();
     467    int value = bytecode.m_value.offset();
     468    unsigned target = jumpTarget(currentInstruction, bytecode.m_targetLabel);
     469
     470    emitLoadTag(value, regT0);
     471    static_assert((JSValue::UndefinedTag + 1 == JSValue::NullTag) && (JSValue::NullTag & 0x1), "");
     472    or32(TrustedImm32(1), regT0);
     473    addJump(branchIfNotNull(regT0), target);
     474}
     475
    452476void JIT::emit_op_jneq_ptr(const Instruction* currentInstruction)
    453477{
  • trunk/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm

    r247387 r248426  
    16771677    macro (value, target) bineq value, NullTag, target end)
    16781678
     1679macro undefinedOrNullJumpOp(opcodeName, opcodeStruct, fn)
     1680    llintOpWithJump(op_%opcodeName%, opcodeStruct, macro (size, get, jump, dispatch)
     1681        get(m_value, t1)
     1682        loadConstantOrVariableTag(size, t1, t0)
     1683        ori 1, t0
     1684        fn(t0, .target)
     1685        dispatch()
     1686
     1687    .target:
     1688        jump(m_targetLabel)
     1689    end)
     1690end
     1691
     1692undefinedOrNullJumpOp(jundefined_or_null, OpJundefinedOrNull,
     1693    macro (value, target) bieq value, NullTag, target end)
     1694
     1695undefinedOrNullJumpOp(jnundefined_or_null, OpJnundefinedOrNull,
     1696    macro (value, target) bineq value, NullTag, target end)
    16791697
    16801698llintOpWithMetadata(op_jneq_ptr, OpJneqPtr, macro (size, get, dispatch, metadata, return)
  • trunk/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm

    r247387 r248426  
    17701770    macro (value, target) bqneq value, ValueNull, target end)
    17711771
     1772macro undefinedOrNullJumpOp(opcodeName, opcodeStruct, fn)
     1773    llintOpWithJump(op_%opcodeName%, opcodeStruct, macro (size, get, jump, dispatch)
     1774        get(m_value, t1)
     1775        loadConstantOrVariable(size, t1, t0)
     1776        andq ~TagBitUndefined, t0
     1777        fn(t0, .target)
     1778        dispatch()
     1779
     1780    .target:
     1781        jump(m_targetLabel)
     1782    end)
     1783end
     1784
     1785undefinedOrNullJumpOp(jundefined_or_null, OpJundefinedOrNull,
     1786    macro (value, target) bqeq value, ValueNull, target end)
     1787
     1788undefinedOrNullJumpOp(jnundefined_or_null, OpJnundefinedOrNull,
     1789    macro (value, target) bqneq value, ValueNull, target end)
    17721790
    17731791llintOpWithMetadata(op_jneq_ptr, OpJneqPtr, macro (size, get, dispatch, metadata, return)
Note: See TracChangeset for help on using the changeset viewer.