Title: [203488] trunk/Source/_javascript_Core
Revision
203488
Author
fpi...@apple.com
Date
2016-07-20 22:24:54 -0700 (Wed, 20 Jul 2016)

Log Message

FTL snippet generators should be able to request a different register for output and input
https://bugs.webkit.org/show_bug.cgi?id=160010
rdar://problem/27439330

Reviewed by Saam Barati.
        
The BitOr and BitXor snippet generators have problems if the register for the right input is
the same as the register for the result. We could fix those generators, but I'm not convinced
that the other snippet generators don't have this bug. So, the approach that this patch takes
is to teach the FTL to request that B3 to use a different register for the result than for
any input to the snippet patchpoint.
        
Air already has the ability to let any instruction do an EarlyDef, which means exactly this.
But B3 did not expose this via ValueRep. This patch exposes this in ValueRep as
SomeEarlyRegister. That's most of the change.
        
This adds a testb3 test for SomeEarlyRegister and a regression test for this particular
problem. The regression test failed on trunk JSC before this.

* b3/B3LowerToAir.cpp:
(JSC::B3::Air::LowerToAir::lower):
* b3/B3PatchpointSpecial.cpp:
(JSC::B3::PatchpointSpecial::forEachArg):
(JSC::B3::PatchpointSpecial::admitsStack):
* b3/B3StackmapSpecial.cpp:
(JSC::B3::StackmapSpecial::forEachArgImpl):
(JSC::B3::StackmapSpecial::isArgValidForRep):
* b3/B3Validate.cpp:
* b3/B3ValueRep.cpp:
(JSC::B3::ValueRep::addUsedRegistersTo):
(JSC::B3::ValueRep::dump):
(WTF::printInternal):
* b3/B3ValueRep.h:
(JSC::B3::ValueRep::ValueRep):
(JSC::B3::ValueRep::reg):
(JSC::B3::ValueRep::isAny):
(JSC::B3::ValueRep::isReg):
(JSC::B3::ValueRep::isSomeRegister): Deleted.
* b3/testb3.cpp:
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::emitBinarySnippet):
(JSC::FTL::DFG::LowerDFGToB3::emitBinaryBitOpSnippet):
(JSC::FTL::DFG::LowerDFGToB3::emitRightShiftSnippet):
* tests/stress/ftl-bit-xor-right-result-interference.js: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (203487 => 203488)


--- trunk/Source/_javascript_Core/ChangeLog	2016-07-21 03:30:48 UTC (rev 203487)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-07-21 05:24:54 UTC (rev 203488)
@@ -1,3 +1,50 @@
+2016-07-20  Filip Pizlo  <fpi...@apple.com>
+
+        FTL snippet generators should be able to request a different register for output and input
+        https://bugs.webkit.org/show_bug.cgi?id=160010
+        rdar://problem/27439330
+
+        Reviewed by Saam Barati.
+        
+        The BitOr and BitXor snippet generators have problems if the register for the right input is
+        the same as the register for the result. We could fix those generators, but I'm not convinced
+        that the other snippet generators don't have this bug. So, the approach that this patch takes
+        is to teach the FTL to request that B3 to use a different register for the result than for
+        any input to the snippet patchpoint.
+        
+        Air already has the ability to let any instruction do an EarlyDef, which means exactly this.
+        But B3 did not expose this via ValueRep. This patch exposes this in ValueRep as
+        SomeEarlyRegister. That's most of the change.
+        
+        This adds a testb3 test for SomeEarlyRegister and a regression test for this particular
+        problem. The regression test failed on trunk JSC before this.
+
+        * b3/B3LowerToAir.cpp:
+        (JSC::B3::Air::LowerToAir::lower):
+        * b3/B3PatchpointSpecial.cpp:
+        (JSC::B3::PatchpointSpecial::forEachArg):
+        (JSC::B3::PatchpointSpecial::admitsStack):
+        * b3/B3StackmapSpecial.cpp:
+        (JSC::B3::StackmapSpecial::forEachArgImpl):
+        (JSC::B3::StackmapSpecial::isArgValidForRep):
+        * b3/B3Validate.cpp:
+        * b3/B3ValueRep.cpp:
+        (JSC::B3::ValueRep::addUsedRegistersTo):
+        (JSC::B3::ValueRep::dump):
+        (WTF::printInternal):
+        * b3/B3ValueRep.h:
+        (JSC::B3::ValueRep::ValueRep):
+        (JSC::B3::ValueRep::reg):
+        (JSC::B3::ValueRep::isAny):
+        (JSC::B3::ValueRep::isReg):
+        (JSC::B3::ValueRep::isSomeRegister): Deleted.
+        * b3/testb3.cpp:
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::emitBinarySnippet):
+        (JSC::FTL::DFG::LowerDFGToB3::emitBinaryBitOpSnippet):
+        (JSC::FTL::DFG::LowerDFGToB3::emitRightShiftSnippet):
+        * tests/stress/ftl-bit-xor-right-result-interference.js: Added.
+
 2016-07-20  Michael Saboff  <msab...@apple.com>
 
         CrashOnOverflow in JSC::Yarr::YarrPatternConstructor::setupAlternativeOffsets

