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/bytecode/BytecodeDumper.cpp

    r222689 r223318  
    361361
    362362template<class Block>
     363void BytecodeDumper<Block>::printCompareJump(PrintStream& out, const typename Block::Instruction*, const typename Block::Instruction*& it, int location, const char* op)
     364{
     365    int r0 = (++it)->u.operand;
     366    int r1 = (++it)->u.operand;
     367    int offset = (++it)->u.operand;
     368    printLocationAndOp(out, location, it, op);
     369    out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
     370}
     371
     372template<class Block>
    363373void BytecodeDumper<Block>::printGetByIdOp(PrintStream& out, int location, const typename Block::Instruction*& it)
    364374{
     
    835845        break;
    836846    }
     847    case op_below: {
     848        printBinaryOp(out, location, it, "below");
     849        break;
     850    }
     851    case op_beloweq: {
     852        printBinaryOp(out, location, it, "beloweq");
     853        break;
     854    }
    837855    case op_inc: {
    838856        int r0 = (++it)->u.operand;
     
    11981216    }
    11991217    case op_jless: {
    1200         int r0 = (++it)->u.operand;
    1201         int r1 = (++it)->u.operand;
    1202         int offset = (++it)->u.operand;
    1203         printLocationAndOp(out, location, it, "jless");
    1204         out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
     1218        printCompareJump(out, begin, it, location, "jless");
    12051219        break;
    12061220    }
    12071221    case op_jlesseq: {
    1208         int r0 = (++it)->u.operand;
    1209         int r1 = (++it)->u.operand;
    1210         int offset = (++it)->u.operand;
    1211         printLocationAndOp(out, location, it, "jlesseq");
    1212         out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
     1222        printCompareJump(out, begin, it, location, "jlesseq");
    12131223        break;
    12141224    }
    12151225    case op_jgreater: {
    1216         int r0 = (++it)->u.operand;
    1217         int r1 = (++it)->u.operand;
    1218         int offset = (++it)->u.operand;
    1219         printLocationAndOp(out, location, it, "jgreater");
    1220         out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
     1226        printCompareJump(out, begin, it, location, "jgreater");
    12211227        break;
    12221228    }
    12231229    case op_jgreatereq: {
    1224         int r0 = (++it)->u.operand;
    1225         int r1 = (++it)->u.operand;
    1226         int offset = (++it)->u.operand;
    1227         printLocationAndOp(out, location, it, "jgreatereq");
    1228         out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
     1230        printCompareJump(out, begin, it, location, "jgreatereq");
    12291231        break;
    12301232    }
    12311233    case op_jnless: {
    1232         int r0 = (++it)->u.operand;
    1233         int r1 = (++it)->u.operand;
    1234         int offset = (++it)->u.operand;
    1235         printLocationAndOp(out, location, it, "jnless");
    1236         out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
     1234        printCompareJump(out, begin, it, location, "jnless");
    12371235        break;
    12381236    }
    12391237    case op_jnlesseq: {
    1240         int r0 = (++it)->u.operand;
    1241         int r1 = (++it)->u.operand;
    1242         int offset = (++it)->u.operand;
    1243         printLocationAndOp(out, location, it, "jnlesseq");
    1244         out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
     1238        printCompareJump(out, begin, it, location, "jnlesseq");
    12451239        break;
    12461240    }
    12471241    case op_jngreater: {
    1248         int r0 = (++it)->u.operand;
    1249         int r1 = (++it)->u.operand;
    1250         int offset = (++it)->u.operand;
    1251         printLocationAndOp(out, location, it, "jngreater");
    1252         out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
     1242        printCompareJump(out, begin, it, location, "jngreater");
    12531243        break;
    12541244    }
    12551245    case op_jngreatereq: {
    1256         int r0 = (++it)->u.operand;
    1257         int r1 = (++it)->u.operand;
    1258         int offset = (++it)->u.operand;
    1259         printLocationAndOp(out, location, it, "jngreatereq");
    1260         out.printf("%s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), offset, location + offset);
     1246        printCompareJump(out, begin, it, location, "jngreatereq");
     1247        break;
     1248    }
     1249    case op_jbelow: {
     1250        printCompareJump(out, begin, it, location, "jbelow");
     1251        break;
     1252    }
     1253    case op_jbeloweq: {
     1254        printCompareJump(out, begin, it, location, "jbeloweq");
    12611255        break;
    12621256    }
Note: See TracChangeset for help on using the changeset viewer.