/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=4 sw=4 et tw=99:
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
 * May 28, 2008.
 *
 * The Initial Developer of the Original Code is
 *   Brendan Eich <brendan@mozilla.org>
 *
 * Contributor(s):
 *   David Mandelin <dmandelin@mozilla.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#if !defined jsjaeger_poly_ic_h__ && defined JS_METHODJIT
#define jsjaeger_poly_ic_h__

#include "jscntxt.h"
#include "jstl.h"
#include "jsvector.h"
#include "assembler/assembler/MacroAssembler.h"
#include "assembler/assembler/CodeLocation.h"
#include "methodjit/MethodJIT.h"
#include "methodjit/ICRepatcher.h"
#include "BaseAssembler.h"
#include "RematInfo.h"
#include "BaseCompiler.h"
#include "methodjit/ICLabels.h"
#include "assembler/moco/MocoStubs.h"

namespace js {
namespace mjit {
namespace ic {

/* Maximum number of stubs for a given callsite. */
static const uint32 MAX_PIC_STUBS = 16;
static const uint32 MAX_GETELEM_IC_STUBS = 17;

void PurgePICs(JSContext *cx);

enum LookupStatus {
    Lookup_Error = 0,
    Lookup_Uncacheable,
    Lookup_Cacheable
};

struct BaseIC : public MacroAssemblerTypedefs {
    BaseIC() { }

    // Address of inline fast-path.
    CodeLocationLabel fastPathStart;

    // Address to rejoin to the fast-path.
    CodeLocationLabel fastPathRejoin;

    // Start of the slow path.
    CodeLocationLabel slowPathStart;

    // Slow path stub call.
    CodeLocationCall slowPathCall;

    // Whether or not the callsite has been hit at least once.
    bool hit : 1;
    bool slowCallPatched : 1;

    // Number of stubs generated.
    uint32 stubsGenerated : 5;

    // Offset from start of stub to jump target of second shape guard as Nitro
    // asm data location. This is 0 if there is only one shape guard in the
    // last stub.
    int secondShapeGuard : 11;

    // Opcode this was compiled for.
    JSOp op : 9;

    void reset() {
        hit = false;
        slowCallPatched = false;
        stubsGenerated = 0;
        secondShapeGuard = 0;
    }
    bool shouldUpdate(JSContext *cx);
    void spew(JSContext *cx, const char *event, const char *reason);
    LookupStatus disable(JSContext *cx, const char *reason, void *stub);
    bool isCallOp();
};

class BasePolyIC : public BaseIC {
    typedef Vector<JSC::ExecutablePool *, 2, SystemAllocPolicy> ExecPoolVector;

    // ExecutablePools that IC stubs were generated into.  Very commonly (eg.
    // 99.5% of BasePolyICs) there are 0 or 1, and there are lots of
    // BasePolyICs, so we space-optimize for that case.  If the bottom bit of
    // the pointer is 0, execPool should be used, and it will be NULL (for 0
    // pools) or non-NULL (for 1 pool).  If the bottom bit of the
    // pointer is 1, taggedExecPools should be used, but only after de-tagging
    // (for 2 or more pools).
    union {
        JSC::ExecutablePool *execPool;      // valid when bottom bit is a 0
        ExecPoolVector *taggedExecPools;    // valid when bottom bit is a 1
    } u;

    static bool isTagged(void *p) {
        return !!(intptr_t(p) & 1);
    }

    static ExecPoolVector *tag(ExecPoolVector *p) {
        JS_ASSERT(!isTagged(p));
        return (ExecPoolVector *)(intptr_t(p) | 1);
    }

    static ExecPoolVector *detag(ExecPoolVector *p) {
        JS_ASSERT(isTagged(p));
        return (ExecPoolVector *)(intptr_t(p) & ~1);
    }

    bool areZeroPools()     { return !u.execPool; }
    bool isOnePool()        { return u.execPool && !isTagged(u.execPool); }
    bool areMultiplePools() { return isTagged(u.taggedExecPools); }

    ExecPoolVector *multiplePools() {
        JS_ASSERT(areMultiplePools());
        return detag(u.taggedExecPools);
    }

  public:
    BasePolyIC() {
        u.execPool = NULL;
    }

    ~BasePolyIC() {
        releasePools();
        if (areMultiplePools())
            js_delete(multiplePools());
    }

