/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef jit_JitcodeMap_h
#define jit_JitcodeMap_h

#include "ds/SplayTree.h"
#include "jit/CompactBuffer.h"
#include "jit/CompileInfo.h"
#include "jit/shared/CodeGenerator-shared.h"

namespace js {
namespace jit {

/*
 * The Ion jitcode map implements tables to allow mapping from addresses in ion jitcode
 * to the list of (JSScript *, jsbytecode *) pairs that are implicitly active in the frame at
 * that point in the native code.
 *
 * To represent this information efficiently, a multi-level table is used.
 *
 * At the top level, a global splay-tree of JitcodeGlobalEntry describings the mapping for
 * each individual IonCode script generated by compiles.  The entries are ordered by their
 * nativeStartAddr.
 *
 * Every entry in the table is of fixed size, but there are different entry types,
 * distinguished by the kind field.
 */

class JitcodeIonTable;
class JitcodeRegionEntry;

class JitcodeGlobalEntry
{
  public:
    enum Kind {
        INVALID = 0,
        Ion,
        Baseline,
        IonCache,
        Query,
        LIMIT
    };
    JS_STATIC_ASSERT(LIMIT <= 8);

    struct BytecodeLocation {
        JSScript *script;
        jsbytecode *pc;
        BytecodeLocation(JSScript *script, jsbytecode *pc) : script(script), pc(pc) {}
    };
    typedef Vector<BytecodeLocation, 0, SystemAllocPolicy> BytecodeLocationVector;

    struct BaseEntry
    {
        void *nativeStartAddr_;
        void *nativeEndAddr_;
        Kind kind_;

        void init() {
            nativeStartAddr_ = nullptr;
            nativeEndAddr_ = nullptr;
            kind_ = INVALID;
        }

        void init(Kind kind, void *nativeStartAddr, void *nativeEndAddr) {
            JS_ASSERT(nativeStartAddr);
            JS_ASSERT(nativeEndAddr);
            JS_ASSERT(kind > INVALID && kind < LIMIT);
            nativeStartAddr_ = nativeStartAddr;
            nativeEndAddr_ = nativeEndAddr;
            kind_ = kind;
        }

        Kind kind() const {
            return kind_;
        }
        void *nativeStartAddr() const {
            return nativeStartAddr_;
        }
        void *nativeEndAddr() const {
            return nativeEndAddr_;
        }

        bool startsBelowPointer(void *ptr) const {
            return ((uint8_t *)nativeStartAddr()) <= ((uint8_t *) ptr);
        }
        bool endsAbovePointer(void *ptr) const {
            return ((uint8_t *)nativeEndAddr()) > ((uint8_t *) ptr);
        }
        bool containsPointer(void *ptr) const {
            return startsBelowPointer(ptr) && endsAbovePointer(ptr);
        }
    };

    struct IonEntry : public BaseEntry
    {
        uintptr_t scriptList_;

        // regionTable_ points to the start of the region table within the
        // packed map for compile represented by this entry.  Since the
        // region table occurs at the tail of the memory region, this pointer
        // points somewhere inside the region memory space, and not to the start
        // of the memory space.
        JitcodeIonTable *regionTable_;

        static const unsigned LowBits = 3;
        static const uintptr_t LowMask = (uintptr_t(1) << LowBits) - 1;

        enum ScriptListTag {
            Single = 0,
            Multi = 7
        };

        struct SizedScriptList {
            uint32_t size;
            JSScript *scripts[0];
            SizedScriptList(uint32_t sz, JSScript **scr) : size(sz) {
                for (uint32_t i = 0; i < size; i++)
                    scripts[i] = scr[i];
            }

            static uint32_t AllocSizeFor(uint32_t nscripts) {
                return sizeof(SizedScriptList) + (nscripts * sizeof(JSScript *));
            }
        };

        void init(void *nativeStartAddr, void *nativeEndAddr,
                  JSScript *script, JitcodeIonTable *regionTable)
        {
            JS_ASSERT((uintptr_t(script) & LowMask) == 0);
            JS_ASSERT(script);
            JS_ASSERT(regionTable);
            BaseEntry::init(Ion, nativeStartAddr, nativeEndAddr);
            scriptList_ = uintptr_t(script);
            regionTable_ = regionTable;
        }

