summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/b3/air/AirTmpWidth.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/b3/air/AirTmpWidth.cpp')
-rw-r--r--Source/JavaScriptCore/b3/air/AirTmpWidth.cpp183
1 files changed, 183 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/b3/air/AirTmpWidth.cpp b/Source/JavaScriptCore/b3/air/AirTmpWidth.cpp
new file mode 100644
index 000000000..f1173c022
--- /dev/null
+++ b/Source/JavaScriptCore/b3/air/AirTmpWidth.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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 "AirTmpWidth.h"
+
+#if ENABLE(B3_JIT)
+
+#include "AirCode.h"
+#include "AirInstInlines.h"
+#include <wtf/ListDump.h>
+
+namespace JSC { namespace B3 { namespace Air {
+
+TmpWidth::TmpWidth()
+{
+}
+
+TmpWidth::TmpWidth(Code& code)
+{
+ recompute(code);
+}
+
+TmpWidth::~TmpWidth()
+{
+}
+
+void TmpWidth::recompute(Code& code)
+{
+ // Set this to true to cause this analysis to always return pessimistic results.
+ const bool beCareful = false;
+
+ const bool verbose = false;
+
+ if (verbose) {
+ dataLog("Code before TmpWidth:\n");
+ dataLog(code);
+ }
+
+ m_width.clear();
+
+ auto assumeTheWorst = [&] (Tmp tmp) {
+ Widths& widths = m_width.add(tmp, Widths()).iterator->value;
+ Arg::Type type = Arg(tmp).type();
+ widths.use = Arg::conservativeWidth(type);
+ widths.def = Arg::conservativeWidth(type);
+ };
+
+ // Assume the worst for registers.
+ RegisterSet::allRegisters().forEach(
+ [&] (Reg reg) {
+ assumeTheWorst(Tmp(reg));
+ });
+
+ if (beCareful) {
+ code.forAllTmps(assumeTheWorst);
+
+ // We fall through because the fixpoint that follows can only make things even more
+ // conservative. This mode isn't meant to be fast, just safe.
+ }
+
+ // Now really analyze everything but Move's over Tmp's, but set aside those Move's so we can find
+ // them quickly during the fixpoint below. Note that we can make this analysis stronger by
+ // recognizing more kinds of Move's or anything that has Move-like behavior, though it's probably not
+ // worth it.
+ Vector<Inst*> moves;
+ for (BasicBlock* block : code) {
+ for (Inst& inst : *block) {
+ if (inst.kind.opcode == Move && inst.args[1].isTmp()) {
+ if (inst.args[0].isTmp()) {
+ // Make sure that both sides of the Move have a width already initialized. The
+ // fixpoint below assumes that it never has to add things to the HashMap.
+ m_width.add(inst.args[0].tmp(), Widths(Arg::GP));
+ m_width.add(inst.args[1].tmp(), Widths(Arg::GP));
+
+ moves.append(&inst);
+ continue;
+ }
+ if (inst.args[0].isImm()
+ && inst.args[0].value() >= 0) {
+ Tmp tmp = inst.args[1].tmp();
+ Widths& widths = m_width.add(tmp, Widths(Arg::GP)).iterator->value;
+
+ if (inst.args[0].value() <= std::numeric_limits<int8_t>::max())
+ widths.def = std::max(widths.def, Arg::Width8);
+ else if (inst.args[0].value() <= std::numeric_limits<int16_t>::max())
+ widths.def = std::max(widths.def, Arg::Width16);
+ else if (inst.args[0].value() <= std::numeric_limits<int32_t>::max())
+ widths.def = std::max(widths.def, Arg::Width32);
+ else
+ widths.def = std::max(widths.def, Arg::Width64);
+
+ continue;
+ }
+ }
+ inst.forEachTmp(
+ [&] (Tmp& tmp, Arg::Role role, Arg::Type type, Arg::Width width) {
+ Widths& widths = m_width.add(tmp, Widths(type)).iterator->value;
+
+ if (Arg::isAnyUse(role))
+ widths.use = std::max(widths.use, width);
+
+ if (Arg::isZDef(role))
+ widths.def = std::max(widths.def, width);
+ else if (Arg::isAnyDef(role))
+ widths.def = Arg::conservativeWidth(type);
+ });
+ }
+ }
+
+ // Finally, fixpoint over the Move's.
+ bool changed = true;
+ while (changed) {
+ changed = false;
+ for (Inst* move : moves) {
+ ASSERT(move->kind.opcode == Move);
+ ASSERT(move->args[0].isTmp());
+ ASSERT(move->args[1].isTmp());
+
+ // We already ensure that both tmps are added to the width map. That's important
+ // because you cannot add both tmps here while simultaneously getting a reference to
+ // their values, since the second add would invalidate the reference returned by the
+ // first one.
+ Widths& srcWidths = m_width.find(move->args[0].tmp())->value;
+ Widths& dstWidths = m_width.find(move->args[1].tmp())->value;
+
+ // Legend:
+ //
+ // Move %src, %dst
+
+ // defWidth(%dst) is a promise about how many high bits are zero. The smaller the width, the
+ // stronger the promise. This Move may weaken that promise if we know that %src is making a
+ // weaker promise. Such forward flow is the only thing that determines defWidth().
+ if (dstWidths.def < srcWidths.def) {
+ dstWidths.def = srcWidths.def;
+ changed = true;
+ }
+
+ // srcWidth(%src) is a promise about how many high bits are ignored. The smaller the width,
+ // the stronger the promise. This Move may weaken that promise if we know that %dst is making
+ // a weaker promise. Such backward flow is the only thing that determines srcWidth().
+ if (srcWidths.use < dstWidths.use) {
+ srcWidths.use = dstWidths.use;
+ changed = true;
+ }
+ }
+ }
+
+ if (verbose)
+ dataLog("width: ", mapDump(m_width), "\n");
+}
+
+void TmpWidth::Widths::dump(PrintStream& out) const
+{
+ out.print("{use = ", use, ", def = ", def, "}");
+}
+
+} } } // namespace JSC::B3::Air
+
+#endif // ENABLE(B3_JIT)
+