Modified: trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp (203487 => 203488)


--- trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp	2016-07-21 03:30:48 UTC (rev 203487)
+++ trunk/Source/_javascript_Core/b3/B3LowerToAir.cpp	2016-07-21 05:24:54 UTC (rev 203488)
@@ -2193,6 +2193,7 @@
                 case ValueRep::ColdAny:
                 case ValueRep::LateColdAny:
                 case ValueRep::SomeRegister:
+                case ValueRep::SomeEarlyRegister:
                     inst.args.append(tmp(patchpointValue));
                     break;
                 case ValueRep::Register: {

Modified: trunk/Source/_javascript_Core/b3/B3PatchpointSpecial.cpp (203487 => 203488)


--- trunk/Source/_javascript_Core/b3/B3PatchpointSpecial.cpp	2016-07-21 03:30:48 UTC (rev 203487)
+++ trunk/Source/_javascript_Core/b3/B3PatchpointSpecial.cpp	2016-07-21 05:24:54 UTC (rev 203488)
@@ -46,17 +46,25 @@
 
 void PatchpointSpecial::forEachArg(Inst& inst, const ScopedLambda<Inst::EachArgCallback>& callback)
 {
+    PatchpointValue* patchpoint = inst.origin->as<PatchpointValue>();
     unsigned argIndex = 1;
 
-    if (inst.origin->type() != Void)
-        callback(inst.args[argIndex++], Arg::Def, inst.origin->airType(), inst.origin->airWidth());
+    if (patchpoint->type() != Void) {
+        Arg::Role role;
+        if (patchpoint->resultConstraint.kind() == ValueRep::SomeEarlyRegister)
+            role = Arg::EarlyDef;
+        else
+            role = Arg::Def;
+        
+        callback(inst.args[argIndex++], role, inst.origin->airType(), inst.origin->airWidth());
+    }
 
     forEachArgImpl(0, argIndex, inst, SameAsRep, Nullopt, callback);
     argIndex += inst.origin->numChildren();
 
-    for (unsigned i = inst.origin->as<PatchpointValue>()->numGPScratchRegisters; i--;)
+    for (unsigned i = patchpoint->numGPScratchRegisters; i--;)
         callback(inst.args[argIndex++], Arg::Scratch, Arg::GP, Arg::conservativeWidth(Arg::GP));
-    for (unsigned i = inst.origin->as<PatchpointValue>()->numFPScratchRegisters; i--;)
+    for (unsigned i = patchpoint->numFPScratchRegisters; i--;)
         callback(inst.args[argIndex++], Arg::Scratch, Arg::FP, Arg::conservativeWidth(Arg::FP));
 }
 
@@ -109,6 +117,7 @@
         case ValueRep::StackArgument:
             return true;
         case ValueRep::SomeRegister:
+        case ValueRep::SomeEarlyRegister:
         case ValueRep::Register:
         case ValueRep::LateRegister:
             return false;

Modified: trunk/Source/_javascript_Core/b3/B3StackmapSpecial.cpp (203487 => 203488)


--- trunk/Source/_javascript_Core/b3/B3StackmapSpecial.cpp	2016-07-21 03:30:48 UTC (rev 203487)
+++ trunk/Source/_javascript_Core/b3/B3StackmapSpecial.cpp	2016-07-21 05:24:54 UTC (rev 203488)
@@ -116,6 +116,9 @@
             case ValueRep::LateColdAny:
                 role = Arg::LateColdUse;
                 break;
+            default:
+                RELEASE_ASSERT_NOT_REACHED();
+                break;
             }
             break;
         case ForceLateUse:
@@ -230,6 +233,7 @@
         // We already verified by isArgValidForValue().
         return true;
     case ValueRep::SomeRegister:
+    case ValueRep::SomeEarlyRegister:
         return arg.isTmp();
     case ValueRep::LateRegister:
     case ValueRep::Register:

Modified: trunk/Source/_javascript_Core/b3/B3Validate.cpp (203487 => 203488)