        void init(void *nativeStartAddr, void *nativeEndAddr,
                  unsigned numScripts, JSScript **scripts, JitcodeIonTable *regionTable)
        {
            JS_ASSERT((uintptr_t(scripts) & LowMask) == 0);
            JS_ASSERT(numScripts >= 1);
            JS_ASSERT(numScripts <= 6);
            JS_ASSERT(scripts);
            JS_ASSERT(regionTable);
            BaseEntry::init(Ion, nativeStartAddr, nativeEndAddr);
            scriptList_ = uintptr_t(scripts) | numScripts;
            regionTable_ = regionTable;
        }

        void init(void *nativeStartAddr, void *nativeEndAddr,
                  SizedScriptList *scripts, JitcodeIonTable *regionTable)
        {
            JS_ASSERT((uintptr_t(scripts) & LowMask) == 0);
            JS_ASSERT(scripts->size > 6);
            JS_ASSERT(scripts);
            JS_ASSERT(regionTable);

            BaseEntry::init(Ion, nativeStartAddr, nativeEndAddr);
            scriptList_ = uintptr_t(scripts) | uintptr_t(Multi);
            regionTable_ = regionTable;
        }

        ScriptListTag scriptListTag() const {
            return static_cast<ScriptListTag>(scriptList_ & LowMask);
        }
        void *scriptListPointer() const {
            return reinterpret_cast<void *>(scriptList_ & ~LowMask);
        }

        JSScript *singleScript() const {
            JS_ASSERT(scriptListTag() == Single);
            return reinterpret_cast<JSScript *>(scriptListPointer());
        }
        JSScript **rawScriptArray() const {
            JS_ASSERT(scriptListTag() < Multi);
            return reinterpret_cast<JSScript **>(scriptListPointer());
        }
        SizedScriptList *sizedScriptList() const {
            JS_ASSERT(scriptListTag() == Multi);
            return reinterpret_cast<SizedScriptList *>(scriptListPointer());
        }

        unsigned numScripts() const {
            ScriptListTag tag = scriptListTag();
            if (tag == Single)
                return 1;

            if (tag < Multi) {
                JS_ASSERT(int(tag) >= 2);
                return static_cast<unsigned>(tag);
            }

            return sizedScriptList()->size;
        }

        JSScript *getScript(unsigned idx) const {
            JS_ASSERT(idx < numScripts());

            ScriptListTag tag = scriptListTag();

            if (tag == Single)
                return singleScript();

            if (tag < Multi) {
                JS_ASSERT(int(tag) >= 2);
                return rawScriptArray()[idx];
            }

            return sizedScriptList()->scripts[idx];
        }

        void destroy();

        JitcodeIonTable *regionTable() const {
            return regionTable_;
        }

        int scriptIndex(JSScript *script) const {
            unsigned count = numScripts();
            for (unsigned i = 0; i < count; i++) {
                if (getScript(i) == script)
                    return i;
            }
            return -1;
        }

        bool callStackAtAddr(JSRuntime *rt, void *ptr, BytecodeLocationVector &results,
                             uint32_t *depth) const;
    };

    struct BaselineEntry : public BaseEntry
    {
        JSScript *script_;

        void init(void *nativeStartAddr, void *nativeEndAddr, JSScript *script)
        {
            JS_ASSERT(script != nullptr);
            BaseEntry::init(Baseline, nativeStartAddr, nativeEndAddr);
            script_ = script;
        }

        JSScript *script() const {
            return script_;
        }

        void destroy() {}

        bool callStackAtAddr(JSRuntime *rt, void *ptr, BytecodeLocationVector &results,
                             uint32_t *depth) const;
    };

    struct IonCacheEntry : public BaseEntry
    {
        void *rejoinAddr_;

        void init(void *nativeStartAddr, void *nativeEndAddr, void *rejoinAddr)
        {
            JS_ASSERT(rejoinAddr != nullptr);
            BaseEntry::init(IonCache, nativeStartAddr, nativeEndAddr);
            rejoinAddr_ = rejoinAddr;
        }

        void *rejoinAddr() const {
            return rejoinAddr_;
        }

