[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/WebKit/WebKit/main/Source/JavaScriptCore/jit/BinarySwitch.cpp [Back]  [Original]

/*
 * 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)


Web Proxy Viewer  |  New URL  |  Original Page