    void reset() {
        BaseIC::reset();
        releasePools();
        if (areZeroPools()) {
            // Common case:  do nothing.
        } else if (isOnePool()) {
            u.execPool = NULL;
        } else {
            multiplePools()->clear();
        }
    }

    void releasePools() {
        if (areZeroPools()) {
            // Common case:  do nothing.
        } else if (isOnePool()) {
            u.execPool->release();
        } else {
            ExecPoolVector *execPools = multiplePools();
            for (size_t i = 0; i < execPools->length(); i++)
                (*execPools)[i]->release();
        }
    }

    bool addPool(JSContext *cx, JSC::ExecutablePool *pool) {
        if (areZeroPools()) {
            u.execPool = pool;
            return true;
        }
        if (isOnePool()) {
            JSC::ExecutablePool *oldPool = u.execPool;
            JS_ASSERT(!isTagged(oldPool));
            ExecPoolVector *execPools = js_new<ExecPoolVector>(SystemAllocPolicy()); 
            if (!execPools)
                return false;
            if (!execPools->append(oldPool) || !execPools->append(pool)) {
                js_delete(execPools);
                return false;
            }
            u.taggedExecPools = tag(execPools);
            return true;
        }
        return multiplePools()->append(pool); 
    }
};

struct GetElementIC : public BasePolyIC {
    GetElementIC() { reset(); }

    // On stub entry:
    //   If hasInlineTypeCheck() is true, and inlineTypeCheckPatched is false,
    //     - typeReg contains the type of the |id| parameter.
    //   If hasInlineTypeCheck() is true, and inlineTypeCheckPatched is true,
    //     - typeReg contains the shape of |objReg| iff typeRegHasBaseShape
    //       is true.
    //   Otherwise, typeReg is garbage.
    //
    // On stub exit, typeReg must contain the type of the result value.
    RegisterID typeReg   : 5;

    // On stub entry, objReg contains the object pointer for the |obj| parameter.
    // On stub exit, objReg must contain the payload of the result value.
    RegisterID objReg    : 5;

    // Offset from the fast path to the inline type check.
    // This is only set if hasInlineTypeCheck() is true.
    unsigned inlineTypeGuard  : 8;

    // Offset from the fast path to the inline clasp guard. This is always
    // set; if |id| is known to not be int32, then it's an unconditional
    // jump to the slow path.
    unsigned inlineClaspGuard : 8;

    // This is usable if hasInlineTypeGuard() returns true, which implies
    // that a dense array fast path exists. The inline type guard serves as
    // the head of the chain of all string-based element stubs.
    bool inlineTypeGuardPatched : 1;

    // This is always usable, and specifies whether the inline clasp guard
    // has been patched. If hasInlineTypeGuard() is true, it guards against
    // a dense array, and guarantees the inline type guard has passed.
    // Otherwise, there is no inline type guard, and the clasp guard is just
    // an unconditional jump.
    bool inlineClaspGuardPatched : 1;

    ////////////////////////////////////////////
    // State for string-based property stubs. //
    ////////////////////////////////////////////

    // True if typeReg is guaranteed to have the shape of objReg.
    bool typeRegHasBaseShape : 1;

    // These offsets are used for string-key dependent stubs, such as named
    // property accesses. They are separated from the int-key dependent stubs,
    // in order to guarantee that the id type needs only one guard per type.
    int atomGuard : 8;          // optional, non-zero if present
    int firstShapeGuard : 8;    // always set
    int secondShapeGuard : 8;   // optional, non-zero if present

    bool hasLastStringStub : 1;
    JITCode lastStringStub;

    // A limited ValueRemat instance. It may contains either:
    //  1) A constant, or
    //  2) A known type and data reg, or
    //  3) A data reg.
    // The sync bits are not set, and the type reg is never set and should not
    // be used, as it is encapsulated more accurately in |typeReg|. Also, note
    // carefully that the data reg is immutable.
    ValueRemat idRemat;

    bool hasInlineTypeGuard() const {
        return !idRemat.isTypeKnown();
    }
    bool shouldPatchInlineTypeGuard() {
        return hasInlineTypeGuard() && !inlineTypeGuardPatched;
    }
    bool shouldPatchUnconditionalClaspGuard() {
        // The clasp guard is only unconditional if the type is known to not
        // be an int32.
        if (idRemat.isTypeKnown() && idRemat.knownType() != JSVAL_TYPE_INT32)
            return !inlineClaspGuardPatched;
        return false;
    }