        void destroy() {}

        bool callStackAtAddr(JSRuntime *rt, void *ptr, BytecodeLocationVector &results,
                             uint32_t *depth) const;
    };

    // QueryEntry is never stored in the table, just used for queries
    // where an instance of JitcodeGlobalEntry is required to do tree
    // lookups.
    struct QueryEntry : public BaseEntry
    {
        void init(void *addr) {
            BaseEntry::init(Query, addr, addr);
        }
        uint8_t *addr() const {
            return reinterpret_cast<uint8_t *>(nativeStartAddr());
        }
        void destroy() {}
    };

  private:
    union {
        // Shadowing BaseEntry instance to allow access to base fields
        // and type extraction.
        BaseEntry base_;

        // The most common entry type: describing jitcode generated by
        // Ion main-line code.
        IonEntry ion_;

        // Baseline jitcode.
        BaselineEntry baseline_;

        // IonCache stubs.
        IonCacheEntry ionCache_;

        // When doing queries on the SplayTree for particular addresses,
        // the query addresses are representd using a QueryEntry.
        QueryEntry query_;
    };

  public:
    JitcodeGlobalEntry() {
        base_.init();
    }

    JitcodeGlobalEntry(const IonEntry &ion) {
        ion_ = ion;
    }

    JitcodeGlobalEntry(const BaselineEntry &baseline) {
        baseline_ = baseline;
    }

    JitcodeGlobalEntry(const IonCacheEntry &ionCache) {
        ionCache_ = ionCache;
    }

    JitcodeGlobalEntry(const QueryEntry &query) {
        query_ = query;
    }

    static JitcodeGlobalEntry MakeQuery(void *ptr) {
        QueryEntry query;
        query.init(ptr);
        return JitcodeGlobalEntry(query);
    }

    void destroy() {
        switch (kind()) {
          case Ion:
            ionEntry().destroy();
            break;
          case Baseline:
            baselineEntry().destroy();
            break;
          case IonCache:
            ionCacheEntry().destroy();
            break;
          case Query:
            queryEntry().destroy();
            break;
          default:
            MOZ_ASSUME_UNREACHABLE("Invalid JitcodeGlobalEntry kind.");
        }
    }

    void *nativeStartAddr() const {
        return base_.nativeStartAddr();
    }
    void *nativeEndAddr() const {
        return base_.nativeEndAddr();
    }

    bool startsBelowPointer(void *ptr) const {
        return base_.startsBelowPointer(ptr);
    }
    bool endsAbovePointer(void *ptr) const {
        return base_.endsAbovePointer(ptr);
    }
    bool containsPointer(void *ptr) const {
        return base_.containsPointer(ptr);
    }

    bool overlapsWith(const JitcodeGlobalEntry &entry) const {
        // Catch full containment of |entry| within |this|, and partial overlaps.
        if (containsPointer(entry.nativeStartAddr()) || containsPointer(entry.nativeEndAddr()))
            return true;

        // Catch full containment of |this| within |entry|.
        if (startsBelowPointer(entry.nativeEndAddr()) && endsAbovePointer(entry.nativeStartAddr()))
            return true;

        return false;
    }

    Kind kind() const {
        return base_.kind();
    }

    bool isIon() const {
        return kind() == Ion;
    }
    bool isBaseline() const {
        return kind() == Baseline;
    }
    bool isIonCache() const {
        return kind() == IonCache;
    }
    bool isQuery() const {
        return kind() == Query;
    }

    IonEntry &ionEntry() {
        JS_ASSERT(isIon());
        return ion_;
    }
    BaselineEntry &baselineEntry() {
        JS_ASSERT(isBaseline());
        return baseline_;
    }
    IonCacheEntry &ionCacheEntry() {
        JS_ASSERT(isIonCache());
        return ionCache_;
    }
    QueryEntry &queryEntry() {
        JS_ASSERT(isQuery());
        return query_;
    }

    const IonEntry &ionEntry() const {
        JS_ASSERT(isIon());
        return ion_;
    }
    const BaselineEntry &baselineEntry() const {
        JS_ASSERT(isBaseline());
        return baseline_;
    }
    const IonCacheEntry &ionCacheEntry() const {
        JS_ASSERT(isIonCache());
        return ionCache_;
    }
    const QueryEntry &queryEntry() const {
        JS_ASSERT(isQuery());
        return query_;
    }