--- trunk/Source/_javascript_Core/b3/B3Validate.cpp	2016-07-21 03:30:48 UTC (rev 203487)
+++ trunk/Source/_javascript_Core/b3/B3Validate.cpp	2016-07-21 05:24:54 UTC (rev 203488)
@@ -337,6 +337,7 @@
                     switch (value->as<PatchpointValue>()->resultConstraint.kind()) {
                     case ValueRep::WarmAny:
                     case ValueRep::SomeRegister:
+                    case ValueRep::SomeEarlyRegister:
                     case ValueRep::Register:
                     case ValueRep::StackArgument:
                         break;
@@ -345,7 +346,7 @@
                         break;
                     }
                     
-                    validateStackmapConstraint(value, ConstrainedValue(value, value->as<PatchpointValue>()->resultConstraint));
+                    validateStackmapConstraint(value, ConstrainedValue(value, value->as<PatchpointValue>()->resultConstraint), ConstraintRole::Def);
                 }
                 validateStackmap(value);
                 break;
@@ -432,7 +433,11 @@
             validateStackmapConstraint(stackmap, child);
     }
     
-    void validateStackmapConstraint(Value* context, const ConstrainedValue& value)
+    enum class ConstraintRole {
+        Use,
+        Def
+    };
+    void validateStackmapConstraint(Value* context, const ConstrainedValue& value, ConstraintRole role = ConstraintRole::Use)
     {
         switch (value.rep().kind()) {
         case ValueRep::WarmAny:
@@ -441,6 +446,9 @@
         case ValueRep::SomeRegister:
         case ValueRep::StackArgument:
             break;
+        case ValueRep::SomeEarlyRegister:
+            VALIDATE(role == ConstraintRole::Def, ("At ", *context, ": ", value));
+            break;
         case ValueRep::Register:
         case ValueRep::LateRegister:
             if (value.rep().reg().isGPR())

Modified: trunk/Source/_javascript_Core/b3/B3ValueRep.cpp (203487 => 203488)


--- trunk/Source/_javascript_Core/b3/B3ValueRep.cpp	2016-07-21 03:30:48 UTC (rev 203487)
+++ trunk/Source/_javascript_Core/b3/B3ValueRep.cpp	2016-07-21 05:24:54 UTC (rev 203488)
@@ -40,6 +40,7 @@
     case ColdAny:
     case LateColdAny:
     case SomeRegister:
+    case SomeEarlyRegister:
     case Constant:
         return;
     case LateRegister:
@@ -70,6 +71,7 @@
     case ColdAny:
     case LateColdAny:
     case SomeRegister:
+    case SomeEarlyRegister:
         return;
     case LateRegister:
     case Register:
@@ -173,6 +175,9 @@
     case ValueRep::SomeRegister:
         out.print("SomeRegister");
         return;
+    case ValueRep::SomeEarlyRegister:
+        out.print("SomeEarlyRegister");
+        return;
     case ValueRep::Register:
         out.print("Register");
         return;

Modified: trunk/Source/_javascript_Core/b3/B3ValueRep.h (203487 => 203488)


--- trunk/Source/_javascript_Core/b3/B3ValueRep.h	2016-07-21 03:30:48 UTC (rev 203487)
+++ trunk/Source/_javascript_Core/b3/B3ValueRep.h	2016-07-21 05:24:54 UTC (rev 203488)
@@ -67,6 +67,11 @@
         // register that this claims to clobber!
         SomeRegister,
 
+        // As an input representation, this tells us that B3 should pick some register, but implies
+        // that the def happens before any of the effects of the stackmap. This is only valid for
+        // the result constraint of a Patchpoint.
+        SomeEarlyRegister,
+
         // As an input representation, this forces a particular register. As an output
         // representation, this tells us what register B3 picked.
         Register,
@@ -103,7 +108,7 @@
     ValueRep(Kind kind)
         : m_kind(kind)
     {
-        ASSERT(kind == WarmAny || kind == ColdAny || kind == LateColdAny || kind == SomeRegister);
+        ASSERT(kind == WarmAny || kind == ColdAny || kind == LateColdAny || kind == SomeRegister || kind == SomeEarlyRegister);
     }
 
     static ValueRep reg(Reg reg)
@@ -177,8 +182,6 @@
 
     bool isAny() const { return kind() == WarmAny || kind() == ColdAny || kind() == LateColdAny; }
 
-    bool isSomeRegister() const { return kind() == SomeRegister; }
-    
     bool isReg() const { return kind() == Register || kind() == LateRegister; }
     
     Reg reg() const

Modified: trunk/Source/_javascript_Core/b3/testb3.cpp (203487 => 203488)


--- trunk/Source/_javascript_Core/b3/testb3.cpp	2016-07-21 03:30:48 UTC (rev 203487)
+++ trunk/Source/_javascript_Core/b3/testb3.cpp	2016-07-21 05:24:54 UTC (rev 203488)
@@ -12377,6 +12377,49 @@
     validate(proc);
 }
 
+void testSomeEarlyRegister()
+{
+    auto run = [&] (bool succeed) {
+        Procedure proc;
+        
+        BasicBlock* root = proc.addBlock();
+        
+        PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin());
+        patchpoint->resultConstraint = ValueRep::reg(GPRInfo::returnValueGPR);
+        bool ranFirstPatchpoint = false;
+        patchpoint->setGenerator(
+            [&] (CCallHelpers&, const StackmapGenerationParams& params) {
+                CHECK(params[0].gpr() == GPRInfo::returnValueGPR);
+                ranFirstPatchpoint = true;
+            });
+        
+        Value* arg = patchpoint;
+        
+        patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin());
+        patchpoint->appendSomeRegister(arg);
+        if (succeed)
+            patchpoint->resultConstraint = ValueRep::SomeEarlyRegister;
+        bool ranSecondPatchpoint = false;
+        patchpoint->setGenerator(
+            [&] (CCallHelpers&, const StackmapGenerationParams& params) {
+                if (succeed)
+                    CHECK(params[0].gpr() != params[1].gpr());
+                else
+                    CHECK(params[0].gpr() == params[1].gpr());
+                ranSecondPatchpoint = true;
+            });
+        
+        root->appendNew<Value>(proc, Return, Origin(), patchpoint);
+        
+        compile(proc);
+        CHECK(ranFirstPatchpoint);
+        CHECK(ranSecondPatchpoint);
+    };
+    
+    run(true);
+    run(false);
+}
+
 // Make sure the compiler does not try to optimize anything out.
 NEVER_INLINE double zero()
 {
@@ -13783,6 +13826,7 @@
     RUN(testInterpreter());
     RUN(testReduceStrengthCheckBottomUseInAnotherBlock());
     RUN(testResetReachabilityDanglingReference());
+    RUN(testSomeEarlyRegister());
 
     if (tasks.isEmpty())
         usage();

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (203487 => 203488)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2016-07-21 03:30:48 UTC (rev 203487)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2016-07-21 05:24:54 UTC (rev 203488)
@@ -8108,6 +8108,7 @@
         if (scratchFPRUsage == NeedScratchFPR)
             patchpoint->numFPScratchRegisters++;
         patchpoint->clobber(RegisterSet::macroScratchRegisters());
+        patchpoint->resultConstraint = ValueRep::SomeEarlyRegister;
         State* state = &m_ftlState;
         patchpoint->setGenerator(
             [=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
@@ -8170,6 +8171,7 @@
             preparePatchpointForExceptions(patchpoint);
         patchpoint->numGPScratchRegisters = 1;
         patchpoint->clobber(RegisterSet::macroScratchRegisters());
+        patchpoint->resultConstraint = ValueRep::SomeEarlyRegister;
         State* state = &m_ftlState;
         patchpoint->setGenerator(
             [=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
@@ -8225,6 +8227,7 @@
         patchpoint->numGPScratchRegisters = 1;
         patchpoint->numFPScratchRegisters = 1;
         patchpoint->clobber(RegisterSet::macroScratchRegisters());
+        patchpoint->resultConstraint = ValueRep::SomeEarlyRegister;
         State* state = &m_ftlState;
         patchpoint->setGenerator(
             [=] (CCallHelpers& jit, const StackmapGenerationParams& params) {

Added: trunk/Source/_javascript_Core/tests/stress/ftl-bit-xor-right-result-interference.js (0 => 203488)


--- trunk/Source/_javascript_Core/tests/stress/ftl-bit-xor-right-result-interference.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/ftl-bit-xor-right-result-interference.js	2016-07-21 05:24:54 UTC (rev 203488)
@@ -0,0 +1,31 @@
+var toggle = 0;
+
+function bar()
+{
+    if (toggle ^= 1)
+        return 42;
+    else
+        return {valueOf: function() { return 42; }};
+}
+
+noInline(bar);
+
+function baz()
+{
+    return 7;
+}
+
+noInline(baz);
+
+function foo()
+{
+    return bar() ^ baz();
+}
+
+noInline(foo);
+
+for (var i = 0; i < 100000; ++i) {
+    var result = foo();
+    if (result != 45)
+        throw "Error: bad result: " + result;
+}
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to