/* -*- 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_AsmJS_h
#define jit_AsmJS_h

#ifdef XP_MACOSX
# include <pthread.h>
# include <mach/mach.h>
#endif

namespace js {

class ScriptSource;
class SPSProfiler;
class AsmJSModule;
namespace frontend { struct TokenStream; struct ParseNode; }
namespace jit { class MIRGenerator; class LIRGraph; }

// Called after parsing a function 'fn' which contains the "use asm" directive.
// This function performs type-checking and code-generation. If type-checking
// succeeds, the generated native function is assigned to |moduleFun|.
// Otherwise, a warning will be emitted and |moduleFun| is left unchanged. The
// function returns 'false' only if a real JS semantic error (probably OOM) is
// pending.
extern bool
CompileAsmJS(JSContext *cx, frontend::TokenStream &ts, frontend::ParseNode *fn,
             const CompileOptions &options,
             ScriptSource *scriptSource, uint32_t bufStart, uint32_t bufEnd,
             MutableHandleFunction moduleFun);

// Called for any "use asm" function which successfully typechecks. This
// function performs the validation and dynamic linking of a module to its
// given arguments. If validation succeeds, the module's return value (its
// exports) are returned via |vp|.  Otherwise, there was a validation error and
// execution fall back to the usual path (bytecode generation, interpretation,
// etc). The function returns 'false' only if a real JS semantic error (OOM or
// exception thrown when executing GetProperty on the arguments) is pending.
extern JSBool
LinkAsmJS(JSContext *cx, unsigned argc, JS::Value *vp);

// The js::Native for the functions nested in an asm.js module. Calling this
// native will trampoline into generated code.
extern JSBool
CallAsmJS(JSContext *cx, unsigned argc, JS::Value *vp);

// Force any currently-executing asm.js code to call
// js_HandleExecutionInterrupt.
void
TriggerOperationCallbackForAsmJSCode(JSRuntime *rt);

// The JSRuntime maintains a stack of AsmJSModule activations. An "activation"
// of module A is an initial call from outside A into a function inside A,
// followed by a sequence of calls inside A, and terminated by a call that
// leaves A. The AsmJSActivation stack serves three purposes:
//  - record the correct cx to pass to VM calls from asm.js;
//  - record enough information to pop all the frames of an activation if an
//    exception is thrown;
//  - record the information necessary for asm.js signal handlers to safely
//    recover from (expected) out-of-bounds access, the operation callback,
//    stack overflow, division by zero, etc.
class AsmJSActivation
{
    JSContext *cx_;
    const AsmJSModule &module_;
    AsmJSActivation *prev_;
    void *errorRejoinSP_;
    SPSProfiler *profiler_;
    void *resumePC_;

  public:
    AsmJSActivation(JSContext *cx, const AsmJSModule &module);
    ~AsmJSActivation();

    JSContext *cx() { return cx_; }
    const AsmJSModule &module() const { return module_; }

    // Read by JIT code:
    static unsigned offsetOfContext() { return offsetof(AsmJSActivation, cx_); }
    static unsigned offsetOfResumePC() { return offsetof(AsmJSActivation, resumePC_); }

    // Initialized by JIT code:
    static unsigned offsetOfErrorRejoinSP() { return offsetof(AsmJSActivation, errorRejoinSP_); }

    // Set from SIGSEGV handler:
    void setResumePC(void *pc) { resumePC_ = pc; }
};

// The assumed page size; dynamically checked in CompileAsmJS.
const size_t AsmJSPageSize = 4096;

// The asm.js spec requires that the ArrayBuffer's byteLength be a multiple of 4096.
static const size_t AsmJSAllocationGranularity = 4096;

#ifdef JS_CPU_X64
// On x64, the internal ArrayBuffer data array is inflated to 4GiB (only the
// byteLength portion of which is accessible) so that out-of-bounds accesses
// (made using a uint32 index) are guaranteed to raise a SIGSEGV.
static const size_t AsmJSBufferProtectedSize = 4 * 1024ULL * 1024ULL * 1024ULL;
#endif

#ifdef XP_MACOSX
class AsmJSMachExceptionHandler
{
    bool installed_;
    pthread_t thread_;
    mach_port_t port_;

    void release();

  public:
    AsmJSMachExceptionHandler();
    ~AsmJSMachExceptionHandler() { release(); }
    mach_port_t port() const { return port_; }
    bool installed() const { return installed_; }
    bool install(JSRuntime *rt);
    void clearCurrentThread();
    void setCurrentThread();
};
#endif

struct DependentAsmJSModuleExit
{
    const AsmJSModule *module;
    size_t exitIndex;

    DependentAsmJSModuleExit(const AsmJSModule *module, size_t exitIndex)
      : module(module),
        exitIndex(exitIndex)
    { }
};

// Struct type for passing parallel compilation data between the main thread
// and compilation workers.
struct AsmJSParallelTask
{
    LifoAlloc lifo;         // Provider of all heap memory used for compilation.

    uint32_t funcNum;       // Index |i| of function in |Module.function(i)|.
    jit::MIRGenerator *mir; // Passed from main thread to worker.
    jit::LIRGraph *lir;     // Passed from worker to main thread.
    unsigned compileTime;

    AsmJSParallelTask(size_t defaultChunkSize)
      : lifo(defaultChunkSize),
        funcNum(0), mir(NULL), lir(NULL), compileTime(0)
    { }

    void init(uint32_t newFuncNum, jit::MIRGenerator *newMir) {
        funcNum = newFuncNum;
        mir = newMir;
        lir = NULL;
    }
};

// Returns true if the given native is the one that is used to implement asm.js
// module functions.
#ifdef JS_ION
extern bool
IsAsmJSModuleNative(js::Native native);
#else
inline bool
IsAsmJSModuleNative(js::Native native)
{
    return false;
}
#endif

// Exposed for shell testing:

// Return whether asm.js optimization is inhibitted by the platform or
// dynamically disabled:
extern JSBool
IsAsmJSCompilationAvailable(JSContext *cx, unsigned argc, Value *vp);

// Return whether the given value is a function containing "use asm" that has
// been validated according to the asm.js spec.
extern JSBool
IsAsmJSModule(JSContext *cx, unsigned argc, Value *vp);

// Return whether the given value is a nested function in an asm.js module that
// has been both compile- and link-time validated.
extern JSBool
IsAsmJSFunction(JSContext *cx, unsigned argc, Value *vp);

} // namespace js

#endif /* jit_AsmJS_h */