    // Read the inline call stack at a given point in the native code and append into
    // the given vector.  Innermost (script,pc) pair will be appended first, and
    // outermost appended last.
    //
    // Returns false on memory failure.
    bool callStackAtAddr(JSRuntime *rt, void *ptr, BytecodeLocationVector &results,
                         uint32_t *depth) const
    {
        switch (kind()) {
          case Ion:
            return ionEntry().callStackAtAddr(rt, ptr, results, depth);
          case Baseline:
            return baselineEntry().callStackAtAddr(rt, ptr, results, depth);
          case IonCache:
            return ionCacheEntry().callStackAtAddr(rt, ptr, results, depth);
          default:
            MOZ_ASSUME_UNREACHABLE("Invalid JitcodeGlobalEntry kind.");
        }
        return false;
    }

    // Figure out the number of the (JSScript *, jsbytecode *) pairs that are active
    // at this location.
    uint32_t lookupInlineCallDepth(void *ptr);

    // Compare two global entries.
    static int compare(const JitcodeGlobalEntry &ent1, const JitcodeGlobalEntry &ent2);
};

/*
 * Global table of JitcodeGlobalEntry values sorted by native address range.
 */
class JitcodeGlobalTable
{
  public:
    typedef SplayTree<JitcodeGlobalEntry, JitcodeGlobalEntry> EntryTree;

    typedef Vector<JitcodeGlobalEntry, 0, SystemAllocPolicy> EntryVector;

  private:
    static const size_t LIFO_CHUNK_SIZE = 16 * 1024;
    LifoAlloc treeAlloc_;
    EntryTree tree_;
    EntryVector entries_;

  public:
    JitcodeGlobalTable() : treeAlloc_(LIFO_CHUNK_SIZE), tree_(&treeAlloc_), entries_() {}
    ~JitcodeGlobalTable() {}

    bool empty() const {
        return tree_.empty();
    }

    bool lookup(void *ptr, JitcodeGlobalEntry *result);
    void lookupInfallible(void *ptr, JitcodeGlobalEntry *result);

    bool addEntry(const JitcodeGlobalEntry::IonEntry &entry) {
        return addEntry(JitcodeGlobalEntry(entry));
    }
    bool addEntry(const JitcodeGlobalEntry::BaselineEntry &entry) {
        return addEntry(JitcodeGlobalEntry(entry));
    }
    bool addEntry(const JitcodeGlobalEntry::IonCacheEntry &entry) {
        return addEntry(JitcodeGlobalEntry(entry));
    }

    void removeEntry(void *startAddr);

