Ignore:
Timestamp:
Oct 14, 2017, 8:35:48 AM (8 years ago)
Author:
Yusuke Suzuki
Message:

Reland "Add Above/Below comparisons for UInt32 patterns"
https://bugs.webkit.org/show_bug.cgi?id=177281

Reviewed by Saam Barati.

JSTests:

  • stress/uint32-comparison-jump.js: Added.

(shouldBe):
(above):
(aboveOrEqual):
(below):
(belowOrEqual):
(notAbove):
(notAboveOrEqual):
(notBelow):
(notBelowOrEqual):

  • stress/uint32-comparison.js: Added.

(shouldBe):
(above):
(aboveOrEqual):
(below):
(belowOrEqual):
(aboveTest):
(aboveOrEqualTest):
(belowTest):
(belowOrEqualTest):

Source/JavaScriptCore:

We reland this patch without DFGStrengthReduction change to see what causes
regression in the iOS bot.

Sometimes, we would like to have UInt32 operations in JS. While VM does
not support UInt32 nicely, VM supports efficient Int32 operations. As long
as signedness does not matter, we can just perform Int32 operations instead
and recognize its bit pattern as UInt32.

But of course, some operations respect signedness. The most frequently
used one is comparison. Octane/zlib performs UInt32 comparison by performing
val >>> 0. It emits op_urshift and op_unsigned. op_urshift produces
UInt32 in Int32 form. And op_unsigned will generate Double value if
the generated Int32 is < 0 (which should be UInt32).

There is a chance for optimization. The given code pattern is the following.

op_unsigned(op_urshift(@1)) lessThan:< op_unsigned(op_urshift(@2))

This can be converted to the following.

op_urshift(@1) below:< op_urshift(@2)

The above conversion is nice since

  1. We can avoid op_unsigned. This could be unsignedness check in DFG. Since

this check depends on the value of Int32, dropping this check is not as easy as
removing Int32 edge filters.

  1. We can perform unsigned comparison in Int32 form. We do not need to convert

them to DoubleRep.

Since the above comparison exists in Octane/zlib's *super* hot path, dropping
op_unsigned offers huge win.

At first, my patch attempts to convert the above thing in DFG pipeline.
However it poses several problems.

  1. MovHint is not well removed. It makes UInt32ToNumber (which is for op_unsigned) live.
  2. UInt32ToNumber could cause an OSR exit. So if we have the following nodes,

2: UInt32ToNumber(@0)
3: MovHint(@2, xxx)
4: UInt32ToNumber(@1)
5: MovHint(@1, xxx)

we could drop @5's MovHint. But @3 is difficult since @4 can exit.

So, instead, we start introducing a simple optimization in the bytecode compiler.
It performs pattern matching for op_urshift and comparison to drop op_unsigned.
We adds op_below and op_above families to bytecodes. They only accept Int32 and
perform unsigned comparison.

This offers 4% performance improvement in Octane/zlib.

baseline patched

zlib x2 431.07483+-16.28434 414.33407+-9.38375 might be 1.0404x faster

  • bytecode/BytecodeDumper.cpp:

(JSC::BytecodeDumper<Block>::printCompareJump):
(JSC::BytecodeDumper<Block>::dumpBytecode):

  • bytecode/BytecodeDumper.h:
  • bytecode/BytecodeList.json:
  • bytecode/BytecodeUseDef.h:

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

  • bytecode/Opcode.h:

(JSC::isBranch):

  • bytecode/PreciseJumpTargetsInlines.h:

(JSC::extractStoredJumpTargetsForBytecodeOffset):

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::emitJumpIfTrue):
(JSC::BytecodeGenerator::emitJumpIfFalse):

  • bytecompiler/NodesCodegen.cpp:

(JSC::BinaryOpNode::emitBytecode):

  • 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/DFGIntegerRangeOptimizationPhase.cpp:
  • dfg/DFGNodeType.h:
  • dfg/DFGPredictionPropagationPhase.cpp:
  • dfg/DFGSafeToExecute.h:

(JSC::DFG::safeToExecute):

  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::compileCompareUnsigned):

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

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGValidate.cpp:
  • ftl/FTLCapabilities.cpp:

(JSC::FTL::canCompile):

  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileCompareBelow):
(JSC::FTL::DFG::LowerDFGToB3::compileCompareBelowEq):

  • jit/JIT.cpp:

(JSC::JIT::privateCompileMainPass):

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