    void reset() {
        BasePolyIC::reset();
        inlineTypeGuardPatched = false;
        inlineClaspGuardPatched = false;
        typeRegHasBaseShape = false;
        hasLastStringStub = false;
    }
    void purge(Repatcher &repatcher);
    LookupStatus update(JSContext *cx, JSObject *obj, const Value &v, jsid id, Value *vp);
    LookupStatus attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid id,
                               Value *vp);
    LookupStatus attachTypedArray(JSContext *cx, JSObject *obj, const Value &v, jsid id,
                                  Value *vp);
    LookupStatus disable(JSContext *cx, const char *reason);
    LookupStatus error(JSContext *cx);
    bool shouldUpdate(JSContext *cx);
};

struct SetElementIC : public BaseIC {
    SetElementIC() : execPool(NULL) { reset(); }
    ~SetElementIC() {
        if (execPool)
            execPool->release();
    }

    // On stub entry:
    //   objReg contains the payload of the |obj| parameter.
    // On stub exit:
    //   objReg may be clobbered.
    RegisterID objReg    : 5;

    // Information on how to rematerialize |objReg|.
    int32 objRemat       : MIN_STATE_REMAT_BITS;

    // Offset from the start of the fast path to the inline clasp guard.
    unsigned inlineClaspGuard : 6;

    // True if the clasp guard has been patched; false otherwise.
    bool inlineClaspGuardPatched : 1;

    // Offset from the start of the fast path to the inline hole guard.
    unsigned inlineHoleGuard : 8;

    // True if the capacity guard has been patched; false otherwise.
    bool inlineHoleGuardPatched : 1;

    // True if this is from a strict-mode script.
    bool strictMode : 1;

    // A bitmask of registers that are volatile and must be preserved across
    // stub calls inside the IC.
    uint32 volatileMask : 16;

    // If true, then keyValue contains a constant index value >= 0. Otherwise,
    // keyReg contains a dynamic integer index in any range.
    bool hasConstantKey : 1;
    union {
        RegisterID keyReg;
        int32      keyValue;
    };

    // Rematerialize information about the value being stored.
    ValueRemat vr;

    // Optional executable pool for the out-of-line hole stub.
    JSC::ExecutablePool *execPool;

    void reset() {
        BaseIC::reset();
        if (execPool != NULL)
            execPool->release();
        execPool = NULL;
        inlineClaspGuardPatched = false;
        inlineHoleGuardPatched = false;
    }
    void purge(Repatcher &repatcher);
    LookupStatus attachTypedArray(JSContext *cx, JSObject *obj, int32 key);
    LookupStatus attachHoleStub(JSContext *cx, JSObject *obj, int32 key);
    LookupStatus update(JSContext *cx, const Value &objval, const Value &idval);
    LookupStatus disable(JSContext *cx, const char *reason);
    LookupStatus error(JSContext *cx);
};

struct PICInfo : public BasePolyIC {
    PICInfo() { reset(); }

    // Operation this is a PIC for.
    enum Kind
#ifdef _MSC_VER
    : uint8_t
#endif
    {
        GET,        // JSOP_GETPROP
        CALL,       // JSOP_CALLPROP
        SET,        // JSOP_SETPROP, JSOP_SETNAME
        SETMETHOD,  // JSOP_SETMETHOD
        NAME,       // JSOP_NAME
        BIND,       // JSOP_BINDNAME
        XNAME       // JSOP_GETXPROP
    };

    union {
        struct {
            RegisterID typeReg  : 5;  // reg used for checking type
            bool hasTypeCheck   : 1;  // type check and reg are present

            // Reverse offset from slowPathStart to the type check slow path.
            int32 typeCheckOffset;
        } get;
        ValueRemat vr;
    } u;

    // Address of the start of the last generated stub, if any. Note that this
    // does not correctly overlay with the allocated memory; it does however
    // overlay the portion that may need to be patched, which is good enough.
    JITCode lastStubStart;

    // Return the start address of the last path in this PIC, which is the
    // inline path if no stubs have been generated yet.
    CodeLocationLabel lastPathStart() {
        if (!stubsGenerated)
            return fastPathStart;
        return CodeLocationLabel(lastStubStart.start());
    }

    CodeLocationLabel getFastShapeGuard() {
        return fastPathStart.labelAtOffset(shapeGuard);
    }