  private:
    bool addEntry(const JitcodeGlobalEntry &entry);
};


/*
 * Container class for main jitcode table.
 * The Region table's memory is structured as follows:
 *
 *      +------------------------------------------------+   |
 *      |  Region 1 Run                                  |   |
 *      |------------------------------------------------|   |
 *      |  Region 2 Run                                  |   |
 *      |                                                |   |
 *      |                                                |   |
 *      |------------------------------------------------|   |
 *      |  Region 3 Run                                  |   |
 *      |                                                |   |
 *      |------------------------------------------------|   |-- Payload
 *      |                                                |   |
 *      |               ...                              |   |
 *      |                                                |   |
 *      |------------------------------------------------|   |
 *      |  Region M Run                                  |   |
 *      |                                                |   |
 *      +================================================+ <- RegionTable pointer points here
 *      | uint23_t numRegions = M                        |   |
 *      +------------------------------------------------+   |
 *      | Region 1                                       |   |
 *      |   uint32_t entryOffset = size(Payload)         |   |
 *      +------------------------------------------------+   |
 *      |                                                |   |-- Table
 *      |   ...                                          |   |
 *      |                                                |   |
 *      +------------------------------------------------+   |
 *      | Region M                                       |   |
 *      |   uint32_t entryOffset                         |   |
 *      +------------------------------------------------+   |
 *
 * The region table is composed of two sections: a tail section that contains a table of
 * fixed-size entries containing offsets into the the head section, and a head section that
 * holds a sequence of variable-sized runs.  The table in the tail section serves to
 * locate the variable-length encoded structures in the head section.
 *
 * The entryOffsets in the table indicate the bytes offset to subtract from the regionTable
 * pointer to arrive at the encoded region in the payload.
 *
 *
 * Variable-length entries in payload
 * ----------------------------------
 * The entryOffsets in the region table's fixed-sized entries refer to a location within the
 * variable-length payload section.  This location contains a compactly encoded "run" of
 * mappings.
 *
 * Each run starts by describing the offset within the native code it starts at, and the
 * sequence of (JSScript *, jsbytecode *) pairs active at that site.  Following that, there
 * are a number of variable-length entries encoding (nativeOffsetDelta, bytecodeOffsetDelta)
 * pairs for the run.
 *
 *      VarUint32 nativeOffset;
 *          - The offset from nativeStartAddr in the global table entry at which
 *            the jitcode for this region starts.
 *
 *      Uint8_t scriptDepth;
 *          - The depth of inlined scripts for this region.
 *
 *      List<VarUint32> inlineScriptPcStack;
 *          - We encode (2 * scriptDepth) VarUint32s here.  Each pair of uint32s are taken
 *            as an index into the scriptList in the global table entry, and a pcOffset
 *            respectively.
 *
 *      List<NativeAndBytecodeDelta> deltaRun;
 *          - The rest of the entry is a deltaRun that stores a series of variable-length
 *            encoded NativeAndBytecodeDelta datums.
 */
class JitcodeRegionEntry
{
  private:
    static const unsigned MAX_RUN_LENGTH = 100;

  public:
    static void WriteHead(CompactBufferWriter &writer,
                          uint32_t nativeOffset, uint8_t scriptDepth);
    static void ReadHead(CompactBufferReader &reader,
                         uint32_t *nativeOffset, uint8_t *scriptDepth);

    static void WriteScriptPc(CompactBufferWriter &writer, uint32_t scriptIdx, uint32_t pcOffset);
    static void ReadScriptPc(CompactBufferReader &reader, uint32_t *scriptIdx, uint32_t *pcOffset);

    static void WriteDelta(CompactBufferWriter &writer, uint32_t nativeDelta, int32_t pcDelta);
    static void ReadDelta(CompactBufferReader &reader, uint32_t *nativeDelta, int32_t *pcDelta);

    // Given a pointer into an array of NativeToBytecode (and a pointer to the end of the array),
    // compute the number of entries that would be consume by outputting a run starting
    // at this one.
    static uint32_t ExpectedRunLength(const CodeGeneratorShared::NativeToBytecode *entry,
                                      const CodeGeneratorShared::NativeToBytecode *end);

    // Write a run, starting at the given NativeToBytecode entry, into the given buffer writer.
    static bool WriteRun(CompactBufferWriter &writer,
                         JSScript **scriptList, uint32_t scriptListSize,
                         uint32_t runLength, const CodeGeneratorShared::NativeToBytecode *entry);

    // Delta Run entry formats are encoded little-endian:
    //
    //  byte 0
    //  NNNN-BBB0
    //      Single byte format.  nativeDelta in [0, 15], pcDelta in [0, 7]
    //
    static const uint32_t ENC1_MASK = 0x1;
    static const uint32_t ENC1_MASK_VAL = 0x0;

    static const uint32_t ENC1_NATIVE_DELTA_MAX = 0xf;
    static const unsigned ENC1_NATIVE_DELTA_SHIFT = 4;

    static const uint32_t ENC1_PC_DELTA_MASK = 0x0e;
    static const int32_t ENC1_PC_DELTA_MAX = 0x7;
    static const unsigned ENC1_PC_DELTA_SHIFT = 1;

    //  byte 1    byte 0
    //  NNNN-NNNN BBBB-BB01
    //      Two-byte format.  nativeDelta in [0, 255], pcDelta in [0, 63]
    //
    static const uint32_t ENC2_MASK = 0x3;
    static const uint32_t ENC2_MASK_VAL = 0x1;

