summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/b3/B3ReduceStrength.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2016-04-10 09:28:39 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2016-04-10 09:28:39 +0000
commit32761a6cee1d0dee366b885b7b9c777e67885688 (patch)
treed6bec92bebfb216f4126356e55518842c2f476a1 /Source/JavaScriptCore/b3/B3ReduceStrength.cpp
parenta4e969f4965059196ca948db781e52f7cfebf19e (diff)
downloadWebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/JavaScriptCore/b3/B3ReduceStrength.cpp')
-rw-r--r--Source/JavaScriptCore/b3/B3ReduceStrength.cpp2420
1 files changed, 0 insertions, 2420 deletions
diff --git a/Source/JavaScriptCore/b3/B3ReduceStrength.cpp b/Source/JavaScriptCore/b3/B3ReduceStrength.cpp
deleted file mode 100644
index 4363b9e2c..000000000
--- a/Source/JavaScriptCore/b3/B3ReduceStrength.cpp
+++ /dev/null
@@ -1,2420 +0,0 @@
-/*
- * Copyright (C) 2015-2016 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 "B3BlockInsertionSet.h"
-#include "B3ComputeDivisionMagic.h"
-#include "B3ControlValue.h"
-#include "B3Dominators.h"
-#include "B3IndexSet.h"
-#include "B3InsertionSetInlines.h"
-#include "B3MemoryValue.h"
-#include "B3PhaseScope.h"
-#include "B3PhiChildren.h"
-#include "B3ProcedureInlines.h"
-#include "B3PureCSE.h"
-#include "B3SlotBaseValue.h"
-#include "B3StackSlot.h"
-#include "B3UpsilonValue.h"
-#include "B3ValueKeyInlines.h"
-#include "B3ValueInlines.h"
-#include "B3Variable.h"
-#include "B3VariableValue.h"
-#include <wtf/GraphNodeWorklist.h>
-#include <wtf/HashMap.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;
-
-// FIXME: This IntRange stuff should be refactored into a general constant propagator. It's weird
-// that it's just sitting here in this file.
-class IntRange {
-public:
- IntRange()
- {
- }
-
- IntRange(int64_t min, int64_t max)
- : m_min(min)
- , m_max(max)
- {
- }
-
- template<typename T>
- static IntRange top()
- {
- return IntRange(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
- }
-
- static IntRange top(Type type)
- {
- switch (type) {
- case Int32:
- return top<int32_t>();
- case Int64:
- return top<int64_t>();
- default:
- RELEASE_ASSERT_NOT_REACHED();
- return IntRange();
- }
- }
-
- template<typename T>
- static IntRange rangeForMask(T mask)
- {
- if (!(mask + 1))
- return top<T>();
- return IntRange(0, mask);
- }
-
- static IntRange rangeForMask(int64_t mask, Type type)
- {
- switch (type) {
- case Int32:
- return rangeForMask<int32_t>(static_cast<int32_t>(mask));
- case Int64:
- return rangeForMask<int64_t>(mask);
- default:
- RELEASE_ASSERT_NOT_REACHED();
- return IntRange();
- }
- }
-
- template<typename T>
- static IntRange rangeForZShr(int32_t shiftAmount)
- {
- typename std::make_unsigned<T>::type mask = 0;
- mask--;
- mask >>= shiftAmount;
- return rangeForMask<T>(static_cast<T>(mask));
- }
-
- static IntRange rangeForZShr(int32_t shiftAmount, Type type)
- {
- switch (type) {
- case Int32:
- return rangeForZShr<int32_t>(shiftAmount);
- case Int64:
- return rangeForZShr<int64_t>(shiftAmount);
- default:
- RELEASE_ASSERT_NOT_REACHED();
- return IntRange();
- }
- }
-
- int64_t min() const { return m_min; }
- int64_t max() const { return m_max; }
-
- void dump(PrintStream& out) const
- {
- out.print("[", m_min, ",", m_max, "]");
- }
-
- template<typename T>
- bool couldOverflowAdd(const IntRange& other)
- {
- return sumOverflows<T>(m_min, other.m_min)
- || sumOverflows<T>(m_min, other.m_max)
- || sumOverflows<T>(m_max, other.m_min)
- || sumOverflows<T>(m_max, other.m_max);
- }
-
- bool couldOverflowAdd(const IntRange& other, Type type)
- {
- switch (type) {
- case Int32:
- return couldOverflowAdd<int32_t>(other);
- case Int64:
- return couldOverflowAdd<int64_t>(other);
- default:
- return true;
- }
- }
-
- template<typename T>
- bool couldOverflowSub(const IntRange& other)
- {
- return differenceOverflows<T>(m_min, other.m_min)
- || differenceOverflows<T>(m_min, other.m_max)
- || differenceOverflows<T>(m_max, other.m_min)
- || differenceOverflows<T>(m_max, other.m_max);
- }
-
- bool couldOverflowSub(const IntRange& other, Type type)
- {
- switch (type) {
- case Int32:
- return couldOverflowSub<int32_t>(other);
- case Int64:
- return couldOverflowSub<int64_t>(other);
- default:
- return true;
- }
- }
-
- template<typename T>
- bool couldOverflowMul(const IntRange& other)
- {
- return productOverflows<T>(m_min, other.m_min)
- || productOverflows<T>(m_min, other.m_max)
- || productOverflows<T>(m_max, other.m_min)
- || productOverflows<T>(m_max, other.m_max);
- }
-
- bool couldOverflowMul(const IntRange& other, Type type)
- {
- switch (type) {
- case Int32:
- return couldOverflowMul<int32_t>(other);
- case Int64:
- return couldOverflowMul<int64_t>(other);
- default:
- return true;
- }
- }
-
- template<typename T>
- IntRange shl(int32_t shiftAmount)
- {
- T newMin = static_cast<T>(m_min) << static_cast<T>(shiftAmount);
- T newMax = static_cast<T>(m_max) << static_cast<T>(shiftAmount);
-
- if ((newMin >> shiftAmount) != static_cast<T>(m_min))
- newMin = std::numeric_limits<T>::min();
- if ((newMax >> shiftAmount) != static_cast<T>(m_max))
- newMax = std::numeric_limits<T>::max();
-
- return IntRange(newMin, newMax);
- }
-
- IntRange shl(int32_t shiftAmount, Type type)
- {
- switch (type) {
- case Int32:
- return shl<int32_t>(shiftAmount);
- case Int64:
- return shl<int64_t>(shiftAmount);
- default:
- RELEASE_ASSERT_NOT_REACHED();
- return IntRange();
- }
- }
-
- template<typename T>
- IntRange sShr(int32_t shiftAmount)
- {
- T newMin = static_cast<T>(m_min) >> static_cast<T>(shiftAmount);
- T newMax = static_cast<T>(m_max) >> static_cast<T>(shiftAmount);
-
- return IntRange(newMin, newMax);
- }
-
- IntRange sShr(int32_t shiftAmount, Type type)
- {
- switch (type) {
- case Int32:
- return sShr<int32_t>(shiftAmount);
- case Int64:
- return sShr<int64_t>(shiftAmount);
- default:
- RELEASE_ASSERT_NOT_REACHED();
- return IntRange();
- }
- }
-
- template<typename T>
- IntRange zShr(int32_t shiftAmount)
- {
- // This is an awkward corner case for all of the other logic.
- if (!shiftAmount)
- return *this;
-
- // If the input range may be negative, then all we can say about the output range is that it
- // will be masked. That's because -1 right shifted just produces that mask.
- if (m_min < 0)
- return rangeForZShr<T>(shiftAmount);
-
- // If the input range is non-negative, then this just brings the range closer to zero.
- typedef typename std::make_unsigned<T>::type UnsignedT;
- UnsignedT newMin = static_cast<UnsignedT>(m_min) >> static_cast<UnsignedT>(shiftAmount);
- UnsignedT newMax = static_cast<UnsignedT>(m_max) >> static_cast<UnsignedT>(shiftAmount);
-
- return IntRange(newMin, newMax);
- }
-
- IntRange zShr(int32_t shiftAmount, Type type)
- {
- switch (type) {
- case Int32:
- return zShr<int32_t>(shiftAmount);
- case Int64:
- return zShr<int64_t>(shiftAmount);
- default:
- RELEASE_ASSERT_NOT_REACHED();
- return IntRange();
- }
- }
-
- template<typename T>
- IntRange add(const IntRange& other)
- {
- if (couldOverflowAdd<T>(other))
- return top<T>();
- return IntRange(m_min + other.m_min, m_max + other.m_max);
- }
-
- IntRange add(const IntRange& other, Type type)
- {
- switch (type) {
- case Int32:
- return add<int32_t>(other);
- case Int64:
- return add<int64_t>(other);
- default:
- RELEASE_ASSERT_NOT_REACHED();
- return IntRange();
- }
- }
-
- template<typename T>
- IntRange sub(const IntRange& other)
- {
- if (couldOverflowSub<T>(other))
- return top<T>();
- return IntRange(m_min - other.m_max, m_max - other.m_min);
- }
-
- IntRange sub(const IntRange& other, Type type)
- {
- switch (type) {
- case Int32:
- return sub<int32_t>(other);
- case Int64:
- return sub<int64_t>(other);
- default:
- RELEASE_ASSERT_NOT_REACHED();
- return IntRange();
- }
- }
-
- template<typename T>
- IntRange mul(const IntRange& other)
- {
- if (couldOverflowMul<T>(other))
- return top<T>();
- return IntRange(
- std::min(
- std::min(m_min * other.m_min, m_min * other.m_max),
- std::min(m_max * other.m_min, m_max * other.m_max)),
- std::max(
- std::max(m_min * other.m_min, m_min * other.m_max),
- std::max(m_max * other.m_min, m_max * other.m_max)));
- }
-
- IntRange mul(const IntRange& other, Type type)
- {
- switch (type) {
- case Int32:
- return mul<int32_t>(other);
- case Int64:
- return mul<int64_t>(other);
- default:
- RELEASE_ASSERT_NOT_REACHED();
- return IntRange();
- }
- }
-
-private:
- int64_t m_min { 0 };
- int64_t m_max { 0 };
-};
-
-class ReduceStrength {
-public:
- ReduceStrength(Procedure& proc)
- : m_proc(proc)
- , m_insertionSet(proc)
- , m_blockInsertionSet(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);
- }
-
- m_proc.resetValueOwners();
- m_dominators = &m_proc.dominators(); // Recompute if necessary.
- m_pureCSE.clear();
-
- for (BasicBlock* block : m_proc.blocksInPreOrder()) {
- m_block = block;
-
- for (m_index = 0; m_index < block->size(); ++m_index) {
- if (verbose) {
- dataLog(
- "Looking at ", *block, " #", m_index, ": ",
- deepDump(m_proc, block->at(m_index)), "\n");
- }
- m_value = m_block->at(m_index);
- m_value->performSubstitution();
-
- reduceValueStrength();
- replaceIfRedundant();
- }
- m_insertionSet.execute(m_block);
- }
-
- if (m_blockInsertionSet.execute()) {
- m_proc.resetReachability();
- m_proc.invalidateCFG();
- m_dominators = &m_proc.dominators(); // Recompute if necessary.
- m_changedCFG = true;
- }
-
- simplifyCFG();
-
- if (m_changedCFG) {
- m_proc.resetReachability();
- m_proc.invalidateCFG();
- m_changed = true;
- }
-
- killDeadCode();
- simplifySSA();
-
- result |= m_changed;
- } while (m_changed);
- return result;
- }
-
-private:
- void reduceValueStrength()
- {
- 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: Integer Add(value, value)
- // Into this: Shl(value, 1)
- // This is a useful canonicalization. It's not meant to be a strength reduction.
- if (m_value->isInteger() && m_value->child(0) == m_value->child(1)) {
- replaceWithNewValue(
- m_proc.add<Value>(
- Shl, m_value->origin(), m_value->child(0),
- m_insertionSet.insert<Const32Value>(m_index, m_value->origin(), 1)));
- break;
- }
-
- // Turn this: Add(value, zero)
- // Into an Identity.
- //
- // Addition is subtle with doubles. Zero is not the neutral value, negative zero is:
- // 0 + 0 = 0
- // 0 + -0 = 0
- // -0 + 0 = 0
- // -0 + -0 = -0
- if (m_value->child(1)->isInt(0) || m_value->child(1)->isNegativeZero()) {
- replaceWithIdentity(m_value->child(0));
- break;
- }
-
- // Turn this: Integer Add(Sub(0, value), -1)
- // Into this: BitXor(value, -1)
- if (m_value->isInteger()
- && m_value->child(0)->opcode() == Sub
- && m_value->child(1)->isInt(-1)
- && m_value->child(0)->child(0)->isInt(0)) {
- replaceWithNewValue(m_proc.add<Value>(BitXor, m_value->origin(), m_value->child(0)->child(1), m_value->child(1)));
- 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;
- }
-
- if (isInt(m_value->type())) {
- // Turn this: Sub(value, constant)
- // Into this: Add(value, -constant)
- 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;
- }
-
- // Turn this: Sub(0, value)
- // Into this: Neg(value)
- if (m_value->child(0)->isInt(0)) {
- replaceWithNew<Value>(Neg, m_value->origin(), m_value->child(1));
- break;
- }
- }
-
- break;
-
- case Neg:
- // Turn this: Neg(constant)
- // Into this: -constant
- if (Value* constant = m_value->child(0)->negConstant(m_proc)) {
- replaceWithNewValue(constant);
- break;
- }
-
- // Turn this: Neg(Neg(value))
- // Into this: value
- if (m_value->child(0)->opcode() == Neg) {
- replaceWithIdentity(m_value->child(0)->child(0));
- break;
- }
-
- break;
-
- case Mul:
- handleCommutativity();
-
- // Turn this: Mul(constant1, constant2)
- // Into this: constant1 * constant2
- if (Value* value = m_value->child(0)->mulConstant(m_proc, m_value->child(1))) {
- replaceWithNewValue(value);
- break;
- }
-
- if (m_value->child(1)->hasInt()) {
- int64_t factor = m_value->child(1)->asInt();
-
- // Turn this: Mul(value, 0)
- // Into this: 0
- // Note that we don't do this for doubles because that's wrong. For example, -1 * 0
- // and 1 * 0 yield different results.
- if (!factor) {
- replaceWithIdentity(m_value->child(1));
- break;
- }
-
- // Turn this: Mul(value, 1)
- // Into this: value
- if (factor == 1) {
- replaceWithIdentity(m_value->child(0));
- break;
- }
-
- // Turn this: Mul(value, -1)
- // Into this: Sub(0, value)
- if (factor == -1) {
- replaceWithNewValue(
- m_proc.add<Value>(
- Sub, m_value->origin(),
- m_insertionSet.insertIntConstant(m_index, m_value, 0),
- m_value->child(0)));
- break;
- }
-
- // Turn this: Mul(value, constant)
- // Into this: Shl(value, log2(constant))
- if (hasOneBitSet(factor)) {
- unsigned shiftAmount = WTF::fastLog2(static_cast<uint64_t>(factor));
- replaceWithNewValue(
- m_proc.add<Value>(
- Shl, m_value->origin(), m_value->child(0),
- m_insertionSet.insert<Const32Value>(
- m_index, m_value->origin(), shiftAmount)));
- break;
- }
- } else if (m_value->child(1)->hasDouble()) {
- double factor = m_value->child(1)->asDouble();
-
- // Turn this: Mul(value, 1)
- // Into this: value
- if (factor == 1) {
- replaceWithIdentity(m_value->child(0));
- 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.
- if (replaceWithNewValue(m_value->child(0)->divConstant(m_proc, m_value->child(1))))
- break;
-
- if (m_value->child(1)->hasInt()) {
- switch (m_value->child(1)->asInt()) {
- case -1:
- // Turn this: Div(value, -1)
- // Into this: Neg(value)
- replaceWithNewValue(
- m_proc.add<Value>(Neg, m_value->origin(), m_value->child(0)));
- break;
-
- case 0:
- // Turn this: Div(value, 0)
- // Into this: 0
- // We can do this because it's precisely correct for ChillDiv and for Div we
- // are allowed to do whatever we want.
- replaceWithIdentity(m_value->child(1));
- break;
-
- case 1:
- // Turn this: Div(value, 1)
- // Into this: value
- replaceWithIdentity(m_value->child(0));
- break;
-
- default:
- // Perform super comprehensive strength reduction of division. Currently we
- // only do this for 32-bit divisions, since we need a high multiply
- // operation. We emulate it using 64-bit multiply. We can't emulate 64-bit
- // high multiply with a 128-bit multiply because we don't have a 128-bit
- // multiply. We could do it with a patchpoint if we cared badly enough.
-
- if (m_value->type() != Int32)
- break;
-
- int32_t divisor = m_value->child(1)->asInt32();
- DivisionMagic<int32_t> magic = computeDivisionMagic(divisor);
-
- // Perform the "high" multiplication. We do it just to get the high bits.
- // This is sort of like multiplying by the reciprocal, just more gnarly. It's
- // from Hacker's Delight and I don't claim to understand it.
- Value* magicQuotient = m_insertionSet.insert<Value>(
- m_index, Trunc, m_value->origin(),
- m_insertionSet.insert<Value>(
- m_index, ZShr, m_value->origin(),
- m_insertionSet.insert<Value>(
- m_index, Mul, m_value->origin(),
- m_insertionSet.insert<Value>(
- m_index, SExt32, m_value->origin(), m_value->child(0)),
- m_insertionSet.insert<Const64Value>(
- m_index, m_value->origin(), magic.magicMultiplier)),
- m_insertionSet.insert<Const32Value>(
- m_index, m_value->origin(), 32)));
-
- if (divisor > 0 && magic.magicMultiplier < 0) {
- magicQuotient = m_insertionSet.insert<Value>(
- m_index, Add, m_value->origin(), magicQuotient, m_value->child(0));
- }
- if (divisor < 0 && magic.magicMultiplier > 0) {
- magicQuotient = m_insertionSet.insert<Value>(
- m_index, Sub, m_value->origin(), magicQuotient, m_value->child(0));
- }
- if (magic.shift > 0) {
- magicQuotient = m_insertionSet.insert<Value>(
- m_index, SShr, m_value->origin(), magicQuotient,
- m_insertionSet.insert<Const32Value>(
- m_index, m_value->origin(), magic.shift));
- }
- replaceWithIdentity(
- m_insertionSet.insert<Value>(
- m_index, Add, m_value->origin(), magicQuotient,
- m_insertionSet.insert<Value>(
- m_index, ZShr, m_value->origin(), magicQuotient,
- m_insertionSet.insert<Const32Value>(
- m_index, m_value->origin(), 31))));
- break;
- }
- break;
- }
- break;
-
- case Mod:
- case ChillMod:
- // Turn this: Mod(constant1, constant2)
- // Into this: constant1 / constant2
- // Note that this uses ChillMod semantics.
- if (replaceWithNewValue(m_value->child(0)->modConstant(m_proc, m_value->child(1))))
- break;
-
- // Modulo by constant is more efficient if we turn it into Div, and then let Div get
- // optimized.
- if (m_value->child(1)->hasInt()) {
- switch (m_value->child(1)->asInt()) {
- case 0:
- // Turn this: Mod(value, 0)
- // Into this: 0
- // This is correct according to ChillMod semantics.
- replaceWithIdentity(m_value->child(1));
- break;
-
- default:
- // Turn this: Mod(N, D)
- // Into this: Sub(N, Mul(Div(N, D), D))
- //
- // This is a speed-up because we use our existing Div optimizations.
- //
- // Here's an easier way to look at it:
- // N % D = N - N / D * D
- //
- // Note that this does not work for D = 0 and ChillMod. The expected result is 0.
- // That's why we have a special-case above.
- // X % 0 = X - X / 0 * 0 = X (should be 0)
- //
- // This does work for the D = -1 special case.
- // -2^31 % -1 = -2^31 - -2^31 / -1 * -1
- // = -2^31 - -2^31 * -1
- // = -2^31 - -2^31
- // = 0
-
- Opcode divOpcode;
- switch (m_value->opcode()) {
- case Mod:
- divOpcode = Div;
- break;
- case ChillMod:
- divOpcode = ChillDiv;
- break;
- default:
- divOpcode = Oops;
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
-
- replaceWithIdentity(
- m_insertionSet.insert<Value>(
- m_index, Sub, m_value->origin(),
- m_value->child(0),
- m_insertionSet.insert<Value>(
- m_index, Mul, m_value->origin(),
- m_insertionSet.insert<Value>(
- m_index, divOpcode, m_value->origin(),
- m_value->child(0), m_value->child(1)),
- m_value->child(1))));
- break;
- }
- break;
- }
-
- 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)) {
- replaceWithIdentity(m_value->child(0));
- break;
- }
-
- // Turn this: BitAnd(value, zero-constant)
- // Into this: zero-constant.
- if (m_value->child(1)->isInt(0)) {
- replaceWithIdentity(m_value->child(1));
- 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))) {
- replaceWithIdentity(m_value->child(0));
- break;
- }
-
- // Turn this: BitAnd(64-bit value, 32 ones)
- // Into this: ZExt32(Trunc(64-bit value))
- if (m_value->child(1)->isInt64(0xffffffffllu)) {
- Value* newValue = m_insertionSet.insert<Value>(
- m_index, ZExt32, m_value->origin(),
- m_insertionSet.insert<Value>(m_index, Trunc, m_value->origin(), m_value->child(0)));
- replaceWithIdentity(newValue);
- break;
- }
-
- // Turn this: BitAnd(SExt8(value), mask) where (mask & 0xffffff00) == 0
- // Into this: BitAnd(value, mask)
- if (m_value->child(0)->opcode() == SExt8 && m_value->child(1)->hasInt32()
- && !(m_value->child(1)->asInt32() & 0xffffff00)) {
- m_value->child(0) = m_value->child(0)->child(0);
- m_changed = true;
- }
-
- // Turn this: BitAnd(SExt16(value), mask) where (mask & 0xffff0000) == 0
- // Into this: BitAnd(value, mask)
- if (m_value->child(0)->opcode() == SExt16 && m_value->child(1)->hasInt32()
- && !(m_value->child(1)->asInt32() & 0xffff0000)) {
- m_value->child(0) = m_value->child(0)->child(0);
- m_changed = true;
- }
-
- // Turn this: BitAnd(SExt32(value), mask) where (mask & 0xffffffff00000000) == 0
- // Into this: BitAnd(ZExt32(value), mask)
- if (m_value->child(0)->opcode() == SExt32 && m_value->child(1)->hasInt32()
- && !(m_value->child(1)->asInt32() & 0xffffffff00000000llu)) {
- m_value->child(0) = m_insertionSet.insert<Value>(
- m_index, ZExt32, m_value->origin(),
- m_value->child(0)->child(0), m_value->child(0)->child(1));
- m_changed = true;
- }
-
- // Turn this: BitAnd(Op(value, constant1), constant2)
- // where !(constant1 & constant2)
- // and Op is BitOr or BitXor
- // into this: BitAnd(value, constant2)
- if (m_value->child(1)->hasInt()) {
- int64_t constant2 = m_value->child(1)->asInt();
- switch (m_value->child(0)->opcode()) {
- case BitOr:
- case BitXor:
- if (m_value->child(0)->child(1)->hasInt()
- && !(m_value->child(0)->child(1)->asInt() & constant2)) {
- m_value->child(0) = m_value->child(0)->child(0);
- m_changed = true;
- break;
- }
- break;
- default:
- 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)) {
- replaceWithIdentity(m_value->child(0));
- break;
- }
-
- // Turn this: BitOr(value, zero-constant)
- // Into this: value.
- if (m_value->child(1)->isInt(0)) {
- replaceWithIdentity(m_value->child(0));
- 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))) {
- replaceWithIdentity(m_value->child(1));
- 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)) {
- replaceWithIdentity(m_value->child(0));
- 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 (handleShiftAmount())
- 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 (m_value->child(1)->hasInt32()
- && m_value->child(0)->opcode() == Shl
- && m_value->child(0)->child(1)->hasInt32()
- && m_value->child(1)->asInt32() == m_value->child(0)->child(1)->asInt32()) {
- switch (m_value->child(1)->asInt32()) {
- case 16:
- if (m_value->type() == Int32) {
- // Turn this: SShr(Shl(value, 16), 16)
- // Into this: SExt16(value)
- replaceWithNewValue(
- m_proc.add<Value>(
- SExt16, m_value->origin(), m_value->child(0)->child(0)));
- }
- break;
-
- case 24:
- if (m_value->type() == Int32) {
- // Turn this: SShr(Shl(value, 24), 24)
- // Into this: SExt8(value)
- replaceWithNewValue(
- m_proc.add<Value>(
- SExt8, m_value->origin(), m_value->child(0)->child(0)));
- }
- break;
-
- case 32:
- if (m_value->type() == Int64) {
- // Turn this: SShr(Shl(value, 32), 32)
- // Into this: SExt32(Trunc(value))
- replaceWithNewValue(
- m_proc.add<Value>(
- SExt32, m_value->origin(),
- m_insertionSet.insert<Value>(
- m_index, Trunc, m_value->origin(),
- m_value->child(0)->child(0))));
- }
- break;
-
- // FIXME: Add cases for 48 and 56, but that would translate to SExt32(SExt8) or
- // SExt32(SExt16), which we don't currently lower efficiently.
-
- default:
- break;
- }
-
- if (m_value->opcode() != SShr)
- break;
- }
-
- if (handleShiftAmount())
- 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 (handleShiftAmount())
- break;
-
- break;
-
- case Abs:
- // Turn this: Abs(constant)
- // Into this: fabs<value->type()>(constant)
- if (Value* constant = m_value->child(0)->absConstant(m_proc)) {
- replaceWithNewValue(constant);
- break;
- }
-
- // Turn this: Abs(Abs(value))
- // Into this: Abs(value)
- if (m_value->child(0)->opcode() == Abs) {
- replaceWithIdentity(m_value->child(0));
- break;
- }
-
- // Turn this: Abs(BitwiseCast(value))
- // Into this: BitwiseCast(And(value, mask-top-bit))
- if (m_value->child(0)->opcode() == BitwiseCast) {
- Value* mask;
- if (m_value->type() == Double)
- mask = m_insertionSet.insert<Const64Value>(m_index, m_value->origin(), ~(1ll << 63));
- else
- mask = m_insertionSet.insert<Const32Value>(m_index, m_value->origin(), ~(1l << 31));
-
- Value* bitAnd = m_insertionSet.insert<Value>(m_index, BitAnd, m_value->origin(),
- m_value->child(0)->child(0),
- mask);
- Value* cast = m_insertionSet.insert<Value>(m_index, BitwiseCast, m_value->origin(), bitAnd);
- replaceWithIdentity(cast);
- break;
- }
- break;
-
- case Ceil:
- // Turn this: Ceil(constant)
- // Into this: ceil<value->type()>(constant)
- if (Value* constant = m_value->child(0)->ceilConstant(m_proc)) {
- replaceWithNewValue(constant);
- break;
- }
-
- // Turn this: Ceil(roundedValue)
- // Into this: roundedValue
- if (m_value->child(0)->isRounded()) {
- replaceWithIdentity(m_value->child(0));
- break;
- }
- break;
-
- case Floor:
- // Turn this: Floor(constant)
- // Into this: floor<value->type()>(constant)
- if (Value* constant = m_value->child(0)->floorConstant(m_proc)) {
- replaceWithNewValue(constant);
- break;
- }
-
- // Turn this: Floor(roundedValue)
- // Into this: roundedValue
- if (m_value->child(0)->isRounded()) {
- replaceWithIdentity(m_value->child(0));
- break;
- }
- break;
-
- case Sqrt:
- // Turn this: Sqrt(constant)
- // Into this: sqrt<value->type()>(constant)
- if (Value* constant = m_value->child(0)->sqrtConstant(m_proc)) {
- replaceWithNewValue(constant);
- break;
- }
- break;
-
- case BitwiseCast:
- // Turn this: BitwiseCast(constant)
- // Into this: bitwise_cast<value->type()>(constant)
- if (Value* constant = m_value->child(0)->bitwiseCastConstant(m_proc)) {
- replaceWithNewValue(constant);
- break;
- }
-
- // Turn this: BitwiseCast(BitwiseCast(value))
- // Into this: value
- if (m_value->child(0)->opcode() == BitwiseCast) {
- replaceWithIdentity(m_value->child(0)->child(0));
- break;
- }
- break;
-
- case SExt8:
- // Turn this: SExt8(constant)
- // Into this: static_cast<int8_t>(constant)
- if (m_value->child(0)->hasInt32()) {
- int32_t result = static_cast<int8_t>(m_value->child(0)->asInt32());
- replaceWithNewValue(m_proc.addIntConstant(m_value, result));
- break;
- }
-
- // Turn this: SExt8(SExt8(value))
- // or this: SExt8(SExt16(value))
- // Into this: SExt8(value)
- if (m_value->child(0)->opcode() == SExt8 || m_value->child(0)->opcode() == SExt16) {
- m_value->child(0) = m_value->child(0)->child(0);
- m_changed = true;
- }
-
- if (m_value->child(0)->opcode() == BitAnd && m_value->child(0)->child(1)->hasInt32()) {
- Value* input = m_value->child(0)->child(0);
- int32_t mask = m_value->child(0)->child(1)->asInt32();
-
- // Turn this: SExt8(BitAnd(input, mask)) where (mask & 0xff) == 0xff
- // Into this: SExt8(input)
- if ((mask & 0xff) == 0xff) {
- m_value->child(0) = input;
- m_changed = true;
- break;
- }
-
- // Turn this: SExt8(BitAnd(input, mask)) where (mask & 0x80) == 0
- // Into this: BitAnd(input, const & 0x7f)
- if (!(mask & 0x80)) {
- replaceWithNewValue(
- m_proc.add<Value>(
- BitAnd, m_value->origin(), input,
- m_insertionSet.insert<Const32Value>(
- m_index, m_value->origin(), mask & 0x7f)));
- break;
- }
- }
- break;
-
- case SExt16:
- // Turn this: SExt16(constant)
- // Into this: static_cast<int16_t>(constant)
- if (m_value->child(0)->hasInt32()) {
- int32_t result = static_cast<int16_t>(m_value->child(0)->asInt32());
- replaceWithNewValue(m_proc.addIntConstant(m_value, result));
- break;
- }
-
- // Turn this: SExt16(SExt16(value))
- // Into this: SExt16(value)
- if (m_value->child(0)->opcode() == SExt16) {
- m_value->child(0) = m_value->child(0)->child(0);
- m_changed = true;
- }
-
- // Turn this: SExt16(SExt8(value))
- // Into this: SExt8(value)
- if (m_value->child(0)->opcode() == SExt8) {
- replaceWithIdentity(m_value->child(0));
- break;
- }
-
- if (m_value->child(0)->opcode() == BitAnd && m_value->child(0)->child(1)->hasInt32()) {
- Value* input = m_value->child(0)->child(0);
- int32_t mask = m_value->child(0)->child(1)->asInt32();
-
- // Turn this: SExt16(BitAnd(input, mask)) where (mask & 0xffff) == 0xffff
- // Into this: SExt16(input)
- if ((mask & 0xffff) == 0xffff) {
- m_value->child(0) = input;
- m_changed = true;
- break;
- }
-
- // Turn this: SExt16(BitAnd(input, mask)) where (mask & 0x8000) == 0
- // Into this: BitAnd(input, const & 0x7fff)
- if (!(mask & 0x8000)) {
- replaceWithNewValue(
- m_proc.add<Value>(
- BitAnd, m_value->origin(), input,
- m_insertionSet.insert<Const32Value>(
- m_index, m_value->origin(), mask & 0x7fff)));
- break;
- }
- }
- break;
-
- case SExt32:
- // Turn this: SExt32(constant)
- // Into this: static_cast<int64_t>(constant)
- if (m_value->child(0)->hasInt32()) {
- replaceWithNewValue(m_proc.addIntConstant(m_value, m_value->child(0)->asInt32()));
- break;
- }
-
- // Turn this: SExt32(BitAnd(input, mask)) where (mask & 0x80000000) == 0
- // Into this: ZExt32(BitAnd(input, mask))
- if (m_value->child(0)->opcode() == BitAnd && m_value->child(0)->child(1)->hasInt32()
- && !(m_value->child(0)->child(1)->asInt32() & 0x80000000)) {
- replaceWithNewValue(
- m_proc.add<Value>(
- ZExt32, m_value->origin(), m_value->child(0)));
- break;
- }
- break;
-
- case ZExt32:
- // Turn this: ZExt32(constant)
- // Into this: static_cast<uint64_t>(static_cast<uint32_t>(constant))
- if (m_value->child(0)->hasInt32()) {
- replaceWithNewValue(
- m_proc.addIntConstant(
- m_value,
- static_cast<uint64_t>(static_cast<uint32_t>(m_value->child(0)->asInt32()))));
- break;
- }
- break;
-
- case Trunc:
- // Turn this: Trunc(constant)
- // Into this: static_cast<int32_t>(constant)
- if (m_value->child(0)->hasInt64()) {
- replaceWithNewValue(
- m_proc.addIntConstant(m_value, static_cast<int32_t>(m_value->child(0)->asInt64())));
- break;
- }
-
- // Turn this: Trunc(SExt32(value)) or Trunc(ZExt32(value))
- // Into this: value
- if (m_value->child(0)->opcode() == SExt32 || m_value->child(0)->opcode() == ZExt32) {
- replaceWithIdentity(m_value->child(0)->child(0));
- break;
- }
-
- // Turn this: Trunc(Op(value, constant))
- // where !(constant & 0xffffffff)
- // and Op is Add, Sub, BitOr, or BitXor
- // into this: Trunc(value)
- switch (m_value->child(0)->opcode()) {
- case Add:
- case Sub:
- case BitOr:
- case BitXor:
- if (m_value->child(0)->child(1)->hasInt64()
- && !(m_value->child(0)->child(1)->asInt64() & 0xffffffffll)) {
- m_value->child(0) = m_value->child(0)->child(0);
- m_changed = true;
- break;
- }
- break;
- default:
- break;
- }
- break;
-
- case FloatToDouble:
- // Turn this: FloatToDouble(constant)
- // Into this: ConstDouble(constant)
- if (Value* constant = m_value->child(0)->floatToDoubleConstant(m_proc)) {
- replaceWithNewValue(constant);
- break;
- }
- break;
-
- case DoubleToFloat:
- // Turn this: DoubleToFloat(FloatToDouble(value))
- // Into this: value
- if (m_value->child(0)->opcode() == FloatToDouble) {
- replaceWithIdentity(m_value->child(0)->child(0));
- break;
- }
-
- // Turn this: DoubleToFloat(constant)
- // Into this: ConstFloat(constant)
- if (Value* constant = m_value->child(0)->doubleToFloatConstant(m_proc)) {
- replaceWithNewValue(constant);
- break;
- }
- break;
-
- case Select:
- // Turn this: Select(constant, a, b)
- // Into this: constant ? a : b
- if (m_value->child(0)->hasInt32()) {
- replaceWithIdentity(
- m_value->child(0)->asInt32() ? m_value->child(1) : m_value->child(2));
- break;
- }
-
- // Turn this: Select(Equal(x, 0), a, b)
- // Into this: Select(x, b, a)
- if (m_value->child(0)->opcode() == Equal && m_value->child(0)->child(1)->isInt(0)) {
- m_value->child(0) = m_value->child(0)->child(0);
- std::swap(m_value->child(1), m_value->child(2));
- m_changed = true;
- break;
- }
-
- // Turn this: Select(BitXor(bool, 1), a, b)
- // Into this: Select(bool, b, a)
- if (m_value->child(0)->opcode() == BitXor
- && m_value->child(0)->child(1)->isInt32(1)
- && m_value->child(0)->child(0)->returnsBool()) {
- m_value->child(0) = m_value->child(0)->child(0);
- std::swap(m_value->child(1), m_value->child(2));
- m_changed = true;
- break;
- }
-
- // Turn this: Select(BitAnd(bool, xyz1), a, b)
- // Into this: Select(bool, a, b)
- if (m_value->child(0)->opcode() == BitAnd
- && m_value->child(0)->child(1)->hasInt()
- && m_value->child(0)->child(1)->asInt() & 1
- && m_value->child(0)->child(0)->returnsBool()) {
- m_value->child(0) = m_value->child(0)->child(0);
- m_changed = true;
- break;
- }
-
- // Turn this: Select(stuff, x, x)
- // Into this: x
- if (m_value->child(1) == m_value->child(2)) {
- replaceWithIdentity(m_value->child(1));
- break;
- }
- break;
-
- case Load8Z:
- case Load8S:
- case Load16Z:
- case Load16S:
- case Load:
- case Store8:
- case Store16:
- 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: Load(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 CCall: {
- // Turn this: Call(fmod, constant1, constant2)
- // Into this: fcall-constant(constant1, constant2)
- double(*fmodDouble)(double, double) = fmod;
- if (m_value->type() == Double
- && m_value->numChildren() == 3
- && m_value->child(0)->isIntPtr(reinterpret_cast<intptr_t>(fmodDouble))
- && m_value->child(1)->type() == Double
- && m_value->child(2)->type() == Double) {
- replaceWithNewValue(m_value->child(1)->modConstant(m_proc, m_value->child(2)));
- }
- 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)) {
- replaceWithIdentity(m_value->child(0));
- 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)) {
- replaceWithIdentity(m_value->child(0));
- 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_insertionSet.insertIntConstant(m_index, m_value->origin(), Int32, 0));
- 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 EqualOrUnordered:
- handleCommutativity();
-
- // Turn this: Equal(const1, const2)
- // Into this: isunordered(const1, const2) || const1 == const2.
- // Turn this: Equal(value, const_NaN)
- // Into this: 1.
- replaceWithNewValue(
- m_proc.addBoolConstant(
- m_value->origin(),
- m_value->child(1)->equalOrUnorderedConstant(m_value->child(0))));
- break;
-
- case CheckAdd: {
- if (replaceWithNewValue(m_value->child(0)->checkAddConstant(m_proc, m_value->child(1))))
- break;
-
- handleCommutativity();
-
- if (m_value->child(1)->isInt(0)) {
- replaceWithIdentity(m_value->child(0));
- break;
- }
-
- IntRange leftRange = rangeFor(m_value->child(0));
- IntRange rightRange = rangeFor(m_value->child(1));
- if (!leftRange.couldOverflowAdd(rightRange, m_value->type())) {
- replaceWithNewValue(
- m_proc.add<Value>(Add, m_value->origin(), m_value->child(0), m_value->child(1)));
- break;
- }
- break;
- }
-
- case CheckSub: {
- if (replaceWithNewValue(m_value->child(0)->checkSubConstant(m_proc, m_value->child(1))))
- break;
-
- if (m_value->child(1)->isInt(0)) {
- replaceWithIdentity(m_value->child(0));
- break;
- }
-
- if (Value* negatedConstant = m_value->child(1)->checkNegConstant(m_proc)) {
- m_insertionSet.insertValue(m_index, negatedConstant);
- m_value->as<CheckValue>()->convertToAdd();
- m_value->child(1) = negatedConstant;
- m_changed = true;
- break;
- }
-
- IntRange leftRange = rangeFor(m_value->child(0));
- IntRange rightRange = rangeFor(m_value->child(1));
- if (!leftRange.couldOverflowSub(rightRange, m_value->type())) {
- replaceWithNewValue(
- m_proc.add<Value>(Sub, m_value->origin(), m_value->child(0), m_value->child(1)));
- break;
- }
- break;
- }
-
- case CheckMul: {
- if (replaceWithNewValue(m_value->child(0)->checkMulConstant(m_proc, m_value->child(1))))
- break;
-
- handleCommutativity();
-
- if (m_value->child(1)->hasInt()) {
- bool modified = true;
- switch (m_value->child(1)->asInt()) {
- case 0:
- replaceWithNewValue(m_proc.addIntConstant(m_value, 0));
- break;
- case 1:
- replaceWithIdentity(m_value->child(0));
- break;
- case 2:
- m_value->as<CheckValue>()->convertToAdd();
- m_value->child(1) = m_value->child(0);
- m_changed = true;
- break;
- default:
- modified = false;
- break;
- }
- if (modified)
- break;
- }
-
- IntRange leftRange = rangeFor(m_value->child(0));
- IntRange rightRange = rangeFor(m_value->child(1));
- if (!leftRange.couldOverflowMul(rightRange, m_value->type())) {
- replaceWithNewValue(
- m_proc.add<Value>(Mul, m_value->origin(), m_value->child(0), m_value->child(1)));
- break;
- }
- break;
- }
-
- case Check: {
- CheckValue* checkValue = m_value->as<CheckValue>();
-
- if (checkValue->child(0)->isLikeZero()) {
- checkValue->replaceWithNop();
- m_changed = true;
- break;
- }
-
- if (checkValue->child(0)->isLikeNonZero()) {
- PatchpointValue* patchpoint =
- m_insertionSet.insert<PatchpointValue>(m_index, Void, checkValue->origin());
-
- patchpoint->effects = Effects();
- patchpoint->effects.reads = HeapRange::top();
- patchpoint->effects.exitsSideways = true;
-
- for (unsigned i = 1; i < checkValue->numChildren(); ++i)
- patchpoint->append(checkValue->constrainedChild(i));
-
- patchpoint->setGenerator(checkValue->generator());
-
- // Replace the rest of the block with an Oops.
- for (unsigned i = m_index + 1; i < m_block->size() - 1; ++i)
- m_block->at(i)->replaceWithNop();
- m_block->last()->as<ControlValue>()->convertToOops();
- m_block->last()->setOrigin(checkValue->origin());
-
- // Replace ourselves last.
- checkValue->replaceWithNop();
- m_changedCFG = true;
- break;
- }
-
- if (checkValue->child(0)->opcode() == NotEqual
- && checkValue->child(0)->child(1)->isInt(0)) {
- checkValue->child(0) = checkValue->child(0)->child(0);
- m_changed = true;
- }
-
- // If we are checking some bounded-size SSA expression that leads to a Select that
- // has a constant as one of its results, then turn the Select into a Branch and split
- // the code between the Check and the Branch. For example, this:
- //
- // @a = Select(@p, @x, 42)
- // @b = Add(@a, 35)
- // Check(@b)
- //
- // becomes this:
- //
- // Branch(@p, #truecase, #falsecase)
- //
- // BB#truecase:
- // @b_truecase = Add(@x, 35)
- // Check(@b_truecase)
- // Upsilon(@x, ^a)
- // Upsilon(@b_truecase, ^b)
- // Jump(#continuation)
- //
- // BB#falsecase:
- // @b_falsecase = Add(42, 35)
- // Check(@b_falsecase)
- // Upsilon(42, ^a)
- // Upsilon(@b_falsecase, ^b)
- // Jump(#continuation)
- //
- // BB#continuation:
- // @a = Phi()
- // @b = Phi()
- //
- // The goal of this optimization is to kill a lot of code in one of those basic
- // blocks. This is pretty much guaranteed since one of those blocks will replace all
- // uses of the Select with a constant, and that constant will be transitively used
- // from the check.
- static const unsigned selectSpecializationBound = 3;
- Value* select = findRecentNodeMatching(
- m_value->child(0), selectSpecializationBound,
- [&] (Value* value) -> bool {
- return value->opcode() == Select
- && (value->child(1)->isConstant() && value->child(2)->isConstant());
- });
- if (select) {
- specializeSelect(select);
- break;
- }
- 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;
- }
-
- // Turn this: Branch(BitAnd(bool, xyb1), then, else)
- // Into this: Branch(bool, then, else)
- if (branch->child(0)->opcode() == BitAnd
- && branch->child(0)->child(1)->hasInt()
- && branch->child(0)->child(1)->asInt() & 1
- && branch->child(0)->child(0)->returnsBool()) {
- branch->child(0) = branch->child(0)->child(0);
- 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;
- }
-
- // If a check for the same property dominates us, we can kill the branch. This sort
- // of makes sense here because it's cheap, but hacks like this show that we're going
- // to need SCCP.
- Value* check = m_pureCSE.findMatch(
- ValueKey(Check, Void, branch->child(0)), m_block, *m_dominators);
- if (check) {
- // The Check would have side-exited if child(0) was non-zero. So, it must be
- // zero here.
- branch->taken().block()->removePredecessor(m_block);
- branch->convertToJump(branch->notTaken().block());
- m_changedCFG = true;
- }
- break;
- }
-
- default:
- break;
- }
- }
-
- // Find a node that:
- // - functor(node) returns true.
- // - it's reachable from the given node via children.
- // - it's in the last "bound" slots in the current basic block.
- // This algorithm is optimized under the assumption that the bound is small.
- template<typename Functor>
- Value* findRecentNodeMatching(Value* start, unsigned bound, const Functor& functor)
- {
- unsigned startIndex = bound < m_index ? m_index - bound : 0;
- Value* result = nullptr;
- start->walk(
- [&] (Value* value) -> Value::WalkStatus {
- bool found = false;
- for (unsigned i = startIndex; i <= m_index; ++i) {
- if (m_block->at(i) == value)
- found = true;
- }
- if (!found)
- return Value::IgnoreChildren;
-
- if (functor(value)) {
- result = value;
- return Value::Stop;
- }
-
- return Value::Continue;
- });
- return result;
- }
-
- // This specializes a sequence of code up to a Select. This doesn't work when we're at a
- // terminal. It would be cool to fix that eventually. The main problem is that instead of
- // splitting the block, we should just insert the then/else blocks. We'll have to create
- // double the Phis and double the Upsilons. It'll probably be the sort of optimization that
- // we want to do only after we've done loop optimizations, since this will *definitely*
- // obscure things. In fact, even this simpler form of select specialization will possibly
- // obscure other optimizations. It would be great to have two modes of strength reduction,
- // one that does obscuring optimizations and runs late, and another that does not do
- // obscuring optimizations and runs early.
- // FIXME: Make select specialization handle branches.
- // FIXME: Have a form of strength reduction that does no obscuring optimizations and runs
- // early.
- void specializeSelect(Value* source)
- {
- if (verbose)
- dataLog("Specializing select: ", deepDump(m_proc, source), "\n");
-
- // This mutates startIndex to account for the fact that m_block got the front of it
- // chopped off.
- BasicBlock* predecessor =
- m_blockInsertionSet.splitForward(m_block, m_index, &m_insertionSet);
-
- // Splitting will commit the insertion set, which changes the exact position of the
- // source. That's why we do the search after splitting.
- unsigned startIndex = UINT_MAX;
- for (unsigned i = predecessor->size(); i--;) {
- if (predecessor->at(i) == source) {
- startIndex = i;
- break;
- }
- }
-
- RELEASE_ASSERT(startIndex != UINT_MAX);
-
- // By BasicBlock convention, caseIndex == 0 => then, caseIndex == 1 => else.
- static const unsigned numCases = 2;
- BasicBlock* cases[numCases];
- for (unsigned i = 0; i < numCases; ++i)
- cases[i] = m_blockInsertionSet.insertBefore(m_block);
-
- HashMap<Value*, Value*> mappings[2];
-
- // Save things we want to know about the source.
- Value* predicate = source->child(0);
-
- for (unsigned i = 0; i < numCases; ++i)
- mappings[i].add(source, source->child(1 + i));
-
- auto cloneValue = [&] (Value* value) {
- ASSERT(value != source);
-
- for (unsigned i = 0; i < numCases; ++i) {
- Value* clone = m_proc.clone(value);
- for (Value*& child : clone->children()) {
- if (Value* newChild = mappings[i].get(child))
- child = newChild;
- }
- if (value->type() != Void)
- mappings[i].add(value, clone);
-
- cases[i]->append(clone);
- if (value->type() != Void)
- cases[i]->appendNew<UpsilonValue>(m_proc, value->origin(), clone, value);
- }
-
- value->replaceWithPhi();
- };
-
- // The jump that the splitter inserted is of no use to us.
- predecessor->removeLast(m_proc);
-
- // Hance the source, it's special.
- for (unsigned i = 0; i < numCases; ++i) {
- cases[i]->appendNew<UpsilonValue>(
- m_proc, source->origin(), source->child(1 + i), source);
- }
- source->replaceWithPhi();
- m_insertionSet.insertValue(m_index, source);
-
- // Now handle all values between the source and the check.
- for (unsigned i = startIndex + 1; i < predecessor->size(); ++i) {
- Value* value = predecessor->at(i);
- value->owner = nullptr;
-
- cloneValue(value);
-
- if (value->type() != Void)
- m_insertionSet.insertValue(m_index, value);
- else
- m_proc.deleteValue(value);
- }
-
- // Finally, deal with the check.
- cloneValue(m_value);
-
- // Remove the values from the predecessor.
- predecessor->values().resize(startIndex);
-
- predecessor->appendNew<ControlValue>(
- m_proc, Branch, source->origin(), predicate,
- FrequentedBlock(cases[0]), FrequentedBlock(cases[1]));
-
- for (unsigned i = 0; i < numCases; ++i) {
- cases[i]->appendNew<ControlValue>(
- m_proc, Jump, m_value->origin(), FrequentedBlock(m_block));
- }
-
- m_changed = true;
-
- predecessor->updatePredecessorsAfter();
- }
-
- // 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()
- {
- // Note that we have commutative operations that take more than two children. Those operations may
- // commute their first two children while leaving the rest unaffected.
- ASSERT(m_value->numChildren() >= 2);
-
- // 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;
- }
- }
-
- // FIXME: This should really be a forward analysis. Instead, we uses a bounded-search backwards
- // analysis.
- IntRange rangeFor(Value* value, unsigned timeToLive = 5)
- {
- if (!timeToLive)
- return IntRange::top(value->type());
-
- switch (value->opcode()) {
- case Const32:
- case Const64: {
- int64_t intValue = value->asInt();
- return IntRange(intValue, intValue);
- }
-
- case BitAnd:
- if (value->child(1)->hasInt())
- return IntRange::rangeForMask(value->child(1)->asInt(), value->type());
- break;
-
- case SShr:
- if (value->child(1)->hasInt32()) {
- return rangeFor(value->child(0), timeToLive - 1).sShr(
- value->child(1)->asInt32(), value->type());
- }
- break;
-
- case ZShr:
- if (value->child(1)->hasInt32()) {
- return rangeFor(value->child(0), timeToLive - 1).zShr(
- value->child(1)->asInt32(), value->type());
- }
- break;
-
- case Shl:
- if (value->child(1)->hasInt32()) {
- return rangeFor(value->child(0), timeToLive - 1).shl(
- value->child(1)->asInt32(), value->type());
- }
- break;
-
- case Add:
- return rangeFor(value->child(0), timeToLive - 1).add(
- rangeFor(value->child(1), timeToLive - 1), value->type());
-
- case Sub:
- return rangeFor(value->child(0), timeToLive - 1).sub(
- rangeFor(value->child(1), timeToLive - 1), value->type());
-
- case Mul:
- return rangeFor(value->child(0), timeToLive - 1).mul(
- rangeFor(value->child(1), timeToLive - 1), value->type());
-
- default:
- break;
- }
-
- return IntRange::top(value->type());
- }
-
- template<typename ValueType, typename... Arguments>
- void replaceWithNew(Arguments... arguments)
- {
- replaceWithNewValue(m_proc.add<ValueType>(arguments...));
- }
-
- bool replaceWithNewValue(Value* newValue)
- {
- if (!newValue)
- return false;
- m_insertionSet.insertValue(m_index, newValue);
- m_value->replaceWithIdentity(newValue);
- m_changed = true;
- return true;
- }
-
- void replaceWithIdentity(Value* newValue)
- {
- m_value->replaceWithIdentity(newValue);
- m_changed = true;
- }
-
- bool handleShiftAmount()
- {
- // Shift anything by zero is identity.
- if (m_value->child(1)->isInt32(0)) {
- replaceWithIdentity(m_value->child(0));
- return true;
- }
-
- // The shift already masks its shift amount. If the shift amount is being masked by a
- // redundant amount, then remove the mask. For example,
- // Turn this: Shl(@x, BitAnd(@y, 63))
- // Into this: Shl(@x, @y)
- unsigned mask = sizeofType(m_value->type()) * 8 - 1;
- if (m_value->child(1)->opcode() == BitAnd
- && m_value->child(1)->child(1)->hasInt32()
- && (m_value->child(1)->child(1)->asInt32() & mask) == mask) {
- m_value->child(1) = m_value->child(1)->child(0);
- m_changed = true;
- // Don't need to return true, since we're still the same shift, and we can still cascade
- // through other optimizations.
- }
-
- return false;
- }
-
- void replaceIfRedundant()
- {
- m_changed |= m_pureCSE.process(m_value, *m_dominators);
- }
-
- 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;
- // We don't care about effects of SSA operations, since we model them more
- // accurately than the effects() method does.
- if (value->opcode() != Phi && value->opcode() != Upsilon)
- effects = value->effects();
-
- 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;
- }
-
- IndexSet<Variable> liveVariables;
-
- 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)) {
- if (VariableValue* variableValue = value->as<VariableValue>())
- liveVariables.add(variableValue->variable());
- 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);
- }
-
- for (Variable* variable : m_proc.variables()) {
- if (!liveVariables.contains(variable))
- m_proc.deleteVariable(variable);
- }
- }
-
- void simplifySSA()
- {
- // This runs Aycock and Horspool's algorithm on our Phi functions [1]. For most CFG patterns,
- // this can take a suboptimal arrangement of Phi functions and make it optimal, as if you had
- // run Cytron, Ferrante, Rosen, Wegman, and Zadeck. It's only suboptimal for irreducible
- // CFGs. In practice, that doesn't matter, since we expect clients of B3 to run their own SSA
- // conversion before lowering to B3, and in the case of the DFG, that conversion uses Cytron
- // et al. In that context, this algorithm is intended to simplify Phi functions that were
- // made redundant by prior CFG simplification. But according to Aycock and Horspool's paper,
- // this algorithm is good enough that a B3 client could just give us maximal Phi's (i.e. Phi
- // for each variable at each basic block) and we will make them optimal.
- // [1] http://pages.cpsc.ucalgary.ca/~aycock/papers/ssa.ps
-
- // Aycock and Horspool prescribe two rules that are to be run to fixpoint:
- //
- // 1) If all of the Phi's children are the same (i.e. it's one child referenced from one or
- // more Upsilons), then replace all uses of the Phi with the one child.
- //
- // 2) If all of the Phi's children are either the Phi itself or exactly one other child, then
- // replace all uses of the Phi with the one other child.
- //
- // Rule (2) subsumes rule (1), so we can just run (2). We only run one fixpoint iteration
- // here. This premise is that in common cases, this will only find optimization opportunities
- // as a result of CFG simplification and usually CFG simplification will only do one round
- // of block merging per ReduceStrength fixpoint iteration, so it's OK for this to only do one
- // round of Phi merging - since Phis are the value analogue of blocks.
-
- PhiChildren phiChildren(m_proc);
-
- for (Value* phi : phiChildren.phis()) {
- Value* otherChild = nullptr;
- bool ok = true;
- for (Value* child : phiChildren[phi].values()) {
- if (child == phi)
- continue;
- if (child == otherChild)
- continue;
- if (!otherChild) {
- otherChild = child;
- continue;
- }
- ok = false;
- break;
- }
- if (!ok)
- continue;
- if (!otherChild) {
- // Wow, this would be super weird. It probably won't happen, except that things could
- // get weird as a consequence of stepwise simplifications in the strength reduction
- // fixpoint.
- continue;
- }
-
- // Turn the Phi into an Identity and turn the Upsilons into Nops.
- m_changed = true;
- for (Value* upsilon : phiChildren[phi])
- upsilon->replaceWithNop();
- phi->replaceWithIdentity(otherChild);
- }
- }
-
- Procedure& m_proc;
- InsertionSet m_insertionSet;
- BlockInsertionSet m_blockInsertionSet;
- BasicBlock* m_block { nullptr };
- unsigned m_index { 0 };
- Value* m_value { nullptr };
- Dominators* m_dominators { nullptr };
- PureCSE m_pureCSE;
- bool m_changed { false };
- bool m_changedCFG { false };
-};
-
-} // anonymous namespace
-
-bool reduceStrength(Procedure& proc)
-{
- PhaseScope phaseScope(proc, "reduceStrength");
- ReduceStrength reduceStrength(proc);
- return reduceStrength.run();
-}
-
-} } // namespace JSC::B3
-
-#endif // ENABLE(B3_JIT)
-