js/src/vm/ForkJoin.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #if defined(XP_WIN)
michael@0 8 # include <io.h> // for isatty()
michael@0 9 #else
michael@0 10 # include <unistd.h> // for isatty()
michael@0 11 #endif
michael@0 12
michael@0 13 #include "vm/ForkJoin.h"
michael@0 14
michael@0 15 #include "mozilla/ThreadLocal.h"
michael@0 16
michael@0 17 #include "jscntxt.h"
michael@0 18 #include "jslock.h"
michael@0 19 #include "jsprf.h"
michael@0 20
michael@0 21 #include "builtin/TypedObject.h"
michael@0 22
michael@0 23 #ifdef JS_THREADSAFE
michael@0 24 # include "jit/BaselineJIT.h"
michael@0 25 # include "vm/Monitor.h"
michael@0 26 #endif
michael@0 27
michael@0 28 #if defined(JS_THREADSAFE) && defined(JS_ION)
michael@0 29 # include "jit/JitCommon.h"
michael@0 30 # ifdef DEBUG
michael@0 31 # include "jit/Ion.h"
michael@0 32 # include "jit/JitCompartment.h"
michael@0 33 # include "jit/MIR.h"
michael@0 34 # include "jit/MIRGraph.h"
michael@0 35 # endif
michael@0 36 #endif // THREADSAFE && ION
michael@0 37
michael@0 38 #include "vm/Interpreter-inl.h"
michael@0 39
michael@0 40 using namespace js;
michael@0 41 using namespace js::parallel;
michael@0 42 using namespace js::jit;
michael@0 43
michael@0 44 using mozilla::ThreadLocal;
michael@0 45
michael@0 46 ///////////////////////////////////////////////////////////////////////////
michael@0 47 // Degenerate configurations
michael@0 48 //
michael@0 49 // When JS_THREADSAFE or JS_ION is not defined, we simply run the
michael@0 50 // |func| callback sequentially. We also forego the feedback
michael@0 51 // altogether.
michael@0 52
michael@0 53 static bool
michael@0 54 ExecuteSequentially(JSContext *cx_, HandleValue funVal, uint16_t *sliceStart,
michael@0 55 uint16_t sliceEnd);
michael@0 56
michael@0 57 #if !defined(JS_THREADSAFE) || !defined(JS_ION)
michael@0 58 bool
michael@0 59 js::ForkJoin(JSContext *cx, CallArgs &args)
michael@0 60 {
michael@0 61 RootedValue argZero(cx, args[0]);
michael@0 62 uint16_t sliceStart = uint16_t(args[1].toInt32());
michael@0 63 uint16_t sliceEnd = uint16_t(args[2].toInt32());
michael@0 64 if (!ExecuteSequentially(cx, argZero, &sliceStart, sliceEnd))
michael@0 65 return false;
michael@0 66 MOZ_ASSERT(sliceStart == sliceEnd);
michael@0 67 return true;
michael@0 68 }
michael@0 69
michael@0 70 JSContext *
michael@0 71 ForkJoinContext::acquireJSContext()
michael@0 72 {
michael@0 73 return nullptr;
michael@0 74 }
michael@0 75
michael@0 76 void
michael@0 77 ForkJoinContext::releaseJSContext()
michael@0 78 {
michael@0 79 }
michael@0 80
michael@0 81 bool
michael@0 82 ForkJoinContext::isMainThread() const
michael@0 83 {
michael@0 84 return true;
michael@0 85 }
michael@0 86
michael@0 87 JSRuntime *
michael@0 88 ForkJoinContext::runtime()
michael@0 89 {
michael@0 90 MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
michael@0 91 }
michael@0 92
michael@0 93 bool
michael@0 94 ForkJoinContext::check()
michael@0 95 {
michael@0 96 MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
michael@0 97 }
michael@0 98
michael@0 99 void
michael@0 100 ForkJoinContext::requestGC(JS::gcreason::Reason reason)
michael@0 101 {
michael@0 102 MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
michael@0 103 }
michael@0 104
michael@0 105 void
michael@0 106 ForkJoinContext::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
michael@0 107 {
michael@0 108 MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
michael@0 109 }
michael@0 110
michael@0 111 bool
michael@0 112 ForkJoinContext::setPendingAbortFatal(ParallelBailoutCause cause)
michael@0 113 {
michael@0 114 MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
michael@0 115 return false;
michael@0 116 }
michael@0 117
michael@0 118 void
michael@0 119 ParallelBailoutRecord::setCause(ParallelBailoutCause cause,
michael@0 120 JSScript *outermostScript,
michael@0 121 JSScript *currentScript,
michael@0 122 jsbytecode *currentPc)
michael@0 123 {
michael@0 124 MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
michael@0 125 }
michael@0 126
michael@0 127 void
michael@0 128 js::ParallelBailoutRecord::updateCause(ParallelBailoutCause cause,
michael@0 129 JSScript *outermostScript,
michael@0 130 JSScript *currentScript,
michael@0 131 jsbytecode *currentPc)
michael@0 132 {
michael@0 133 MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
michael@0 134 }
michael@0 135
michael@0 136 void
michael@0 137 ParallelBailoutRecord::addTrace(JSScript *script,
michael@0 138 jsbytecode *pc)
michael@0 139 {
michael@0 140 MOZ_ASSUME_UNREACHABLE("Not THREADSAFE build");
michael@0 141 }
michael@0 142
michael@0 143 bool
michael@0 144 js::InExclusiveParallelSection()
michael@0 145 {
michael@0 146 return false;
michael@0 147 }
michael@0 148
michael@0 149 bool
michael@0 150 js::ParallelTestsShouldPass(JSContext *cx)
michael@0 151 {
michael@0 152 return false;
michael@0 153 }
michael@0 154
michael@0 155 bool
michael@0 156 js::intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp)
michael@0 157 {
michael@0 158 return true;
michael@0 159 }
michael@0 160
michael@0 161 static bool
michael@0 162 intrinsic_SetForkJoinTargetRegionPar(ForkJoinContext *cx, unsigned argc, Value *vp)
michael@0 163 {
michael@0 164 return true;
michael@0 165 }
michael@0 166
michael@0 167 JS_JITINFO_NATIVE_PARALLEL(js::intrinsic_SetForkJoinTargetRegionInfo,
michael@0 168 intrinsic_SetForkJoinTargetRegionPar);
michael@0 169
michael@0 170 bool
michael@0 171 js::intrinsic_ClearThreadLocalArenas(JSContext *cx, unsigned argc, Value *vp)
michael@0 172 {
michael@0 173 return true;
michael@0 174 }
michael@0 175
michael@0 176 static bool
michael@0 177 intrinsic_ClearThreadLocalArenasPar(ForkJoinContext *cx, unsigned argc, Value *vp)
michael@0 178 {
michael@0 179 return true;
michael@0 180 }
michael@0 181
michael@0 182 JS_JITINFO_NATIVE_PARALLEL(js::intrinsic_ClearThreadLocalArenasInfo,
michael@0 183 intrinsic_ClearThreadLocalArenasPar);
michael@0 184
michael@0 185 #endif // !JS_THREADSAFE || !JS_ION
michael@0 186
michael@0 187 ///////////////////////////////////////////////////////////////////////////
michael@0 188 // All configurations
michael@0 189 //
michael@0 190 // Some code that is shared between degenerate and parallel configurations.
michael@0 191
michael@0 192 static bool
michael@0 193 ExecuteSequentially(JSContext *cx, HandleValue funVal, uint16_t *sliceStart,
michael@0 194 uint16_t sliceEnd)
michael@0 195 {
michael@0 196 FastInvokeGuard fig(cx, funVal);
michael@0 197 InvokeArgs &args = fig.args();
michael@0 198 if (!args.init(3))
michael@0 199 return false;
michael@0 200 args.setCallee(funVal);
michael@0 201 args.setThis(UndefinedValue());
michael@0 202 args[0].setInt32(0);
michael@0 203 args[1].setInt32(*sliceStart);
michael@0 204 args[2].setInt32(sliceEnd);
michael@0 205 if (!fig.invoke(cx))
michael@0 206 return false;
michael@0 207 *sliceStart = (uint16_t)(args.rval().toInt32());
michael@0 208 return true;
michael@0 209 }
michael@0 210
michael@0 211 ThreadLocal<ForkJoinContext*> ForkJoinContext::tlsForkJoinContext;
michael@0 212
michael@0 213 /* static */ bool
michael@0 214 ForkJoinContext::initialize()
michael@0 215 {
michael@0 216 if (!tlsForkJoinContext.initialized()) {
michael@0 217 if (!tlsForkJoinContext.init())
michael@0 218 return false;
michael@0 219 }
michael@0 220 return true;
michael@0 221 }
michael@0 222
michael@0 223 ///////////////////////////////////////////////////////////////////////////
michael@0 224 // Parallel configurations
michael@0 225 //
michael@0 226 // The remainder of this file is specific to cases where both
michael@0 227 // JS_THREADSAFE and JS_ION are enabled.
michael@0 228
michael@0 229 #if defined(JS_THREADSAFE) && defined(JS_ION)
michael@0 230
michael@0 231 ///////////////////////////////////////////////////////////////////////////
michael@0 232 // Class Declarations and Function Prototypes
michael@0 233
michael@0 234 namespace js {
michael@0 235
michael@0 236 // When writing tests, it is often useful to specify different modes
michael@0 237 // of operation.
michael@0 238 enum ForkJoinMode {
michael@0 239 // WARNING: If you change this enum, you MUST update
michael@0 240 // ForkJoinMode() in Utilities.js
michael@0 241
michael@0 242 // The "normal" behavior: attempt parallel, fallback to
michael@0 243 // sequential. If compilation is ongoing in a helper thread, then
michael@0 244 // run sequential warmup iterations in the meantime. If those
michael@0 245 // iterations wind up completing all the work, just abort.
michael@0 246 ForkJoinModeNormal,
michael@0 247
michael@0 248 // Like normal, except that we will keep running warmup iterations
michael@0 249 // until compilations are complete, even if there is no more work
michael@0 250 // to do. This is useful in tests as a "setup" run.
michael@0 251 ForkJoinModeCompile,
michael@0 252
michael@0 253 // Requires that compilation has already completed. Expects parallel
michael@0 254 // execution to proceed without a hitch. (Reports an error otherwise)
michael@0 255 ForkJoinModeParallel,
michael@0 256
michael@0 257 // Requires that compilation has already completed. Expects
michael@0 258 // parallel execution to bailout once but continue after that without
michael@0 259 // further bailouts. (Reports an error otherwise)
michael@0 260 ForkJoinModeRecover,
michael@0 261
michael@0 262 // Expects all parallel executions to yield a bailout. If this is not
michael@0 263 // the case, reports an error.
michael@0 264 ForkJoinModeBailout,
michael@0 265
michael@0 266 NumForkJoinModes
michael@0 267 };
michael@0 268
michael@0 269 class ForkJoinOperation
michael@0 270 {
michael@0 271 public:
michael@0 272 // For tests, make sure to keep this in sync with minItemsTestingThreshold.
michael@0 273 static const uint32_t MAX_BAILOUTS = 3;
michael@0 274 uint32_t bailouts;
michael@0 275
michael@0 276 // Information about the bailout:
michael@0 277 ParallelBailoutCause bailoutCause;
michael@0 278 RootedScript bailoutScript;
michael@0 279 jsbytecode *bailoutBytecode;
michael@0 280
michael@0 281 ForkJoinOperation(JSContext *cx, HandleFunction fun, uint16_t sliceStart,
michael@0 282 uint16_t sliceEnd, ForkJoinMode mode);
michael@0 283 ExecutionStatus apply();
michael@0 284
michael@0 285 private:
michael@0 286 // Most of the functions involved in managing the parallel
michael@0 287 // compilation follow a similar control-flow. They return RedLight
michael@0 288 // if they have either encountered a fatal error or completed the
michael@0 289 // execution, such that no further work is needed. In that event,
michael@0 290 // they take an `ExecutionStatus*` which they use to report
michael@0 291 // whether execution was successful or not. If the function
michael@0 292 // returns `GreenLight`, then the parallel operation is not yet
michael@0 293 // fully completed, so the state machine should carry on.
michael@0 294 enum TrafficLight {
michael@0 295 RedLight,
michael@0 296 GreenLight
michael@0 297 };
michael@0 298
michael@0 299 struct WorklistData {
michael@0 300 // True if we enqueued the callees from the ion-compiled
michael@0 301 // version of this entry
michael@0 302 bool calleesEnqueued;
michael@0 303
michael@0 304 // Last record useCount; updated after warmup
michael@0 305 // iterations;
michael@0 306 uint32_t useCount;
michael@0 307
michael@0 308 // Number of continuous "stalls" --- meaning warmups
michael@0 309 // where useCount did not increase.
michael@0 310 uint32_t stallCount;
michael@0 311
michael@0 312 void reset() {
michael@0 313 calleesEnqueued = false;
michael@0 314 useCount = 0;
michael@0 315 stallCount = 0;
michael@0 316 }
michael@0 317 };
michael@0 318
michael@0 319 JSContext *cx_;
michael@0 320 HandleFunction fun_;
michael@0 321 uint16_t sliceStart_;
michael@0 322 uint16_t sliceEnd_;
michael@0 323 Vector<ParallelBailoutRecord, 16> bailoutRecords_;
michael@0 324 AutoScriptVector worklist_;
michael@0 325 Vector<WorklistData, 16> worklistData_;
michael@0 326 ForkJoinMode mode_;
michael@0 327
michael@0 328 TrafficLight enqueueInitialScript(ExecutionStatus *status);
michael@0 329 TrafficLight compileForParallelExecution(ExecutionStatus *status);
michael@0 330 TrafficLight warmupExecution(bool stopIfComplete, ExecutionStatus *status);
michael@0 331 TrafficLight parallelExecution(ExecutionStatus *status);
michael@0 332 TrafficLight sequentialExecution(bool disqualified, ExecutionStatus *status);
michael@0 333 TrafficLight recoverFromBailout(ExecutionStatus *status);
michael@0 334 TrafficLight fatalError(ExecutionStatus *status);
michael@0 335 bool isInitialScript(HandleScript script);
michael@0 336 void determineBailoutCause();
michael@0 337 bool invalidateBailedOutScripts();
michael@0 338 ExecutionStatus sequentialExecution(bool disqualified);
michael@0 339
michael@0 340 TrafficLight appendCallTargetsToWorklist(uint32_t index, ExecutionStatus *status);
michael@0 341 TrafficLight appendCallTargetToWorklist(HandleScript script, ExecutionStatus *status);
michael@0 342 bool addToWorklist(HandleScript script);
michael@0 343 inline bool hasScript(Vector<types::RecompileInfo> &scripts, JSScript *script);
michael@0 344 }; // class ForkJoinOperation
michael@0 345
michael@0 346 class ForkJoinShared : public ParallelJob, public Monitor
michael@0 347 {
michael@0 348 /////////////////////////////////////////////////////////////////////////
michael@0 349 // Constant fields
michael@0 350
michael@0 351 JSContext *const cx_; // Current context
michael@0 352 ThreadPool *const threadPool_; // The thread pool
michael@0 353 HandleFunction fun_; // The JavaScript function to execute
michael@0 354 uint16_t sliceStart_; // The starting slice id.
michael@0 355 uint16_t sliceEnd_; // The ending slice id + 1.
michael@0 356 PRLock *cxLock_; // Locks cx_ for parallel VM calls
michael@0 357 ParallelBailoutRecord *const records_; // Bailout records for each worker
michael@0 358
michael@0 359 /////////////////////////////////////////////////////////////////////////
michael@0 360 // Per-thread arenas
michael@0 361 //
michael@0 362 // Each worker thread gets an arena to use when allocating.
michael@0 363
michael@0 364 Vector<Allocator *, 16> allocators_;
michael@0 365
michael@0 366 /////////////////////////////////////////////////////////////////////////
michael@0 367 // Locked Fields
michael@0 368 //
michael@0 369 // Only to be accessed while holding the lock.
michael@0 370
michael@0 371 bool gcRequested_; // True if a worker requested a GC
michael@0 372 JS::gcreason::Reason gcReason_; // Reason given to request GC
michael@0 373 Zone *gcZone_; // Zone for GC, or nullptr for full
michael@0 374
michael@0 375 /////////////////////////////////////////////////////////////////////////
michael@0 376 // Asynchronous Flags
michael@0 377 //
michael@0 378 // These can be accessed without the lock and are thus atomic.
michael@0 379
michael@0 380 // Set to true when parallel execution should abort.
michael@0 381 mozilla::Atomic<bool, mozilla::ReleaseAcquire> abort_;
michael@0 382
michael@0 383 // Set to true when a worker bails for a fatal reason.
michael@0 384 mozilla::Atomic<bool, mozilla::ReleaseAcquire> fatal_;
michael@0 385
michael@0 386 public:
michael@0 387 ForkJoinShared(JSContext *cx,
michael@0 388 ThreadPool *threadPool,
michael@0 389 HandleFunction fun,
michael@0 390 uint16_t sliceStart,
michael@0 391 uint16_t sliceEnd,
michael@0 392 ParallelBailoutRecord *records);
michael@0 393 ~ForkJoinShared();
michael@0 394
michael@0 395 bool init();
michael@0 396
michael@0 397 ParallelResult execute();
michael@0 398
michael@0 399 // Invoked from parallel worker threads:
michael@0 400 virtual bool executeFromWorker(ThreadPoolWorker *worker, uintptr_t stackLimit) MOZ_OVERRIDE;
michael@0 401
michael@0 402 // Invoked only from the main thread:
michael@0 403 virtual bool executeFromMainThread(ThreadPoolWorker *worker) MOZ_OVERRIDE;
michael@0 404
michael@0 405 // Executes the user-supplied function a worker or the main thread.
michael@0 406 void executePortion(PerThreadData *perThread, ThreadPoolWorker *worker);
michael@0 407
michael@0 408 // Moves all the per-thread arenas into the main compartment and processes
michael@0 409 // any pending requests for a GC. This can only safely be invoked on the
michael@0 410 // main thread after the workers have completed.
michael@0 411 void transferArenasToCompartmentAndProcessGCRequests();
michael@0 412
michael@0 413
michael@0 414 // Requests a GC, either full or specific to a zone.
michael@0 415 void requestGC(JS::gcreason::Reason reason);
michael@0 416 void requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason);
michael@0 417
michael@0 418 // Requests that computation abort.
michael@0 419 void setAbortFlagDueToInterrupt(ForkJoinContext &cx);
michael@0 420 void setAbortFlagAndRequestInterrupt(bool fatal);
michael@0 421
michael@0 422 // Set the fatal flag for the next abort.
michael@0 423 void setPendingAbortFatal() { fatal_ = true; }
michael@0 424
michael@0 425 JSRuntime *runtime() { return cx_->runtime(); }
michael@0 426 JS::Zone *zone() { return cx_->zone(); }
michael@0 427 JSCompartment *compartment() { return cx_->compartment(); }
michael@0 428
michael@0 429 JSContext *acquireJSContext() { PR_Lock(cxLock_); return cx_; }
michael@0 430 void releaseJSContext() { PR_Unlock(cxLock_); }
michael@0 431 };
michael@0 432
michael@0 433 class AutoEnterWarmup
michael@0 434 {
michael@0 435 JSRuntime *runtime_;
michael@0 436
michael@0 437 public:
michael@0 438 AutoEnterWarmup(JSRuntime *runtime) : runtime_(runtime) { runtime_->forkJoinWarmup++; }
michael@0 439 ~AutoEnterWarmup() { runtime_->forkJoinWarmup--; }
michael@0 440 };
michael@0 441
michael@0 442 class AutoSetForkJoinContext
michael@0 443 {
michael@0 444 public:
michael@0 445 AutoSetForkJoinContext(ForkJoinContext *threadCx) {
michael@0 446 ForkJoinContext::tlsForkJoinContext.set(threadCx);
michael@0 447 }
michael@0 448
michael@0 449 ~AutoSetForkJoinContext() {
michael@0 450 ForkJoinContext::tlsForkJoinContext.set(nullptr);
michael@0 451 }
michael@0 452 };
michael@0 453
michael@0 454 } // namespace js
michael@0 455
michael@0 456 ///////////////////////////////////////////////////////////////////////////
michael@0 457 // ForkJoinActivation
michael@0 458 //
michael@0 459 // Takes care of tidying up GC before we enter a fork join section. Also
michael@0 460 // pauses the barrier verifier, as we cannot enter fork join with the runtime
michael@0 461 // or the zone needing barriers.
michael@0 462
michael@0 463 ForkJoinActivation::ForkJoinActivation(JSContext *cx)
michael@0 464 : Activation(cx, ForkJoin),
michael@0 465 prevIonTop_(cx->mainThread().ionTop),
michael@0 466 av_(cx->runtime(), false)
michael@0 467 {
michael@0 468 // Note: we do not allow GC during parallel sections.
michael@0 469 // Moreover, we do not wish to worry about making
michael@0 470 // write barriers thread-safe. Therefore, we guarantee
michael@0 471 // that there is no incremental GC in progress and force
michael@0 472 // a minor GC to ensure no cross-generation pointers get
michael@0 473 // created:
michael@0 474
michael@0 475 if (JS::IsIncrementalGCInProgress(cx->runtime())) {
michael@0 476 JS::PrepareForIncrementalGC(cx->runtime());
michael@0 477 JS::FinishIncrementalGC(cx->runtime(), JS::gcreason::API);
michael@0 478 }
michael@0 479
michael@0 480 MinorGC(cx->runtime(), JS::gcreason::API);
michael@0 481
michael@0 482 cx->runtime()->gcHelperThread.waitBackgroundSweepEnd();
michael@0 483
michael@0 484 JS_ASSERT(!cx->runtime()->needsBarrier());
michael@0 485 JS_ASSERT(!cx->zone()->needsBarrier());
michael@0 486 }
michael@0 487
michael@0 488 ForkJoinActivation::~ForkJoinActivation()
michael@0 489 {
michael@0 490 cx_->mainThread().ionTop = prevIonTop_;
michael@0 491 }
michael@0 492
michael@0 493 ///////////////////////////////////////////////////////////////////////////
michael@0 494 // js::ForkJoin() and ForkJoinOperation class
michael@0 495 //
michael@0 496 // These are the top-level objects that manage the parallel execution.
michael@0 497 // They handle parallel compilation (if necessary), triggering
michael@0 498 // parallel execution, and recovering from bailouts.
michael@0 499
michael@0 500 static const char *ForkJoinModeString(ForkJoinMode mode);
michael@0 501
michael@0 502 bool
michael@0 503 js::ForkJoin(JSContext *cx, CallArgs &args)
michael@0 504 {
michael@0 505 JS_ASSERT(args.length() == 4); // else the self-hosted code is wrong
michael@0 506 JS_ASSERT(args[0].isObject());
michael@0 507 JS_ASSERT(args[0].toObject().is<JSFunction>());
michael@0 508 JS_ASSERT(args[1].isInt32());
michael@0 509 JS_ASSERT(args[2].isInt32());
michael@0 510 JS_ASSERT(args[3].isInt32());
michael@0 511 JS_ASSERT(args[3].toInt32() < NumForkJoinModes);
michael@0 512
michael@0 513 RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
michael@0 514 uint16_t sliceStart = (uint16_t)(args[1].toInt32());
michael@0 515 uint16_t sliceEnd = (uint16_t)(args[2].toInt32());
michael@0 516 ForkJoinMode mode = (ForkJoinMode)(args[3].toInt32());
michael@0 517
michael@0 518 MOZ_ASSERT(sliceStart == args[1].toInt32());
michael@0 519 MOZ_ASSERT(sliceEnd == args[2].toInt32());
michael@0 520 MOZ_ASSERT(sliceStart <= sliceEnd);
michael@0 521
michael@0 522 ForkJoinOperation op(cx, fun, sliceStart, sliceEnd, mode);
michael@0 523 ExecutionStatus status = op.apply();
michael@0 524 if (status == ExecutionFatal)
michael@0 525 return false;
michael@0 526
michael@0 527 switch (mode) {
michael@0 528 case ForkJoinModeNormal:
michael@0 529 case ForkJoinModeCompile:
michael@0 530 return true;
michael@0 531
michael@0 532 case ForkJoinModeParallel:
michael@0 533 if (status == ExecutionParallel && op.bailouts == 0)
michael@0 534 return true;
michael@0 535 break;
michael@0 536
michael@0 537 case ForkJoinModeRecover:
michael@0 538 if (status != ExecutionSequential && op.bailouts > 0)
michael@0 539 return true;
michael@0 540 break;
michael@0 541
michael@0 542 case ForkJoinModeBailout:
michael@0 543 if (status != ExecutionParallel)
michael@0 544 return true;
michael@0 545 break;
michael@0 546
michael@0 547 case NumForkJoinModes:
michael@0 548 break;
michael@0 549 }
michael@0 550
michael@0 551 const char *statusString = "?";
michael@0 552 switch (status) {
michael@0 553 case ExecutionSequential: statusString = "seq"; break;
michael@0 554 case ExecutionParallel: statusString = "par"; break;
michael@0 555 case ExecutionWarmup: statusString = "warmup"; break;
michael@0 556 case ExecutionFatal: statusString = "fatal"; break;
michael@0 557 }
michael@0 558
michael@0 559 if (ParallelTestsShouldPass(cx)) {
michael@0 560 JS_ReportError(cx, "ForkJoin: mode=%s status=%s bailouts=%d",
michael@0 561 ForkJoinModeString(mode), statusString, op.bailouts);
michael@0 562 return false;
michael@0 563 }
michael@0 564 return true;
michael@0 565 }
michael@0 566
michael@0 567 static const char *
michael@0 568 ForkJoinModeString(ForkJoinMode mode) {
michael@0 569 switch (mode) {
michael@0 570 case ForkJoinModeNormal: return "normal";
michael@0 571 case ForkJoinModeCompile: return "compile";
michael@0 572 case ForkJoinModeParallel: return "parallel";
michael@0 573 case ForkJoinModeRecover: return "recover";
michael@0 574 case ForkJoinModeBailout: return "bailout";
michael@0 575 case NumForkJoinModes: return "max";
michael@0 576 }
michael@0 577 return "???";
michael@0 578 }
michael@0 579
michael@0 580 ForkJoinOperation::ForkJoinOperation(JSContext *cx, HandleFunction fun, uint16_t sliceStart,
michael@0 581 uint16_t sliceEnd, ForkJoinMode mode)
michael@0 582 : bailouts(0),
michael@0 583 bailoutCause(ParallelBailoutNone),
michael@0 584 bailoutScript(cx),
michael@0 585 bailoutBytecode(nullptr),
michael@0 586 cx_(cx),
michael@0 587 fun_(fun),
michael@0 588 sliceStart_(sliceStart),
michael@0 589 sliceEnd_(sliceEnd),
michael@0 590 bailoutRecords_(cx),
michael@0 591 worklist_(cx),
michael@0 592 worklistData_(cx),
michael@0 593 mode_(mode)
michael@0 594 { }
michael@0 595
michael@0 596 ExecutionStatus
michael@0 597 ForkJoinOperation::apply()
michael@0 598 {
michael@0 599 ExecutionStatus status;
michael@0 600
michael@0 601 // High level outline of the procedure:
michael@0 602 //
michael@0 603 // - As we enter, we check for parallel script without "uncompiled" flag.
michael@0 604 // - If present, skip initial enqueue.
michael@0 605 // - While not too many bailouts:
michael@0 606 // - While all scripts in worklist are not compiled:
michael@0 607 // - For each script S in worklist:
michael@0 608 // - Compile S if not compiled
michael@0 609 // -> Error: fallback
michael@0 610 // - If compiled, add call targets to worklist w/o checking uncompiled
michael@0 611 // flag
michael@0 612 // - If some compilations pending, run warmup iteration
michael@0 613 // - Otherwise, clear "uncompiled targets" flag on main script and
michael@0 614 // break from loop
michael@0 615 // - Attempt parallel execution
michael@0 616 // - If successful: return happily
michael@0 617 // - If error: abort sadly
michael@0 618 // - If bailout:
michael@0 619 // - Invalidate any scripts that may need to be invalidated
michael@0 620 // - Re-enqueue main script and any uncompiled scripts that were called
michael@0 621 // - Too many bailouts: Fallback to sequential
michael@0 622
michael@0 623 JS_ASSERT_IF(!jit::IsBaselineEnabled(cx_), !jit::IsIonEnabled(cx_));
michael@0 624 if (!jit::IsBaselineEnabled(cx_) || !jit::IsIonEnabled(cx_))
michael@0 625 return sequentialExecution(true);
michael@0 626
michael@0 627 SpewBeginOp(cx_, "ForkJoinOperation");
michael@0 628
michael@0 629 // How many workers do we have, counting the main thread.
michael@0 630 unsigned numWorkers = cx_->runtime()->threadPool.numWorkers();
michael@0 631
michael@0 632 if (!bailoutRecords_.resize(numWorkers))
michael@0 633 return SpewEndOp(ExecutionFatal);
michael@0 634
michael@0 635 for (uint32_t i = 0; i < numWorkers; i++)
michael@0 636 bailoutRecords_[i].init(cx_);
michael@0 637
michael@0 638 if (enqueueInitialScript(&status) == RedLight)
michael@0 639 return SpewEndOp(status);
michael@0 640
michael@0 641 Spew(SpewOps, "Execution mode: %s", ForkJoinModeString(mode_));
michael@0 642 switch (mode_) {
michael@0 643 case ForkJoinModeNormal:
michael@0 644 case ForkJoinModeCompile:
michael@0 645 case ForkJoinModeBailout:
michael@0 646 break;
michael@0 647
michael@0 648 case ForkJoinModeParallel:
michael@0 649 case ForkJoinModeRecover:
michael@0 650 // These two modes are used to check that every iteration can
michael@0 651 // be executed in parallel. They expect compilation to have
michael@0 652 // been done. But, when using gc zeal, it's possible that
michael@0 653 // compiled scripts were collected.
michael@0 654 if (ParallelTestsShouldPass(cx_) && worklist_.length() != 0) {
michael@0 655 JS_ReportError(cx_, "ForkJoin: compilation required in par or bailout mode");
michael@0 656 return SpewEndOp(ExecutionFatal);
michael@0 657 }
michael@0 658 break;
michael@0 659
michael@0 660 case NumForkJoinModes:
michael@0 661 MOZ_ASSUME_UNREACHABLE("Invalid mode");
michael@0 662 }
michael@0 663
michael@0 664 while (bailouts < MAX_BAILOUTS) {
michael@0 665 for (uint32_t i = 0; i < numWorkers; i++)
michael@0 666 bailoutRecords_[i].reset(cx_);
michael@0 667
michael@0 668 if (compileForParallelExecution(&status) == RedLight)
michael@0 669 return SpewEndOp(status);
michael@0 670
michael@0 671 JS_ASSERT(worklist_.length() == 0);
michael@0 672 if (parallelExecution(&status) == RedLight)
michael@0 673 return SpewEndOp(status);
michael@0 674
michael@0 675 if (recoverFromBailout(&status) == RedLight)
michael@0 676 return SpewEndOp(status);
michael@0 677 }
michael@0 678
michael@0 679 // After enough tries, just execute sequentially.
michael@0 680 return SpewEndOp(sequentialExecution(true));
michael@0 681 }
michael@0 682
michael@0 683 ForkJoinOperation::TrafficLight
michael@0 684 ForkJoinOperation::enqueueInitialScript(ExecutionStatus *status)
michael@0 685 {
michael@0 686 // GreenLight: script successfully enqueued if necessary
michael@0 687 // RedLight: fatal error or fell back to sequential
michael@0 688
michael@0 689 // The kernel should be a self-hosted function.
michael@0 690 if (!fun_->is<JSFunction>())
michael@0 691 return sequentialExecution(true, status);
michael@0 692
michael@0 693 RootedFunction callee(cx_, &fun_->as<JSFunction>());
michael@0 694
michael@0 695 if (!callee->isInterpreted() || !callee->isSelfHostedBuiltin())
michael@0 696 return sequentialExecution(true, status);
michael@0 697
michael@0 698 // If the main script is already compiled, and we have no reason
michael@0 699 // to suspect any of its callees are not compiled, then we can
michael@0 700 // just skip the compilation step.
michael@0 701 RootedScript script(cx_, callee->getOrCreateScript(cx_));
michael@0 702 if (!script)
michael@0 703 return RedLight;
michael@0 704
michael@0 705 if (script->hasParallelIonScript()) {
michael@0 706 // Notify that there's been activity on the entry script.
michael@0 707 JitCompartment *jitComp = cx_->compartment()->jitCompartment();
michael@0 708 if (!jitComp->notifyOfActiveParallelEntryScript(cx_, script)) {
michael@0 709 *status = ExecutionFatal;
michael@0 710 return RedLight;
michael@0 711 }
michael@0 712
michael@0 713 if (!script->parallelIonScript()->hasUncompiledCallTarget()) {
michael@0 714 Spew(SpewOps, "Script %p:%s:%d already compiled, no uncompiled callees",
michael@0 715 script.get(), script->filename(), script->lineno());
michael@0 716 return GreenLight;
michael@0 717 }
michael@0 718
michael@0 719 Spew(SpewOps, "Script %p:%s:%d already compiled, may have uncompiled callees",
michael@0 720 script.get(), script->filename(), script->lineno());
michael@0 721 }
michael@0 722
michael@0 723 // Otherwise, add to the worklist of scripts to process.
michael@0 724 if (addToWorklist(script) == RedLight)
michael@0 725 return fatalError(status);
michael@0 726 return GreenLight;
michael@0 727 }
michael@0 728
michael@0 729 ForkJoinOperation::TrafficLight
michael@0 730 ForkJoinOperation::compileForParallelExecution(ExecutionStatus *status)
michael@0 731 {
michael@0 732 // GreenLight: all scripts compiled
michael@0 733 // RedLight: fatal error or completed work via warmups or fallback
michael@0 734
michael@0 735 // This routine attempts to do whatever compilation is necessary
michael@0 736 // to execute a single parallel attempt. When it returns, either
michael@0 737 // (1) we have fallen back to sequential; (2) we have run enough
michael@0 738 // warmup runs to complete all the work; or (3) we have compiled
michael@0 739 // all scripts we think likely to be executed during a parallel
michael@0 740 // execution.
michael@0 741
michael@0 742 RootedFunction fun(cx_);
michael@0 743 RootedScript script(cx_);
michael@0 744
michael@0 745 // After 3 stalls, we stop waiting for a script to gather type
michael@0 746 // info and move on with execution.
michael@0 747 const uint32_t stallThreshold = 3;
michael@0 748
michael@0 749 // This loop continues to iterate until the full contents of
michael@0 750 // `worklist` have been successfully compiled for parallel
michael@0 751 // execution. The compilations themselves typically occur on
michael@0 752 // helper threads. While we wait for the compilations to complete,
michael@0 753 // or for sufficient type information to be gathered, we execute
michael@0 754 // warmup iterations.
michael@0 755 while (true) {
michael@0 756 bool offMainThreadCompilationsInProgress = false;
michael@0 757 bool gatheringTypeInformation = false;
michael@0 758
michael@0 759 // Walk over the worklist to check on the status of each entry.
michael@0 760 for (uint32_t i = 0; i < worklist_.length(); i++) {
michael@0 761 script = worklist_[i];
michael@0 762 script->ensureNonLazyCanonicalFunction(cx_);
michael@0 763 fun = script->functionNonDelazifying();
michael@0 764
michael@0 765 // No baseline script means no type information, hence we
michael@0 766 // will not be able to compile very well. In such cases,
michael@0 767 // we continue to run baseline iterations until either (1)
michael@0 768 // the potential callee *has* a baseline script or (2) the
michael@0 769 // potential callee's use count stops increasing,
michael@0 770 // indicating that they are not in fact a callee.
michael@0 771 if (!script->hasBaselineScript()) {
michael@0 772 uint32_t previousUseCount = worklistData_[i].useCount;
michael@0 773 uint32_t currentUseCount = script->getUseCount();
michael@0 774 if (previousUseCount < currentUseCount) {
michael@0 775 worklistData_[i].useCount = currentUseCount;
michael@0 776 worklistData_[i].stallCount = 0;
michael@0 777 gatheringTypeInformation = true;
michael@0 778
michael@0 779 Spew(SpewCompile,
michael@0 780 "Script %p:%s:%d has no baseline script, "
michael@0 781 "but use count grew from %d to %d",
michael@0 782 script.get(), script->filename(), script->lineno(),
michael@0 783 previousUseCount, currentUseCount);
michael@0 784 } else {
michael@0 785 uint32_t stallCount = ++worklistData_[i].stallCount;
michael@0 786 if (stallCount < stallThreshold) {
michael@0 787 gatheringTypeInformation = true;
michael@0 788 }
michael@0 789
michael@0 790 Spew(SpewCompile,
michael@0 791 "Script %p:%s:%d has no baseline script, "
michael@0 792 "and use count has %u stalls at %d",
michael@0 793 script.get(), script->filename(), script->lineno(),
michael@0 794 stallCount, previousUseCount);
michael@0 795 }
michael@0 796 continue;
michael@0 797 }
michael@0 798
michael@0 799 if (!script->hasParallelIonScript()) {
michael@0 800 // Script has not yet been compiled. Attempt to compile it.
michael@0 801 SpewBeginCompile(script);
michael@0 802 MethodStatus mstatus = jit::CanEnterInParallel(cx_, script);
michael@0 803 SpewEndCompile(mstatus);
michael@0 804
michael@0 805 switch (mstatus) {
michael@0 806 case Method_Error:
michael@0 807 return fatalError(status);
michael@0 808
michael@0 809 case Method_CantCompile:
michael@0 810 Spew(SpewCompile,
michael@0 811 "Script %p:%s:%d cannot be compiled, "
michael@0 812 "falling back to sequential execution",
michael@0 813 script.get(), script->filename(), script->lineno());
michael@0 814 return sequentialExecution(true, status);
michael@0 815
michael@0 816 case Method_Skipped:
michael@0 817 // A "skipped" result either means that we are compiling
michael@0 818 // in parallel OR some other transient error occurred.
michael@0 819 if (script->isParallelIonCompilingOffThread()) {
michael@0 820 Spew(SpewCompile,
michael@0 821 "Script %p:%s:%d compiling off-thread",
michael@0 822 script.get(), script->filename(), script->lineno());
michael@0 823 offMainThreadCompilationsInProgress = true;
michael@0 824 continue;
michael@0 825 }
michael@0 826 return sequentialExecution(false, status);
michael@0 827
michael@0 828 case Method_Compiled:
michael@0 829 Spew(SpewCompile,
michael@0 830 "Script %p:%s:%d compiled",
michael@0 831 script.get(), script->filename(), script->lineno());
michael@0 832 JS_ASSERT(script->hasParallelIonScript());
michael@0 833
michael@0 834 if (isInitialScript(script)) {
michael@0 835 JitCompartment *jitComp = cx_->compartment()->jitCompartment();
michael@0 836 if (!jitComp->notifyOfActiveParallelEntryScript(cx_, script)) {
michael@0 837 *status = ExecutionFatal;
michael@0 838 return RedLight;
michael@0 839 }
michael@0 840 }
michael@0 841
michael@0 842 break;
michael@0 843 }
michael@0 844 }
michael@0 845
michael@0 846 // At this point, either the script was already compiled
michael@0 847 // or we just compiled it. Check whether its "uncompiled
michael@0 848 // call target" flag is set and add the targets to our
michael@0 849 // worklist if so. Clear the flag after that, since we
michael@0 850 // will be compiling the call targets.
michael@0 851 JS_ASSERT(script->hasParallelIonScript());
michael@0 852 if (appendCallTargetsToWorklist(i, status) == RedLight)
michael@0 853 return RedLight;
michael@0 854 }
michael@0 855
michael@0 856 // If there is compilation occurring in a helper thread, then
michael@0 857 // run a warmup iterations in the main thread while we wait.
michael@0 858 // There is a chance that this warmup will finish all the work
michael@0 859 // we have to do, so we should stop then, unless we are in
michael@0 860 // compile mode, in which case we'll continue to block.
michael@0 861 //
michael@0 862 // Note that even in compile mode, we can't block *forever*:
michael@0 863 // - OMTC compiles will finish;
michael@0 864 // - no work is being done, so use counts on not-yet-baselined
michael@0 865 // scripts will not increase.
michael@0 866 if (offMainThreadCompilationsInProgress || gatheringTypeInformation) {
michael@0 867 bool stopIfComplete = (mode_ != ForkJoinModeCompile);
michael@0 868 if (warmupExecution(stopIfComplete, status) == RedLight)
michael@0 869 return RedLight;
michael@0 870 continue;
michael@0 871 }
michael@0 872
michael@0 873 // All compilations are complete. However, be careful: it is
michael@0 874 // possible that a garbage collection occurred while we were
michael@0 875 // iterating and caused some of the scripts we thought we had
michael@0 876 // compiled to be collected. In that case, we will just have
michael@0 877 // to begin again.
michael@0 878 bool allScriptsPresent = true;
michael@0 879 for (uint32_t i = 0; i < worklist_.length(); i++) {
michael@0 880 if (!worklist_[i]->hasParallelIonScript()) {
michael@0 881 if (worklistData_[i].stallCount < stallThreshold) {
michael@0 882 worklistData_[i].reset();
michael@0 883 allScriptsPresent = false;
michael@0 884
michael@0 885 Spew(SpewCompile,
michael@0 886 "Script %p:%s:%d is not stalled, "
michael@0 887 "but no parallel ion script found, "
michael@0 888 "restarting loop",
michael@0 889 script.get(), script->filename(), script->lineno());
michael@0 890 }
michael@0 891 }
michael@0 892 }
michael@0 893
michael@0 894 if (allScriptsPresent)
michael@0 895 break;
michael@0 896 }
michael@0 897
michael@0 898 Spew(SpewCompile, "Compilation complete (final worklist length %d)",
michael@0 899 worklist_.length());
michael@0 900
michael@0 901 // At this point, all scripts and their transitive callees are
michael@0 902 // either stalled (indicating they are unlikely to be called) or
michael@0 903 // in a compiled state. Therefore we can clear the
michael@0 904 // "hasUncompiledCallTarget" flag on them and then clear the
michael@0 905 // worklist.
michael@0 906 for (uint32_t i = 0; i < worklist_.length(); i++) {
michael@0 907 if (worklist_[i]->hasParallelIonScript()) {
michael@0 908 JS_ASSERT(worklistData_[i].calleesEnqueued);
michael@0 909 worklist_[i]->parallelIonScript()->clearHasUncompiledCallTarget();
michael@0 910 } else {
michael@0 911 JS_ASSERT(worklistData_[i].stallCount >= stallThreshold);
michael@0 912 }
michael@0 913 }
michael@0 914 worklist_.clear();
michael@0 915 worklistData_.clear();
michael@0 916 return GreenLight;
michael@0 917 }
michael@0 918
michael@0 919 ForkJoinOperation::TrafficLight
michael@0 920 ForkJoinOperation::appendCallTargetsToWorklist(uint32_t index, ExecutionStatus *status)
michael@0 921 {
michael@0 922 // GreenLight: call targets appended
michael@0 923 // RedLight: fatal error or completed work via warmups or fallback
michael@0 924
michael@0 925 JS_ASSERT(worklist_[index]->hasParallelIonScript());
michael@0 926
michael@0 927 // Check whether we have already enqueued the targets for
michael@0 928 // this entry and avoid doing it again if so.
michael@0 929 if (worklistData_[index].calleesEnqueued)
michael@0 930 return GreenLight;
michael@0 931 worklistData_[index].calleesEnqueued = true;
michael@0 932
michael@0 933 // Iterate through the callees and enqueue them.
michael@0 934 RootedScript target(cx_);
michael@0 935 IonScript *ion = worklist_[index]->parallelIonScript();
michael@0 936 for (uint32_t i = 0; i < ion->callTargetEntries(); i++) {
michael@0 937 target = ion->callTargetList()[i];
michael@0 938 parallel::Spew(parallel::SpewCompile,
michael@0 939 "Adding call target %s:%u",
michael@0 940 target->filename(), target->lineno());
michael@0 941 if (appendCallTargetToWorklist(target, status) == RedLight)
michael@0 942 return RedLight;
michael@0 943 }
michael@0 944
michael@0 945 return GreenLight;
michael@0 946 }
michael@0 947
michael@0 948 ForkJoinOperation::TrafficLight
michael@0 949 ForkJoinOperation::appendCallTargetToWorklist(HandleScript script, ExecutionStatus *status)
michael@0 950 {
michael@0 951 // GreenLight: call target appended if necessary
michael@0 952 // RedLight: fatal error or completed work via warmups or fallback
michael@0 953
michael@0 954 JS_ASSERT(script);
michael@0 955
michael@0 956 // Fallback to sequential if disabled.
michael@0 957 if (!script->canParallelIonCompile()) {
michael@0 958 Spew(SpewCompile, "Skipping %p:%s:%u, canParallelIonCompile() is false",
michael@0 959 script.get(), script->filename(), script->lineno());
michael@0 960 return sequentialExecution(true, status);
michael@0 961 }
michael@0 962
michael@0 963 if (script->hasParallelIonScript()) {
michael@0 964 // Skip if the code is expected to result in a bailout.
michael@0 965 if (script->parallelIonScript()->bailoutExpected()) {
michael@0 966 Spew(SpewCompile, "Skipping %p:%s:%u, bailout expected",
michael@0 967 script.get(), script->filename(), script->lineno());
michael@0 968 return sequentialExecution(false, status);
michael@0 969 }
michael@0 970 }
michael@0 971
michael@0 972 if (!addToWorklist(script))
michael@0 973 return fatalError(status);
michael@0 974
michael@0 975 return GreenLight;
michael@0 976 }
michael@0 977
michael@0 978 bool
michael@0 979 ForkJoinOperation::addToWorklist(HandleScript script)
michael@0 980 {
michael@0 981 for (uint32_t i = 0; i < worklist_.length(); i++) {
michael@0 982 if (worklist_[i] == script) {
michael@0 983 Spew(SpewCompile, "Skipping %p:%s:%u, already in worklist",
michael@0 984 script.get(), script->filename(), script->lineno());
michael@0 985 return true;
michael@0 986 }
michael@0 987 }
michael@0 988
michael@0 989 Spew(SpewCompile, "Enqueued %p:%s:%u",
michael@0 990 script.get(), script->filename(), script->lineno());
michael@0 991
michael@0 992 // Note that we add all possibly compilable functions to the worklist,
michael@0 993 // even if they're already compiled. This is so that we can return
michael@0 994 // Method_Compiled and not Method_Skipped if we have a worklist full of
michael@0 995 // already-compiled functions.
michael@0 996 if (!worklist_.append(script))
michael@0 997 return false;
michael@0 998
michael@0 999 // we have not yet enqueued the callees of this script
michael@0 1000 if (!worklistData_.append(WorklistData()))
michael@0 1001 return false;
michael@0 1002 worklistData_[worklistData_.length() - 1].reset();
michael@0 1003
michael@0 1004 return true;
michael@0 1005 }
michael@0 1006
michael@0 1007 ForkJoinOperation::TrafficLight
michael@0 1008 ForkJoinOperation::sequentialExecution(bool disqualified, ExecutionStatus *status)
michael@0 1009 {
michael@0 1010 // RedLight: fatal error or completed work
michael@0 1011
michael@0 1012 *status = sequentialExecution(disqualified);
michael@0 1013 return RedLight;
michael@0 1014 }
michael@0 1015
michael@0 1016 ExecutionStatus
michael@0 1017 ForkJoinOperation::sequentialExecution(bool disqualified)
michael@0 1018 {
michael@0 1019 // XXX use disqualified to set parallelIon to ION_DISABLED_SCRIPT?
michael@0 1020
michael@0 1021 Spew(SpewOps, "Executing sequential execution (disqualified=%d).",
michael@0 1022 disqualified);
michael@0 1023
michael@0 1024 if (sliceStart_ == sliceEnd_)
michael@0 1025 return ExecutionSequential;
michael@0 1026
michael@0 1027 RootedValue funVal(cx_, ObjectValue(*fun_));
michael@0 1028 if (!ExecuteSequentially(cx_, funVal, &sliceStart_, sliceEnd_))
michael@0 1029 return ExecutionFatal;
michael@0 1030 MOZ_ASSERT(sliceStart_ == sliceEnd_);
michael@0 1031 return ExecutionSequential;
michael@0 1032 }
michael@0 1033
michael@0 1034 ForkJoinOperation::TrafficLight
michael@0 1035 ForkJoinOperation::fatalError(ExecutionStatus *status)
michael@0 1036 {
michael@0 1037 // RedLight: fatal error
michael@0 1038
michael@0 1039 *status = ExecutionFatal;
michael@0 1040 return RedLight;
michael@0 1041 }
michael@0 1042
michael@0 1043 static const char *
michael@0 1044 BailoutExplanation(ParallelBailoutCause cause)
michael@0 1045 {
michael@0 1046 switch (cause) {
michael@0 1047 case ParallelBailoutNone:
michael@0 1048 return "no particular reason";
michael@0 1049 case ParallelBailoutCompilationSkipped:
michael@0 1050 return "compilation failed (method skipped)";
michael@0 1051 case ParallelBailoutCompilationFailure:
michael@0 1052 return "compilation failed";
michael@0 1053 case ParallelBailoutInterrupt:
michael@0 1054 return "interrupted";
michael@0 1055 case ParallelBailoutFailedIC:
michael@0 1056 return "failed to attach stub to IC";
michael@0 1057 case ParallelBailoutHeapBusy:
michael@0 1058 return "heap busy flag set during interrupt";
michael@0 1059 case ParallelBailoutMainScriptNotPresent:
michael@0 1060 return "main script not present";
michael@0 1061 case ParallelBailoutCalledToUncompiledScript:
michael@0 1062 return "called to uncompiled script";
michael@0 1063 case ParallelBailoutIllegalWrite:
michael@0 1064 return "illegal write";
michael@0 1065 case ParallelBailoutAccessToIntrinsic:
michael@0 1066 return "access to intrinsic";
michael@0 1067 case ParallelBailoutOverRecursed:
michael@0 1068 return "over recursed";
michael@0 1069 case ParallelBailoutOutOfMemory:
michael@0 1070 return "out of memory";
michael@0 1071 case ParallelBailoutUnsupported:
michael@0 1072 return "unsupported";
michael@0 1073 case ParallelBailoutUnsupportedVM:
michael@0 1074 return "unsupported operation in VM call";
michael@0 1075 case ParallelBailoutUnsupportedStringComparison:
michael@0 1076 return "unsupported string comparison";
michael@0 1077 case ParallelBailoutRequestedGC:
michael@0 1078 return "requested GC";
michael@0 1079 case ParallelBailoutRequestedZoneGC:
michael@0 1080 return "requested zone GC";
michael@0 1081 default:
michael@0 1082 return "no known reason";
michael@0 1083 }
michael@0 1084 }
michael@0 1085
michael@0 1086 bool
michael@0 1087 ForkJoinOperation::isInitialScript(HandleScript script)
michael@0 1088 {
michael@0 1089 return fun_->is<JSFunction>() && (fun_->as<JSFunction>().nonLazyScript() == script);
michael@0 1090 }
michael@0 1091
michael@0 1092 void
michael@0 1093 ForkJoinOperation::determineBailoutCause()
michael@0 1094 {
michael@0 1095 bailoutCause = ParallelBailoutNone;
michael@0 1096 for (uint32_t i = 0; i < bailoutRecords_.length(); i++) {
michael@0 1097 if (bailoutRecords_[i].cause == ParallelBailoutNone)
michael@0 1098 continue;
michael@0 1099
michael@0 1100 if (bailoutRecords_[i].cause == ParallelBailoutInterrupt)
michael@0 1101 continue;
michael@0 1102
michael@0 1103 bailoutCause = bailoutRecords_[i].cause;
michael@0 1104 const char *causeStr = BailoutExplanation(bailoutCause);
michael@0 1105 if (bailoutRecords_[i].depth) {
michael@0 1106 bailoutScript = bailoutRecords_[i].trace[0].script;
michael@0 1107 bailoutBytecode = bailoutRecords_[i].trace[0].bytecode;
michael@0 1108
michael@0 1109 const char *filename = bailoutScript->filename();
michael@0 1110 int line = JS_PCToLineNumber(cx_, bailoutScript, bailoutBytecode);
michael@0 1111 JS_ReportWarning(cx_, "Bailed out of parallel operation: %s at %s:%d",
michael@0 1112 causeStr, filename, line);
michael@0 1113
michael@0 1114 Spew(SpewBailouts, "Bailout from thread %d: cause %d at loc %s:%d",
michael@0 1115 i,
michael@0 1116 bailoutCause,
michael@0 1117 bailoutScript->filename(),
michael@0 1118 PCToLineNumber(bailoutScript, bailoutBytecode));
michael@0 1119 } else {
michael@0 1120 JS_ReportWarning(cx_, "Bailed out of parallel operation: %s",
michael@0 1121 causeStr);
michael@0 1122
michael@0 1123 Spew(SpewBailouts, "Bailout from thread %d: cause %d, unknown loc",
michael@0 1124 i,
michael@0 1125 bailoutCause);
michael@0 1126 }
michael@0 1127 }
michael@0 1128 }
michael@0 1129
michael@0 1130 bool
michael@0 1131 ForkJoinOperation::invalidateBailedOutScripts()
michael@0 1132 {
michael@0 1133 Vector<types::RecompileInfo> invalid(cx_);
michael@0 1134 for (uint32_t i = 0; i < bailoutRecords_.length(); i++) {
michael@0 1135 RootedScript script(cx_, bailoutRecords_[i].topScript);
michael@0 1136
michael@0 1137 // No script to invalidate.
michael@0 1138 if (!script || !script->hasParallelIonScript())
michael@0 1139 continue;
michael@0 1140
michael@0 1141 Spew(SpewBailouts,
michael@0 1142 "Bailout from thread %d: cause %d, topScript %p:%s:%d",
michael@0 1143 i,
michael@0 1144 bailoutRecords_[i].cause,
michael@0 1145 script.get(), script->filename(), script->lineno());
michael@0 1146
michael@0 1147 switch (bailoutRecords_[i].cause) {
michael@0 1148 // An interrupt is not the fault of the script, so don't
michael@0 1149 // invalidate it.
michael@0 1150 case ParallelBailoutInterrupt: continue;
michael@0 1151
michael@0 1152 // An illegal write will not be made legal by invalidation.
michael@0 1153 case ParallelBailoutIllegalWrite: continue;
michael@0 1154
michael@0 1155 // For other cases, consider invalidation.
michael@0 1156 default: break;
michael@0 1157 }
michael@0 1158
michael@0 1159 // Already invalidated.
michael@0 1160 if (hasScript(invalid, script))
michael@0 1161 continue;
michael@0 1162
michael@0 1163 Spew(SpewBailouts, "Invalidating script %p:%s:%d due to cause %d",
michael@0 1164 script.get(), script->filename(), script->lineno(),
michael@0 1165 bailoutRecords_[i].cause);
michael@0 1166
michael@0 1167 types::RecompileInfo co = script->parallelIonScript()->recompileInfo();
michael@0 1168
michael@0 1169 if (!invalid.append(co))
michael@0 1170 return false;
michael@0 1171
michael@0 1172 // any script that we have marked for invalidation will need
michael@0 1173 // to be recompiled
michael@0 1174 if (!addToWorklist(script))
michael@0 1175 return false;
michael@0 1176 }
michael@0 1177
michael@0 1178 Invalidate(cx_, invalid);
michael@0 1179
michael@0 1180 return true;
michael@0 1181 }
michael@0 1182
michael@0 1183 ForkJoinOperation::TrafficLight
michael@0 1184 ForkJoinOperation::warmupExecution(bool stopIfComplete, ExecutionStatus *status)
michael@0 1185 {
michael@0 1186 // GreenLight: warmup succeeded, still more work to do
michael@0 1187 // RedLight: fatal error or warmup completed all work (check status)
michael@0 1188
michael@0 1189 if (sliceStart_ == sliceEnd_) {
michael@0 1190 Spew(SpewOps, "Warmup execution finished all the work.");
michael@0 1191
michael@0 1192 if (stopIfComplete) {
michael@0 1193 *status = ExecutionWarmup;
michael@0 1194 return RedLight;
michael@0 1195 }
michael@0 1196
michael@0 1197 // If we finished all slices in warmup, be sure check the
michael@0 1198 // interrupt flag. This is because we won't be running more JS
michael@0 1199 // code, and thus no more automatic checking of the interrupt
michael@0 1200 // flag.
michael@0 1201 if (!CheckForInterrupt(cx_)) {
michael@0 1202 *status = ExecutionFatal;
michael@0 1203 return RedLight;
michael@0 1204 }
michael@0 1205
michael@0 1206 return GreenLight;
michael@0 1207 }
michael@0 1208
michael@0 1209 Spew(SpewOps, "Executing warmup from slice %d.", sliceStart_);
michael@0 1210
michael@0 1211 AutoEnterWarmup warmup(cx_->runtime());
michael@0 1212 RootedValue funVal(cx_, ObjectValue(*fun_));
michael@0 1213 if (!ExecuteSequentially(cx_, funVal, &sliceStart_, sliceStart_ + 1)) {
michael@0 1214 *status = ExecutionFatal;
michael@0 1215 return RedLight;
michael@0 1216 }
michael@0 1217
michael@0 1218 return GreenLight;
michael@0 1219 }
michael@0 1220
michael@0 1221 ForkJoinOperation::TrafficLight
michael@0 1222 ForkJoinOperation::parallelExecution(ExecutionStatus *status)
michael@0 1223 {
michael@0 1224 // GreenLight: bailout occurred, keep trying
michael@0 1225 // RedLight: fatal error or all work completed
michael@0 1226
michael@0 1227 // Recursive use of the ThreadPool is not supported. Right now we
michael@0 1228 // cannot get here because parallel code cannot invoke native
michael@0 1229 // functions such as ForkJoin().
michael@0 1230 JS_ASSERT(ForkJoinContext::current() == nullptr);
michael@0 1231
michael@0 1232 if (sliceStart_ == sliceEnd_) {
michael@0 1233 Spew(SpewOps, "Warmup execution finished all the work.");
michael@0 1234 *status = ExecutionWarmup;
michael@0 1235 return RedLight;
michael@0 1236 }
michael@0 1237
michael@0 1238 ForkJoinActivation activation(cx_);
michael@0 1239 ThreadPool *threadPool = &cx_->runtime()->threadPool;
michael@0 1240 ForkJoinShared shared(cx_, threadPool, fun_, sliceStart_, sliceEnd_, &bailoutRecords_[0]);
michael@0 1241 if (!shared.init()) {
michael@0 1242 *status = ExecutionFatal;
michael@0 1243 return RedLight;
michael@0 1244 }
michael@0 1245
michael@0 1246 switch (shared.execute()) {
michael@0 1247 case TP_SUCCESS:
michael@0 1248 *status = ExecutionParallel;
michael@0 1249 return RedLight;
michael@0 1250
michael@0 1251 case TP_FATAL:
michael@0 1252 *status = ExecutionFatal;
michael@0 1253 return RedLight;
michael@0 1254
michael@0 1255 case TP_RETRY_SEQUENTIALLY:
michael@0 1256 case TP_RETRY_AFTER_GC:
michael@0 1257 break; // bailout
michael@0 1258 }
michael@0 1259
michael@0 1260 return GreenLight;
michael@0 1261 }
michael@0 1262
michael@0 1263 ForkJoinOperation::TrafficLight
michael@0 1264 ForkJoinOperation::recoverFromBailout(ExecutionStatus *status)
michael@0 1265 {
michael@0 1266 // GreenLight: bailout recovered, try to compile-and-run again
michael@0 1267 // RedLight: fatal error
michael@0 1268
michael@0 1269 bailouts += 1;
michael@0 1270 determineBailoutCause();
michael@0 1271
michael@0 1272 SpewBailout(bailouts, bailoutScript, bailoutBytecode, bailoutCause);
michael@0 1273
michael@0 1274 // After any bailout, we always scan over callee list of main
michael@0 1275 // function, if nothing else
michael@0 1276 RootedScript mainScript(cx_, fun_->nonLazyScript());
michael@0 1277 if (!addToWorklist(mainScript))
michael@0 1278 return fatalError(status);
michael@0 1279
michael@0 1280 // Also invalidate and recompile any callees that were implicated
michael@0 1281 // by the bailout
michael@0 1282 if (!invalidateBailedOutScripts())
michael@0 1283 return fatalError(status);
michael@0 1284
michael@0 1285 if (warmupExecution(/*stopIfComplete:*/true, status) == RedLight)
michael@0 1286 return RedLight;
michael@0 1287
michael@0 1288 return GreenLight;
michael@0 1289 }
michael@0 1290
michael@0 1291 bool
michael@0 1292 ForkJoinOperation::hasScript(Vector<types::RecompileInfo> &scripts, JSScript *script)
michael@0 1293 {
michael@0 1294 for (uint32_t i = 0; i < scripts.length(); i++) {
michael@0 1295 if (scripts[i] == script->parallelIonScript()->recompileInfo())
michael@0 1296 return true;
michael@0 1297 }
michael@0 1298 return false;
michael@0 1299 }
michael@0 1300
michael@0 1301 // Can only enter callees with a valid IonScript.
michael@0 1302 template <uint32_t maxArgc>
michael@0 1303 class ParallelIonInvoke
michael@0 1304 {
michael@0 1305 EnterJitCode enter_;
michael@0 1306 void *jitcode_;
michael@0 1307 void *calleeToken_;
michael@0 1308 Value argv_[maxArgc + 2];
michael@0 1309 uint32_t argc_;
michael@0 1310
michael@0 1311 public:
michael@0 1312 Value *args;
michael@0 1313
michael@0 1314 ParallelIonInvoke(JSRuntime *rt,
michael@0 1315 HandleFunction callee,
michael@0 1316 uint32_t argc)
michael@0 1317 : argc_(argc),
michael@0 1318 args(argv_ + 2)
michael@0 1319 {
michael@0 1320 JS_ASSERT(argc <= maxArgc + 2);
michael@0 1321
michael@0 1322 // Set 'callee' and 'this'.
michael@0 1323 argv_[0] = ObjectValue(*callee);
michael@0 1324 argv_[1] = UndefinedValue();
michael@0 1325
michael@0 1326 // Find JIT code pointer.
michael@0 1327 IonScript *ion = callee->nonLazyScript()->parallelIonScript();
michael@0 1328 JitCode *code = ion->method();
michael@0 1329 jitcode_ = code->raw();
michael@0 1330 enter_ = rt->jitRuntime()->enterIon();
michael@0 1331 calleeToken_ = CalleeToToken(callee);
michael@0 1332 }
michael@0 1333
michael@0 1334 bool invoke(PerThreadData *perThread) {
michael@0 1335 RootedValue result(perThread);
michael@0 1336 CALL_GENERATED_CODE(enter_, jitcode_, argc_ + 1, argv_ + 1, nullptr, calleeToken_,
michael@0 1337 nullptr, 0, result.address());
michael@0 1338 return !result.isMagic();
michael@0 1339 }
michael@0 1340 };
michael@0 1341
michael@0 1342 /////////////////////////////////////////////////////////////////////////////
michael@0 1343 // ForkJoinShared
michael@0 1344 //
michael@0 1345
michael@0 1346 ForkJoinShared::ForkJoinShared(JSContext *cx,
michael@0 1347 ThreadPool *threadPool,
michael@0 1348 HandleFunction fun,
michael@0 1349 uint16_t sliceStart,
michael@0 1350 uint16_t sliceEnd,
michael@0 1351 ParallelBailoutRecord *records)
michael@0 1352 : cx_(cx),
michael@0 1353 threadPool_(threadPool),
michael@0 1354 fun_(fun),
michael@0 1355 sliceStart_(sliceStart),
michael@0 1356 sliceEnd_(sliceEnd),
michael@0 1357 cxLock_(nullptr),
michael@0 1358 records_(records),
michael@0 1359 allocators_(cx),
michael@0 1360 gcRequested_(false),
michael@0 1361 gcReason_(JS::gcreason::NUM_REASONS),
michael@0 1362 gcZone_(nullptr),
michael@0 1363 abort_(false),
michael@0 1364 fatal_(false)
michael@0 1365 {
michael@0 1366 }
michael@0 1367
michael@0 1368 bool
michael@0 1369 ForkJoinShared::init()
michael@0 1370 {
michael@0 1371 // Create temporary arenas to hold the data allocated during the
michael@0 1372 // parallel code.
michael@0 1373 //
michael@0 1374 // Note: you might think (as I did, initially) that we could use
michael@0 1375 // compartment |Allocator| for the main thread. This is not true,
michael@0 1376 // because when executing parallel code we sometimes check what
michael@0 1377 // arena list an object is in to decide if it is writable. If we
michael@0 1378 // used the compartment |Allocator| for the main thread, then the
michael@0 1379 // main thread would be permitted to write to any object it wants.
michael@0 1380
michael@0 1381 if (!Monitor::init())
michael@0 1382 return false;
michael@0 1383
michael@0 1384 cxLock_ = PR_NewLock();
michael@0 1385 if (!cxLock_)
michael@0 1386 return false;
michael@0 1387
michael@0 1388 for (unsigned i = 0; i < threadPool_->numWorkers(); i++) {
michael@0 1389 Allocator *allocator = cx_->new_<Allocator>(cx_->zone());
michael@0 1390 if (!allocator)
michael@0 1391 return false;
michael@0 1392
michael@0 1393 if (!allocators_.append(allocator)) {
michael@0 1394 js_delete(allocator);
michael@0 1395 return false;
michael@0 1396 }
michael@0 1397 }
michael@0 1398
michael@0 1399 return true;
michael@0 1400 }
michael@0 1401
michael@0 1402 ForkJoinShared::~ForkJoinShared()
michael@0 1403 {
michael@0 1404 PR_DestroyLock(cxLock_);
michael@0 1405
michael@0 1406 while (allocators_.length() > 0)
michael@0 1407 js_delete(allocators_.popCopy());
michael@0 1408 }
michael@0 1409
michael@0 1410 ParallelResult
michael@0 1411 ForkJoinShared::execute()
michael@0 1412 {
michael@0 1413 // Sometimes a GC request occurs *just before* we enter into the
michael@0 1414 // parallel section. Rather than enter into the parallel section
michael@0 1415 // and then abort, we just check here and abort early.
michael@0 1416 if (cx_->runtime()->interruptPar)
michael@0 1417 return TP_RETRY_SEQUENTIALLY;
michael@0 1418
michael@0 1419 AutoLockMonitor lock(*this);
michael@0 1420
michael@0 1421 ParallelResult jobResult = TP_SUCCESS;
michael@0 1422 {
michael@0 1423 AutoUnlockMonitor unlock(*this);
michael@0 1424
michael@0 1425 // Push parallel tasks and wait until they're all done.
michael@0 1426 jobResult = threadPool_->executeJob(cx_, this, sliceStart_, sliceEnd_);
michael@0 1427 if (jobResult == TP_FATAL)
michael@0 1428 return TP_FATAL;
michael@0 1429 }
michael@0 1430
michael@0 1431 transferArenasToCompartmentAndProcessGCRequests();
michael@0 1432
michael@0 1433 // Check if any of the workers failed.
michael@0 1434 if (abort_) {
michael@0 1435 if (fatal_)
michael@0 1436 return TP_FATAL;
michael@0 1437 return TP_RETRY_SEQUENTIALLY;
michael@0 1438 }
michael@0 1439
michael@0 1440 #ifdef DEBUG
michael@0 1441 Spew(SpewOps, "Completed parallel job [slices: %d, threads: %d, stolen: %d (work stealing:%s)]",
michael@0 1442 sliceEnd_ - sliceStart_,
michael@0 1443 threadPool_->numWorkers(),
michael@0 1444 threadPool_->stolenSlices(),
michael@0 1445 threadPool_->workStealing() ? "ON" : "OFF");
michael@0 1446 #endif
michael@0 1447
michael@0 1448 // Everything went swimmingly. Give yourself a pat on the back.
michael@0 1449 return jobResult;
michael@0 1450 }
michael@0 1451
michael@0 1452 void
michael@0 1453 ForkJoinShared::transferArenasToCompartmentAndProcessGCRequests()
michael@0 1454 {
michael@0 1455 JSCompartment *comp = cx_->compartment();
michael@0 1456 for (unsigned i = 0; i < threadPool_->numWorkers(); i++)
michael@0 1457 comp->adoptWorkerAllocator(allocators_[i]);
michael@0 1458
michael@0 1459 if (gcRequested_) {
michael@0 1460 if (!gcZone_)
michael@0 1461 TriggerGC(cx_->runtime(), gcReason_);
michael@0 1462 else
michael@0 1463 TriggerZoneGC(gcZone_, gcReason_);
michael@0 1464 gcRequested_ = false;
michael@0 1465 gcZone_ = nullptr;
michael@0 1466 }
michael@0 1467 }
michael@0 1468
michael@0 1469 bool
michael@0 1470 ForkJoinShared::executeFromWorker(ThreadPoolWorker *worker, uintptr_t stackLimit)
michael@0 1471 {
michael@0 1472 PerThreadData thisThread(cx_->runtime());
michael@0 1473 if (!thisThread.init()) {
michael@0 1474 setAbortFlagAndRequestInterrupt(true);
michael@0 1475 return false;
michael@0 1476 }
michael@0 1477 TlsPerThreadData.set(&thisThread);
michael@0 1478
michael@0 1479 #ifdef JS_ARM_SIMULATOR
michael@0 1480 stackLimit = Simulator::StackLimit();
michael@0 1481 #endif
michael@0 1482
michael@0 1483 // Don't use setIonStackLimit() because that acquires the ionStackLimitLock, and the
michael@0 1484 // lock has not been initialized in these cases.
michael@0 1485 thisThread.jitStackLimit = stackLimit;
michael@0 1486 executePortion(&thisThread, worker);
michael@0 1487 TlsPerThreadData.set(nullptr);
michael@0 1488
michael@0 1489 return !abort_;
michael@0 1490 }
michael@0 1491
michael@0 1492 bool
michael@0 1493 ForkJoinShared::executeFromMainThread(ThreadPoolWorker *worker)
michael@0 1494 {
michael@0 1495 executePortion(&cx_->mainThread(), worker);
michael@0 1496 return !abort_;
michael@0 1497 }
michael@0 1498
michael@0 1499 void
michael@0 1500 ForkJoinShared::executePortion(PerThreadData *perThread, ThreadPoolWorker *worker)
michael@0 1501 {
michael@0 1502 // WARNING: This code runs ON THE PARALLEL WORKER THREAD.
michael@0 1503 // Be careful when accessing cx_.
michael@0 1504
michael@0 1505 // ForkJoinContext already contains an AutoAssertNoGC; however, the analysis
michael@0 1506 // does not propagate this type information. We duplicate the assertion
michael@0 1507 // here for maximum clarity.
michael@0 1508 JS::AutoAssertNoGC nogc(runtime());
michael@0 1509
michael@0 1510 Allocator *allocator = allocators_[worker->id()];
michael@0 1511 ForkJoinContext cx(perThread, worker, allocator, this, &records_[worker->id()]);
michael@0 1512 AutoSetForkJoinContext autoContext(&cx);
michael@0 1513
michael@0 1514 #ifdef DEBUG
michael@0 1515 // Set the maximum worker and slice number for prettier spewing.
michael@0 1516 cx.maxWorkerId = threadPool_->numWorkers();
michael@0 1517 #endif
michael@0 1518
michael@0 1519 Spew(SpewOps, "Up");
michael@0 1520
michael@0 1521 // Make a new IonContext for the slice, which is needed if we need to
michael@0 1522 // re-enter the VM.
michael@0 1523 IonContext icx(CompileRuntime::get(cx_->runtime()),
michael@0 1524 CompileCompartment::get(cx_->compartment()),
michael@0 1525 nullptr);
michael@0 1526
michael@0 1527 JS_ASSERT(cx.bailoutRecord->topScript == nullptr);
michael@0 1528
michael@0 1529 if (!fun_->nonLazyScript()->hasParallelIonScript()) {
michael@0 1530 // Sometimes, particularly with GCZeal, the parallel ion
michael@0 1531 // script can be collected between starting the parallel
michael@0 1532 // op and reaching this point. In that case, we just fail
michael@0 1533 // and fallback.
michael@0 1534 Spew(SpewOps, "Down (Script no longer present)");
michael@0 1535 cx.bailoutRecord->setCause(ParallelBailoutMainScriptNotPresent);
michael@0 1536 setAbortFlagAndRequestInterrupt(false);
michael@0 1537 } else {
michael@0 1538 ParallelIonInvoke<3> fii(cx_->runtime(), fun_, 3);
michael@0 1539
michael@0 1540 fii.args[0] = Int32Value(worker->id());
michael@0 1541 fii.args[1] = Int32Value(sliceStart_);
michael@0 1542 fii.args[2] = Int32Value(sliceEnd_);
michael@0 1543
michael@0 1544 bool ok = fii.invoke(perThread);
michael@0 1545 JS_ASSERT(ok == !cx.bailoutRecord->topScript);
michael@0 1546 if (!ok)
michael@0 1547 setAbortFlagAndRequestInterrupt(false);
michael@0 1548 }
michael@0 1549
michael@0 1550 Spew(SpewOps, "Down");
michael@0 1551 }
michael@0 1552
michael@0 1553 void
michael@0 1554 ForkJoinShared::setAbortFlagDueToInterrupt(ForkJoinContext &cx)
michael@0 1555 {
michael@0 1556 JS_ASSERT(cx_->runtime()->interruptPar);
michael@0 1557 // The GC Needed flag should not be set during parallel
michael@0 1558 // execution. Instead, one of the requestGC() or
michael@0 1559 // requestZoneGC() methods should be invoked.
michael@0 1560 JS_ASSERT(!cx_->runtime()->gcIsNeeded);
michael@0 1561
michael@0 1562 if (!abort_) {
michael@0 1563 cx.bailoutRecord->setCause(ParallelBailoutInterrupt);
michael@0 1564 setAbortFlagAndRequestInterrupt(false);
michael@0 1565 }
michael@0 1566 }
michael@0 1567
michael@0 1568 void
michael@0 1569 ForkJoinShared::setAbortFlagAndRequestInterrupt(bool fatal)
michael@0 1570 {
michael@0 1571 AutoLockMonitor lock(*this);
michael@0 1572
michael@0 1573 abort_ = true;
michael@0 1574 fatal_ = fatal_ || fatal;
michael@0 1575
michael@0 1576 // Note: The ForkJoin trigger here avoids the expensive memory protection needed to
michael@0 1577 // interrupt Ion code compiled for sequential execution.
michael@0 1578 cx_->runtime()->requestInterrupt(JSRuntime::RequestInterruptAnyThreadForkJoin);
michael@0 1579 }
michael@0 1580
michael@0 1581 void
michael@0 1582 ForkJoinShared::requestGC(JS::gcreason::Reason reason)
michael@0 1583 {
michael@0 1584 AutoLockMonitor lock(*this);
michael@0 1585
michael@0 1586 gcZone_ = nullptr;
michael@0 1587 gcReason_ = reason;
michael@0 1588 gcRequested_ = true;
michael@0 1589 }
michael@0 1590
michael@0 1591 void
michael@0 1592 ForkJoinShared::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
michael@0 1593 {
michael@0 1594 AutoLockMonitor lock(*this);
michael@0 1595
michael@0 1596 if (gcRequested_ && gcZone_ != zone) {
michael@0 1597 // If a full GC has been requested, or a GC for another zone,
michael@0 1598 // issue a request for a full GC.
michael@0 1599 gcZone_ = nullptr;
michael@0 1600 gcReason_ = reason;
michael@0 1601 gcRequested_ = true;
michael@0 1602 } else {
michael@0 1603 // Otherwise, just GC this zone.
michael@0 1604 gcZone_ = zone;
michael@0 1605 gcReason_ = reason;
michael@0 1606 gcRequested_ = true;
michael@0 1607 }
michael@0 1608 }
michael@0 1609
michael@0 1610 /////////////////////////////////////////////////////////////////////////////
michael@0 1611 // ForkJoinContext
michael@0 1612 //
michael@0 1613
michael@0 1614 ForkJoinContext::ForkJoinContext(PerThreadData *perThreadData, ThreadPoolWorker *worker,
michael@0 1615 Allocator *allocator, ForkJoinShared *shared,
michael@0 1616 ParallelBailoutRecord *bailoutRecord)
michael@0 1617 : ThreadSafeContext(shared->runtime(), perThreadData, Context_ForkJoin),
michael@0 1618 bailoutRecord(bailoutRecord),
michael@0 1619 targetRegionStart(nullptr),
michael@0 1620 targetRegionEnd(nullptr),
michael@0 1621 shared_(shared),
michael@0 1622 worker_(worker),
michael@0 1623 acquiredJSContext_(false),
michael@0 1624 nogc_(shared->runtime())
michael@0 1625 {
michael@0 1626 /*
michael@0 1627 * Unsafely set the zone. This is used to track malloc counters and to
michael@0 1628 * trigger GCs and is otherwise not thread-safe to access.
michael@0 1629 */
michael@0 1630 zone_ = shared->zone();
michael@0 1631
michael@0 1632 /*
michael@0 1633 * Unsafely set the compartment. This is used to get read-only access to
michael@0 1634 * shared tables.
michael@0 1635 */
michael@0 1636 compartment_ = shared->compartment();
michael@0 1637
michael@0 1638 allocator_ = allocator;
michael@0 1639 }
michael@0 1640
michael@0 1641 bool
michael@0 1642 ForkJoinContext::isMainThread() const
michael@0 1643 {
michael@0 1644 return perThreadData == &shared_->runtime()->mainThread;
michael@0 1645 }
michael@0 1646
michael@0 1647 JSRuntime *
michael@0 1648 ForkJoinContext::runtime()
michael@0 1649 {
michael@0 1650 return shared_->runtime();
michael@0 1651 }
michael@0 1652
michael@0 1653 JSContext *
michael@0 1654 ForkJoinContext::acquireJSContext()
michael@0 1655 {
michael@0 1656 JSContext *cx = shared_->acquireJSContext();
michael@0 1657 JS_ASSERT(!acquiredJSContext_);
michael@0 1658 acquiredJSContext_ = true;
michael@0 1659 return cx;
michael@0 1660 }
michael@0 1661
michael@0 1662 void
michael@0 1663 ForkJoinContext::releaseJSContext()
michael@0 1664 {
michael@0 1665 JS_ASSERT(acquiredJSContext_);
michael@0 1666 acquiredJSContext_ = false;
michael@0 1667 return shared_->releaseJSContext();
michael@0 1668 }
michael@0 1669
michael@0 1670 bool
michael@0 1671 ForkJoinContext::hasAcquiredJSContext() const
michael@0 1672 {
michael@0 1673 return acquiredJSContext_;
michael@0 1674 }
michael@0 1675
michael@0 1676 bool
michael@0 1677 ForkJoinContext::check()
michael@0 1678 {
michael@0 1679 if (runtime()->interruptPar) {
michael@0 1680 shared_->setAbortFlagDueToInterrupt(*this);
michael@0 1681 return false;
michael@0 1682 }
michael@0 1683 return true;
michael@0 1684 }
michael@0 1685
michael@0 1686 void
michael@0 1687 ForkJoinContext::requestGC(JS::gcreason::Reason reason)
michael@0 1688 {
michael@0 1689 shared_->requestGC(reason);
michael@0 1690 bailoutRecord->setCause(ParallelBailoutRequestedGC);
michael@0 1691 shared_->setAbortFlagAndRequestInterrupt(false);
michael@0 1692 }
michael@0 1693
michael@0 1694 void
michael@0 1695 ForkJoinContext::requestZoneGC(JS::Zone *zone, JS::gcreason::Reason reason)
michael@0 1696 {
michael@0 1697 shared_->requestZoneGC(zone, reason);
michael@0 1698 bailoutRecord->setCause(ParallelBailoutRequestedZoneGC);
michael@0 1699 shared_->setAbortFlagAndRequestInterrupt(false);
michael@0 1700 }
michael@0 1701
michael@0 1702 bool
michael@0 1703 ForkJoinContext::setPendingAbortFatal(ParallelBailoutCause cause)
michael@0 1704 {
michael@0 1705 shared_->setPendingAbortFatal();
michael@0 1706 bailoutRecord->setCause(cause);
michael@0 1707 return false;
michael@0 1708 }
michael@0 1709
michael@0 1710 //////////////////////////////////////////////////////////////////////////////
michael@0 1711 // ParallelBailoutRecord
michael@0 1712
michael@0 1713 void
michael@0 1714 js::ParallelBailoutRecord::init(JSContext *cx)
michael@0 1715 {
michael@0 1716 reset(cx);
michael@0 1717 }
michael@0 1718
michael@0 1719 void
michael@0 1720 js::ParallelBailoutRecord::reset(JSContext *cx)
michael@0 1721 {
michael@0 1722 topScript = nullptr;
michael@0 1723 cause = ParallelBailoutNone;
michael@0 1724 depth = 0;
michael@0 1725 }
michael@0 1726
michael@0 1727 void
michael@0 1728 js::ParallelBailoutRecord::setCause(ParallelBailoutCause cause,
michael@0 1729 JSScript *outermostScript,
michael@0 1730 JSScript *currentScript,
michael@0 1731 jsbytecode *currentPc)
michael@0 1732 {
michael@0 1733 this->cause = cause;
michael@0 1734 updateCause(cause, outermostScript, currentScript, currentPc);
michael@0 1735 }
michael@0 1736
michael@0 1737 void
michael@0 1738 js::ParallelBailoutRecord::updateCause(ParallelBailoutCause cause,
michael@0 1739 JSScript *outermostScript,
michael@0 1740 JSScript *currentScript,
michael@0 1741 jsbytecode *currentPc)
michael@0 1742 {
michael@0 1743 JS_ASSERT_IF(outermostScript, currentScript);
michael@0 1744 JS_ASSERT_IF(outermostScript, outermostScript->hasParallelIonScript());
michael@0 1745 JS_ASSERT_IF(currentScript, outermostScript);
michael@0 1746 JS_ASSERT_IF(!currentScript, !currentPc);
michael@0 1747
michael@0 1748 if (this->cause == ParallelBailoutNone)
michael@0 1749 this->cause = cause;
michael@0 1750
michael@0 1751 if (outermostScript)
michael@0 1752 this->topScript = outermostScript;
michael@0 1753
michael@0 1754 if (currentScript)
michael@0 1755 addTrace(currentScript, currentPc);
michael@0 1756 }
michael@0 1757
michael@0 1758 void
michael@0 1759 js::ParallelBailoutRecord::addTrace(JSScript *script,
michael@0 1760 jsbytecode *pc)
michael@0 1761 {
michael@0 1762 // Ideally, this should never occur, because we should always have
michael@0 1763 // a script when we invoke setCause, but I havent' fully
michael@0 1764 // refactored things to that point yet:
michael@0 1765 if (topScript == nullptr && script != nullptr)
michael@0 1766 topScript = script;
michael@0 1767
michael@0 1768 if (depth < MaxDepth) {
michael@0 1769 trace[depth].script = script;
michael@0 1770 trace[depth].bytecode = pc;
michael@0 1771 depth += 1;
michael@0 1772 }
michael@0 1773 }
michael@0 1774
michael@0 1775 //////////////////////////////////////////////////////////////////////////////
michael@0 1776
michael@0 1777 //
michael@0 1778 // Debug spew
michael@0 1779 //
michael@0 1780
michael@0 1781 #ifdef DEBUG
michael@0 1782
michael@0 1783 static const char *
michael@0 1784 ExecutionStatusToString(ExecutionStatus status)
michael@0 1785 {
michael@0 1786 switch (status) {
michael@0 1787 case ExecutionFatal:
michael@0 1788 return "fatal";
michael@0 1789 case ExecutionSequential:
michael@0 1790 return "sequential";
michael@0 1791 case ExecutionWarmup:
michael@0 1792 return "warmup";
michael@0 1793 case ExecutionParallel:
michael@0 1794 return "parallel";
michael@0 1795 }
michael@0 1796 return "(unknown status)";
michael@0 1797 }
michael@0 1798
michael@0 1799 static const char *
michael@0 1800 MethodStatusToString(MethodStatus status)
michael@0 1801 {
michael@0 1802 switch (status) {
michael@0 1803 case Method_Error:
michael@0 1804 return "error";
michael@0 1805 case Method_CantCompile:
michael@0 1806 return "can't compile";
michael@0 1807 case Method_Skipped:
michael@0 1808 return "skipped";
michael@0 1809 case Method_Compiled:
michael@0 1810 return "compiled";
michael@0 1811 }
michael@0 1812 return "(unknown status)";
michael@0 1813 }
michael@0 1814
michael@0 1815 static unsigned
michael@0 1816 NumberOfDigits(unsigned n)
michael@0 1817 {
michael@0 1818 if (n == 0)
michael@0 1819 return 1;
michael@0 1820 unsigned d = 0;
michael@0 1821 while (n != 0) {
michael@0 1822 d++;
michael@0 1823 n /= 10;
michael@0 1824 }
michael@0 1825 return d;
michael@0 1826 }
michael@0 1827
michael@0 1828 static const size_t BufferSize = 4096;
michael@0 1829
michael@0 1830 class ParallelSpewer
michael@0 1831 {
michael@0 1832 uint32_t depth;
michael@0 1833 bool colorable;
michael@0 1834 bool active[NumSpewChannels];
michael@0 1835
michael@0 1836 const char *color(const char *colorCode) {
michael@0 1837 if (!colorable)
michael@0 1838 return "";
michael@0 1839 return colorCode;
michael@0 1840 }
michael@0 1841
michael@0 1842 const char *reset() { return color("\x1b[0m"); }
michael@0 1843 const char *bold() { return color("\x1b[1m"); }
michael@0 1844 const char *red() { return color("\x1b[31m"); }
michael@0 1845 const char *green() { return color("\x1b[32m"); }
michael@0 1846 const char *yellow() { return color("\x1b[33m"); }
michael@0 1847 const char *cyan() { return color("\x1b[36m"); }
michael@0 1848 const char *workerColor(uint32_t id) {
michael@0 1849 static const char *colors[] = {
michael@0 1850 "\x1b[7m\x1b[31m", "\x1b[7m\x1b[32m", "\x1b[7m\x1b[33m",
michael@0 1851 "\x1b[7m\x1b[34m", "\x1b[7m\x1b[35m", "\x1b[7m\x1b[36m",
michael@0 1852 "\x1b[7m\x1b[37m",
michael@0 1853 "\x1b[31m", "\x1b[32m", "\x1b[33m",
michael@0 1854 "\x1b[34m", "\x1b[35m", "\x1b[36m",
michael@0 1855 "\x1b[37m"
michael@0 1856 };
michael@0 1857 return color(colors[id % 14]);
michael@0 1858 }
michael@0 1859
michael@0 1860 public:
michael@0 1861 ParallelSpewer()
michael@0 1862 : depth(0)
michael@0 1863 {
michael@0 1864 const char *env;
michael@0 1865
michael@0 1866 mozilla::PodArrayZero(active);
michael@0 1867 env = getenv("PAFLAGS");
michael@0 1868 if (env) {
michael@0 1869 if (strstr(env, "ops"))
michael@0 1870 active[SpewOps] = true;
michael@0 1871 if (strstr(env, "compile"))
michael@0 1872 active[SpewCompile] = true;
michael@0 1873 if (strstr(env, "bailouts"))
michael@0 1874 active[SpewBailouts] = true;
michael@0 1875 if (strstr(env, "full")) {
michael@0 1876 for (uint32_t i = 0; i < NumSpewChannels; i++)
michael@0 1877 active[i] = true;
michael@0 1878 }
michael@0 1879 }
michael@0 1880
michael@0 1881 env = getenv("TERM");
michael@0 1882 if (env && isatty(fileno(stderr))) {
michael@0 1883 if (strcmp(env, "xterm-color") == 0 || strcmp(env, "xterm-256color") == 0)
michael@0 1884 colorable = true;
michael@0 1885 }
michael@0 1886 }
michael@0 1887
michael@0 1888 bool isActive(js::parallel::SpewChannel channel) {
michael@0 1889 return active[channel];
michael@0 1890 }
michael@0 1891
michael@0 1892 void spewVA(js::parallel::SpewChannel channel, const char *fmt, va_list ap) {
michael@0 1893 if (!active[channel])
michael@0 1894 return;
michael@0 1895
michael@0 1896 // Print into a buffer first so we use one fprintf, which usually
michael@0 1897 // doesn't get interrupted when running with multiple threads.
michael@0 1898 char buf[BufferSize];
michael@0 1899
michael@0 1900 if (ForkJoinContext *cx = ForkJoinContext::current()) {
michael@0 1901 // Print the format first into a buffer to right-justify the
michael@0 1902 // worker ids.
michael@0 1903 char bufbuf[BufferSize];
michael@0 1904 JS_snprintf(bufbuf, BufferSize, "[%%sParallel:%%0%du%%s] ",
michael@0 1905 NumberOfDigits(cx->maxWorkerId));
michael@0 1906 JS_snprintf(buf, BufferSize, bufbuf, workerColor(cx->workerId()),
michael@0 1907 cx->workerId(), reset());
michael@0 1908 } else {
michael@0 1909 JS_snprintf(buf, BufferSize, "[Parallel:M] ");
michael@0 1910 }
michael@0 1911
michael@0 1912 for (uint32_t i = 0; i < depth; i++)
michael@0 1913 JS_snprintf(buf + strlen(buf), BufferSize, " ");
michael@0 1914
michael@0 1915 JS_vsnprintf(buf + strlen(buf), BufferSize, fmt, ap);
michael@0 1916 JS_snprintf(buf + strlen(buf), BufferSize, "\n");
michael@0 1917
michael@0 1918 fprintf(stderr, "%s", buf);
michael@0 1919 }
michael@0 1920
michael@0 1921 void spew(js::parallel::SpewChannel channel, const char *fmt, ...) {
michael@0 1922 va_list ap;
michael@0 1923 va_start(ap, fmt);
michael@0 1924 spewVA(channel, fmt, ap);
michael@0 1925 va_end(ap);
michael@0 1926 }
michael@0 1927
michael@0 1928 void beginOp(JSContext *cx, const char *name) {
michael@0 1929 if (!active[SpewOps])
michael@0 1930 return;
michael@0 1931
michael@0 1932 if (cx) {
michael@0 1933 jsbytecode *pc;
michael@0 1934 RootedScript script(cx, cx->currentScript(&pc));
michael@0 1935 if (script && pc) {
michael@0 1936 NonBuiltinScriptFrameIter iter(cx);
michael@0 1937 if (iter.done()) {
michael@0 1938 spew(SpewOps, "%sBEGIN %s%s (%s:%u)", bold(), name, reset(),
michael@0 1939 script->filename(), PCToLineNumber(script, pc));
michael@0 1940 } else {
michael@0 1941 spew(SpewOps, "%sBEGIN %s%s (%s:%u -> %s:%u)", bold(), name, reset(),
michael@0 1942 iter.script()->filename(), PCToLineNumber(iter.script(), iter.pc()),
michael@0 1943 script->filename(), PCToLineNumber(script, pc));
michael@0 1944 }
michael@0 1945 } else {
michael@0 1946 spew(SpewOps, "%sBEGIN %s%s", bold(), name, reset());
michael@0 1947 }
michael@0 1948 } else {
michael@0 1949 spew(SpewOps, "%sBEGIN %s%s", bold(), name, reset());
michael@0 1950 }
michael@0 1951
michael@0 1952 depth++;
michael@0 1953 }
michael@0 1954
michael@0 1955 void endOp(ExecutionStatus status) {
michael@0 1956 if (!active[SpewOps])
michael@0 1957 return;
michael@0 1958
michael@0 1959 JS_ASSERT(depth > 0);
michael@0 1960 depth--;
michael@0 1961
michael@0 1962 const char *statusColor;
michael@0 1963 switch (status) {
michael@0 1964 case ExecutionFatal:
michael@0 1965 statusColor = red();
michael@0 1966 break;
michael@0 1967 case ExecutionSequential:
michael@0 1968 statusColor = yellow();
michael@0 1969 break;
michael@0 1970 case ExecutionParallel:
michael@0 1971 statusColor = green();
michael@0 1972 break;
michael@0 1973 default:
michael@0 1974 statusColor = reset();
michael@0 1975 break;
michael@0 1976 }
michael@0 1977
michael@0 1978 spew(SpewOps, "%sEND %s%s%s", bold(),
michael@0 1979 statusColor, ExecutionStatusToString(status), reset());
michael@0 1980 }
michael@0 1981
michael@0 1982 void bailout(uint32_t count, HandleScript script,
michael@0 1983 jsbytecode *pc, ParallelBailoutCause cause) {
michael@0 1984 if (!active[SpewOps])
michael@0 1985 return;
michael@0 1986
michael@0 1987 const char *filename = "";
michael@0 1988 unsigned line=0, column=0;
michael@0 1989 if (script) {
michael@0 1990 line = PCToLineNumber(script, pc, &column);
michael@0 1991 filename = script->filename();
michael@0 1992 }
michael@0 1993
michael@0 1994 spew(SpewOps, "%s%sBAILOUT %d%s: %s (%d) at %s:%d:%d", bold(), yellow(), count, reset(),
michael@0 1995 BailoutExplanation(cause), cause, filename, line, column);
michael@0 1996 }
michael@0 1997
michael@0 1998 void beginCompile(HandleScript script) {
michael@0 1999 if (!active[SpewCompile])
michael@0 2000 return;
michael@0 2001
michael@0 2002 spew(SpewCompile, "COMPILE %p:%s:%u", script.get(), script->filename(), script->lineno());
michael@0 2003 depth++;
michael@0 2004 }
michael@0 2005
michael@0 2006 void endCompile(MethodStatus status) {
michael@0 2007 if (!active[SpewCompile])
michael@0 2008 return;
michael@0 2009
michael@0 2010 JS_ASSERT(depth > 0);
michael@0 2011 depth--;
michael@0 2012
michael@0 2013 const char *statusColor;
michael@0 2014 switch (status) {
michael@0 2015 case Method_Error:
michael@0 2016 case Method_CantCompile:
michael@0 2017 statusColor = red();
michael@0 2018 break;
michael@0 2019 case Method_Skipped:
michael@0 2020 statusColor = yellow();
michael@0 2021 break;
michael@0 2022 case Method_Compiled:
michael@0 2023 statusColor = green();
michael@0 2024 break;
michael@0 2025 default:
michael@0 2026 statusColor = reset();
michael@0 2027 break;
michael@0 2028 }
michael@0 2029
michael@0 2030 spew(SpewCompile, "END %s%s%s", statusColor, MethodStatusToString(status), reset());
michael@0 2031 }
michael@0 2032
michael@0 2033 void spewMIR(MDefinition *mir, const char *fmt, va_list ap) {
michael@0 2034 if (!active[SpewCompile])
michael@0 2035 return;
michael@0 2036
michael@0 2037 char buf[BufferSize];
michael@0 2038 JS_vsnprintf(buf, BufferSize, fmt, ap);
michael@0 2039
michael@0 2040 JSScript *script = mir->block()->info().script();
michael@0 2041 spew(SpewCompile, "%s%s%s: %s (%s:%u)", cyan(), mir->opName(), reset(), buf,
michael@0 2042 script->filename(), PCToLineNumber(script, mir->trackedPc()));
michael@0 2043 }
michael@0 2044
michael@0 2045 void spewBailoutIR(IonLIRTraceData *data) {
michael@0 2046 if (!active[SpewBailouts])
michael@0 2047 return;
michael@0 2048
michael@0 2049 // If we didn't bail from a LIR/MIR but from a propagated parallel
michael@0 2050 // bailout, don't bother printing anything since we've printed it
michael@0 2051 // elsewhere.
michael@0 2052 if (data->mirOpName && data->script) {
michael@0 2053 spew(SpewBailouts, "%sBailout%s: %s / %s%s%s (block %d lir %d) (%s:%u)", yellow(), reset(),
michael@0 2054 data->lirOpName, cyan(), data->mirOpName, reset(),
michael@0 2055 data->blockIndex, data->lirIndex, data->script->filename(),
michael@0 2056 PCToLineNumber(data->script, data->pc));
michael@0 2057 }
michael@0 2058 }
michael@0 2059 };
michael@0 2060
michael@0 2061 // Singleton instance of the spewer.
michael@0 2062 static ParallelSpewer spewer;
michael@0 2063
michael@0 2064 bool
michael@0 2065 parallel::SpewEnabled(SpewChannel channel)
michael@0 2066 {
michael@0 2067 return spewer.isActive(channel);
michael@0 2068 }
michael@0 2069
michael@0 2070 void
michael@0 2071 parallel::Spew(SpewChannel channel, const char *fmt, ...)
michael@0 2072 {
michael@0 2073 va_list ap;
michael@0 2074 va_start(ap, fmt);
michael@0 2075 spewer.spewVA(channel, fmt, ap);
michael@0 2076 va_end(ap);
michael@0 2077 }
michael@0 2078
michael@0 2079 void
michael@0 2080 parallel::SpewBeginOp(JSContext *cx, const char *name)
michael@0 2081 {
michael@0 2082 spewer.beginOp(cx, name);
michael@0 2083 }
michael@0 2084
michael@0 2085 ExecutionStatus
michael@0 2086 parallel::SpewEndOp(ExecutionStatus status)
michael@0 2087 {
michael@0 2088 spewer.endOp(status);
michael@0 2089 return status;
michael@0 2090 }
michael@0 2091
michael@0 2092 void
michael@0 2093 parallel::SpewBailout(uint32_t count, HandleScript script,
michael@0 2094 jsbytecode *pc, ParallelBailoutCause cause)
michael@0 2095 {
michael@0 2096 spewer.bailout(count, script, pc, cause);
michael@0 2097 }
michael@0 2098
michael@0 2099 void
michael@0 2100 parallel::SpewBeginCompile(HandleScript script)
michael@0 2101 {
michael@0 2102 spewer.beginCompile(script);
michael@0 2103 }
michael@0 2104
michael@0 2105 MethodStatus
michael@0 2106 parallel::SpewEndCompile(MethodStatus status)
michael@0 2107 {
michael@0 2108 spewer.endCompile(status);
michael@0 2109 return status;
michael@0 2110 }
michael@0 2111
michael@0 2112 void
michael@0 2113 parallel::SpewMIR(MDefinition *mir, const char *fmt, ...)
michael@0 2114 {
michael@0 2115 va_list ap;
michael@0 2116 va_start(ap, fmt);
michael@0 2117 spewer.spewMIR(mir, fmt, ap);
michael@0 2118 va_end(ap);
michael@0 2119 }
michael@0 2120
michael@0 2121 void
michael@0 2122 parallel::SpewBailoutIR(IonLIRTraceData *data)
michael@0 2123 {
michael@0 2124 spewer.spewBailoutIR(data);
michael@0 2125 }
michael@0 2126
michael@0 2127 #endif // DEBUG
michael@0 2128
michael@0 2129 bool
michael@0 2130 js::InExclusiveParallelSection()
michael@0 2131 {
michael@0 2132 return InParallelSection() && ForkJoinContext::current()->hasAcquiredJSContext();
michael@0 2133 }
michael@0 2134
michael@0 2135 bool
michael@0 2136 js::ParallelTestsShouldPass(JSContext *cx)
michael@0 2137 {
michael@0 2138 return jit::IsIonEnabled(cx) &&
michael@0 2139 jit::IsBaselineEnabled(cx) &&
michael@0 2140 !jit::js_JitOptions.eagerCompilation &&
michael@0 2141 jit::js_JitOptions.baselineUsesBeforeCompile != 0 &&
michael@0 2142 cx->runtime()->gcZeal() == 0;
michael@0 2143 }
michael@0 2144
michael@0 2145 void
michael@0 2146 js::RequestInterruptForForkJoin(JSRuntime *rt, JSRuntime::InterruptMode mode)
michael@0 2147 {
michael@0 2148 if (mode != JSRuntime::RequestInterruptAnyThreadDontStopIon)
michael@0 2149 rt->interruptPar = true;
michael@0 2150 }
michael@0 2151
michael@0 2152 bool
michael@0 2153 js::intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp)
michael@0 2154 {
michael@0 2155 // This version of SetForkJoinTargetRegion is called during
michael@0 2156 // sequential execution. It is a no-op. The parallel version
michael@0 2157 // is intrinsic_SetForkJoinTargetRegionPar(), below.
michael@0 2158 return true;
michael@0 2159 }
michael@0 2160
michael@0 2161 static bool
michael@0 2162 intrinsic_SetForkJoinTargetRegionPar(ForkJoinContext *cx, unsigned argc, Value *vp)
michael@0 2163 {
michael@0 2164 // Sets the *target region*, which is the portion of the output
michael@0 2165 // buffer that the current iteration is permitted to write to.
michael@0 2166 //
michael@0 2167 // Note: it is important that the target region should be an
michael@0 2168 // entire element (or several elements) of the output array and
michael@0 2169 // not some region that spans from the middle of one element into
michael@0 2170 // the middle of another. This is because the guarding code
michael@0 2171 // assumes that handles, which never straddle across elements,
michael@0 2172 // will either be contained entirely within the target region or
michael@0 2173 // be contained entirely without of the region, and not straddling
michael@0 2174 // the region nor encompassing it.
michael@0 2175
michael@0 2176 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 2177 JS_ASSERT(argc == 3);
michael@0 2178 JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
michael@0 2179 JS_ASSERT(args[1].isInt32());
michael@0 2180 JS_ASSERT(args[2].isInt32());
michael@0 2181
michael@0 2182 uint8_t *mem = args[0].toObject().as<TypedObject>().typedMem();
michael@0 2183 int32_t start = args[1].toInt32();
michael@0 2184 int32_t end = args[2].toInt32();
michael@0 2185
michael@0 2186 cx->targetRegionStart = mem + start;
michael@0 2187 cx->targetRegionEnd = mem + end;
michael@0 2188 return true;
michael@0 2189 }
michael@0 2190
michael@0 2191 JS_JITINFO_NATIVE_PARALLEL(js::intrinsic_SetForkJoinTargetRegionInfo,
michael@0 2192 intrinsic_SetForkJoinTargetRegionPar);
michael@0 2193
michael@0 2194 bool
michael@0 2195 js::intrinsic_ClearThreadLocalArenas(JSContext *cx, unsigned argc, Value *vp)
michael@0 2196 {
michael@0 2197 return true;
michael@0 2198 }
michael@0 2199
michael@0 2200 static bool
michael@0 2201 intrinsic_ClearThreadLocalArenasPar(ForkJoinContext *cx, unsigned argc, Value *vp)
michael@0 2202 {
michael@0 2203 cx->allocator()->arenas.wipeDuringParallelExecution(cx->runtime());
michael@0 2204 return true;
michael@0 2205 }
michael@0 2206
michael@0 2207 JS_JITINFO_NATIVE_PARALLEL(js::intrinsic_ClearThreadLocalArenasInfo,
michael@0 2208 intrinsic_ClearThreadLocalArenasPar);
michael@0 2209
michael@0 2210 #endif // JS_THREADSAFE && JS_ION

mercurial