    static const uint32_t ENC2_NATIVE_DELTA_MAX = 0xff;
    static const unsigned ENC2_NATIVE_DELTA_SHIFT = 8;

    static const uint32_t ENC2_PC_DELTA_MASK = 0x00fc;
    static const int32_t ENC2_PC_DELTA_MAX = 0x3f;
    static const unsigned ENC2_PC_DELTA_SHIFT = 2;

    //  byte 2    byte 1    byte 0
    //  NNNN-NNNN NNNB-BBBB BBBB-B011
    //      Three-byte format.  nativeDelta in [0, 2047], pcDelta in [-512, 511]
    //
    static const uint32_t ENC3_MASK = 0x7;
    static const uint32_t ENC3_MASK_VAL = 0x3;

    static const uint32_t ENC3_NATIVE_DELTA_MAX = 0x7ff;
    static const unsigned ENC3_NATIVE_DELTA_SHIFT = 13;

    static const uint32_t ENC3_PC_DELTA_MASK = 0x001ff8;
    static const int32_t ENC3_PC_DELTA_MAX = 0x1ff;
    static const int32_t ENC3_PC_DELTA_MIN = -ENC3_PC_DELTA_MAX - 1;
    static const unsigned ENC3_PC_DELTA_SHIFT = 3;

    //  byte 3    byte 2    byte 1    byte 0
    //  NNNN-NNNN NNNN-NNNN BBBB-BBBB BBBB-B111
    //      Three-byte format.  nativeDelta in [0, 65535], pcDelta in [-4096, 4095]
    static const uint32_t ENC4_MASK = 0x7;
    static const uint32_t ENC4_MASK_VAL = 0x7;

    static const uint32_t ENC4_NATIVE_DELTA_MAX = 0xffff;
    static const unsigned ENC4_NATIVE_DELTA_SHIFT = 16;

    static const uint32_t ENC4_PC_DELTA_MASK = 0x0000fff8;
    static const int32_t ENC4_PC_DELTA_MAX = 0xfff;
    static const int32_t ENC4_PC_DELTA_MIN = -ENC4_PC_DELTA_MAX - 1;
    static const unsigned ENC4_PC_DELTA_SHIFT = 3;

    static bool IsDeltaEncodeable(uint32_t nativeDelta, int32_t pcDelta) {
        return (nativeDelta <= ENC4_NATIVE_DELTA_MAX) &&
               (pcDelta >= ENC4_PC_DELTA_MIN) && (pcDelta <= ENC4_PC_DELTA_MAX);
    }

  private:
    const uint8_t *data_;
    const uint8_t *end_;

    // Unpacked state from jitcode entry.
    uint32_t nativeOffset_;
    uint8_t scriptDepth_;
    const uint8_t *scriptPcStack_;
    const uint8_t *deltaRun_;

    void unpack();

  public:
    JitcodeRegionEntry(const uint8_t *data, const uint8_t *end)
      : data_(data), end_(end),
        nativeOffset_(0), scriptDepth_(0),
        scriptPcStack_(nullptr), deltaRun_(nullptr)
    {
        JS_ASSERT(data_ < end_);
        unpack();
        JS_ASSERT(scriptPcStack_ < end_);
        JS_ASSERT(deltaRun_ <= end_);
    }

    uint32_t nativeOffset() const {
        return nativeOffset_;
    }
    uint32_t scriptDepth() const {
        return scriptDepth_;
    }

    class ScriptPcIterator
    {
      private:
        uint32_t count_;
        const uint8_t *start_;
        const uint8_t *end_;

        uint32_t idx_;
        const uint8_t *cur_;

      public:
        ScriptPcIterator(uint32_t count, const uint8_t *start, const uint8_t *end)
          : count_(count), start_(start), end_(end), idx_(0), cur_(start_)
        {}

        bool hasMore() const
        {
            JS_ASSERT((idx_ == count_) == (cur_ == end_));
            JS_ASSERT((idx_ < count_) == (cur_ < end_));
            return cur_ < end_;
        }

