blob: e7096d2d990a3fe6b534ffab52d192d1e3339235 [file] [log] [blame]
/*
* Copyright (C) 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "B3ReduceStrength.h"
#if ENABLE(B3_JIT)
#include "B3BasicBlockInlines.h"
#include "B3ControlValue.h"
#include "B3IndexSet.h"
#include "B3InsertionSetInlines.h"
#include "B3MemoryValue.h"
#include "B3PhaseScope.h"
#include "B3ProcedureInlines.h"
#include "B3UpsilonValue.h"
#include "B3UseCounts.h"
#include "B3ValueInlines.h"
#include <wtf/GraphNodeWorklist.h>
namespace JSC { namespace B3 {
namespace {
// The goal of this phase is to:
//
// - Replace operations with less expensive variants. This includes constant folding and classic
// strength reductions like turning Mul(x, 1 << k) into Shl(x, k).
//
// - Reassociate constant operations. For example, Load(Add(x, c)) is turned into Load(x, offset = c)
// and Add(Add(x, c), d) is turned into Add(x, c + d).
//
// - Canonicalize operations. There are some cases where it's not at all obvious which kind of
// operation is less expensive, but it's useful for subsequent phases - particularly LowerToAir -
// to have only one way of representing things.
//
// This phase runs to fixpoint. Therefore, the canonicalizations must be designed to be monotonic.
// For example, if we had a canonicalization that said that Add(x, -c) should be Sub(x, c) and
// another canonicalization that said that Sub(x, d) should be Add(x, -d), then this phase would end
// up running forever. We don't want that.
//
// Therefore, we need to prioritize certain canonical forms over others. Naively, we want strength
// reduction to reduce the number of values, and so a form involving fewer total values is more
// canonical. But we might break this, for example when reducing strength of Mul(x, 9). This could be
// better written as Add(Shl(x, 3), x), which also happens to be representable using a single
// instruction on x86.
//
// Here are some of the rules we have:
//
// Canonical form of logical not: BitXor(value, 1). We may have to avoid using this form if we don't
// know for sure that 'value' is 0-or-1 (i.e. returnsBool). In that case we fall back on
// Equal(value, 0).
//
// Canonical form of commutative operations: if the operation involves a constant, the constant must
// come second. Add(x, constant) is canonical, while Add(constant, x) is not. If there are no
// constants then the canonical form involves the lower-indexed value first. Given Add(x, y), it's
// canonical if x->index() <= y->index().
bool verbose = false;
class ReduceStrength {
public:
ReduceStrength(Procedure& proc)
: m_proc(proc)
, m_insertionSet(proc)
{
}
bool run()
{
bool result = false;
bool first = true;
unsigned index = 0;
do {
m_changed = false;
m_changedCFG = false;
++index;
if (first)
first = false;
else if (verbose) {
dataLog("B3 after iteration #", index - 1, " of reduceStrength:\n");
dataLog(m_proc);
}
for (BasicBlock* block : m_proc.blocksInPreOrder()) {
m_block = block;
for (m_index = 0; m_index < block->size(); ++m_index)
process();
m_insertionSet.execute(m_block);
}
simplifyCFG();
if (m_changedCFG) {
m_proc.resetReachability();
m_changed = true;
}
killDeadCode();
result |= m_changed;
} while (m_changed);
return result;
}
private:
void process()
{
m_value = m_block->at(m_index);
m_value->performSubstitution();
switch (m_value->opcode()) {
case Add:
handleCommutativity();
// Turn this: Add(Add(value, constant1), constant2)
// Into this: Add(value, constant1 + constant2)
if (m_value->child(0)->opcode() == Add && isInt(m_value->type())) {
Value* newSum = m_value->child(1)->addConstant(m_proc, m_value->child(0)->child(1));
if (newSum) {
m_insertionSet.insertValue(m_index, newSum);
m_value->child(0) = m_value->child(0)->child(0);
m_value->child(1) = newSum;
m_changed = true;
}
}
// Turn this: Add(constant1, constant2)
// Into this: constant1 + constant2
if (Value* constantAdd = m_value->child(0)->addConstant(m_proc, m_value->child(1))) {
replaceWithNewValue(constantAdd);
break;
}
// Turn this: Add(value, zero)
// Into an Identity.
if (m_value->child(1)->isInt(0)) {
m_value->replaceWithIdentity(m_value->child(0));
m_changed = true;
break;
}
break;
case Sub:
// Turn this: Sub(constant1, constant2)
// Into this: constant1 - constant2
if (Value* constantSub = m_value->child(0)->subConstant(m_proc, m_value->child(1))) {
replaceWithNewValue(constantSub);
break;
}
// Turn this: Sub(value, constant)
// Into this: Add(value, -constant)
if (isInt(m_value->type())) {
if (Value* negatedConstant = m_value->child(1)->negConstant(m_proc)) {
m_insertionSet.insertValue(m_index, negatedConstant);
replaceWithNew<Value>(
Add, m_value->origin(), m_value->child(0), negatedConstant);
break;
}
}
break;
case Div:
case ChillDiv:
// Turn this: Div(constant1, constant2)
// Into this: constant1 / constant2
// Note that this uses ChillDiv semantics. That's fine, because the rules for Div
// are strictly weaker: it has corner cases where it's allowed to do anything it
// likes.
replaceWithNewValue(m_value->child(0)->divConstant(m_proc, m_value->child(1)));
break;
case BitAnd:
handleCommutativity();
// Turn this: BitAnd(constant1, constant2)
// Into this: constant1 & constant2
if (Value* constantBitAnd = m_value->child(0)->bitAndConstant(m_proc, m_value->child(1))) {
replaceWithNewValue(constantBitAnd);
break;
}
// Turn this: BitAnd(BitAnd(value, constant1), constant2)
// Into this: BitAnd(value, constant1 & constant2).
if (m_value->child(0)->opcode() == BitAnd) {
Value* newConstant = m_value->child(1)->bitAndConstant(m_proc, m_value->child(0)->child(1));
if (newConstant) {
m_insertionSet.insertValue(m_index, newConstant);
m_value->child(0) = m_value->child(0)->child(0);
m_value->child(1) = newConstant;
m_changed = true;
}
}
// Turn this: BitAnd(valueX, valueX)
// Into this: valueX.
if (m_value->child(0) == m_value->child(1)) {
m_value->replaceWithIdentity(m_value->child(0));
m_changed = true;
break;
}
// Turn this: BitAnd(value, zero-constant)
// Into this: zero-constant.
if (m_value->child(1)->isInt(0)) {
m_value->replaceWithIdentity(m_value->child(1));
m_changed = true;
break;
}
// Turn this: BitAnd(value, all-ones)
// Into this: value.
if ((m_value->type() == Int64 && m_value->child(1)->isInt(0xffffffffffffffff))
|| (m_value->type() == Int32 && m_value->child(1)->isInt(0xffffffff))) {
m_value->replaceWithIdentity(m_value->child(0));
m_changed = true;
break;
}
break;
case BitOr:
handleCommutativity();
// Turn this: BitOr(constant1, constant2)
// Into this: constant1 | constant2
if (Value* constantBitOr = m_value->child(0)->bitOrConstant(m_proc, m_value->child(1))) {
replaceWithNewValue(constantBitOr);
break;
}
// Turn this: BitOr(BitOr(value, constant1), constant2)
// Into this: BitOr(value, constant1 & constant2).
if (m_value->child(0)->opcode() == BitOr) {
Value* newConstant = m_value->child(1)->bitOrConstant(m_proc, m_value->child(0)->child(1));
if (newConstant) {
m_insertionSet.insertValue(m_index, newConstant);
m_value->child(0) = m_value->child(0)->child(0);
m_value->child(1) = newConstant;
m_changed = true;
}
}
// Turn this: BitOr(valueX, valueX)
// Into this: valueX.
if (m_value->child(0) == m_value->child(1)) {
m_value->replaceWithIdentity(m_value->child(0));
m_changed = true;
break;
}
// Turn this: BitOr(value, zero-constant)
// Into this: value.
if (m_value->child(1)->isInt(0)) {
m_value->replaceWithIdentity(m_value->child(0));
m_changed = true;
break;
}
// Turn this: BitOr(value, all-ones)
// Into this: all-ones.
if ((m_value->type() == Int64 && m_value->child(1)->isInt(0xffffffffffffffff))
|| (m_value->type() == Int32 && m_value->child(1)->isInt(0xffffffff))) {
m_value->replaceWithIdentity(m_value->child(1));
m_changed = true;
break;
}
break;
case BitXor:
handleCommutativity();
// Turn this: BitXor(constant1, constant2)
// Into this: constant1 ^ constant2
if (Value* constantBitXor = m_value->child(0)->bitXorConstant(m_proc, m_value->child(1))) {
replaceWithNewValue(constantBitXor);
break;
}
// Turn this: BitXor(BitXor(value, constant1), constant2)
// Into this: BitXor(value, constant1 ^ constant2).
if (m_value->child(0)->opcode() == BitXor) {
Value* newConstant = m_value->child(1)->bitXorConstant(m_proc, m_value->child(0)->child(1));
if (newConstant) {
m_insertionSet.insertValue(m_index, newConstant);
m_value->child(0) = m_value->child(0)->child(0);
m_value->child(1) = newConstant;
m_changed = true;
}
}
// Turn this: BitXor(compare, 1)
// Into this: invertedCompare
if (m_value->child(1)->isInt32(1)) {
if (Value* invertedCompare = m_value->child(0)->invertedCompare(m_proc)) {
replaceWithNewValue(invertedCompare);
break;
}
}
// Turn this: BitXor(valueX, valueX)
// Into this: zero-constant.
if (m_value->child(0) == m_value->child(1)) {
replaceWithNewValue(m_proc.addIntConstant(m_value, 0));
break;
}
// Turn this: BitXor(value, zero-constant)
// Into this: value.
if (m_value->child(1)->isInt(0)) {
m_value->replaceWithIdentity(m_value->child(0));
m_changed = true;
break;
}
break;
case Shl:
// Turn this: Shl(constant1, constant2)
// Into this: constant1 << constant2
if (Value* constant = m_value->child(0)->shlConstant(m_proc, m_value->child(1))) {
replaceWithNewValue(constant);
break;
}
if (handleShiftByZero())
break;
break;
case SShr:
// Turn this: SShr(constant1, constant2)
// Into this: constant1 >> constant2
if (Value* constant = m_value->child(0)->sShrConstant(m_proc, m_value->child(1))) {
replaceWithNewValue(constant);
break;
}
if (handleShiftByZero())
break;
break;
case ZShr:
// Turn this: ZShr(constant1, constant2)
// Into this: (unsigned)constant1 >> constant2
if (Value* constant = m_value->child(0)->zShrConstant(m_proc, m_value->child(1))) {
replaceWithNewValue(constant);
break;
}
if (handleShiftByZero())
break;
break;
case Load8Z:
case Load8S:
case Load16Z:
case Load16S:
case LoadFloat:
case Load:
case Store8:
case Store16:
case StoreFloat:
case Store: {
Value* address = m_value->lastChild();
MemoryValue* memory = m_value->as<MemoryValue>();
// Turn this: Load(Add(address, offset1), offset = offset2)
// Into this: Load(address, offset = offset1 + offset2)
//
// Also turns this: Store(value, Add(address, offset1), offset = offset2)
// Into this: Store(value, address, offset = offset1 + offset2)
if (address->opcode() == Add && address->child(1)->hasIntPtr()) {
intptr_t offset = address->child(1)->asIntPtr();
if (!sumOverflows<intptr_t>(offset, memory->offset())) {
offset += memory->offset();
int32_t smallOffset = static_cast<int32_t>(offset);
if (smallOffset == offset) {
address = address->child(0);
memory->lastChild() = address;
memory->setOffset(smallOffset);
m_changed = true;
}
}
}
// Turn this: Load(constant1, offset = constant2)
// Into this: Laod(constant1 + constant2)
//
// This is a fun canonicalization. It purely regresses naively generated code. We rely
// on constant materialization to be smart enough to materialize this constant the smart
// way. We want this canonicalization because we want to know if two memory accesses see
// the same address.
if (memory->offset()) {
if (Value* newAddress = address->addConstant(m_proc, memory->offset())) {
m_insertionSet.insertValue(m_index, newAddress);
address = newAddress;
memory->lastChild() = newAddress;
memory->setOffset(0);
m_changed = true;
}
}
break;
}
case Equal:
handleCommutativity();
// Turn this: Equal(bool, 0)
// Into this: BitXor(bool, 1)
if (m_value->child(0)->returnsBool() && m_value->child(1)->isInt32(0)) {
replaceWithNew<Value>(
BitXor, m_value->origin(), m_value->child(0),
m_insertionSet.insert<Const32Value>(m_index, m_value->origin(), 1));
break;
}
// Turn this Equal(bool, 1)
// Into this: bool
if (m_value->child(0)->returnsBool() && m_value->child(1)->isInt32(1)) {
m_value->replaceWithIdentity(m_value->child(0));
m_changed = true;
break;
}
// Turn this: Equal(const1, const2)
// Into this: const1 == const2
replaceWithNewValue(
m_proc.addBoolConstant(
m_value->origin(),
m_value->child(0)->equalConstant(m_value->child(1))));
break;
case NotEqual:
handleCommutativity();
if (m_value->child(0)->returnsBool()) {
// Turn this: NotEqual(bool, 0)
// Into this: bool
if (m_value->child(1)->isInt32(0)) {
m_value->replaceWithIdentity(m_value->child(0));
m_changed = true;
break;
}
// Turn this: NotEqual(bool, 1)
// Into this: Equal(bool, 0)
if (m_value->child(1)->isInt32(1)) {
replaceWithNew<Value>(
Equal, m_value->origin(), m_value->child(0), m_value->child(1));
break;
}
}
// Turn this: NotEqual(const1, const2)
// Into this: const1 != const2
replaceWithNewValue(
m_proc.addBoolConstant(
m_value->origin(),
m_value->child(0)->notEqualConstant(m_value->child(1))));
break;
case LessThan:
// FIXME: We could do a better job of canonicalizing integer comparisons.
// https://bugs.webkit.org/show_bug.cgi?id=150958
replaceWithNewValue(
m_proc.addBoolConstant(
m_value->origin(),
m_value->child(0)->lessThanConstant(m_value->child(1))));
break;
case GreaterThan:
replaceWithNewValue(
m_proc.addBoolConstant(
m_value->origin(),
m_value->child(0)->greaterThanConstant(m_value->child(1))));
break;
case LessEqual:
replaceWithNewValue(
m_proc.addBoolConstant(
m_value->origin(),
m_value->child(0)->lessEqualConstant(m_value->child(1))));
break;
case GreaterEqual:
replaceWithNewValue(
m_proc.addBoolConstant(
m_value->origin(),
m_value->child(0)->greaterEqualConstant(m_value->child(1))));
break;
case Above:
replaceWithNewValue(
m_proc.addBoolConstant(
m_value->origin(),
m_value->child(0)->aboveConstant(m_value->child(1))));
break;
case Below:
replaceWithNewValue(
m_proc.addBoolConstant(
m_value->origin(),
m_value->child(0)->belowConstant(m_value->child(1))));
break;
case AboveEqual:
replaceWithNewValue(
m_proc.addBoolConstant(
m_value->origin(),
m_value->child(0)->aboveEqualConstant(m_value->child(1))));
break;
case BelowEqual:
replaceWithNewValue(
m_proc.addBoolConstant(
m_value->origin(),
m_value->child(0)->belowEqualConstant(m_value->child(1))));
break;
case Branch: {
ControlValue* branch = m_value->as<ControlValue>();
// Turn this: Branch(NotEqual(x, 0))
// Into this: Branch(x)
if (branch->child(0)->opcode() == NotEqual && branch->child(0)->child(1)->isInt(0)) {
branch->child(0) = branch->child(0)->child(0);
m_changed = true;
}
// Turn this: Branch(Equal(x, 0), then, else)
// Into this: Branch(x, else, then)
if (branch->child(0)->opcode() == Equal && branch->child(0)->child(1)->isInt(0)) {
branch->child(0) = branch->child(0)->child(0);
std::swap(branch->taken(), branch->notTaken());
m_changed = true;
}
// Turn this: Branch(BitXor(bool, 1), then, else)
// Into this: Branch(bool, else, then)
if (branch->child(0)->opcode() == BitXor
&& branch->child(0)->child(1)->isInt32(1)
&& branch->child(0)->child(0)->returnsBool()) {
branch->child(0) = branch->child(0)->child(0);
std::swap(branch->taken(), branch->notTaken());
m_changed = true;
}
TriState triState = branch->child(0)->asTriState();
// Turn this: Branch(0, then, else)
// Into this: Jump(else)
if (triState == FalseTriState) {
branch->taken().block()->removePredecessor(m_block);
branch->convertToJump(branch->notTaken().block());
m_changedCFG = true;
break;
}
// Turn this: Branch(not 0, then, else)
// Into this: Jump(then)
if (triState == TrueTriState) {
branch->notTaken().block()->removePredecessor(m_block);
branch->convertToJump(branch->taken().block());
m_changedCFG = true;
break;
}
break;
}
default:
break;
}
}
// Turn this: Add(constant, value)
// Into this: Add(value, constant)
//
// Also:
// Turn this: Add(value1, value2)
// Into this: Add(value2, value1)
// If we decide that value2 coming first is the canonical ordering.
void handleCommutativity()
{
// Leave it alone if the right child is a constant.
if (m_value->child(1)->isConstant())
return;
if (m_value->child(0)->isConstant()) {
std::swap(m_value->child(0), m_value->child(1));
m_changed = true;
return;
}
// Sort the operands. This is an important canonicalization. We use the index instead of
// the address to make this at least slightly deterministic.
if (m_value->child(0)->index() > m_value->child(1)->index()) {
std::swap(m_value->child(0), m_value->child(1));
m_changed = true;
return;
}
}
template<typename ValueType, typename... Arguments>
void replaceWithNew(Arguments... arguments)
{
replaceWithNewValue(m_proc.add<ValueType>(arguments...));
}
void replaceWithNewValue(Value* newValue)
{
if (!newValue)
return;
m_insertionSet.insertValue(m_index, newValue);
m_value->replaceWithIdentity(newValue);
m_changed = true;
}
bool handleShiftByZero()
{
// Shift anything by zero is identity.
if (m_value->child(1)->isInt(0)) {
m_value->replaceWithIdentity(m_value->child(0));
m_changed = true;
return true;
}
return false;
}
void simplifyCFG()
{
if (verbose) {
dataLog("Before simplifyCFG:\n");
dataLog(m_proc);
}
// We have three easy simplification rules:
//
// 1) If a successor is a block that just jumps to another block, then jump directly to
// that block.
//
// 2) If all successors are the same and the operation has no effects, then use a jump
// instead.
//
// 3) If you jump to a block that is not you and has one predecessor, then merge.
//
// Note that because of the first rule, this phase may introduce critical edges. That's fine.
// If you need broken critical edges, then you have to break them yourself.
// Note that this relies on predecessors being at least conservatively correct. It's fine for
// predecessors to mention a block that isn't actually a predecessor. It's *not* fine for a
// predecessor to be omitted. We assert as much in the loop. In practice, we precisely preserve
// predecessors during strength reduction since that minimizes the total number of fixpoint
// iterations needed to kill a lot of code.
for (BasicBlock* block : m_proc) {
if (verbose)
dataLog("Considering block ", *block, ":\n");
checkPredecessorValidity();
// We don't care about blocks that don't have successors.
if (!block->numSuccessors())
continue;
// First check if any of the successors of this block can be forwarded over.
for (BasicBlock*& successor : block->successorBlocks()) {
if (successor != block
&& successor->size() == 1
&& successor->last()->opcode() == Jump) {
BasicBlock* newSuccessor = successor->successorBlock(0);
if (newSuccessor != successor) {
if (verbose) {
dataLog(
"Replacing ", pointerDump(block), "->", pointerDump(successor),
" with ", pointerDump(block), "->", pointerDump(newSuccessor),
"\n");
}
// Note that we do not do replacePredecessor() because the block we're
// skipping will still have newSuccessor as its successor.
newSuccessor->addPredecessor(block);
successor = newSuccessor;
m_changedCFG = true;
}
}
}
// Now check if the block's terminal can be replaced with a jump.
if (block->numSuccessors() > 1) {
// The terminal must not have weird effects.
Effects effects = block->last()->effects();
effects.terminal = false;
if (!effects.mustExecute()) {
// All of the successors must be the same.
bool allSame = true;
BasicBlock* firstSuccessor = block->successorBlock(0);
for (unsigned i = 1; i < block->numSuccessors(); ++i) {
if (block->successorBlock(i) != firstSuccessor) {
allSame = false;
break;
}
}
if (allSame) {
if (verbose) {
dataLog(
"Changing ", pointerDump(block), "'s terminal to a Jump.\n");
}
block->last()->as<ControlValue>()->convertToJump(firstSuccessor);
m_changedCFG = true;
}
}
}
// Finally handle jumps to a block with one predecessor.
if (block->numSuccessors() == 1) {
BasicBlock* successor = block->successorBlock(0);
if (successor != block && successor->numPredecessors() == 1) {
RELEASE_ASSERT(successor->predecessor(0) == block);
// We can merge the two blocks, because the predecessor only jumps to the successor
// and the successor is only reachable from the predecessor.
// Remove the terminal.
Value* value = block->values().takeLast();
Origin jumpOrigin = value->origin();
RELEASE_ASSERT(value->as<ControlValue>());
m_proc.deleteValue(value);
// Append the full contents of the successor to the predecessor.
block->values().appendVector(successor->values());
// Make sure that the successor has nothing left in it. Make sure that the block
// has a terminal so that nobody chokes when they look at it.
successor->values().resize(0);
successor->appendNew<ControlValue>(m_proc, Oops, jumpOrigin);
// Ensure that predecessors of block's new successors know what's up.
for (BasicBlock* newSuccessor : block->successorBlocks())
newSuccessor->replacePredecessor(successor, block);
if (verbose) {
dataLog(
"Merged ", pointerDump(block), "->", pointerDump(successor), "\n");
}
m_changedCFG = true;
}
}
}
if (m_changedCFG && verbose) {
dataLog("B3 after simplifyCFG:\n");
dataLog(m_proc);
}
}
void checkPredecessorValidity()
{
if (!shouldValidateIRAtEachPhase())
return;
for (BasicBlock* block : m_proc) {
for (BasicBlock* successor : block->successorBlocks())
RELEASE_ASSERT(successor->containsPredecessor(block));
}
}
void killDeadCode()
{
GraphNodeWorklist<Value*, IndexSet<Value>> worklist;
Vector<UpsilonValue*, 64> upsilons;
for (BasicBlock* block : m_proc) {
for (Value* value : *block) {
Effects effects = value->effects();
// We don't care about SSA Effects, since we model them more accurately than the
// effects() method does.
effects.writesSSAState = false;
effects.readsSSAState = false;
if (effects.mustExecute())
worklist.push(value);
if (UpsilonValue* upsilon = value->as<UpsilonValue>())
upsilons.append(upsilon);
}
}
for (;;) {
while (Value* value = worklist.pop()) {
for (Value* child : value->children())
worklist.push(child);
}
bool didPush = false;
for (size_t upsilonIndex = 0; upsilonIndex < upsilons.size(); ++upsilonIndex) {
UpsilonValue* upsilon = upsilons[upsilonIndex];
if (worklist.saw(upsilon->phi())) {
worklist.push(upsilon);
upsilons[upsilonIndex--] = upsilons.last();
upsilons.takeLast();
didPush = true;
}
}
if (!didPush)
break;
}
for (BasicBlock* block : m_proc) {
size_t sourceIndex = 0;
size_t targetIndex = 0;
while (sourceIndex < block->size()) {
Value* value = block->at(sourceIndex++);
if (worklist.saw(value))
block->at(targetIndex++) = value;
else {
m_proc.deleteValue(value);
// It's not entirely clear if this is needed. I think it makes sense to have
// this force a rerun of the fixpoint for now, since that will make it easier
// to do peephole optimizations: removing dead code will make the peephole
// easier to spot.
m_changed = true;
}
}
block->values().resize(targetIndex);
}
}
Procedure& m_proc;
InsertionSet m_insertionSet;
BasicBlock* m_block;
unsigned m_index;
Value* m_value;
bool m_changed;
bool m_changedCFG;
};
} // anonymous namespace
bool reduceStrength(Procedure& proc)
{
PhaseScope phaseScope(proc, "reduceStrength");
ReduceStrength reduceStrength(proc);
return reduceStrength.run();
}
} } // namespace JSC::B3
#endif // ENABLE(B3_JIT)