FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

GitHub Viewer

/* * Copyright (C) 2013-2019 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 "BinarySwitch.h" #if ENABLE(JIT) #include #include namespace JSC { namespace BinarySwitchInternal { static constexpr bool verbose = false; } static unsigned globalCounter; // We use a different seed every time we are invoked. BinarySwitch::BinarySwitch(GPRReg value, std::span cases, Type type) : m_weakRandom(globalCounter++) , m_type(type) , m_value(value) , m_totalCases(cases.size()) { RELEASE_ASSERT(type == Int32 || type == IntPtr); if (cases.empty()) return; if (BinarySwitchInternal::verbose) dataLog("Original cases: ", listDump(cases), "\n"); m_cases.reserveInitialCapacity(cases.size()); for (unsigned i = 0; i < cases.size(); ++i) m_cases.append(Case(cases[i], i)); std::sort(m_cases.begin(), m_cases.end()); if (BinarySwitchInternal::verbose) dataLog("Sorted cases: ", listDump(m_cases), "\n"); #if ASSERT_ENABLED for (unsigned i = 1; i < m_cases.size(); ++i) ASSERT(m_cases[i - 1] < m_cases[i], i, m_cases.size(), m_cases[i].value, m_cases[i].index); #endif build(0, false, m_cases.size()); } BinarySwitch::BinarySwitch(GPRReg value, std::span runs) : m_weakRandom(globalCounter++) , m_type(UInt32CheckRuns) , m_value(value) { if (runs.empty()) return; m_cases.reserveInitialCapacity(runs.size()); unsigned keyStart = 0; for (auto [firstKey, runLength] : runs) { RELEASE_ASSERT(runLength); RELEASE_ASSERT(firstKey == keyStart); m_cases.append(Case(firstKey, keyStart)); keyStart += static_cast(runLength); } m_totalCases = keyStart; if (BinarySwitchInternal::verbose) dataLog("CheckRuns cases: ", listDump(m_cases), " totalKeys=", m_totalCases, "\n"); #if ASSERT_ENABLED for (unsigned i = 1; i < m_cases.size(); ++i) ASSERT(m_cases[i - 1].index < m_cases[i].index); #endif buildCheckRuns(0, m_cases.size()); } BinarySwitch::~BinarySwitch() = default; bool BinarySwitch::advance(MacroAssembler& jit) { if (m_cases.isEmpty()) { m_fallThrough.append(jit.jump()); return false; } if (m_index == m_branches.size()) { RELEASE_ASSERT(m_jumpStack.isEmpty()); return false; } for (;;) { const BranchCode& code = m_branches[m_index++]; switch (code.kind) { case NotEqualToFallThrough: switch (m_type) { case Int32: m_fallThrough.append(jit.branch32( MacroAssembler::NotEqual, m_value, MacroAssembler::Imm32(static_cast(m_cases[code.index].value)))); break; case IntPtr: m_fallThrough.append(jit.branchPtr( MacroAssembler::NotEqual, m_value, MacroAssembler::ImmPtr(std::bit_cast(static_cast(m_cases[code.index].value))))); break; case UInt32CheckRuns: RELEASE_ASSERT_NOT_REACHED(); break; } break; case NotEqualToPush: switch (m_type) { case Int32: m_jumpStack.append(jit.branch32( MacroAssembler::NotEqual, m_value, MacroAssembler::Imm32(static_cast(m_cases[code.index].value)))); break; case IntPtr: m_jumpStack.append(jit.branchPtr( MacroAssembler::NotEqual, m_value, MacroAssembler::ImmPtr(std::bit_cast(static_cast(m_cases[code.index].value))))); break; case UInt32CheckRuns: RELEASE_ASSERT_NOT_REACHED(); break; } break; case LessThanToPush: switch (m_type) { case Int32: m_jumpStack.append(jit.branch32( MacroAssembler::LessThan, m_value, MacroAssembler::Imm32(static_cast(m_cases[code.index].value)))); break; case IntPtr: m_jumpStack.append(jit.branchPtr( MacroAssembler::LessThan, m_value, MacroAssembler::ImmPtr(std::bit_cast(static_cast(m_cases[code.index].value))))); break; case UInt32CheckRuns: RELEASE_ASSERT_NOT_REACHED(); break; } break; case BelowToPush: ASSERT(isCheckRuns()); m_jumpStack.append(jit.branch32( MacroAssembler::Below, m_value, MacroAssembler::Imm32(static_cast(m_cases[code.index].value)))); break; case AboveOrEqualToFallThrough: ASSERT(isCheckRuns()); m_fallThrough.append(jit.branch32( MacroAssembler::AboveOrEqual, m_value, MacroAssembler::Imm32(static_cast(m_totalCases)))); break; case Pop: m_jumpStack.takeLast().link(&jit); break; case ExecuteCase: m_caseIndex = code.index; return true; } } } class RandomNumberGenerator { public: using result_type = uint32_t; RandomNumberGenerator(WeakRandom& weakRandom) : m_weakRandom(weakRandom) { } uint32_t operator()() { return m_weakRandom.getUint32(); } static constexpr uint32_t NODELETE min() { return std::numeric_limits::min(); } static constexpr uint32_t NODELETE max() { return std::numeric_limits::max(); } private: WeakRandom& m_weakRandom; }; void BinarySwitch::build(unsigned start, bool hardStart, unsigned end) { if (BinarySwitchInternal::verbose) dataLog("Building with start = ", start, ", hardStart = ", hardStart, ", end = ", end, "\n"); auto append = [&] (const BranchCode& code) { if (BinarySwitchInternal::verbose) dataLog("==> ", code, "\n"); m_branches.append(code); }; unsigned size = end - start; RELEASE_ASSERT(size); // This code uses some random numbers to keep things balanced. It's important to keep in mind // that this does not improve average-case throughput under the assumption that all cases fire // with equal probability. It just ensures that there will not be some switch structure that // when combined with some input will always produce pathologically good or pathologically bad // performance. const unsigned leafThreshold = 3; if (size ", code, "\n"); m_branches.append(code); }; unsigned size = end - start; RELEASE_ASSERT(size); if (size == 1) { if (end == m_cases.size()) append(BranchCode(AboveOrEqualToFallThrough)); append(BranchCode(ExecuteCase, start)); return; } unsigned medianIndex = std::midpoint(start, end); if (size & 1) medianIndex += m_weakRandom.getUint32() & 1; RELEASE_ASSERT(medianIndex > start); RELEASE_ASSERT(medianIndex < end); append(BranchCode(BelowToPush, medianIndex)); buildCheckRuns(medianIndex, end); append(BranchCode(Pop)); buildCheckRuns(start, medianIndex); } void BinarySwitch::Case::dump(PrintStream& out) const { out.print(""); } void BinarySwitch::BranchCode::dump(PrintStream& out) const { switch (kind) { case NotEqualToFallThrough: out.print("NotEqualToFallThrough"); break; case NotEqualToPush: out.print("NotEqualToPush"); break; case LessThanToPush: out.print("LessThanToPush"); break; case BelowToPush: out.print("BelowToPush"); break; case AboveOrEqualToFallThrough: out.print("AboveOrEqualToFallThrough"); break; case Pop: out.print("Pop"); break; case ExecuteCase: out.print("ExecuteCase"); break; } if (index != UINT_MAX) out.print("(", index, ")"); } } // namespace JSC #endif // ENABLE(JIT)

Back | FazBrowse Home | New Git URL