        void readNext(uint32_t *scriptIdxOut, uint32_t *pcOffsetOut)
        {
            JS_ASSERT(scriptIdxOut);
            JS_ASSERT(pcOffsetOut);
            JS_ASSERT(hasMore());

            CompactBufferReader reader(cur_, end_);
            ReadScriptPc(reader, scriptIdxOut, pcOffsetOut);

            cur_ = reader.currentPosition();
            JS_ASSERT(cur_ <= end_);

            idx_++;
            JS_ASSERT_IF(idx_ == count_, cur_ == end_);
        }

        void reset() {
            idx_ = 0;
            cur_ = start_;
        }
    };

    ScriptPcIterator scriptPcIterator() const {
        // End of script+pc sequence is the start of the delta run.
        return ScriptPcIterator(scriptDepth_, scriptPcStack_,  deltaRun_);
    }

    class DeltaIterator {
      private:
        const uint8_t *start_;
        const uint8_t *end_;
        const uint8_t *cur_;

      public:
        DeltaIterator(const uint8_t *start, const uint8_t *end)
          : start_(start), end_(end), cur_(start)
        {}

        bool hasMore() const
        {
            JS_ASSERT(cur_ <= end_);
            return cur_ < end_;
        }

        void readNext(uint32_t *nativeDeltaOut, int32_t *pcDeltaOut)
        {
            JS_ASSERT(nativeDeltaOut != nullptr);
            JS_ASSERT(pcDeltaOut != nullptr);

            JS_ASSERT(hasMore());

            CompactBufferReader reader(cur_, end_);
            ReadDelta(reader, nativeDeltaOut, pcDeltaOut);

            cur_ = reader.currentPosition();
            JS_ASSERT(cur_ <= end_);
        }

        void reset() {
            cur_ = start_;
        }
    };
    DeltaIterator deltaIterator() const {
        return DeltaIterator(deltaRun_, end_);
    }

    uint32_t findPcOffset(uint32_t queryNativeOffset, uint32_t startPcOffset) const;
};

class JitcodeIonTable
{
  private:
    /* Variable length payload section "below" here. */
    uint32_t numRegions_;
    uint32_t regionOffsets_[0];

    const uint8_t *payloadEnd() const {
        return reinterpret_cast<const uint8_t *>(this);
    }

  public:
    JitcodeIonTable(uint32_t numRegions)
      : numRegions_(numRegions)
    {
        for (uint32_t i = 0; i < numRegions; i++)
            regionOffsets_[i] = 0;
    }

    bool makeIonEntry(JSContext *cx, JitCode *code, uint32_t numScripts, JSScript **scripts,
                      JitcodeGlobalEntry::IonEntry &out);

    uint32_t numRegions() const {
        return numRegions_;
    }

    uint32_t regionOffset(uint32_t regionIndex) const {
        JS_ASSERT(regionIndex < numRegions());
        return regionOffsets_[regionIndex];
    }

    JitcodeRegionEntry regionEntry(uint32_t regionIndex) const {
        const uint8_t *regionStart = payloadEnd() - regionOffset(regionIndex);
        const uint8_t *regionEnd = payloadEnd();
        if (regionIndex < numRegions_ - 1)
            regionEnd -= regionOffset(regionIndex + 1);
        return JitcodeRegionEntry(regionStart, regionEnd);
    }

    bool regionContainsOffset(uint32_t regionIndex, uint32_t nativeOffset) {
        JS_ASSERT(regionIndex < numRegions());

        JitcodeRegionEntry ent = regionEntry(regionIndex);
        if (nativeOffset < ent.nativeOffset())
            return false;

        if (regionIndex == numRegions_ - 1)
            return true;

        return nativeOffset < regionEntry(regionIndex + 1).nativeOffset();
    }

    uint32_t findRegionEntry(uint32_t offset) const;

    const uint8_t *payloadStart() const {
        // The beginning of the payload the beginning of the first region are the same.
        return payloadEnd() - regionOffset(0);
    }

    static bool WriteIonTable(CompactBufferWriter &writer,
                              JSScript **scriptList, uint32_t scriptListSize,
                              const CodeGeneratorShared::NativeToBytecode *start,
                              const CodeGeneratorShared::NativeToBytecode *end,
                              uint32_t *tableOffsetOut, uint32_t *numRegionsOut);
};


} // namespace jit
} // namespace js

#endif /* jit_JitcodeMap_h */