(JSC::JIT::emit_op_below):
(JSC::JIT::emit_op_beloweq):
(JSC::JIT::emit_op_jbelow):
(JSC::JIT::emit_op_jbeloweq):
(JSC::JIT::emit_compareUnsignedAndJump):
(JSC::JIT::emit_compareUnsigned):

  • jit/JITArithmetic32_64.cpp:

(JSC::JIT::emit_compareUnsignedAndJump):
(JSC::JIT::emit_compareUnsigned):

  • llint/LowLevelInterpreter.asm:
  • llint/LowLevelInterpreter32_64.asm:
  • llint/LowLevelInterpreter64.asm:
  • parser/Nodes.h:

(JSC::ExpressionNode::isBinaryOpNode const):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/jit/JITArithmetic.cpp

    r222689 r223318  
    196196
    197197    emit_compareAndJumpSlow(op1, op2, target, DoubleLessThanOrUnordered, operationCompareGreaterEq, true, iter);
     198}
     199
     200void JIT::emit_op_below(Instruction* currentInstruction)
     201{
     202    int dst = currentInstruction[1].u.operand;
     203    int op1 = currentInstruction[2].u.operand;
     204    int op2 = currentInstruction[3].u.operand;
     205    emit_compareUnsigned(dst, op1, op2, Below);
     206}
     207
     208void JIT::emit_op_beloweq(Instruction* currentInstruction)
     209{
     210    int dst = currentInstruction[1].u.operand;
     211    int op1 = currentInstruction[2].u.operand;
     212    int op2 = currentInstruction[3].u.operand;
     213    emit_compareUnsigned(dst, op1, op2, BelowOrEqual);
     214}
     215
     216void JIT::emit_op_jbelow(Instruction* currentInstruction)
     217{
     218    int op1 = currentInstruction[1].u.operand;
     219    int op2 = currentInstruction[2].u.operand;
     220    unsigned target = currentInstruction[3].u.operand;
     221
     222    emit_compareUnsignedAndJump(op1, op2, target, Below);
     223}
     224
     225void JIT::emit_op_jbeloweq(Instruction* currentInstruction)
     226{
     227    int op1 = currentInstruction[1].u.operand;
     228    int op2 = currentInstruction[2].u.operand;
     229    unsigned target = currentInstruction[3].u.operand;
     230
     231    emit_compareUnsignedAndJump(op1, op2, target, BelowOrEqual);
    198232}
    199233
     
    265299}
    266300
     301void JIT::emit_compareUnsignedAndJump(int op1, int op2, unsigned target, RelationalCondition condition)
     302{
     303    if (isOperandConstantInt(op2)) {
     304        emitGetVirtualRegister(op1, regT0);
     305        int32_t op2imm = getOperandConstantInt(op2);
     306        addJump(branch32(condition, regT0, Imm32(op2imm)), target);
     307    } else if (isOperandConstantInt(op1)) {
     308        emitGetVirtualRegister(op2, regT1);
     309        int32_t op1imm = getOperandConstantInt(op1);
     310        addJump(branch32(commute(condition), regT1, Imm32(op1imm)), target);
     311    } else {
     312        emitGetVirtualRegisters(op1, regT0, op2, regT1);
     313        addJump(branch32(condition, regT0, regT1), target);
     314    }
     315}
     316
     317void JIT::emit_compareUnsigned(int dst, int op1, int op2, RelationalCondition condition)
     318{
     319    if (isOperandConstantInt(op2)) {
     320        emitGetVirtualRegister(op1, regT0);
     321        int32_t op2imm = getOperandConstantInt(op2);
     322        compare32(condition, regT0, Imm32(op2imm), regT0);
     323    } else if (isOperandConstantInt(op1)) {
     324        emitGetVirtualRegister(op2, regT0);
     325        int32_t op1imm = getOperandConstantInt(op1);
     326        compare32(commute(condition), regT0, Imm32(op1imm), regT0);
     327    } else {
     328        emitGetVirtualRegisters(op1, regT0, op2, regT1);
     329        compare32(condition, regT0, regT1, regT0);
     330    }
     331    emitTagBool(regT0);
     332    emitPutVirtualRegister(dst);
     333}
     334
    267335void JIT::emit_compareAndJumpSlow(int op1, int op2, unsigned target, DoubleCondition condition, size_t (JIT_OPERATION *operation)(ExecState*, EncodedJSValue, EncodedJSValue), bool invert, Vector<SlowCaseEntry>::iterator& iter)
    268336{
Note: See TracChangeset for help on using the changeset viewer.