mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-27 02:29:35 +00:00
287 lines
11 KiB
C++
287 lines
11 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
*/
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "jit/IonAnalysis.h"
|
|
#include "jit/MIRGenerator.h"
|
|
#include "jit/MIRGraph.h"
|
|
#include "jit/RangeAnalysis.h"
|
|
#include "jit/ValueNumbering.h"
|
|
|
|
#include "jsapi-tests/testJitMinimalFunc.h"
|
|
#include "jsapi-tests/tests.h"
|
|
|
|
using namespace js;
|
|
using namespace js::jit;
|
|
|
|
static MBasicBlock*
|
|
FollowTrivialGotos(MBasicBlock* block)
|
|
{
|
|
while (block->phisEmpty() && *block->begin() == block->lastIns() && block->lastIns()->isGoto())
|
|
block = block->lastIns()->toGoto()->getSuccessor(0);
|
|
return block;
|
|
}
|
|
|
|
BEGIN_TEST(testJitGVN_FixupOSROnlyLoop)
|
|
{
|
|
// This is a testcase which constructs the very rare circumstances that
|
|
// require the FixupOSROnlyLoop logic.
|
|
|
|
MinimalFunc func;
|
|
|
|
MBasicBlock* entry = func.createEntryBlock();
|
|
MBasicBlock* osrEntry = func.createOsrEntryBlock();
|
|
MBasicBlock* outerHeader = func.createBlock(entry);
|
|
MBasicBlock* merge = func.createBlock(outerHeader);
|
|
MBasicBlock* innerHeader = func.createBlock(merge);
|
|
MBasicBlock* innerBackedge = func.createBlock(innerHeader);
|
|
MBasicBlock* outerBackedge = func.createBlock(innerHeader);
|
|
MBasicBlock* exit = func.createBlock(outerHeader);
|
|
|
|
MConstant* c = MConstant::New(func.alloc, BooleanValue(false));
|
|
entry->add(c);
|
|
entry->end(MTest::New(func.alloc, c, outerHeader, exit));
|
|
osrEntry->end(MGoto::New(func.alloc, merge));
|
|
|
|
merge->end(MGoto::New(func.alloc, innerHeader));
|
|
|
|
// Use Beta nodes to hide the constants and suppress folding.
|
|
MConstant* x = MConstant::New(func.alloc, BooleanValue(false));
|
|
outerHeader->add(x);
|
|
MBeta* xBeta = MBeta::New(func.alloc, x, Range::NewInt32Range(func.alloc, 0, 1));
|
|
outerHeader->add(xBeta);
|
|
outerHeader->end(MTest::New(func.alloc, xBeta, merge, exit));
|
|
|
|
MConstant* y = MConstant::New(func.alloc, BooleanValue(false));
|
|
innerHeader->add(y);
|
|
MBeta* yBeta = MBeta::New(func.alloc, y, Range::NewInt32Range(func.alloc, 0, 1));
|
|
innerHeader->add(yBeta);
|
|
innerHeader->end(MTest::New(func.alloc, yBeta, innerBackedge, outerBackedge));
|
|
|
|
innerBackedge->end(MGoto::New(func.alloc, innerHeader));
|
|
outerBackedge->end(MGoto::New(func.alloc, outerHeader));
|
|
|
|
MConstant* u = MConstant::New(func.alloc, UndefinedValue());
|
|
exit->add(u);
|
|
exit->end(MReturn::New(func.alloc, u));
|
|
|
|
innerHeader->addPredecessorWithoutPhis(innerBackedge);
|
|
outerHeader->addPredecessorWithoutPhis(outerBackedge);
|
|
exit->addPredecessorWithoutPhis(entry);
|
|
merge->addPredecessorWithoutPhis(osrEntry);
|
|
|
|
outerHeader->setLoopHeader(outerBackedge);
|
|
innerHeader->setLoopHeader(innerBackedge);
|
|
|
|
if (!func.runGVN())
|
|
return false;
|
|
|
|
// The loops are no longer reachable from the normal entry. They are
|
|
// doinated by the osrEntry.
|
|
MOZ_RELEASE_ASSERT(func.graph.osrBlock() == osrEntry);
|
|
MBasicBlock* newInner = FollowTrivialGotos(osrEntry->lastIns()->toGoto()->target());
|
|
MBasicBlock* newOuter = FollowTrivialGotos(newInner->lastIns()->toTest()->ifFalse());
|
|
MBasicBlock* newExit = FollowTrivialGotos(entry);
|
|
MOZ_RELEASE_ASSERT(newInner->isLoopHeader());
|
|
MOZ_RELEASE_ASSERT(newOuter->isLoopHeader());
|
|
MOZ_RELEASE_ASSERT(newExit->lastIns()->isReturn());
|
|
|
|
// One more time.
|
|
ClearDominatorTree(func.graph);
|
|
if (!func.runGVN())
|
|
return false;
|
|
|
|
// The loops are no longer reachable from the normal entry. They are
|
|
// doinated by the osrEntry.
|
|
MOZ_RELEASE_ASSERT(func.graph.osrBlock() == osrEntry);
|
|
newInner = FollowTrivialGotos(osrEntry->lastIns()->toGoto()->target());
|
|
newOuter = FollowTrivialGotos(newInner->lastIns()->toTest()->ifFalse());
|
|
newExit = FollowTrivialGotos(entry);
|
|
MOZ_RELEASE_ASSERT(newInner->isLoopHeader());
|
|
MOZ_RELEASE_ASSERT(newOuter->isLoopHeader());
|
|
MOZ_RELEASE_ASSERT(newExit->lastIns()->isReturn());
|
|
|
|
return true;
|
|
}
|
|
END_TEST(testJitGVN_FixupOSROnlyLoop)
|
|
|
|
BEGIN_TEST(testJitGVN_FixupOSROnlyLoopNested)
|
|
{
|
|
// Same as testJitGVN_FixupOSROnlyLoop but adds another level of loop
|
|
// nesting for added excitement.
|
|
|
|
MinimalFunc func;
|
|
|
|
MBasicBlock* entry = func.createEntryBlock();
|
|
MBasicBlock* osrEntry = func.createOsrEntryBlock();
|
|
MBasicBlock* outerHeader = func.createBlock(entry);
|
|
MBasicBlock* middleHeader = func.createBlock(outerHeader);
|
|
MBasicBlock* merge = func.createBlock(middleHeader);
|
|
MBasicBlock* innerHeader = func.createBlock(merge);
|
|
MBasicBlock* innerBackedge = func.createBlock(innerHeader);
|
|
MBasicBlock* middleBackedge = func.createBlock(innerHeader);
|
|
MBasicBlock* outerBackedge = func.createBlock(middleHeader);
|
|
MBasicBlock* exit = func.createBlock(outerHeader);
|
|
|
|
MConstant* c = MConstant::New(func.alloc, BooleanValue(false));
|
|
entry->add(c);
|
|
entry->end(MTest::New(func.alloc, c, outerHeader, exit));
|
|
osrEntry->end(MGoto::New(func.alloc, merge));
|
|
|
|
merge->end(MGoto::New(func.alloc, innerHeader));
|
|
|
|
// Use Beta nodes to hide the constants and suppress folding.
|
|
MConstant* x = MConstant::New(func.alloc, BooleanValue(false));
|
|
outerHeader->add(x);
|
|
MBeta* xBeta = MBeta::New(func.alloc, x, Range::NewInt32Range(func.alloc, 0, 1));
|
|
outerHeader->add(xBeta);
|
|
outerHeader->end(MTest::New(func.alloc, xBeta, middleHeader, exit));
|
|
|
|
MConstant* y = MConstant::New(func.alloc, BooleanValue(false));
|
|
middleHeader->add(y);
|
|
MBeta* yBeta = MBeta::New(func.alloc, y, Range::NewInt32Range(func.alloc, 0, 1));
|
|
middleHeader->add(yBeta);
|
|
middleHeader->end(MTest::New(func.alloc, yBeta, merge, outerBackedge));
|
|
|
|
MConstant* w = MConstant::New(func.alloc, BooleanValue(false));
|
|
innerHeader->add(w);
|
|
MBeta* wBeta = MBeta::New(func.alloc, w, Range::NewInt32Range(func.alloc, 0, 1));
|
|
innerHeader->add(wBeta);
|
|
innerHeader->end(MTest::New(func.alloc, wBeta, innerBackedge, middleBackedge));
|
|
|
|
innerBackedge->end(MGoto::New(func.alloc, innerHeader));
|
|
middleBackedge->end(MGoto::New(func.alloc, middleHeader));
|
|
outerBackedge->end(MGoto::New(func.alloc, outerHeader));
|
|
|
|
MConstant* u = MConstant::New(func.alloc, UndefinedValue());
|
|
exit->add(u);
|
|
exit->end(MReturn::New(func.alloc, u));
|
|
|
|
innerHeader->addPredecessorWithoutPhis(innerBackedge);
|
|
middleHeader->addPredecessorWithoutPhis(middleBackedge);
|
|
outerHeader->addPredecessorWithoutPhis(outerBackedge);
|
|
exit->addPredecessorWithoutPhis(entry);
|
|
merge->addPredecessorWithoutPhis(osrEntry);
|
|
|
|
outerHeader->setLoopHeader(outerBackedge);
|
|
middleHeader->setLoopHeader(middleBackedge);
|
|
innerHeader->setLoopHeader(innerBackedge);
|
|
|
|
if (!func.runGVN())
|
|
return false;
|
|
|
|
// The loops are no longer reachable from the normal entry. They are
|
|
// doinated by the osrEntry.
|
|
MOZ_RELEASE_ASSERT(func.graph.osrBlock() == osrEntry);
|
|
MBasicBlock* newInner = FollowTrivialGotos(osrEntry->lastIns()->toGoto()->target());
|
|
MBasicBlock* newMiddle = FollowTrivialGotos(newInner->lastIns()->toTest()->ifFalse());
|
|
MBasicBlock* newOuter = FollowTrivialGotos(newMiddle->lastIns()->toTest()->ifFalse());
|
|
MBasicBlock* newExit = FollowTrivialGotos(entry);
|
|
MOZ_RELEASE_ASSERT(newInner->isLoopHeader());
|
|
MOZ_RELEASE_ASSERT(newMiddle->isLoopHeader());
|
|
MOZ_RELEASE_ASSERT(newOuter->isLoopHeader());
|
|
MOZ_RELEASE_ASSERT(newExit->lastIns()->isReturn());
|
|
|
|
// One more time.
|
|
ClearDominatorTree(func.graph);
|
|
if (!func.runGVN())
|
|
return false;
|
|
|
|
// The loops are no longer reachable from the normal entry. They are
|
|
// doinated by the osrEntry.
|
|
MOZ_RELEASE_ASSERT(func.graph.osrBlock() == osrEntry);
|
|
newInner = FollowTrivialGotos(osrEntry->lastIns()->toGoto()->target());
|
|
newMiddle = FollowTrivialGotos(newInner->lastIns()->toTest()->ifFalse());
|
|
newOuter = FollowTrivialGotos(newMiddle->lastIns()->toTest()->ifFalse());
|
|
newExit = FollowTrivialGotos(entry);
|
|
MOZ_RELEASE_ASSERT(newInner->isLoopHeader());
|
|
MOZ_RELEASE_ASSERT(newMiddle->isLoopHeader());
|
|
MOZ_RELEASE_ASSERT(newOuter->isLoopHeader());
|
|
MOZ_RELEASE_ASSERT(newExit->lastIns()->isReturn());
|
|
|
|
return true;
|
|
}
|
|
END_TEST(testJitGVN_FixupOSROnlyLoopNested)
|
|
|
|
BEGIN_TEST(testJitGVN_PinnedPhis)
|
|
{
|
|
// Set up a loop which gets optimized away, with phis which must be
|
|
// cleaned up, permitting more phis to be cleaned up.
|
|
|
|
MinimalFunc func;
|
|
|
|
MBasicBlock* entry = func.createEntryBlock();
|
|
MBasicBlock* outerHeader = func.createBlock(entry);
|
|
MBasicBlock* outerBlock = func.createBlock(outerHeader);
|
|
MBasicBlock* innerHeader = func.createBlock(outerBlock);
|
|
MBasicBlock* innerBackedge = func.createBlock(innerHeader);
|
|
MBasicBlock* exit = func.createBlock(innerHeader);
|
|
|
|
MPhi* phi0 = MPhi::New(func.alloc);
|
|
MPhi* phi1 = MPhi::New(func.alloc);
|
|
MPhi* phi2 = MPhi::New(func.alloc);
|
|
MPhi* phi3 = MPhi::New(func.alloc);
|
|
|
|
MParameter* p = func.createParameter();
|
|
entry->add(p);
|
|
MConstant* z0 = MConstant::New(func.alloc, Int32Value(0));
|
|
MConstant* z1 = MConstant::New(func.alloc, Int32Value(1));
|
|
MConstant* z2 = MConstant::New(func.alloc, Int32Value(2));
|
|
MConstant* z3 = MConstant::New(func.alloc, Int32Value(2));
|
|
phi0->addInputSlow(z0);
|
|
phi1->addInputSlow(z1);
|
|
phi2->addInputSlow(z2);
|
|
phi3->addInputSlow(z3);
|
|
entry->add(z0);
|
|
entry->add(z1);
|
|
entry->add(z2);
|
|
entry->add(z3);
|
|
entry->end(MGoto::New(func.alloc, outerHeader));
|
|
|
|
outerHeader->addPhi(phi0);
|
|
outerHeader->addPhi(phi1);
|
|
outerHeader->addPhi(phi2);
|
|
outerHeader->addPhi(phi3);
|
|
outerHeader->end(MGoto::New(func.alloc, outerBlock));
|
|
|
|
outerBlock->end(MGoto::New(func.alloc, innerHeader));
|
|
|
|
MConstant* true_ = MConstant::New(func.alloc, BooleanValue(true));
|
|
innerHeader->add(true_);
|
|
innerHeader->end(MTest::New(func.alloc, true_, innerBackedge, exit));
|
|
|
|
innerBackedge->end(MGoto::New(func.alloc, innerHeader));
|
|
|
|
MInstruction* z4 = MAdd::New(func.alloc, phi0, phi1);
|
|
MConstant* z5 = MConstant::New(func.alloc, Int32Value(4));
|
|
MInstruction* z6 = MAdd::New(func.alloc, phi2, phi3);
|
|
MConstant* z7 = MConstant::New(func.alloc, Int32Value(6));
|
|
phi0->addInputSlow(z4);
|
|
phi1->addInputSlow(z5);
|
|
phi2->addInputSlow(z6);
|
|
phi3->addInputSlow(z7);
|
|
exit->add(z4);
|
|
exit->add(z5);
|
|
exit->add(z6);
|
|
exit->add(z7);
|
|
exit->end(MGoto::New(func.alloc, outerHeader));
|
|
|
|
innerHeader->addPredecessorWithoutPhis(innerBackedge);
|
|
outerHeader->addPredecessorWithoutPhis(exit);
|
|
|
|
outerHeader->setLoopHeader(exit);
|
|
innerHeader->setLoopHeader(innerBackedge);
|
|
|
|
if (!func.runGVN())
|
|
return false;
|
|
|
|
MOZ_RELEASE_ASSERT(innerHeader->phisEmpty());
|
|
MOZ_RELEASE_ASSERT(exit->isDead());
|
|
|
|
return true;
|
|
}
|
|
END_TEST(testJitGVN_PinnedPhis)
|