js/src/vm/ForkJoin.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/vm/ForkJoin.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2210 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#if defined(XP_WIN)
    1.11 +# include <io.h>     // for isatty()
    1.12 +#else
    1.13 +# include <unistd.h> // for isatty()
    1.14 +#endif
    1.15 +
    1.16 +#include "vm/ForkJoin.h"
    1.17 +
    1.18 +#include "mozilla/ThreadLocal.h"
    1.19 +
    1.20 +#include "jscntxt.h"
    1.21 +#include "jslock.h"
    1.22 +#include "jsprf.h"
    1.23 +
    1.24 +#include "builtin/TypedObject.h"
    1.25 +
    1.26 +#ifdef JS_THREADSAFE
    1.27 +# include "jit/BaselineJIT.h"
    1.28 +# include "vm/Monitor.h"
    1.29 +#endif
    1.30 +
    1.31 +#if defined(JS_THREADSAFE) && defined(JS_ION)
    1.32 +# include "jit/JitCommon.h"
    1.33 +# ifdef DEBUG
    1.34 +#  include "jit/Ion.h"
    1.35 +#  include "jit/JitCompartment.h"
    1.36 +#  include "jit/MIR.h"
    1.37 +#  include "jit/MIRGraph.h"
    1.38 +# endif
    1.39 +#endif // THREADSAFE && ION
    1.40 +
    1.41 +#include "vm/Interpreter-inl.h"
    1.42 +
    1.43 +using namespace js;
    1.44 +using namespace js::parallel;
    1.45 +using namespace js::jit;
    1.46 +
    1.47 +using mozilla::ThreadLocal;
    1.48 +
    1.49 +///////////////////////////////////////////////////////////////////////////
    1.50 +// Degenerate configurations
    1.51 +//
    1.52 +// When JS_THREADSAFE or JS_ION is not defined, we simply run the
    1.53 +// |func| callback sequentially.  We also forego the feedback
    1.54 +// altogether.
    1.55 +
    1.56 +static bool
    1.57 +ExecuteSequentially(JSContext *cx_, HandleValue funVal, uint16_t *sliceStart,
    1.58 +                    uint16_t sliceEnd);
    1.59 +
    1.60 +#if !defined(JS_THREADSAFE) || !defined(JS_ION)
    1.61 +bool
    1.62 +js::ForkJoin(JSContext *cx, CallArgs &args)
    1.63 +{
    1.64 +    RootedValue argZero(cx, args[0]);
    1.65 +    uint16_t sliceStart = uint16_t(args[1].toInt32());
    1.66 +    uint16_t sliceEnd = uint16_t(args[2].toInt32());
    1.67 +    if (!ExecuteSequentially(cx, argZero, &sliceStart, sliceEnd))
    1.68 +        return false;
    1.69 +    MOZ_ASSERT(sliceStart == sliceEnd);
    1.70 +    return true;
    1.71 +}
    1.72 +
    1.73 +JSContext *
    1.74 +ForkJoinContext::acquireJSContext()
    1.75 +{
    1.76 +    return nullptr;
    1.77 +}
    1.78 +
    1.79 +void
    1.80 +ForkJoinContext::releaseJSContext()
    1.81 +{
    1.82 +}
    1.83 +
    1.84 +bool
    1.85 +ForkJoinContext::isMainThread() const
    1.86 +{
    1.87 +    return true;
    1.88 +}
    1.89 +
    1.90 +JSRuntime *
    1.91 +ForkJoinContext::runtime()
    1.92 +{
    1.93 +    MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
    1.94 +}
    1.95 +
    1.96 +bool
    1.97 +ForkJoinContext::check()
    1.98 +{
    1.99 +    MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
   1.100 +}
   1.101 +
   1.102 +void
   1.103 +ForkJoinContext::requestGC(JS::gcreason::Reason reason)
   1.104 +{
   1.105 +    MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
   1.106 +}
   1.107 +
   1.108 +void
   1.109 +ForkJoinContext::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
   1.110 +{
   1.111 +    MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
   1.112 +}
   1.113 +
   1.114 +bool
   1.115 +ForkJoinContext::setPendingAbortFatal(ParallelBailoutCause cause)
   1.116 +{
   1.117 +    MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
   1.118 +    return false;
   1.119 +}
   1.120 +
   1.121 +void
   1.122 +ParallelBailoutRecord::setCause(ParallelBailoutCause cause,
   1.123 +                                JSScript *outermostScript,
   1.124 +                                JSScript *currentScript,
   1.125 +                                jsbytecode *currentPc)
   1.126 +{
   1.127 +    MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
   1.128 +}
   1.129 +
   1.130 +void
   1.131 +js::ParallelBailoutRecord::updateCause(ParallelBailoutCause cause,
   1.132 +                                       JSScript *outermostScript,
   1.133 +                                       JSScript *currentScript,
   1.134 +                                       jsbytecode *currentPc)
   1.135 +{
   1.136 +    MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
   1.137 +}
   1.138 +
   1.139 +void
   1.140 +ParallelBailoutRecord::addTrace(JSScript *script,
   1.141 +                                jsbytecode *pc)
   1.142 +{
   1.143 +    MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
   1.144 +}
   1.145 +
   1.146 +bool
   1.147 +js::InExclusiveParallelSection()
   1.148 +{
   1.149 +    return false;
   1.150 +}
   1.151 +
   1.152 +bool
   1.153 +js::ParallelTestsShouldPass(JSContext *cx)
   1.154 +{
   1.155 +    return false;
   1.156 +}
   1.157 +
   1.158 +bool
   1.159 +js::intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp)
   1.160 +{
   1.161 +    return true;
   1.162 +}
   1.163 +
   1.164 +static bool
   1.165 +intrinsic_SetForkJoinTargetRegionPar(ForkJoinContext *cx, unsigned argc, Value *vp)
   1.166 +{
   1.167 +    return true;
   1.168 +}
   1.169 +
   1.170 +JS_JITINFO_NATIVE_PARALLEL(js::intrinsic_SetForkJoinTargetRegionInfo,
   1.171 +                           intrinsic_SetForkJoinTargetRegionPar);
   1.172 +
   1.173 +bool
   1.174 +js::intrinsic_ClearThreadLocalArenas(JSContext *cx, unsigned argc, Value *vp)
   1.175 +{
   1.176 +    return true;
   1.177 +}
   1.178 +
   1.179 +static bool
   1.180 +intrinsic_ClearThreadLocalArenasPar(ForkJoinContext *cx, unsigned argc, Value *vp)
   1.181 +{
   1.182 +    return true;
   1.183 +}
   1.184 +
   1.185 +JS_JITINFO_NATIVE_PARALLEL(js::intrinsic_ClearThreadLocalArenasInfo,
   1.186 +                           intrinsic_ClearThreadLocalArenasPar);
   1.187 +
   1.188 +#endif // !JS_THREADSAFE || !JS_ION
   1.189 +
   1.190 +///////////////////////////////////////////////////////////////////////////
   1.191 +// All configurations
   1.192 +//
   1.193 +// Some code that is shared between degenerate and parallel configurations.
   1.194 +
   1.195 +static bool
   1.196 +ExecuteSequentially(JSContext *cx, HandleValue funVal, uint16_t *sliceStart,
   1.197 +                    uint16_t sliceEnd)
   1.198 +{
   1.199 +    FastInvokeGuard fig(cx, funVal);
   1.200 +    InvokeArgs &args = fig.args();
   1.201 +    if (!args.init(3))
   1.202 +        return false;
   1.203 +    args.setCallee(funVal);
   1.204 +    args.setThis(UndefinedValue());
   1.205 +    args[0].setInt32(0);
   1.206 +    args[1].setInt32(*sliceStart);
   1.207 +    args[2].setInt32(sliceEnd);
   1.208 +    if (!fig.invoke(cx))
   1.209 +        return false;
   1.210 +    *sliceStart = (uint16_t)(args.rval().toInt32());
   1.211 +    return true;
   1.212 +}
   1.213 +
   1.214 +ThreadLocal<ForkJoinContext*> ForkJoinContext::tlsForkJoinContext;
   1.215 +
   1.216 +/* static */ bool
   1.217 +ForkJoinContext::initialize()
   1.218 +{
   1.219 +    if (!tlsForkJoinContext.initialized()) {
   1.220 +        if (!tlsForkJoinContext.init())
   1.221 +            return false;
   1.222 +    }
   1.223 +    return true;
   1.224 +}
   1.225 +
   1.226 +///////////////////////////////////////////////////////////////////////////
   1.227 +// Parallel configurations
   1.228 +//
   1.229 +// The remainder of this file is specific to cases where both
   1.230 +// JS_THREADSAFE and JS_ION are enabled.
   1.231 +
   1.232 +#if defined(JS_THREADSAFE) && defined(JS_ION)
   1.233 +
   1.234 +///////////////////////////////////////////////////////////////////////////
   1.235 +// Class Declarations and Function Prototypes
   1.236 +
   1.237 +namespace js {
   1.238 +
   1.239 +// When writing tests, it is often useful to specify different modes
   1.240 +// of operation.
   1.241 +enum ForkJoinMode {
   1.242 +    // WARNING: If you change this enum, you MUST update
   1.243 +    // ForkJoinMode() in Utilities.js
   1.244 +
   1.245 +    // The "normal" behavior: attempt parallel, fallback to
   1.246 +    // sequential.  If compilation is ongoing in a helper thread, then
   1.247 +    // run sequential warmup iterations in the meantime. If those
   1.248 +    // iterations wind up completing all the work, just abort.
   1.249 +    ForkJoinModeNormal,
   1.250 +
   1.251 +    // Like normal, except that we will keep running warmup iterations
   1.252 +    // until compilations are complete, even if there is no more work
   1.253 +    // to do. This is useful in tests as a "setup" run.
   1.254 +    ForkJoinModeCompile,
   1.255 +
   1.256 +    // Requires that compilation has already completed. Expects parallel
   1.257 +    // execution to proceed without a hitch. (Reports an error otherwise)
   1.258 +    ForkJoinModeParallel,
   1.259 +
   1.260 +    // Requires that compilation has already completed. Expects
   1.261 +    // parallel execution to bailout once but continue after that without
   1.262 +    // further bailouts. (Reports an error otherwise)
   1.263 +    ForkJoinModeRecover,
   1.264 +
   1.265 +    // Expects all parallel executions to yield a bailout.  If this is not
   1.266 +    // the case, reports an error.
   1.267 +    ForkJoinModeBailout,
   1.268 +
   1.269 +    NumForkJoinModes
   1.270 +};
   1.271 +
   1.272 +class ForkJoinOperation
   1.273 +{
   1.274 +  public:
   1.275 +    // For tests, make sure to keep this in sync with minItemsTestingThreshold.
   1.276 +    static const uint32_t MAX_BAILOUTS = 3;
   1.277 +    uint32_t bailouts;
   1.278 +
   1.279 +    // Information about the bailout:
   1.280 +    ParallelBailoutCause bailoutCause;
   1.281 +    RootedScript bailoutScript;
   1.282 +    jsbytecode *bailoutBytecode;
   1.283 +
   1.284 +    ForkJoinOperation(JSContext *cx, HandleFunction fun, uint16_t sliceStart,
   1.285 +                      uint16_t sliceEnd, ForkJoinMode mode);
   1.286 +    ExecutionStatus apply();
   1.287 +
   1.288 +  private:
   1.289 +    // Most of the functions involved in managing the parallel
   1.290 +    // compilation follow a similar control-flow. They return RedLight
   1.291 +    // if they have either encountered a fatal error or completed the
   1.292 +    // execution, such that no further work is needed. In that event,
   1.293 +    // they take an `ExecutionStatus*` which they use to report
   1.294 +    // whether execution was successful or not. If the function
   1.295 +    // returns `GreenLight`, then the parallel operation is not yet
   1.296 +    // fully completed, so the state machine should carry on.
   1.297 +    enum TrafficLight {
   1.298 +        RedLight,
   1.299 +        GreenLight
   1.300 +    };
   1.301 +
   1.302 +    struct WorklistData {
   1.303 +        // True if we enqueued the callees from the ion-compiled
   1.304 +        // version of this entry
   1.305 +        bool calleesEnqueued;
   1.306 +
   1.307 +        // Last record useCount; updated after warmup
   1.308 +        // iterations;
   1.309 +        uint32_t useCount;
   1.310 +
   1.311 +        // Number of continuous "stalls" --- meaning warmups
   1.312 +        // where useCount did not increase.
   1.313 +        uint32_t stallCount;
   1.314 +
   1.315 +        void reset() {
   1.316 +            calleesEnqueued = false;
   1.317 +            useCount = 0;
   1.318 +            stallCount = 0;
   1.319 +        }
   1.320 +    };
   1.321 +
   1.322 +    JSContext *cx_;
   1.323 +    HandleFunction fun_;
   1.324 +    uint16_t sliceStart_;
   1.325 +    uint16_t sliceEnd_;
   1.326 +    Vector<ParallelBailoutRecord, 16> bailoutRecords_;
   1.327 +    AutoScriptVector worklist_;
   1.328 +    Vector<WorklistData, 16> worklistData_;
   1.329 +    ForkJoinMode mode_;
   1.330 +
   1.331 +    TrafficLight enqueueInitialScript(ExecutionStatus *status);
   1.332 +    TrafficLight compileForParallelExecution(ExecutionStatus *status);
   1.333 +    TrafficLight warmupExecution(bool stopIfComplete, ExecutionStatus *status);
   1.334 +    TrafficLight parallelExecution(ExecutionStatus *status);
   1.335 +    TrafficLight sequentialExecution(bool disqualified, ExecutionStatus *status);
   1.336 +    TrafficLight recoverFromBailout(ExecutionStatus *status);
   1.337 +    TrafficLight fatalError(ExecutionStatus *status);
   1.338 +    bool isInitialScript(HandleScript script);
   1.339 +    void determineBailoutCause();
   1.340 +    bool invalidateBailedOutScripts();
   1.341 +    ExecutionStatus sequentialExecution(bool disqualified);
   1.342 +
   1.343 +    TrafficLight appendCallTargetsToWorklist(uint32_t index, ExecutionStatus *status);
   1.344 +    TrafficLight appendCallTargetToWorklist(HandleScript script, ExecutionStatus *status);
   1.345 +    bool addToWorklist(HandleScript script);
   1.346 +    inline bool hasScript(Vector<types::RecompileInfo> &scripts, JSScript *script);
   1.347 +}; // class ForkJoinOperation
   1.348 +
   1.349 +class ForkJoinShared : public ParallelJob, public Monitor
   1.350 +{
   1.351 +    /////////////////////////////////////////////////////////////////////////
   1.352 +    // Constant fields
   1.353 +
   1.354 +    JSContext *const cx_;                  // Current context
   1.355 +    ThreadPool *const threadPool_;         // The thread pool
   1.356 +    HandleFunction fun_;                   // The JavaScript function to execute
   1.357 +    uint16_t sliceStart_;                  // The starting slice id.
   1.358 +    uint16_t sliceEnd_;                    // The ending slice id + 1.
   1.359 +    PRLock *cxLock_;                       // Locks cx_ for parallel VM calls
   1.360 +    ParallelBailoutRecord *const records_; // Bailout records for each worker
   1.361 +
   1.362 +    /////////////////////////////////////////////////////////////////////////
   1.363 +    // Per-thread arenas
   1.364 +    //
   1.365 +    // Each worker thread gets an arena to use when allocating.
   1.366 +
   1.367 +    Vector<Allocator *, 16> allocators_;
   1.368 +
   1.369 +    /////////////////////////////////////////////////////////////////////////
   1.370 +    // Locked Fields
   1.371 +    //
   1.372 +    // Only to be accessed while holding the lock.
   1.373 +
   1.374 +    bool gcRequested_;              // True if a worker requested a GC
   1.375 +    JS::gcreason::Reason gcReason_; // Reason given to request GC
   1.376 +    Zone *gcZone_;                  // Zone for GC, or nullptr for full
   1.377 +
   1.378 +    /////////////////////////////////////////////////////////////////////////
   1.379 +    // Asynchronous Flags
   1.380 +    //
   1.381 +    // These can be accessed without the lock and are thus atomic.
   1.382 +
   1.383 +    // Set to true when parallel execution should abort.
   1.384 +    mozilla::Atomic<bool, mozilla::ReleaseAcquire> abort_;
   1.385 +
   1.386 +    // Set to true when a worker bails for a fatal reason.
   1.387 +    mozilla::Atomic<bool, mozilla::ReleaseAcquire> fatal_;
   1.388 +
   1.389 +  public:
   1.390 +    ForkJoinShared(JSContext *cx,
   1.391 +                   ThreadPool *threadPool,
   1.392 +                   HandleFunction fun,
   1.393 +                   uint16_t sliceStart,
   1.394 +                   uint16_t sliceEnd,
   1.395 +                   ParallelBailoutRecord *records);
   1.396 +    ~ForkJoinShared();
   1.397 +
   1.398 +    bool init();
   1.399 +
   1.400 +    ParallelResult execute();
   1.401 +
   1.402 +    // Invoked from parallel worker threads:
   1.403 +    virtual bool executeFromWorker(ThreadPoolWorker *worker, uintptr_t stackLimit) MOZ_OVERRIDE;
   1.404 +
   1.405 +    // Invoked only from the main thread:
   1.406 +    virtual bool executeFromMainThread(ThreadPoolWorker *worker) MOZ_OVERRIDE;
   1.407 +
   1.408 +    // Executes the user-supplied function a worker or the main thread.
   1.409 +    void executePortion(PerThreadData *perThread, ThreadPoolWorker *worker);
   1.410 +
   1.411 +    // Moves all the per-thread arenas into the main compartment and processes
   1.412 +    // any pending requests for a GC. This can only safely be invoked on the
   1.413 +    // main thread after the workers have completed.
   1.414 +    void transferArenasToCompartmentAndProcessGCRequests();
   1.415 +
   1.416 +
   1.417 +    // Requests a GC, either full or specific to a zone.
   1.418 +    void requestGC(JS::gcreason::Reason reason);
   1.419 +    void requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason);
   1.420 +
   1.421 +    // Requests that computation abort.
   1.422 +    void setAbortFlagDueToInterrupt(ForkJoinContext &cx);
   1.423 +    void setAbortFlagAndRequestInterrupt(bool fatal);
   1.424 +
   1.425 +    // Set the fatal flag for the next abort.
   1.426 +    void setPendingAbortFatal() { fatal_ = true; }
   1.427 +
   1.428 +    JSRuntime *runtime() { return cx_->runtime(); }
   1.429 +    JS::Zone *zone() { return cx_->zone(); }
   1.430 +    JSCompartment *compartment() { return cx_->compartment(); }
   1.431 +
   1.432 +    JSContext *acquireJSContext() { PR_Lock(cxLock_); return cx_; }
   1.433 +    void releaseJSContext() { PR_Unlock(cxLock_); }
   1.434 +};
   1.435 +
   1.436 +class AutoEnterWarmup
   1.437 +{
   1.438 +    JSRuntime *runtime_;
   1.439 +
   1.440 +  public:
   1.441 +    AutoEnterWarmup(JSRuntime *runtime) : runtime_(runtime) { runtime_->forkJoinWarmup++; }
   1.442 +    ~AutoEnterWarmup() { runtime_->forkJoinWarmup--; }
   1.443 +};
   1.444 +
   1.445 +class AutoSetForkJoinContext
   1.446 +{
   1.447 +  public:
   1.448 +    AutoSetForkJoinContext(ForkJoinContext *threadCx) {
   1.449 +        ForkJoinContext::tlsForkJoinContext.set(threadCx);
   1.450 +    }
   1.451 +
   1.452 +    ~AutoSetForkJoinContext() {
   1.453 +        ForkJoinContext::tlsForkJoinContext.set(nullptr);
   1.454 +    }
   1.455 +};
   1.456 +
   1.457 +} // namespace js
   1.458 +
   1.459 +///////////////////////////////////////////////////////////////////////////
   1.460 +// ForkJoinActivation
   1.461 +//
   1.462 +// Takes care of tidying up GC before we enter a fork join section. Also
   1.463 +// pauses the barrier verifier, as we cannot enter fork join with the runtime
   1.464 +// or the zone needing barriers.
   1.465 +
   1.466 +ForkJoinActivation::ForkJoinActivation(JSContext *cx)
   1.467 +  : Activation(cx, ForkJoin),
   1.468 +    prevIonTop_(cx->mainThread().ionTop),
   1.469 +    av_(cx->runtime(), false)
   1.470 +{
   1.471 +    // Note: we do not allow GC during parallel sections.
   1.472 +    // Moreover, we do not wish to worry about making
   1.473 +    // write barriers thread-safe.  Therefore, we guarantee
   1.474 +    // that there is no incremental GC in progress and force
   1.475 +    // a minor GC to ensure no cross-generation pointers get
   1.476 +    // created:
   1.477 +
   1.478 +    if (JS::IsIncrementalGCInProgress(cx->runtime())) {
   1.479 +        JS::PrepareForIncrementalGC(cx->runtime());
   1.480 +        JS::FinishIncrementalGC(cx->runtime(), JS::gcreason::API);
   1.481 +    }
   1.482 +
   1.483 +    MinorGC(cx->runtime(), JS::gcreason::API);
   1.484 +
   1.485 +    cx->runtime()->gcHelperThread.waitBackgroundSweepEnd();
   1.486 +
   1.487 +    JS_ASSERT(!cx->runtime()->needsBarrier());
   1.488 +    JS_ASSERT(!cx->zone()->needsBarrier());
   1.489 +}
   1.490 +
   1.491 +ForkJoinActivation::~ForkJoinActivation()
   1.492 +{
   1.493 +    cx_->mainThread().ionTop = prevIonTop_;
   1.494 +}
   1.495 +
   1.496 +///////////////////////////////////////////////////////////////////////////
   1.497 +// js::ForkJoin() and ForkJoinOperation class
   1.498 +//
   1.499 +// These are the top-level objects that manage the parallel execution.
   1.500 +// They handle parallel compilation (if necessary), triggering
   1.501 +// parallel execution, and recovering from bailouts.
   1.502 +
   1.503 +static const char *ForkJoinModeString(ForkJoinMode mode);
   1.504 +
   1.505 +bool
   1.506 +js::ForkJoin(JSContext *cx, CallArgs &args)
   1.507 +{
   1.508 +    JS_ASSERT(args.length() == 4); // else the self-hosted code is wrong
   1.509 +    JS_ASSERT(args[0].isObject());
   1.510 +    JS_ASSERT(args[0].toObject().is<JSFunction>());
   1.511 +    JS_ASSERT(args[1].isInt32());
   1.512 +    JS_ASSERT(args[2].isInt32());
   1.513 +    JS_ASSERT(args[3].isInt32());
   1.514 +    JS_ASSERT(args[3].toInt32() < NumForkJoinModes);
   1.515 +
   1.516 +    RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
   1.517 +    uint16_t sliceStart = (uint16_t)(args[1].toInt32());
   1.518 +    uint16_t sliceEnd = (uint16_t)(args[2].toInt32());
   1.519 +    ForkJoinMode mode = (ForkJoinMode)(args[3].toInt32());
   1.520 +
   1.521 +    MOZ_ASSERT(sliceStart == args[1].toInt32());
   1.522 +    MOZ_ASSERT(sliceEnd == args[2].toInt32());
   1.523 +    MOZ_ASSERT(sliceStart <= sliceEnd);
   1.524 +
   1.525 +    ForkJoinOperation op(cx, fun, sliceStart, sliceEnd, mode);
   1.526 +    ExecutionStatus status = op.apply();
   1.527 +    if (status == ExecutionFatal)
   1.528 +        return false;
   1.529 +
   1.530 +    switch (mode) {
   1.531 +      case ForkJoinModeNormal:
   1.532 +      case ForkJoinModeCompile:
   1.533 +        return true;
   1.534 +
   1.535 +      case ForkJoinModeParallel:
   1.536 +        if (status == ExecutionParallel && op.bailouts == 0)
   1.537 +            return true;
   1.538 +        break;
   1.539 +
   1.540 +      case ForkJoinModeRecover:
   1.541 +        if (status != ExecutionSequential && op.bailouts > 0)
   1.542 +            return true;
   1.543 +        break;
   1.544 +
   1.545 +      case ForkJoinModeBailout:
   1.546 +        if (status != ExecutionParallel)
   1.547 +            return true;
   1.548 +        break;
   1.549 +
   1.550 +      case NumForkJoinModes:
   1.551 +        break;
   1.552 +    }
   1.553 +
   1.554 +    const char *statusString = "?";
   1.555 +    switch (status) {
   1.556 +      case ExecutionSequential: statusString = "seq"; break;
   1.557 +      case ExecutionParallel: statusString = "par"; break;
   1.558 +      case ExecutionWarmup: statusString = "warmup"; break;
   1.559 +      case ExecutionFatal: statusString = "fatal"; break;
   1.560 +    }
   1.561 +
   1.562 +    if (ParallelTestsShouldPass(cx)) {
   1.563 +        JS_ReportError(cx, "ForkJoin: mode=%s status=%s bailouts=%d",
   1.564 +                       ForkJoinModeString(mode), statusString, op.bailouts);
   1.565 +        return false;
   1.566 +    }
   1.567 +    return true;
   1.568 +}
   1.569 +
   1.570 +static const char *
   1.571 +ForkJoinModeString(ForkJoinMode mode) {
   1.572 +    switch (mode) {
   1.573 +      case ForkJoinModeNormal: return "normal";
   1.574 +      case ForkJoinModeCompile: return "compile";
   1.575 +      case ForkJoinModeParallel: return "parallel";
   1.576 +      case ForkJoinModeRecover: return "recover";
   1.577 +      case ForkJoinModeBailout: return "bailout";
   1.578 +      case NumForkJoinModes: return "max";
   1.579 +    }
   1.580 +    return "???";
   1.581 +}
   1.582 +
   1.583 +ForkJoinOperation::ForkJoinOperation(JSContext *cx, HandleFunction fun, uint16_t sliceStart,
   1.584 +                                     uint16_t sliceEnd, ForkJoinMode mode)
   1.585 +  : bailouts(0),
   1.586 +    bailoutCause(ParallelBailoutNone),
   1.587 +    bailoutScript(cx),
   1.588 +    bailoutBytecode(nullptr),
   1.589 +    cx_(cx),
   1.590 +    fun_(fun),
   1.591 +    sliceStart_(sliceStart),
   1.592 +    sliceEnd_(sliceEnd),
   1.593 +    bailoutRecords_(cx),
   1.594 +    worklist_(cx),
   1.595 +    worklistData_(cx),
   1.596 +    mode_(mode)
   1.597 +{ }
   1.598 +
   1.599 +ExecutionStatus
   1.600 +ForkJoinOperation::apply()
   1.601 +{
   1.602 +    ExecutionStatus status;
   1.603 +
   1.604 +    // High level outline of the procedure:
   1.605 +    //
   1.606 +    // - As we enter, we check for parallel script without "uncompiled" flag.
   1.607 +    // - If present, skip initial enqueue.
   1.608 +    // - While not too many bailouts:
   1.609 +    //   - While all scripts in worklist are not compiled:
   1.610 +    //     - For each script S in worklist:
   1.611 +    //       - Compile S if not compiled
   1.612 +    //         -> Error: fallback
   1.613 +    //       - If compiled, add call targets to worklist w/o checking uncompiled
   1.614 +    //         flag
   1.615 +    //     - If some compilations pending, run warmup iteration
   1.616 +    //     - Otherwise, clear "uncompiled targets" flag on main script and
   1.617 +    //       break from loop
   1.618 +    //   - Attempt parallel execution
   1.619 +    //     - If successful: return happily
   1.620 +    //     - If error: abort sadly
   1.621 +    //     - If bailout:
   1.622 +    //       - Invalidate any scripts that may need to be invalidated
   1.623 +    //       - Re-enqueue main script and any uncompiled scripts that were called
   1.624 +    // - Too many bailouts: Fallback to sequential
   1.625 +
   1.626 +    JS_ASSERT_IF(!jit::IsBaselineEnabled(cx_), !jit::IsIonEnabled(cx_));
   1.627 +    if (!jit::IsBaselineEnabled(cx_) || !jit::IsIonEnabled(cx_))
   1.628 +        return sequentialExecution(true);
   1.629 +
   1.630 +    SpewBeginOp(cx_, "ForkJoinOperation");
   1.631 +
   1.632 +    // How many workers do we have, counting the main thread.
   1.633 +    unsigned numWorkers = cx_->runtime()->threadPool.numWorkers();
   1.634 +
   1.635 +    if (!bailoutRecords_.resize(numWorkers))
   1.636 +        return SpewEndOp(ExecutionFatal);
   1.637 +
   1.638 +    for (uint32_t i = 0; i < numWorkers; i++)
   1.639 +        bailoutRecords_[i].init(cx_);
   1.640 +
   1.641 +    if (enqueueInitialScript(&status) == RedLight)
   1.642 +        return SpewEndOp(status);
   1.643 +
   1.644 +    Spew(SpewOps, "Execution mode: %s", ForkJoinModeString(mode_));
   1.645 +    switch (mode_) {
   1.646 +      case ForkJoinModeNormal:
   1.647 +      case ForkJoinModeCompile:
   1.648 +      case ForkJoinModeBailout:
   1.649 +        break;
   1.650 +
   1.651 +      case ForkJoinModeParallel:
   1.652 +      case ForkJoinModeRecover:
   1.653 +        // These two modes are used to check that every iteration can
   1.654 +        // be executed in parallel. They expect compilation to have
   1.655 +        // been done. But, when using gc zeal, it's possible that
   1.656 +        // compiled scripts were collected.
   1.657 +        if (ParallelTestsShouldPass(cx_) && worklist_.length() != 0) {
   1.658 +            JS_ReportError(cx_, "ForkJoin: compilation required in par or bailout mode");
   1.659 +            return SpewEndOp(ExecutionFatal);
   1.660 +        }
   1.661 +        break;
   1.662 +
   1.663 +      case NumForkJoinModes:
   1.664 +        MOZ_ASSUME_UNREACHABLE("Invalid mode");
   1.665 +    }
   1.666 +
   1.667 +    while (bailouts < MAX_BAILOUTS) {
   1.668 +        for (uint32_t i = 0; i < numWorkers; i++)
   1.669 +            bailoutRecords_[i].reset(cx_);
   1.670 +
   1.671 +        if (compileForParallelExecution(&status) == RedLight)
   1.672 +            return SpewEndOp(status);
   1.673 +
   1.674 +        JS_ASSERT(worklist_.length() == 0);
   1.675 +        if (parallelExecution(&status) == RedLight)
   1.676 +            return SpewEndOp(status);
   1.677 +
   1.678 +        if (recoverFromBailout(&status) == RedLight)
   1.679 +            return SpewEndOp(status);
   1.680 +    }
   1.681 +
   1.682 +    // After enough tries, just execute sequentially.
   1.683 +    return SpewEndOp(sequentialExecution(true));
   1.684 +}
   1.685 +
   1.686 +ForkJoinOperation::TrafficLight
   1.687 +ForkJoinOperation::enqueueInitialScript(ExecutionStatus *status)
   1.688 +{
   1.689 +    // GreenLight: script successfully enqueued if necessary
   1.690 +    // RedLight: fatal error or fell back to sequential
   1.691 +
   1.692 +    // The kernel should be a self-hosted function.
   1.693 +    if (!fun_->is<JSFunction>())
   1.694 +        return sequentialExecution(true, status);
   1.695 +
   1.696 +    RootedFunction callee(cx_, &fun_->as<JSFunction>());
   1.697 +
   1.698 +    if (!callee->isInterpreted() || !callee->isSelfHostedBuiltin())
   1.699 +        return sequentialExecution(true, status);
   1.700 +
   1.701 +    // If the main script is already compiled, and we have no reason
   1.702 +    // to suspect any of its callees are not compiled, then we can
   1.703 +    // just skip the compilation step.
   1.704 +    RootedScript script(cx_, callee->getOrCreateScript(cx_));
   1.705 +    if (!script)
   1.706 +        return RedLight;
   1.707 +
   1.708 +    if (script->hasParallelIonScript()) {
   1.709 +        // Notify that there's been activity on the entry script.
   1.710 +        JitCompartment *jitComp = cx_->compartment()->jitCompartment();
   1.711 +        if (!jitComp->notifyOfActiveParallelEntryScript(cx_, script)) {
   1.712 +            *status = ExecutionFatal;
   1.713 +            return RedLight;
   1.714 +        }
   1.715 +
   1.716 +        if (!script->parallelIonScript()->hasUncompiledCallTarget()) {
   1.717 +            Spew(SpewOps, "Script %p:%s:%d already compiled, no uncompiled callees",
   1.718 +                 script.get(), script->filename(), script->lineno());
   1.719 +            return GreenLight;
   1.720 +        }
   1.721 +
   1.722 +        Spew(SpewOps, "Script %p:%s:%d already compiled, may have uncompiled callees",
   1.723 +             script.get(), script->filename(), script->lineno());
   1.724 +    }
   1.725 +
   1.726 +    // Otherwise, add to the worklist of scripts to process.
   1.727 +    if (addToWorklist(script) == RedLight)
   1.728 +        return fatalError(status);
   1.729 +    return GreenLight;
   1.730 +}
   1.731 +
   1.732 +ForkJoinOperation::TrafficLight
   1.733 +ForkJoinOperation::compileForParallelExecution(ExecutionStatus *status)
   1.734 +{
   1.735 +    // GreenLight: all scripts compiled
   1.736 +    // RedLight: fatal error or completed work via warmups or fallback
   1.737 +
   1.738 +    // This routine attempts to do whatever compilation is necessary
   1.739 +    // to execute a single parallel attempt. When it returns, either
   1.740 +    // (1) we have fallen back to sequential; (2) we have run enough
   1.741 +    // warmup runs to complete all the work; or (3) we have compiled
   1.742 +    // all scripts we think likely to be executed during a parallel
   1.743 +    // execution.
   1.744 +
   1.745 +    RootedFunction fun(cx_);
   1.746 +    RootedScript script(cx_);
   1.747 +
   1.748 +    // After 3 stalls, we stop waiting for a script to gather type
   1.749 +    // info and move on with execution.
   1.750 +    const uint32_t stallThreshold = 3;
   1.751 +
   1.752 +    // This loop continues to iterate until the full contents of
   1.753 +    // `worklist` have been successfully compiled for parallel
   1.754 +    // execution. The compilations themselves typically occur on
   1.755 +    // helper threads. While we wait for the compilations to complete,
   1.756 +    // or for sufficient type information to be gathered, we execute
   1.757 +    // warmup iterations.
   1.758 +    while (true) {
   1.759 +        bool offMainThreadCompilationsInProgress = false;
   1.760 +        bool gatheringTypeInformation = false;
   1.761 +
   1.762 +        // Walk over the worklist to check on the status of each entry.
   1.763 +        for (uint32_t i = 0; i < worklist_.length(); i++) {
   1.764 +            script = worklist_[i];
   1.765 +            script->ensureNonLazyCanonicalFunction(cx_);
   1.766 +            fun = script->functionNonDelazifying();
   1.767 +
   1.768 +            // No baseline script means no type information, hence we
   1.769 +            // will not be able to compile very well.  In such cases,
   1.770 +            // we continue to run baseline iterations until either (1)
   1.771 +            // the potential callee *has* a baseline script or (2) the
   1.772 +            // potential callee's use count stops increasing,
   1.773 +            // indicating that they are not in fact a callee.
   1.774 +            if (!script->hasBaselineScript()) {
   1.775 +                uint32_t previousUseCount = worklistData_[i].useCount;
   1.776 +                uint32_t currentUseCount = script->getUseCount();
   1.777 +                if (previousUseCount < currentUseCount) {
   1.778 +                    worklistData_[i].useCount = currentUseCount;
   1.779 +                    worklistData_[i].stallCount = 0;
   1.780 +                    gatheringTypeInformation = true;
   1.781 +
   1.782 +                    Spew(SpewCompile,
   1.783 +                         "Script %p:%s:%d has no baseline script, "
   1.784 +                         "but use count grew from %d to %d",
   1.785 +                         script.get(), script->filename(), script->lineno(),
   1.786 +                         previousUseCount, currentUseCount);
   1.787 +                } else {
   1.788 +                    uint32_t stallCount = ++worklistData_[i].stallCount;
   1.789 +                    if (stallCount < stallThreshold) {
   1.790 +                        gatheringTypeInformation = true;
   1.791 +                    }
   1.792 +
   1.793 +                    Spew(SpewCompile,
   1.794 +                         "Script %p:%s:%d has no baseline script, "
   1.795 +                         "and use count has %u stalls at %d",
   1.796 +                         script.get(), script->filename(), script->lineno(),
   1.797 +                         stallCount, previousUseCount);
   1.798 +                }
   1.799 +                continue;
   1.800 +            }
   1.801 +
   1.802 +            if (!script->hasParallelIonScript()) {
   1.803 +                // Script has not yet been compiled. Attempt to compile it.
   1.804 +                SpewBeginCompile(script);
   1.805 +                MethodStatus mstatus = jit::CanEnterInParallel(cx_, script);
   1.806 +                SpewEndCompile(mstatus);
   1.807 +
   1.808 +                switch (mstatus) {
   1.809 +                  case Method_Error:
   1.810 +                    return fatalError(status);
   1.811 +
   1.812 +                  case Method_CantCompile:
   1.813 +                    Spew(SpewCompile,
   1.814 +                         "Script %p:%s:%d cannot be compiled, "
   1.815 +                         "falling back to sequential execution",
   1.816 +                         script.get(), script->filename(), script->lineno());
   1.817 +                    return sequentialExecution(true, status);
   1.818 +
   1.819 +                  case Method_Skipped:
   1.820 +                    // A "skipped" result either means that we are compiling
   1.821 +                    // in parallel OR some other transient error occurred.
   1.822 +                    if (script->isParallelIonCompilingOffThread()) {
   1.823 +                        Spew(SpewCompile,
   1.824 +                             "Script %p:%s:%d compiling off-thread",
   1.825 +                             script.get(), script->filename(), script->lineno());
   1.826 +                        offMainThreadCompilationsInProgress = true;
   1.827 +                        continue;
   1.828 +                    }
   1.829 +                    return sequentialExecution(false, status);
   1.830 +
   1.831 +                  case Method_Compiled:
   1.832 +                    Spew(SpewCompile,
   1.833 +                         "Script %p:%s:%d compiled",
   1.834 +                         script.get(), script->filename(), script->lineno());
   1.835 +                    JS_ASSERT(script->hasParallelIonScript());
   1.836 +
   1.837 +                    if (isInitialScript(script)) {
   1.838 +                        JitCompartment *jitComp = cx_->compartment()->jitCompartment();
   1.839 +                        if (!jitComp->notifyOfActiveParallelEntryScript(cx_, script)) {
   1.840 +                            *status = ExecutionFatal;
   1.841 +                            return RedLight;
   1.842 +                        }
   1.843 +                    }
   1.844 +
   1.845 +                    break;
   1.846 +                }
   1.847 +            }
   1.848 +
   1.849 +            // At this point, either the script was already compiled
   1.850 +            // or we just compiled it.  Check whether its "uncompiled
   1.851 +            // call target" flag is set and add the targets to our
   1.852 +            // worklist if so. Clear the flag after that, since we
   1.853 +            // will be compiling the call targets.
   1.854 +            JS_ASSERT(script->hasParallelIonScript());
   1.855 +            if (appendCallTargetsToWorklist(i, status) == RedLight)
   1.856 +                return RedLight;
   1.857 +        }
   1.858 +
   1.859 +        // If there is compilation occurring in a helper thread, then
   1.860 +        // run a warmup iterations in the main thread while we wait.
   1.861 +        // There is a chance that this warmup will finish all the work
   1.862 +        // we have to do, so we should stop then, unless we are in
   1.863 +        // compile mode, in which case we'll continue to block.
   1.864 +        //
   1.865 +        // Note that even in compile mode, we can't block *forever*:
   1.866 +        // - OMTC compiles will finish;
   1.867 +        // - no work is being done, so use counts on not-yet-baselined
   1.868 +        //   scripts will not increase.
   1.869 +        if (offMainThreadCompilationsInProgress || gatheringTypeInformation) {
   1.870 +            bool stopIfComplete = (mode_ != ForkJoinModeCompile);
   1.871 +            if (warmupExecution(stopIfComplete, status) == RedLight)
   1.872 +                return RedLight;
   1.873 +            continue;
   1.874 +        }
   1.875 +
   1.876 +        // All compilations are complete. However, be careful: it is
   1.877 +        // possible that a garbage collection occurred while we were
   1.878 +        // iterating and caused some of the scripts we thought we had
   1.879 +        // compiled to be collected. In that case, we will just have
   1.880 +        // to begin again.
   1.881 +        bool allScriptsPresent = true;
   1.882 +        for (uint32_t i = 0; i < worklist_.length(); i++) {
   1.883 +            if (!worklist_[i]->hasParallelIonScript()) {
   1.884 +                if (worklistData_[i].stallCount < stallThreshold) {
   1.885 +                    worklistData_[i].reset();
   1.886 +                    allScriptsPresent = false;
   1.887 +
   1.888 +                    Spew(SpewCompile,
   1.889 +                         "Script %p:%s:%d is not stalled, "
   1.890 +                         "but no parallel ion script found, "
   1.891 +                         "restarting loop",
   1.892 +                         script.get(), script->filename(), script->lineno());
   1.893 +                }
   1.894 +            }
   1.895 +        }
   1.896 +
   1.897 +        if (allScriptsPresent)
   1.898 +            break;
   1.899 +    }
   1.900 +
   1.901 +    Spew(SpewCompile, "Compilation complete (final worklist length %d)",
   1.902 +         worklist_.length());
   1.903 +
   1.904 +    // At this point, all scripts and their transitive callees are
   1.905 +    // either stalled (indicating they are unlikely to be called) or
   1.906 +    // in a compiled state.  Therefore we can clear the
   1.907 +    // "hasUncompiledCallTarget" flag on them and then clear the
   1.908 +    // worklist.
   1.909 +    for (uint32_t i = 0; i < worklist_.length(); i++) {
   1.910 +        if (worklist_[i]->hasParallelIonScript()) {
   1.911 +            JS_ASSERT(worklistData_[i].calleesEnqueued);
   1.912 +            worklist_[i]->parallelIonScript()->clearHasUncompiledCallTarget();
   1.913 +        } else {
   1.914 +            JS_ASSERT(worklistData_[i].stallCount >= stallThreshold);
   1.915 +        }
   1.916 +    }
   1.917 +    worklist_.clear();
   1.918 +    worklistData_.clear();
   1.919 +    return GreenLight;
   1.920 +}
   1.921 +
   1.922 +ForkJoinOperation::TrafficLight
   1.923 +ForkJoinOperation::appendCallTargetsToWorklist(uint32_t index, ExecutionStatus *status)
   1.924 +{
   1.925 +    // GreenLight: call targets appended
   1.926 +    // RedLight: fatal error or completed work via warmups or fallback
   1.927 +
   1.928 +    JS_ASSERT(worklist_[index]->hasParallelIonScript());
   1.929 +
   1.930 +    // Check whether we have already enqueued the targets for
   1.931 +    // this entry and avoid doing it again if so.
   1.932 +    if (worklistData_[index].calleesEnqueued)
   1.933 +        return GreenLight;
   1.934 +    worklistData_[index].calleesEnqueued = true;
   1.935 +
   1.936 +    // Iterate through the callees and enqueue them.
   1.937 +    RootedScript target(cx_);
   1.938 +    IonScript *ion = worklist_[index]->parallelIonScript();
   1.939 +    for (uint32_t i = 0; i < ion->callTargetEntries(); i++) {
   1.940 +        target = ion->callTargetList()[i];
   1.941 +        parallel::Spew(parallel::SpewCompile,
   1.942 +                       "Adding call target %s:%u",
   1.943 +                       target->filename(), target->lineno());
   1.944 +        if (appendCallTargetToWorklist(target, status) == RedLight)
   1.945 +            return RedLight;
   1.946 +    }
   1.947 +
   1.948 +    return GreenLight;
   1.949 +}
   1.950 +
   1.951 +ForkJoinOperation::TrafficLight
   1.952 +ForkJoinOperation::appendCallTargetToWorklist(HandleScript script, ExecutionStatus *status)
   1.953 +{
   1.954 +    // GreenLight: call target appended if necessary
   1.955 +    // RedLight: fatal error or completed work via warmups or fallback
   1.956 +
   1.957 +    JS_ASSERT(script);
   1.958 +
   1.959 +    // Fallback to sequential if disabled.
   1.960 +    if (!script->canParallelIonCompile()) {
   1.961 +        Spew(SpewCompile, "Skipping %p:%s:%u, canParallelIonCompile() is false",
   1.962 +             script.get(), script->filename(), script->lineno());
   1.963 +        return sequentialExecution(true, status);
   1.964 +    }
   1.965 +
   1.966 +    if (script->hasParallelIonScript()) {
   1.967 +        // Skip if the code is expected to result in a bailout.
   1.968 +        if (script->parallelIonScript()->bailoutExpected()) {
   1.969 +            Spew(SpewCompile, "Skipping %p:%s:%u, bailout expected",
   1.970 +                 script.get(), script->filename(), script->lineno());
   1.971 +            return sequentialExecution(false, status);
   1.972 +        }
   1.973 +    }
   1.974 +
   1.975 +    if (!addToWorklist(script))
   1.976 +        return fatalError(status);
   1.977 +
   1.978 +    return GreenLight;
   1.979 +}
   1.980 +
   1.981 +bool
   1.982 +ForkJoinOperation::addToWorklist(HandleScript script)
   1.983 +{
   1.984 +    for (uint32_t i = 0; i < worklist_.length(); i++) {
   1.985 +        if (worklist_[i] == script) {
   1.986 +            Spew(SpewCompile, "Skipping %p:%s:%u, already in worklist",
   1.987 +                 script.get(), script->filename(), script->lineno());
   1.988 +            return true;
   1.989 +        }
   1.990 +    }
   1.991 +
   1.992 +    Spew(SpewCompile, "Enqueued %p:%s:%u",
   1.993 +         script.get(), script->filename(), script->lineno());
   1.994 +
   1.995 +    // Note that we add all possibly compilable functions to the worklist,
   1.996 +    // even if they're already compiled. This is so that we can return
   1.997 +    // Method_Compiled and not Method_Skipped if we have a worklist full of
   1.998 +    // already-compiled functions.
   1.999 +    if (!worklist_.append(script))
  1.1000 +        return false;
  1.1001 +
  1.1002 +    // we have not yet enqueued the callees of this script
  1.1003 +    if (!worklistData_.append(WorklistData()))
  1.1004 +        return false;
  1.1005 +    worklistData_[worklistData_.length() - 1].reset();
  1.1006 +
  1.1007 +    return true;
  1.1008 +}
  1.1009 +
  1.1010 +ForkJoinOperation::TrafficLight
  1.1011 +ForkJoinOperation::sequentialExecution(bool disqualified, ExecutionStatus *status)
  1.1012 +{
  1.1013 +    // RedLight: fatal error or completed work
  1.1014 +
  1.1015 +    *status = sequentialExecution(disqualified);
  1.1016 +    return RedLight;
  1.1017 +}
  1.1018 +
  1.1019 +ExecutionStatus
  1.1020 +ForkJoinOperation::sequentialExecution(bool disqualified)
  1.1021 +{
  1.1022 +    // XXX use disqualified to set parallelIon to ION_DISABLED_SCRIPT?
  1.1023 +
  1.1024 +    Spew(SpewOps, "Executing sequential execution (disqualified=%d).",
  1.1025 +         disqualified);
  1.1026 +
  1.1027 +    if (sliceStart_ == sliceEnd_)
  1.1028 +        return ExecutionSequential;
  1.1029 +
  1.1030 +    RootedValue funVal(cx_, ObjectValue(*fun_));
  1.1031 +    if (!ExecuteSequentially(cx_, funVal, &sliceStart_, sliceEnd_))
  1.1032 +        return ExecutionFatal;
  1.1033 +    MOZ_ASSERT(sliceStart_ == sliceEnd_);
  1.1034 +    return ExecutionSequential;
  1.1035 +}
  1.1036 +
  1.1037 +ForkJoinOperation::TrafficLight
  1.1038 +ForkJoinOperation::fatalError(ExecutionStatus *status)
  1.1039 +{
  1.1040 +    // RedLight: fatal error
  1.1041 +
  1.1042 +    *status = ExecutionFatal;
  1.1043 +    return RedLight;
  1.1044 +}
  1.1045 +
  1.1046 +static const char *
  1.1047 +BailoutExplanation(ParallelBailoutCause cause)
  1.1048 +{
  1.1049 +    switch (cause) {
  1.1050 +      case ParallelBailoutNone:
  1.1051 +        return "no particular reason";
  1.1052 +      case ParallelBailoutCompilationSkipped:
  1.1053 +        return "compilation failed (method skipped)";
  1.1054 +      case ParallelBailoutCompilationFailure:
  1.1055 +        return "compilation failed";
  1.1056 +      case ParallelBailoutInterrupt:
  1.1057 +        return "interrupted";
  1.1058 +      case ParallelBailoutFailedIC:
  1.1059 +        return "failed to attach stub to IC";
  1.1060 +      case ParallelBailoutHeapBusy:
  1.1061 +        return "heap busy flag set during interrupt";
  1.1062 +      case ParallelBailoutMainScriptNotPresent:
  1.1063 +        return "main script not present";
  1.1064 +      case ParallelBailoutCalledToUncompiledScript:
  1.1065 +        return "called to uncompiled script";
  1.1066 +      case ParallelBailoutIllegalWrite:
  1.1067 +        return "illegal write";
  1.1068 +      case ParallelBailoutAccessToIntrinsic:
  1.1069 +        return "access to intrinsic";
  1.1070 +      case ParallelBailoutOverRecursed:
  1.1071 +        return "over recursed";
  1.1072 +      case ParallelBailoutOutOfMemory:
  1.1073 +        return "out of memory";
  1.1074 +      case ParallelBailoutUnsupported:
  1.1075 +        return "unsupported";
  1.1076 +      case ParallelBailoutUnsupportedVM:
  1.1077 +        return "unsupported operation in VM call";
  1.1078 +      case ParallelBailoutUnsupportedStringComparison:
  1.1079 +        return "unsupported string comparison";
  1.1080 +      case ParallelBailoutRequestedGC:
  1.1081 +        return "requested GC";
  1.1082 +      case ParallelBailoutRequestedZoneGC:
  1.1083 +        return "requested zone GC";
  1.1084 +      default:
  1.1085 +        return "no known reason";
  1.1086 +    }
  1.1087 +}
  1.1088 +
  1.1089 +bool
  1.1090 +ForkJoinOperation::isInitialScript(HandleScript script)
  1.1091 +{
  1.1092 +    return fun_->is<JSFunction>() && (fun_->as<JSFunction>().nonLazyScript() == script);
  1.1093 +}
  1.1094 +
  1.1095 +void
  1.1096 +ForkJoinOperation::determineBailoutCause()
  1.1097 +{
  1.1098 +    bailoutCause = ParallelBailoutNone;
  1.1099 +    for (uint32_t i = 0; i < bailoutRecords_.length(); i++) {
  1.1100 +        if (bailoutRecords_[i].cause == ParallelBailoutNone)
  1.1101 +            continue;
  1.1102 +
  1.1103 +        if (bailoutRecords_[i].cause == ParallelBailoutInterrupt)
  1.1104 +            continue;
  1.1105 +
  1.1106 +        bailoutCause = bailoutRecords_[i].cause;
  1.1107 +        const char *causeStr = BailoutExplanation(bailoutCause);
  1.1108 +        if (bailoutRecords_[i].depth) {
  1.1109 +            bailoutScript = bailoutRecords_[i].trace[0].script;
  1.1110 +            bailoutBytecode = bailoutRecords_[i].trace[0].bytecode;
  1.1111 +
  1.1112 +            const char *filename = bailoutScript->filename();
  1.1113 +            int line = JS_PCToLineNumber(cx_, bailoutScript, bailoutBytecode);
  1.1114 +            JS_ReportWarning(cx_, "Bailed out of parallel operation: %s at %s:%d",
  1.1115 +                             causeStr, filename, line);
  1.1116 +
  1.1117 +            Spew(SpewBailouts, "Bailout from thread %d: cause %d at loc %s:%d",
  1.1118 +                 i,
  1.1119 +                 bailoutCause,
  1.1120 +                 bailoutScript->filename(),
  1.1121 +                 PCToLineNumber(bailoutScript, bailoutBytecode));
  1.1122 +        } else {
  1.1123 +            JS_ReportWarning(cx_, "Bailed out of parallel operation: %s",
  1.1124 +                             causeStr);
  1.1125 +
  1.1126 +            Spew(SpewBailouts, "Bailout from thread %d: cause %d, unknown loc",
  1.1127 +                 i,
  1.1128 +                 bailoutCause);
  1.1129 +        }
  1.1130 +    }
  1.1131 +}
  1.1132 +
  1.1133 +bool
  1.1134 +ForkJoinOperation::invalidateBailedOutScripts()
  1.1135 +{
  1.1136 +    Vector<types::RecompileInfo> invalid(cx_);
  1.1137 +    for (uint32_t i = 0; i < bailoutRecords_.length(); i++) {
  1.1138 +        RootedScript script(cx_, bailoutRecords_[i].topScript);
  1.1139 +
  1.1140 +        // No script to invalidate.
  1.1141 +        if (!script || !script->hasParallelIonScript())
  1.1142 +            continue;
  1.1143 +
  1.1144 +        Spew(SpewBailouts,
  1.1145 +             "Bailout from thread %d: cause %d, topScript %p:%s:%d",
  1.1146 +             i,
  1.1147 +             bailoutRecords_[i].cause,
  1.1148 +             script.get(), script->filename(), script->lineno());
  1.1149 +
  1.1150 +        switch (bailoutRecords_[i].cause) {
  1.1151 +          // An interrupt is not the fault of the script, so don't
  1.1152 +          // invalidate it.
  1.1153 +          case ParallelBailoutInterrupt: continue;
  1.1154 +
  1.1155 +          // An illegal write will not be made legal by invalidation.
  1.1156 +          case ParallelBailoutIllegalWrite: continue;
  1.1157 +
  1.1158 +          // For other cases, consider invalidation.
  1.1159 +          default: break;
  1.1160 +        }
  1.1161 +
  1.1162 +        // Already invalidated.
  1.1163 +        if (hasScript(invalid, script))
  1.1164 +            continue;
  1.1165 +
  1.1166 +        Spew(SpewBailouts, "Invalidating script %p:%s:%d due to cause %d",
  1.1167 +             script.get(), script->filename(), script->lineno(),
  1.1168 +             bailoutRecords_[i].cause);
  1.1169 +
  1.1170 +        types::RecompileInfo co = script->parallelIonScript()->recompileInfo();
  1.1171 +
  1.1172 +        if (!invalid.append(co))
  1.1173 +            return false;
  1.1174 +
  1.1175 +        // any script that we have marked for invalidation will need
  1.1176 +        // to be recompiled
  1.1177 +        if (!addToWorklist(script))
  1.1178 +            return false;
  1.1179 +    }
  1.1180 +
  1.1181 +    Invalidate(cx_, invalid);
  1.1182 +
  1.1183 +    return true;
  1.1184 +}
  1.1185 +
  1.1186 +ForkJoinOperation::TrafficLight
  1.1187 +ForkJoinOperation::warmupExecution(bool stopIfComplete, ExecutionStatus *status)
  1.1188 +{
  1.1189 +    // GreenLight: warmup succeeded, still more work to do
  1.1190 +    // RedLight: fatal error or warmup completed all work (check status)
  1.1191 +
  1.1192 +    if (sliceStart_ == sliceEnd_) {
  1.1193 +        Spew(SpewOps, "Warmup execution finished all the work.");
  1.1194 +
  1.1195 +        if (stopIfComplete) {
  1.1196 +            *status = ExecutionWarmup;
  1.1197 +            return RedLight;
  1.1198 +        }
  1.1199 +
  1.1200 +        // If we finished all slices in warmup, be sure check the
  1.1201 +        // interrupt flag. This is because we won't be running more JS
  1.1202 +        // code, and thus no more automatic checking of the interrupt
  1.1203 +        // flag.
  1.1204 +        if (!CheckForInterrupt(cx_)) {
  1.1205 +            *status = ExecutionFatal;
  1.1206 +            return RedLight;
  1.1207 +        }
  1.1208 +
  1.1209 +        return GreenLight;
  1.1210 +    }
  1.1211 +
  1.1212 +    Spew(SpewOps, "Executing warmup from slice %d.", sliceStart_);
  1.1213 +
  1.1214 +    AutoEnterWarmup warmup(cx_->runtime());
  1.1215 +    RootedValue funVal(cx_, ObjectValue(*fun_));
  1.1216 +    if (!ExecuteSequentially(cx_, funVal, &sliceStart_, sliceStart_ + 1)) {
  1.1217 +        *status = ExecutionFatal;
  1.1218 +        return RedLight;
  1.1219 +    }
  1.1220 +
  1.1221 +    return GreenLight;
  1.1222 +}
  1.1223 +
  1.1224 +ForkJoinOperation::TrafficLight
  1.1225 +ForkJoinOperation::parallelExecution(ExecutionStatus *status)
  1.1226 +{
  1.1227 +    // GreenLight: bailout occurred, keep trying
  1.1228 +    // RedLight: fatal error or all work completed
  1.1229 +
  1.1230 +    // Recursive use of the ThreadPool is not supported.  Right now we
  1.1231 +    // cannot get here because parallel code cannot invoke native
  1.1232 +    // functions such as ForkJoin().
  1.1233 +    JS_ASSERT(ForkJoinContext::current() == nullptr);
  1.1234 +
  1.1235 +    if (sliceStart_ == sliceEnd_) {
  1.1236 +        Spew(SpewOps, "Warmup execution finished all the work.");
  1.1237 +        *status = ExecutionWarmup;
  1.1238 +        return RedLight;
  1.1239 +    }
  1.1240 +
  1.1241 +    ForkJoinActivation activation(cx_);
  1.1242 +    ThreadPool *threadPool = &cx_->runtime()->threadPool;
  1.1243 +    ForkJoinShared shared(cx_, threadPool, fun_, sliceStart_, sliceEnd_, &bailoutRecords_[0]);
  1.1244 +    if (!shared.init()) {
  1.1245 +        *status = ExecutionFatal;
  1.1246 +        return RedLight;
  1.1247 +    }
  1.1248 +
  1.1249 +    switch (shared.execute()) {
  1.1250 +      case TP_SUCCESS:
  1.1251 +        *status = ExecutionParallel;
  1.1252 +        return RedLight;
  1.1253 +
  1.1254 +      case TP_FATAL:
  1.1255 +        *status = ExecutionFatal;
  1.1256 +        return RedLight;
  1.1257 +
  1.1258 +      case TP_RETRY_SEQUENTIALLY:
  1.1259 +      case TP_RETRY_AFTER_GC:
  1.1260 +        break; // bailout
  1.1261 +    }
  1.1262 +
  1.1263 +    return GreenLight;
  1.1264 +}
  1.1265 +
  1.1266 +ForkJoinOperation::TrafficLight
  1.1267 +ForkJoinOperation::recoverFromBailout(ExecutionStatus *status)
  1.1268 +{
  1.1269 +    // GreenLight: bailout recovered, try to compile-and-run again
  1.1270 +    // RedLight: fatal error
  1.1271 +
  1.1272 +    bailouts += 1;
  1.1273 +    determineBailoutCause();
  1.1274 +
  1.1275 +    SpewBailout(bailouts, bailoutScript, bailoutBytecode, bailoutCause);
  1.1276 +
  1.1277 +    // After any bailout, we always scan over callee list of main
  1.1278 +    // function, if nothing else
  1.1279 +    RootedScript mainScript(cx_, fun_->nonLazyScript());
  1.1280 +    if (!addToWorklist(mainScript))
  1.1281 +        return fatalError(status);
  1.1282 +
  1.1283 +    // Also invalidate and recompile any callees that were implicated
  1.1284 +    // by the bailout
  1.1285 +    if (!invalidateBailedOutScripts())
  1.1286 +        return fatalError(status);
  1.1287 +
  1.1288 +    if (warmupExecution(/*stopIfComplete:*/true, status) == RedLight)
  1.1289 +        return RedLight;
  1.1290 +
  1.1291 +    return GreenLight;
  1.1292 +}
  1.1293 +
  1.1294 +bool
  1.1295 +ForkJoinOperation::hasScript(Vector<types::RecompileInfo> &scripts, JSScript *script)
  1.1296 +{
  1.1297 +    for (uint32_t i = 0; i < scripts.length(); i++) {
  1.1298 +        if (scripts[i] == script->parallelIonScript()->recompileInfo())
  1.1299 +            return true;
  1.1300 +    }
  1.1301 +    return false;
  1.1302 +}
  1.1303 +
  1.1304 +// Can only enter callees with a valid IonScript.
  1.1305 +template <uint32_t maxArgc>
  1.1306 +class ParallelIonInvoke
  1.1307 +{
  1.1308 +    EnterJitCode enter_;
  1.1309 +    void *jitcode_;
  1.1310 +    void *calleeToken_;
  1.1311 +    Value argv_[maxArgc + 2];
  1.1312 +    uint32_t argc_;
  1.1313 +
  1.1314 +  public:
  1.1315 +    Value *args;
  1.1316 +
  1.1317 +    ParallelIonInvoke(JSRuntime *rt,
  1.1318 +                      HandleFunction callee,
  1.1319 +                      uint32_t argc)
  1.1320 +      : argc_(argc),
  1.1321 +        args(argv_ + 2)
  1.1322 +    {
  1.1323 +        JS_ASSERT(argc <= maxArgc + 2);
  1.1324 +
  1.1325 +        // Set 'callee' and 'this'.
  1.1326 +        argv_[0] = ObjectValue(*callee);
  1.1327 +        argv_[1] = UndefinedValue();
  1.1328 +
  1.1329 +        // Find JIT code pointer.
  1.1330 +        IonScript *ion = callee->nonLazyScript()->parallelIonScript();
  1.1331 +        JitCode *code = ion->method();
  1.1332 +        jitcode_ = code->raw();
  1.1333 +        enter_ = rt->jitRuntime()->enterIon();
  1.1334 +        calleeToken_ = CalleeToToken(callee);
  1.1335 +    }
  1.1336 +
  1.1337 +    bool invoke(PerThreadData *perThread) {
  1.1338 +        RootedValue result(perThread);
  1.1339 +        CALL_GENERATED_CODE(enter_, jitcode_, argc_ + 1, argv_ + 1, nullptr, calleeToken_,
  1.1340 +                            nullptr, 0, result.address());
  1.1341 +        return !result.isMagic();
  1.1342 +    }
  1.1343 +};
  1.1344 +
  1.1345 +/////////////////////////////////////////////////////////////////////////////
  1.1346 +// ForkJoinShared
  1.1347 +//
  1.1348 +
  1.1349 +ForkJoinShared::ForkJoinShared(JSContext *cx,
  1.1350 +                               ThreadPool *threadPool,
  1.1351 +                               HandleFunction fun,
  1.1352 +                               uint16_t sliceStart,
  1.1353 +                               uint16_t sliceEnd,
  1.1354 +                               ParallelBailoutRecord *records)
  1.1355 +  : cx_(cx),
  1.1356 +    threadPool_(threadPool),
  1.1357 +    fun_(fun),
  1.1358 +    sliceStart_(sliceStart),
  1.1359 +    sliceEnd_(sliceEnd),
  1.1360 +    cxLock_(nullptr),
  1.1361 +    records_(records),
  1.1362 +    allocators_(cx),
  1.1363 +    gcRequested_(false),
  1.1364 +    gcReason_(JS::gcreason::NUM_REASONS),
  1.1365 +    gcZone_(nullptr),
  1.1366 +    abort_(false),
  1.1367 +    fatal_(false)
  1.1368 +{
  1.1369 +}
  1.1370 +
  1.1371 +bool
  1.1372 +ForkJoinShared::init()
  1.1373 +{
  1.1374 +    // Create temporary arenas to hold the data allocated during the
  1.1375 +    // parallel code.
  1.1376 +    //
  1.1377 +    // Note: you might think (as I did, initially) that we could use
  1.1378 +    // compartment |Allocator| for the main thread.  This is not true,
  1.1379 +    // because when executing parallel code we sometimes check what
  1.1380 +    // arena list an object is in to decide if it is writable.  If we
  1.1381 +    // used the compartment |Allocator| for the main thread, then the
  1.1382 +    // main thread would be permitted to write to any object it wants.
  1.1383 +
  1.1384 +    if (!Monitor::init())
  1.1385 +        return false;
  1.1386 +
  1.1387 +    cxLock_ = PR_NewLock();
  1.1388 +    if (!cxLock_)
  1.1389 +        return false;
  1.1390 +
  1.1391 +    for (unsigned i = 0; i < threadPool_->numWorkers(); i++) {
  1.1392 +        Allocator *allocator = cx_->new_<Allocator>(cx_->zone());
  1.1393 +        if (!allocator)
  1.1394 +            return false;
  1.1395 +
  1.1396 +        if (!allocators_.append(allocator)) {
  1.1397 +            js_delete(allocator);
  1.1398 +            return false;
  1.1399 +        }
  1.1400 +    }
  1.1401 +
  1.1402 +    return true;
  1.1403 +}
  1.1404 +
  1.1405 +ForkJoinShared::~ForkJoinShared()
  1.1406 +{
  1.1407 +    PR_DestroyLock(cxLock_);
  1.1408 +
  1.1409 +    while (allocators_.length() > 0)
  1.1410 +        js_delete(allocators_.popCopy());
  1.1411 +}
  1.1412 +
  1.1413 +ParallelResult
  1.1414 +ForkJoinShared::execute()
  1.1415 +{
  1.1416 +    // Sometimes a GC request occurs *just before* we enter into the
  1.1417 +    // parallel section.  Rather than enter into the parallel section
  1.1418 +    // and then abort, we just check here and abort early.
  1.1419 +    if (cx_->runtime()->interruptPar)
  1.1420 +        return TP_RETRY_SEQUENTIALLY;
  1.1421 +
  1.1422 +    AutoLockMonitor lock(*this);
  1.1423 +
  1.1424 +    ParallelResult jobResult = TP_SUCCESS;
  1.1425 +    {
  1.1426 +        AutoUnlockMonitor unlock(*this);
  1.1427 +
  1.1428 +        // Push parallel tasks and wait until they're all done.
  1.1429 +        jobResult = threadPool_->executeJob(cx_, this, sliceStart_, sliceEnd_);
  1.1430 +        if (jobResult == TP_FATAL)
  1.1431 +            return TP_FATAL;
  1.1432 +    }
  1.1433 +
  1.1434 +    transferArenasToCompartmentAndProcessGCRequests();
  1.1435 +
  1.1436 +    // Check if any of the workers failed.
  1.1437 +    if (abort_) {
  1.1438 +        if (fatal_)
  1.1439 +            return TP_FATAL;
  1.1440 +        return TP_RETRY_SEQUENTIALLY;
  1.1441 +    }
  1.1442 +
  1.1443 +#ifdef DEBUG
  1.1444 +    Spew(SpewOps, "Completed parallel job [slices: %d, threads: %d, stolen: %d (work stealing:%s)]",
  1.1445 +         sliceEnd_ - sliceStart_,
  1.1446 +         threadPool_->numWorkers(),
  1.1447 +         threadPool_->stolenSlices(),
  1.1448 +         threadPool_->workStealing() ? "ON" : "OFF");
  1.1449 +#endif
  1.1450 +
  1.1451 +    // Everything went swimmingly. Give yourself a pat on the back.
  1.1452 +    return jobResult;
  1.1453 +}
  1.1454 +
  1.1455 +void
  1.1456 +ForkJoinShared::transferArenasToCompartmentAndProcessGCRequests()
  1.1457 +{
  1.1458 +    JSCompartment *comp = cx_->compartment();
  1.1459 +    for (unsigned i = 0; i < threadPool_->numWorkers(); i++)
  1.1460 +        comp->adoptWorkerAllocator(allocators_[i]);
  1.1461 +
  1.1462 +    if (gcRequested_) {
  1.1463 +        if (!gcZone_)
  1.1464 +            TriggerGC(cx_->runtime(), gcReason_);
  1.1465 +        else
  1.1466 +            TriggerZoneGC(gcZone_, gcReason_);
  1.1467 +        gcRequested_ = false;
  1.1468 +        gcZone_ = nullptr;
  1.1469 +    }
  1.1470 +}
  1.1471 +
  1.1472 +bool
  1.1473 +ForkJoinShared::executeFromWorker(ThreadPoolWorker *worker, uintptr_t stackLimit)
  1.1474 +{
  1.1475 +    PerThreadData thisThread(cx_->runtime());
  1.1476 +    if (!thisThread.init()) {
  1.1477 +        setAbortFlagAndRequestInterrupt(true);
  1.1478 +        return false;
  1.1479 +    }
  1.1480 +    TlsPerThreadData.set(&thisThread);
  1.1481 +
  1.1482 +#ifdef JS_ARM_SIMULATOR
  1.1483 +    stackLimit = Simulator::StackLimit();
  1.1484 +#endif
  1.1485 +
  1.1486 +    // Don't use setIonStackLimit() because that acquires the ionStackLimitLock, and the
  1.1487 +    // lock has not been initialized in these cases.
  1.1488 +    thisThread.jitStackLimit = stackLimit;
  1.1489 +    executePortion(&thisThread, worker);
  1.1490 +    TlsPerThreadData.set(nullptr);
  1.1491 +
  1.1492 +    return !abort_;
  1.1493 +}
  1.1494 +
  1.1495 +bool
  1.1496 +ForkJoinShared::executeFromMainThread(ThreadPoolWorker *worker)
  1.1497 +{
  1.1498 +    executePortion(&cx_->mainThread(), worker);
  1.1499 +    return !abort_;
  1.1500 +}
  1.1501 +
  1.1502 +void
  1.1503 +ForkJoinShared::executePortion(PerThreadData *perThread, ThreadPoolWorker *worker)
  1.1504 +{
  1.1505 +    // WARNING: This code runs ON THE PARALLEL WORKER THREAD.
  1.1506 +    // Be careful when accessing cx_.
  1.1507 +
  1.1508 +    // ForkJoinContext already contains an AutoAssertNoGC; however, the analysis
  1.1509 +    // does not propagate this type information. We duplicate the assertion
  1.1510 +    // here for maximum clarity.
  1.1511 +    JS::AutoAssertNoGC nogc(runtime());
  1.1512 +
  1.1513 +    Allocator *allocator = allocators_[worker->id()];
  1.1514 +    ForkJoinContext cx(perThread, worker, allocator, this, &records_[worker->id()]);
  1.1515 +    AutoSetForkJoinContext autoContext(&cx);
  1.1516 +
  1.1517 +#ifdef DEBUG
  1.1518 +    // Set the maximum worker and slice number for prettier spewing.
  1.1519 +    cx.maxWorkerId = threadPool_->numWorkers();
  1.1520 +#endif
  1.1521 +
  1.1522 +    Spew(SpewOps, "Up");
  1.1523 +
  1.1524 +    // Make a new IonContext for the slice, which is needed if we need to
  1.1525 +    // re-enter the VM.
  1.1526 +    IonContext icx(CompileRuntime::get(cx_->runtime()),
  1.1527 +                   CompileCompartment::get(cx_->compartment()),
  1.1528 +                   nullptr);
  1.1529 +
  1.1530 +    JS_ASSERT(cx.bailoutRecord->topScript == nullptr);
  1.1531 +
  1.1532 +    if (!fun_->nonLazyScript()->hasParallelIonScript()) {
  1.1533 +        // Sometimes, particularly with GCZeal, the parallel ion
  1.1534 +        // script can be collected between starting the parallel
  1.1535 +        // op and reaching this point.  In that case, we just fail
  1.1536 +        // and fallback.
  1.1537 +        Spew(SpewOps, "Down (Script no longer present)");
  1.1538 +        cx.bailoutRecord->setCause(ParallelBailoutMainScriptNotPresent);
  1.1539 +        setAbortFlagAndRequestInterrupt(false);
  1.1540 +    } else {
  1.1541 +        ParallelIonInvoke<3> fii(cx_->runtime(), fun_, 3);
  1.1542 +
  1.1543 +        fii.args[0] = Int32Value(worker->id());
  1.1544 +        fii.args[1] = Int32Value(sliceStart_);
  1.1545 +        fii.args[2] = Int32Value(sliceEnd_);
  1.1546 +
  1.1547 +        bool ok = fii.invoke(perThread);
  1.1548 +        JS_ASSERT(ok == !cx.bailoutRecord->topScript);
  1.1549 +        if (!ok)
  1.1550 +            setAbortFlagAndRequestInterrupt(false);
  1.1551 +    }
  1.1552 +
  1.1553 +    Spew(SpewOps, "Down");
  1.1554 +}
  1.1555 +
  1.1556 +void
  1.1557 +ForkJoinShared::setAbortFlagDueToInterrupt(ForkJoinContext &cx)
  1.1558 +{
  1.1559 +    JS_ASSERT(cx_->runtime()->interruptPar);
  1.1560 +    // The GC Needed flag should not be set during parallel
  1.1561 +    // execution.  Instead, one of the requestGC() or
  1.1562 +    // requestZoneGC() methods should be invoked.
  1.1563 +    JS_ASSERT(!cx_->runtime()->gcIsNeeded);
  1.1564 +
  1.1565 +    if (!abort_) {
  1.1566 +        cx.bailoutRecord->setCause(ParallelBailoutInterrupt);
  1.1567 +        setAbortFlagAndRequestInterrupt(false);
  1.1568 +    }
  1.1569 +}
  1.1570 +
  1.1571 +void
  1.1572 +ForkJoinShared::setAbortFlagAndRequestInterrupt(bool fatal)
  1.1573 +{
  1.1574 +    AutoLockMonitor lock(*this);
  1.1575 +
  1.1576 +    abort_ = true;
  1.1577 +    fatal_ = fatal_ || fatal;
  1.1578 +
  1.1579 +    // Note: The ForkJoin trigger here avoids the expensive memory protection needed to
  1.1580 +    // interrupt Ion code compiled for sequential execution.
  1.1581 +    cx_->runtime()->requestInterrupt(JSRuntime::RequestInterruptAnyThreadForkJoin);
  1.1582 +}
  1.1583 +
  1.1584 +void
  1.1585 +ForkJoinShared::requestGC(JS::gcreason::Reason reason)
  1.1586 +{
  1.1587 +    AutoLockMonitor lock(*this);
  1.1588 +
  1.1589 +    gcZone_ = nullptr;
  1.1590 +    gcReason_ = reason;
  1.1591 +    gcRequested_ = true;
  1.1592 +}
  1.1593 +
  1.1594 +void
  1.1595 +ForkJoinShared::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
  1.1596 +{
  1.1597 +    AutoLockMonitor lock(*this);
  1.1598 +
  1.1599 +    if (gcRequested_ && gcZone_ != zone) {
  1.1600 +        // If a full GC has been requested, or a GC for another zone,
  1.1601 +        // issue a request for a full GC.
  1.1602 +        gcZone_ = nullptr;
  1.1603 +        gcReason_ = reason;
  1.1604 +        gcRequested_ = true;
  1.1605 +    } else {
  1.1606 +        // Otherwise, just GC this zone.
  1.1607 +        gcZone_ = zone;
  1.1608 +        gcReason_ = reason;
  1.1609 +        gcRequested_ = true;
  1.1610 +    }
  1.1611 +}
  1.1612 +
  1.1613 +/////////////////////////////////////////////////////////////////////////////
  1.1614 +// ForkJoinContext
  1.1615 +//
  1.1616 +
  1.1617 +ForkJoinContext::ForkJoinContext(PerThreadData *perThreadData, ThreadPoolWorker *worker,
  1.1618 +                                 Allocator *allocator, ForkJoinShared *shared,
  1.1619 +                                 ParallelBailoutRecord *bailoutRecord)
  1.1620 +  : ThreadSafeContext(shared->runtime(), perThreadData, Context_ForkJoin),
  1.1621 +    bailoutRecord(bailoutRecord),
  1.1622 +    targetRegionStart(nullptr),
  1.1623 +    targetRegionEnd(nullptr),
  1.1624 +    shared_(shared),
  1.1625 +    worker_(worker),
  1.1626 +    acquiredJSContext_(false),
  1.1627 +    nogc_(shared->runtime())
  1.1628 +{
  1.1629 +    /*
  1.1630 +     * Unsafely set the zone. This is used to track malloc counters and to
  1.1631 +     * trigger GCs and is otherwise not thread-safe to access.
  1.1632 +     */
  1.1633 +    zone_ = shared->zone();
  1.1634 +
  1.1635 +    /*
  1.1636 +     * Unsafely set the compartment. This is used to get read-only access to
  1.1637 +     * shared tables.
  1.1638 +     */
  1.1639 +    compartment_ = shared->compartment();
  1.1640 +
  1.1641 +    allocator_ = allocator;
  1.1642 +}
  1.1643 +
  1.1644 +bool
  1.1645 +ForkJoinContext::isMainThread() const
  1.1646 +{
  1.1647 +    return perThreadData == &shared_->runtime()->mainThread;
  1.1648 +}
  1.1649 +
  1.1650 +JSRuntime *
  1.1651 +ForkJoinContext::runtime()
  1.1652 +{
  1.1653 +    return shared_->runtime();
  1.1654 +}
  1.1655 +
  1.1656 +JSContext *
  1.1657 +ForkJoinContext::acquireJSContext()
  1.1658 +{
  1.1659 +    JSContext *cx = shared_->acquireJSContext();
  1.1660 +    JS_ASSERT(!acquiredJSContext_);
  1.1661 +    acquiredJSContext_ = true;
  1.1662 +    return cx;
  1.1663 +}
  1.1664 +
  1.1665 +void
  1.1666 +ForkJoinContext::releaseJSContext()
  1.1667 +{
  1.1668 +    JS_ASSERT(acquiredJSContext_);
  1.1669 +    acquiredJSContext_ = false;
  1.1670 +    return shared_->releaseJSContext();
  1.1671 +}
  1.1672 +
  1.1673 +bool
  1.1674 +ForkJoinContext::hasAcquiredJSContext() const
  1.1675 +{
  1.1676 +    return acquiredJSContext_;
  1.1677 +}
  1.1678 +
  1.1679 +bool
  1.1680 +ForkJoinContext::check()
  1.1681 +{
  1.1682 +    if (runtime()->interruptPar) {
  1.1683 +        shared_->setAbortFlagDueToInterrupt(*this);
  1.1684 +        return false;
  1.1685 +    }
  1.1686 +    return true;
  1.1687 +}
  1.1688 +
  1.1689 +void
  1.1690 +ForkJoinContext::requestGC(JS::gcreason::Reason reason)
  1.1691 +{
  1.1692 +    shared_->requestGC(reason);
  1.1693 +    bailoutRecord->setCause(ParallelBailoutRequestedGC);
  1.1694 +    shared_->setAbortFlagAndRequestInterrupt(false);
  1.1695 +}
  1.1696 +
  1.1697 +void
  1.1698 +ForkJoinContext::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
  1.1699 +{
  1.1700 +    shared_->requestZoneGC(zone, reason);
  1.1701 +    bailoutRecord->setCause(ParallelBailoutRequestedZoneGC);
  1.1702 +    shared_->setAbortFlagAndRequestInterrupt(false);
  1.1703 +}
  1.1704 +
  1.1705 +bool
  1.1706 +ForkJoinContext::setPendingAbortFatal(ParallelBailoutCause cause)
  1.1707 +{
  1.1708 +    shared_->setPendingAbortFatal();
  1.1709 +    bailoutRecord->setCause(cause);
  1.1710 +    return false;
  1.1711 +}
  1.1712 +
  1.1713 +//////////////////////////////////////////////////////////////////////////////
  1.1714 +// ParallelBailoutRecord
  1.1715 +
  1.1716 +void
  1.1717 +js::ParallelBailoutRecord::init(JSContext *cx)
  1.1718 +{
  1.1719 +    reset(cx);
  1.1720 +}
  1.1721 +
  1.1722 +void
  1.1723 +js::ParallelBailoutRecord::reset(JSContext *cx)
  1.1724 +{
  1.1725 +    topScript = nullptr;
  1.1726 +    cause = ParallelBailoutNone;
  1.1727 +    depth = 0;
  1.1728 +}
  1.1729 +
  1.1730 +void
  1.1731 +js::ParallelBailoutRecord::setCause(ParallelBailoutCause cause,
  1.1732 +                                    JSScript *outermostScript,
  1.1733 +                                    JSScript *currentScript,
  1.1734 +                                    jsbytecode *currentPc)
  1.1735 +{
  1.1736 +    this->cause = cause;
  1.1737 +    updateCause(cause, outermostScript, currentScript, currentPc);
  1.1738 +}
  1.1739 +
  1.1740 +void
  1.1741 +js::ParallelBailoutRecord::updateCause(ParallelBailoutCause cause,
  1.1742 +                                       JSScript *outermostScript,
  1.1743 +                                       JSScript *currentScript,
  1.1744 +                                       jsbytecode *currentPc)
  1.1745 +{
  1.1746 +    JS_ASSERT_IF(outermostScript, currentScript);
  1.1747 +    JS_ASSERT_IF(outermostScript, outermostScript->hasParallelIonScript());
  1.1748 +    JS_ASSERT_IF(currentScript, outermostScript);
  1.1749 +    JS_ASSERT_IF(!currentScript, !currentPc);
  1.1750 +
  1.1751 +    if (this->cause == ParallelBailoutNone)
  1.1752 +        this->cause = cause;
  1.1753 +
  1.1754 +    if (outermostScript)
  1.1755 +        this->topScript = outermostScript;
  1.1756 +
  1.1757 +    if (currentScript)
  1.1758 +        addTrace(currentScript, currentPc);
  1.1759 +}
  1.1760 +
  1.1761 +void
  1.1762 +js::ParallelBailoutRecord::addTrace(JSScript *script,
  1.1763 +                                    jsbytecode *pc)
  1.1764 +{
  1.1765 +    // Ideally, this should never occur, because we should always have
  1.1766 +    // a script when we invoke setCause, but I havent' fully
  1.1767 +    // refactored things to that point yet:
  1.1768 +    if (topScript == nullptr && script != nullptr)
  1.1769 +        topScript = script;
  1.1770 +
  1.1771 +    if (depth < MaxDepth) {
  1.1772 +        trace[depth].script = script;
  1.1773 +        trace[depth].bytecode = pc;
  1.1774 +        depth += 1;
  1.1775 +    }
  1.1776 +}
  1.1777 +
  1.1778 +//////////////////////////////////////////////////////////////////////////////
  1.1779 +
  1.1780 +//
  1.1781 +// Debug spew
  1.1782 +//
  1.1783 +
  1.1784 +#ifdef DEBUG
  1.1785 +
  1.1786 +static const char *
  1.1787 +ExecutionStatusToString(ExecutionStatus status)
  1.1788 +{
  1.1789 +    switch (status) {
  1.1790 +      case ExecutionFatal:
  1.1791 +        return "fatal";
  1.1792 +      case ExecutionSequential:
  1.1793 +        return "sequential";
  1.1794 +      case ExecutionWarmup:
  1.1795 +        return "warmup";
  1.1796 +      case ExecutionParallel:
  1.1797 +        return "parallel";
  1.1798 +    }
  1.1799 +    return "(unknown status)";
  1.1800 +}
  1.1801 +
  1.1802 +static const char *
  1.1803 +MethodStatusToString(MethodStatus status)
  1.1804 +{
  1.1805 +    switch (status) {
  1.1806 +      case Method_Error:
  1.1807 +        return "error";
  1.1808 +      case Method_CantCompile:
  1.1809 +        return "can't compile";
  1.1810 +      case Method_Skipped:
  1.1811 +        return "skipped";
  1.1812 +      case Method_Compiled:
  1.1813 +        return "compiled";
  1.1814 +    }
  1.1815 +    return "(unknown status)";
  1.1816 +}
  1.1817 +
  1.1818 +static unsigned
  1.1819 +NumberOfDigits(unsigned n)
  1.1820 +{
  1.1821 +    if (n == 0)
  1.1822 +        return 1;
  1.1823 +    unsigned d = 0;
  1.1824 +    while (n != 0) {
  1.1825 +        d++;
  1.1826 +        n /= 10;
  1.1827 +    }
  1.1828 +    return d;
  1.1829 +}
  1.1830 +
  1.1831 +static const size_t BufferSize = 4096;
  1.1832 +
  1.1833 +class ParallelSpewer
  1.1834 +{
  1.1835 +    uint32_t depth;
  1.1836 +    bool colorable;
  1.1837 +    bool active[NumSpewChannels];
  1.1838 +
  1.1839 +    const char *color(const char *colorCode) {
  1.1840 +        if (!colorable)
  1.1841 +            return "";
  1.1842 +        return colorCode;
  1.1843 +    }
  1.1844 +
  1.1845 +    const char *reset() { return color("\x1b[0m"); }
  1.1846 +    const char *bold() { return color("\x1b[1m"); }
  1.1847 +    const char *red() { return color("\x1b[31m"); }
  1.1848 +    const char *green() { return color("\x1b[32m"); }
  1.1849 +    const char *yellow() { return color("\x1b[33m"); }
  1.1850 +    const char *cyan() { return color("\x1b[36m"); }
  1.1851 +    const char *workerColor(uint32_t id) {
  1.1852 +        static const char *colors[] = {
  1.1853 +            "\x1b[7m\x1b[31m", "\x1b[7m\x1b[32m", "\x1b[7m\x1b[33m",
  1.1854 +            "\x1b[7m\x1b[34m", "\x1b[7m\x1b[35m", "\x1b[7m\x1b[36m",
  1.1855 +            "\x1b[7m\x1b[37m",
  1.1856 +            "\x1b[31m", "\x1b[32m", "\x1b[33m",
  1.1857 +            "\x1b[34m", "\x1b[35m", "\x1b[36m",
  1.1858 +            "\x1b[37m"
  1.1859 +        };
  1.1860 +        return color(colors[id % 14]);
  1.1861 +    }
  1.1862 +
  1.1863 +  public:
  1.1864 +    ParallelSpewer()
  1.1865 +      : depth(0)
  1.1866 +    {
  1.1867 +        const char *env;
  1.1868 +
  1.1869 +        mozilla::PodArrayZero(active);
  1.1870 +        env = getenv("PAFLAGS");
  1.1871 +        if (env) {
  1.1872 +            if (strstr(env, "ops"))
  1.1873 +                active[SpewOps] = true;
  1.1874 +            if (strstr(env, "compile"))
  1.1875 +                active[SpewCompile] = true;
  1.1876 +            if (strstr(env, "bailouts"))
  1.1877 +                active[SpewBailouts] = true;
  1.1878 +            if (strstr(env, "full")) {
  1.1879 +                for (uint32_t i = 0; i < NumSpewChannels; i++)
  1.1880 +                    active[i] = true;
  1.1881 +            }
  1.1882 +        }
  1.1883 +
  1.1884 +        env = getenv("TERM");
  1.1885 +        if (env && isatty(fileno(stderr))) {
  1.1886 +            if (strcmp(env, "xterm-color") == 0 || strcmp(env, "xterm-256color") == 0)
  1.1887 +                colorable = true;
  1.1888 +        }
  1.1889 +    }
  1.1890 +
  1.1891 +    bool isActive(js::parallel::SpewChannel channel) {
  1.1892 +        return active[channel];
  1.1893 +    }
  1.1894 +
  1.1895 +    void spewVA(js::parallel::SpewChannel channel, const char *fmt, va_list ap) {
  1.1896 +        if (!active[channel])
  1.1897 +            return;
  1.1898 +
  1.1899 +        // Print into a buffer first so we use one fprintf, which usually
  1.1900 +        // doesn't get interrupted when running with multiple threads.
  1.1901 +        char buf[BufferSize];
  1.1902 +
  1.1903 +        if (ForkJoinContext *cx = ForkJoinContext::current()) {
  1.1904 +            // Print the format first into a buffer to right-justify the
  1.1905 +            // worker ids.
  1.1906 +            char bufbuf[BufferSize];
  1.1907 +            JS_snprintf(bufbuf, BufferSize, "[%%sParallel:%%0%du%%s] ",
  1.1908 +                        NumberOfDigits(cx->maxWorkerId));
  1.1909 +            JS_snprintf(buf, BufferSize, bufbuf, workerColor(cx->workerId()),
  1.1910 +                        cx->workerId(), reset());
  1.1911 +        } else {
  1.1912 +            JS_snprintf(buf, BufferSize, "[Parallel:M] ");
  1.1913 +        }
  1.1914 +
  1.1915 +        for (uint32_t i = 0; i < depth; i++)
  1.1916 +            JS_snprintf(buf + strlen(buf), BufferSize, "  ");
  1.1917 +
  1.1918 +        JS_vsnprintf(buf + strlen(buf), BufferSize, fmt, ap);
  1.1919 +        JS_snprintf(buf + strlen(buf), BufferSize, "\n");
  1.1920 +
  1.1921 +        fprintf(stderr, "%s", buf);
  1.1922 +    }
  1.1923 +
  1.1924 +    void spew(js::parallel::SpewChannel channel, const char *fmt, ...) {
  1.1925 +        va_list ap;
  1.1926 +        va_start(ap, fmt);
  1.1927 +        spewVA(channel, fmt, ap);
  1.1928 +        va_end(ap);
  1.1929 +    }
  1.1930 +
  1.1931 +    void beginOp(JSContext *cx, const char *name) {
  1.1932 +        if (!active[SpewOps])
  1.1933 +            return;
  1.1934 +
  1.1935 +        if (cx) {
  1.1936 +            jsbytecode *pc;
  1.1937 +            RootedScript script(cx, cx->currentScript(&pc));
  1.1938 +            if (script && pc) {
  1.1939 +                NonBuiltinScriptFrameIter iter(cx);
  1.1940 +                if (iter.done()) {
  1.1941 +                    spew(SpewOps, "%sBEGIN %s%s (%s:%u)", bold(), name, reset(),
  1.1942 +                         script->filename(), PCToLineNumber(script, pc));
  1.1943 +                } else {
  1.1944 +                    spew(SpewOps, "%sBEGIN %s%s (%s:%u -> %s:%u)", bold(), name, reset(),
  1.1945 +                         iter.script()->filename(), PCToLineNumber(iter.script(), iter.pc()),
  1.1946 +                         script->filename(), PCToLineNumber(script, pc));
  1.1947 +                }
  1.1948 +            } else {
  1.1949 +                spew(SpewOps, "%sBEGIN %s%s", bold(), name, reset());
  1.1950 +            }
  1.1951 +        } else {
  1.1952 +            spew(SpewOps, "%sBEGIN %s%s", bold(), name, reset());
  1.1953 +        }
  1.1954 +
  1.1955 +        depth++;
  1.1956 +    }
  1.1957 +
  1.1958 +    void endOp(ExecutionStatus status) {
  1.1959 +        if (!active[SpewOps])
  1.1960 +            return;
  1.1961 +
  1.1962 +        JS_ASSERT(depth > 0);
  1.1963 +        depth--;
  1.1964 +
  1.1965 +        const char *statusColor;
  1.1966 +        switch (status) {
  1.1967 +          case ExecutionFatal:
  1.1968 +            statusColor = red();
  1.1969 +            break;
  1.1970 +          case ExecutionSequential:
  1.1971 +            statusColor = yellow();
  1.1972 +            break;
  1.1973 +          case ExecutionParallel:
  1.1974 +            statusColor = green();
  1.1975 +            break;
  1.1976 +          default:
  1.1977 +            statusColor = reset();
  1.1978 +            break;
  1.1979 +        }
  1.1980 +
  1.1981 +        spew(SpewOps, "%sEND %s%s%s", bold(),
  1.1982 +             statusColor, ExecutionStatusToString(status), reset());
  1.1983 +    }
  1.1984 +
  1.1985 +    void bailout(uint32_t count, HandleScript script,
  1.1986 +                 jsbytecode *pc, ParallelBailoutCause cause) {
  1.1987 +        if (!active[SpewOps])
  1.1988 +            return;
  1.1989 +
  1.1990 +        const char *filename = "";
  1.1991 +        unsigned line=0, column=0;
  1.1992 +        if (script) {
  1.1993 +            line = PCToLineNumber(script, pc, &column);
  1.1994 +            filename = script->filename();
  1.1995 +        }
  1.1996 +
  1.1997 +        spew(SpewOps, "%s%sBAILOUT %d%s: %s (%d) at %s:%d:%d", bold(), yellow(), count, reset(),
  1.1998 +             BailoutExplanation(cause), cause, filename, line, column);
  1.1999 +    }
  1.2000 +
  1.2001 +    void beginCompile(HandleScript script) {
  1.2002 +        if (!active[SpewCompile])
  1.2003 +            return;
  1.2004 +
  1.2005 +        spew(SpewCompile, "COMPILE %p:%s:%u", script.get(), script->filename(), script->lineno());
  1.2006 +        depth++;
  1.2007 +    }
  1.2008 +
  1.2009 +    void endCompile(MethodStatus status) {
  1.2010 +        if (!active[SpewCompile])
  1.2011 +            return;
  1.2012 +
  1.2013 +        JS_ASSERT(depth > 0);
  1.2014 +        depth--;
  1.2015 +
  1.2016 +        const char *statusColor;
  1.2017 +        switch (status) {
  1.2018 +          case Method_Error:
  1.2019 +          case Method_CantCompile:
  1.2020 +            statusColor = red();
  1.2021 +            break;
  1.2022 +          case Method_Skipped:
  1.2023 +            statusColor = yellow();
  1.2024 +            break;
  1.2025 +          case Method_Compiled:
  1.2026 +            statusColor = green();
  1.2027 +            break;
  1.2028 +          default:
  1.2029 +            statusColor = reset();
  1.2030 +            break;
  1.2031 +        }
  1.2032 +
  1.2033 +        spew(SpewCompile, "END %s%s%s", statusColor, MethodStatusToString(status), reset());
  1.2034 +    }
  1.2035 +
  1.2036 +    void spewMIR(MDefinition *mir, const char *fmt, va_list ap) {
  1.2037 +        if (!active[SpewCompile])
  1.2038 +            return;
  1.2039 +
  1.2040 +        char buf[BufferSize];
  1.2041 +        JS_vsnprintf(buf, BufferSize, fmt, ap);
  1.2042 +
  1.2043 +        JSScript *script = mir->block()->info().script();
  1.2044 +        spew(SpewCompile, "%s%s%s: %s (%s:%u)", cyan(), mir->opName(), reset(), buf,
  1.2045 +             script->filename(), PCToLineNumber(script, mir->trackedPc()));
  1.2046 +    }
  1.2047 +
  1.2048 +    void spewBailoutIR(IonLIRTraceData *data) {
  1.2049 +        if (!active[SpewBailouts])
  1.2050 +            return;
  1.2051 +
  1.2052 +        // If we didn't bail from a LIR/MIR but from a propagated parallel
  1.2053 +        // bailout, don't bother printing anything since we've printed it
  1.2054 +        // elsewhere.
  1.2055 +        if (data->mirOpName && data->script) {
  1.2056 +            spew(SpewBailouts, "%sBailout%s: %s / %s%s%s (block %d lir %d) (%s:%u)", yellow(), reset(),
  1.2057 +                 data->lirOpName, cyan(), data->mirOpName, reset(),
  1.2058 +                 data->blockIndex, data->lirIndex, data->script->filename(),
  1.2059 +                 PCToLineNumber(data->script, data->pc));
  1.2060 +        }
  1.2061 +    }
  1.2062 +};
  1.2063 +
  1.2064 +// Singleton instance of the spewer.
  1.2065 +static ParallelSpewer spewer;
  1.2066 +
  1.2067 +bool
  1.2068 +parallel::SpewEnabled(SpewChannel channel)
  1.2069 +{
  1.2070 +    return spewer.isActive(channel);
  1.2071 +}
  1.2072 +
  1.2073 +void
  1.2074 +parallel::Spew(SpewChannel channel, const char *fmt, ...)
  1.2075 +{
  1.2076 +    va_list ap;
  1.2077 +    va_start(ap, fmt);
  1.2078 +    spewer.spewVA(channel, fmt, ap);
  1.2079 +    va_end(ap);
  1.2080 +}
  1.2081 +
  1.2082 +void
  1.2083 +parallel::SpewBeginOp(JSContext *cx, const char *name)
  1.2084 +{
  1.2085 +    spewer.beginOp(cx, name);
  1.2086 +}
  1.2087 +
  1.2088 +ExecutionStatus
  1.2089 +parallel::SpewEndOp(ExecutionStatus status)
  1.2090 +{
  1.2091 +    spewer.endOp(status);
  1.2092 +    return status;
  1.2093 +}
  1.2094 +
  1.2095 +void
  1.2096 +parallel::SpewBailout(uint32_t count, HandleScript script,
  1.2097 +                      jsbytecode *pc, ParallelBailoutCause cause)
  1.2098 +{
  1.2099 +    spewer.bailout(count, script, pc, cause);
  1.2100 +}
  1.2101 +
  1.2102 +void
  1.2103 +parallel::SpewBeginCompile(HandleScript script)
  1.2104 +{
  1.2105 +    spewer.beginCompile(script);
  1.2106 +}
  1.2107 +
  1.2108 +MethodStatus
  1.2109 +parallel::SpewEndCompile(MethodStatus status)
  1.2110 +{
  1.2111 +    spewer.endCompile(status);
  1.2112 +    return status;
  1.2113 +}
  1.2114 +
  1.2115 +void
  1.2116 +parallel::SpewMIR(MDefinition *mir, const char *fmt, ...)
  1.2117 +{
  1.2118 +    va_list ap;
  1.2119 +    va_start(ap, fmt);
  1.2120 +    spewer.spewMIR(mir, fmt, ap);
  1.2121 +    va_end(ap);
  1.2122 +}
  1.2123 +
  1.2124 +void
  1.2125 +parallel::SpewBailoutIR(IonLIRTraceData *data)
  1.2126 +{
  1.2127 +    spewer.spewBailoutIR(data);
  1.2128 +}
  1.2129 +
  1.2130 +#endif // DEBUG
  1.2131 +
  1.2132 +bool
  1.2133 +js::InExclusiveParallelSection()
  1.2134 +{
  1.2135 +    return InParallelSection() && ForkJoinContext::current()->hasAcquiredJSContext();
  1.2136 +}
  1.2137 +
  1.2138 +bool
  1.2139 +js::ParallelTestsShouldPass(JSContext *cx)
  1.2140 +{
  1.2141 +    return jit::IsIonEnabled(cx) &&
  1.2142 +           jit::IsBaselineEnabled(cx) &&
  1.2143 +           !jit::js_JitOptions.eagerCompilation &&
  1.2144 +           jit::js_JitOptions.baselineUsesBeforeCompile != 0 &&
  1.2145 +           cx->runtime()->gcZeal() == 0;
  1.2146 +}
  1.2147 +
  1.2148 +void
  1.2149 +js::RequestInterruptForForkJoin(JSRuntime *rt, JSRuntime::InterruptMode mode)
  1.2150 +{
  1.2151 +    if (mode != JSRuntime::RequestInterruptAnyThreadDontStopIon)
  1.2152 +        rt->interruptPar = true;
  1.2153 +}
  1.2154 +
  1.2155 +bool
  1.2156 +js::intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp)
  1.2157 +{
  1.2158 +    // This version of SetForkJoinTargetRegion is called during
  1.2159 +    // sequential execution. It is a no-op. The parallel version
  1.2160 +    // is intrinsic_SetForkJoinTargetRegionPar(), below.
  1.2161 +    return true;
  1.2162 +}
  1.2163 +
  1.2164 +static bool
  1.2165 +intrinsic_SetForkJoinTargetRegionPar(ForkJoinContext *cx, unsigned argc, Value *vp)
  1.2166 +{
  1.2167 +    // Sets the *target region*, which is the portion of the output
  1.2168 +    // buffer that the current iteration is permitted to write to.
  1.2169 +    //
  1.2170 +    // Note: it is important that the target region should be an
  1.2171 +    // entire element (or several elements) of the output array and
  1.2172 +    // not some region that spans from the middle of one element into
  1.2173 +    // the middle of another. This is because the guarding code
  1.2174 +    // assumes that handles, which never straddle across elements,
  1.2175 +    // will either be contained entirely within the target region or
  1.2176 +    // be contained entirely without of the region, and not straddling
  1.2177 +    // the region nor encompassing it.
  1.2178 +
  1.2179 +    CallArgs args = CallArgsFromVp(argc, vp);
  1.2180 +    JS_ASSERT(argc == 3);
  1.2181 +    JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
  1.2182 +    JS_ASSERT(args[1].isInt32());
  1.2183 +    JS_ASSERT(args[2].isInt32());
  1.2184 +
  1.2185 +    uint8_t *mem = args[0].toObject().as<TypedObject>().typedMem();
  1.2186 +    int32_t start = args[1].toInt32();
  1.2187 +    int32_t end = args[2].toInt32();
  1.2188 +
  1.2189 +    cx->targetRegionStart = mem + start;
  1.2190 +    cx->targetRegionEnd = mem + end;
  1.2191 +    return true;
  1.2192 +}
  1.2193 +
  1.2194 +JS_JITINFO_NATIVE_PARALLEL(js::intrinsic_SetForkJoinTargetRegionInfo,
  1.2195 +                           intrinsic_SetForkJoinTargetRegionPar);
  1.2196 +
  1.2197 +bool
  1.2198 +js::intrinsic_ClearThreadLocalArenas(JSContext *cx, unsigned argc, Value *vp)
  1.2199 +{
  1.2200 +    return true;
  1.2201 +}
  1.2202 +
  1.2203 +static bool
  1.2204 +intrinsic_ClearThreadLocalArenasPar(ForkJoinContext *cx, unsigned argc, Value *vp)
  1.2205 +{
  1.2206 +    cx->allocator()->arenas.wipeDuringParallelExecution(cx->runtime());
  1.2207 +    return true;
  1.2208 +}
  1.2209 +
  1.2210 +JS_JITINFO_NATIVE_PARALLEL(js::intrinsic_ClearThreadLocalArenasInfo,
  1.2211 +                           intrinsic_ClearThreadLocalArenasPar);
  1.2212 +
  1.2213 +#endif // JS_THREADSAFE && JS_ION

mercurial