    CodeLocationLabel getSlowTypeCheck() {
        JS_ASSERT(isGet());
        return slowPathStart.labelAtOffset(u.get.typeCheckOffset);
    }

    // Return a JITCode block corresponding to the code memory to attach a
    // new stub to.
    JITCode lastCodeBlock(JITScript *jit) {
        if (!stubsGenerated)
            return JITCode(jit->code.m_code.executableAddress(), jit->code.m_size);
        return lastStubStart;
    }

    void updateLastPath(LinkerHelper &linker, Label label) {
        CodeLocationLabel loc = linker.locationOf(label);
        lastStubStart = JITCode(loc.executableAddress(), linker.size());
    }

    Kind kind : 3;

    // True if register R holds the base object shape along exits from the
    // last stub.
    bool shapeRegHasBaseShape : 1;

    // True if can use the property cache.
    bool usePropCache : 1;

    // State flags.
    bool inlinePathPatched : 1;     // inline path has been patched

    RegisterID shapeReg : 5;        // also the out type reg
    RegisterID objReg   : 5;        // also the out data reg

    // Offset from start of fast path to initial shape guard.
    uint32 shapeGuard;
    
    inline bool isSet() const {
        return kind == SET || kind == SETMETHOD;
    }
    inline bool isGet() const {
        return kind == GET || kind == CALL;
    }
    inline bool isBind() const {
        return kind == BIND;
    }
    inline bool isScopeName() const {
        return kind == NAME || kind == XNAME;
    }
    inline RegisterID typeReg() {
        JS_ASSERT(isGet());
        return u.get.typeReg;
    }
    inline bool hasTypeCheck() {
        JS_ASSERT(isGet());
        return u.get.hasTypeCheck;
    }
    inline bool shapeNeedsRemat() {
        return !shapeRegHasBaseShape;
    }
    inline bool isFastCall() {
        JS_ASSERT(kind == CALL);
        return !hasTypeCheck();
    }

#if !defined JS_HAS_IC_LABELS
    static GetPropLabels getPropLabels_;
    static SetPropLabels setPropLabels_;
    static BindNameLabels bindNameLabels_;
    static ScopeNameLabels scopeNameLabels_;
#else
    union {
        GetPropLabels getPropLabels_;
        SetPropLabels setPropLabels_;
        BindNameLabels bindNameLabels_;
        ScopeNameLabels scopeNameLabels_;
    };
    void setLabels(const ic::GetPropLabels &labels) {
        JS_ASSERT(isGet());
        getPropLabels_ = labels;
    }
    void setLabels(const ic::SetPropLabels &labels) {
        JS_ASSERT(isSet());
        setPropLabels_ = labels;
    }
    void setLabels(const ic::BindNameLabels &labels) {
        JS_ASSERT(kind == BIND);
        bindNameLabels_ = labels;
    }
    void setLabels(const ic::ScopeNameLabels &labels) {
        JS_ASSERT(kind == NAME || kind == XNAME);
        scopeNameLabels_ = labels;
    }
#endif

    GetPropLabels &getPropLabels() {
        JS_ASSERT(isGet());
        return getPropLabels_;
    }
    SetPropLabels &setPropLabels() {
        JS_ASSERT(isSet());
        return setPropLabels_;
    }
    BindNameLabels &bindNameLabels() {
        JS_ASSERT(kind == BIND);
        return bindNameLabels_;
    }
    ScopeNameLabels &scopeNameLabels() {
        JS_ASSERT(kind == NAME || kind == XNAME);
        return scopeNameLabels_;
    }

    // Where in the script did we generate this PIC?
    jsbytecode *pc;
    
    // Index into the script's atom table.
    JSAtom *atom;

    // Reset the data members to the state of a fresh PIC before any patching
    // or stub generation was done.
    void reset() {
        BasePolyIC::reset();
        inlinePathPatched = false;
        shapeRegHasBaseShape = true;
    }
};

#ifdef JS_POLYIC
void PurgePICs(JSContext *cx, JSScript *script);
void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL SetProp(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL CallProp(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL Name(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL XName(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL BindName(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL GetElement(VMFrame &f, ic::GetElementIC *);
void JS_FASTCALL CallElement(VMFrame &f, ic::GetElementIC *);
template <JSBool strict> void JS_FASTCALL SetElement(VMFrame &f, ic::SetElementIC *);
#endif

} /* namespace ic */
} /* namespace mjit */
} /* namespace js */

#endif /* jsjaeger_poly_ic_h__ */

