js/src/jit/ParallelFunctions.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "jit/ParallelFunctions.h"
     9 #include "builtin/TypedObject.h"
    10 #include "jit/arm/Simulator-arm.h"
    11 #include "vm/ArrayObject.h"
    13 #include "jsgcinlines.h"
    14 #include "jsobjinlines.h"
    16 using namespace js;
    17 using namespace jit;
    19 using parallel::Spew;
    20 using parallel::SpewOps;
    21 using parallel::SpewBailouts;
    22 using parallel::SpewBailoutIR;
    24 // Load the current thread context.
    25 ForkJoinContext *
    26 jit::ForkJoinContextPar()
    27 {
    28     return ForkJoinContext::current();
    29 }
    31 // NewGCThingPar() is called in place of NewGCThing() when executing
    32 // parallel code.  It uses the ArenaLists for the current thread and
    33 // allocates from there.
    34 JSObject *
    35 jit::NewGCThingPar(ForkJoinContext *cx, gc::AllocKind allocKind)
    36 {
    37     JS_ASSERT(ForkJoinContext::current() == cx);
    38     return js::NewGCObject<NoGC>(cx, allocKind, 0, gc::TenuredHeap);
    39 }
    41 bool
    42 jit::ParallelWriteGuard(ForkJoinContext *cx, JSObject *object)
    43 {
    44     // Implements the most general form of the write guard, which is
    45     // suitable for writes to any object O. There are two cases to
    46     // consider and test for:
    47     //
    48     // 1. Writes to thread-local memory are safe. Thread-local memory
    49     //    is defined as memory allocated by the current thread.
    50     //    The definition of the PJS API guarantees that such memory
    51     //    cannot have escaped to other parallel threads.
    52     //
    53     // 2. Writes into the output buffer are safe. Some PJS operations
    54     //    supply an out pointer into the final target buffer. The design
    55     //    of the API ensures that this out pointer is always pointing
    56     //    at a fresh region of the buffer that is not accessible to
    57     //    other threads. Thus, even though this output buffer has not
    58     //    been created by the current thread, it is writable.
    59     //
    60     // There are some subtleties to consider:
    61     //
    62     // A. Typed objects and typed arrays are just views onto a base buffer.
    63     //    For the purposes of guarding parallel writes, it is not important
    64     //    whether the *view* is thread-local -- what matters is whether
    65     //    the *underlying buffer* is thread-local.
    66     //
    67     // B. With regard to the output buffer, we have to be careful
    68     //    because of the potential for sequential iterations to be
    69     //    intermingled with parallel ones. During a sequential
    70     //    iteration, the out pointer could escape into global
    71     //    variables and so forth, and thus be used during later
    72     //    parallel operations. However, those out pointers must be
    73     //    pointing to distinct regions of the final output buffer than
    74     //    the ones that are currently being written, so there is no
    75     //    harm done in letting them be read (but not written).
    76     //
    77     //    In order to be able to distinguish escaped out pointers from
    78     //    prior iterations and the proper out pointers from the
    79     //    current iteration, we always track a *target memory region*
    80     //    (which is a span of bytes within the output buffer) and not
    81     //    just the output buffer itself.
    83     JS_ASSERT(ForkJoinContext::current() == cx);
    85     if (object->is<TypedObject>()) {
    86         TypedObject &typedObj = object->as<TypedObject>();
    88         // Note: check target region based on `typedObj`, not the owner.
    89         // This is because `typedObj` may point to some subregion of the
    90         // owner and we only care if that *subregion* is within the
    91         // target region, not the entire owner.
    92         if (IsInTargetRegion(cx, &typedObj))
    93             return true;
    95         // Also check whether owner is thread-local.
    96         ArrayBufferObject &owner = typedObj.owner();
    97         return cx->isThreadLocal(&owner);
    98     }
   100     // For other kinds of writable objects, must be thread-local.
   101     return cx->isThreadLocal(object);
   102 }
   104 // Check that |object| (which must be a typed typedObj) maps
   105 // to memory in the target region.
   106 //
   107 // For efficiency, we assume that all handles which the user has
   108 // access to are either entirely within the target region or entirely
   109 // without, but not straddling the target region nor encompassing
   110 // it. This invariant is maintained by the PJS APIs, where the target
   111 // region and handles are always elements of the same output array.
   112 bool
   113 jit::IsInTargetRegion(ForkJoinContext *cx, TypedObject *typedObj)
   114 {
   115     JS_ASSERT(typedObj->is<TypedObject>()); // in case JIT supplies something bogus
   116     uint8_t *typedMem = typedObj->typedMem();
   117     return (typedMem >= cx->targetRegionStart &&
   118             typedMem <  cx->targetRegionEnd);
   119 }
   121 #ifdef DEBUG
   122 static void
   123 printTrace(const char *prefix, struct IonLIRTraceData *cached)
   124 {
   125     fprintf(stderr, "%s / Block %3u / LIR %3u / Mode %u / LIR %s\n",
   126             prefix,
   127             cached->blockIndex, cached->lirIndex, cached->execModeInt, cached->lirOpName);
   128 }
   130 static struct IonLIRTraceData seqTraceData;
   131 #endif
   133 void
   134 jit::TraceLIR(IonLIRTraceData *current)
   135 {
   136 #ifdef DEBUG
   137     static enum { NotSet, All, Bailouts } traceMode;
   139     // If you set IONFLAGS=trace, this function will be invoked before every LIR.
   140     //
   141     // You can either modify it to do whatever you like, or use gdb scripting.
   142     // For example:
   143     //
   144     // break TraceLIR
   145     // commands
   146     // continue
   147     // exit
   149     if (traceMode == NotSet) {
   150         // Racy, but that's ok.
   151         const char *env = getenv("IONFLAGS");
   152         if (strstr(env, "trace-all"))
   153             traceMode = All;
   154         else
   155             traceMode = Bailouts;
   156     }
   158     IonLIRTraceData *cached;
   159     if (current->execModeInt == 0)
   160         cached = &seqTraceData;
   161     else
   162         cached = &ForkJoinContext::current()->traceData;
   164     if (current->blockIndex == 0xDEADBEEF) {
   165         if (current->execModeInt == 0)
   166             printTrace("BAILOUT", cached);
   167         else
   168             SpewBailoutIR(cached);
   169     }
   171     memcpy(cached, current, sizeof(IonLIRTraceData));
   173     if (traceMode == All)
   174         printTrace("Exec", cached);
   175 #endif
   176 }
   178 bool
   179 jit::CheckOverRecursedPar(ForkJoinContext *cx)
   180 {
   181     JS_ASSERT(ForkJoinContext::current() == cx);
   182     int stackDummy_;
   184     // When an interrupt is requested, the main thread stack limit is
   185     // overwritten with a sentinel value that brings us here.
   186     // Therefore, we must check whether this is really a stack overrun
   187     // and, if not, check whether an interrupt was requested.
   188     //
   189     // When not on the main thread, we don't overwrite the stack
   190     // limit, but we do still call into this routine if the interrupt
   191     // flag is set, so we still need to double check.
   193 #ifdef JS_ARM_SIMULATOR
   194     if (Simulator::Current()->overRecursed()) {
   195         cx->bailoutRecord->setCause(ParallelBailoutOverRecursed);
   196         return false;
   197     }
   198 #endif
   200     uintptr_t realStackLimit;
   201     if (cx->isMainThread())
   202         realStackLimit = GetNativeStackLimit(cx);
   203     else
   204         realStackLimit = cx->perThreadData->jitStackLimit;
   206     if (!JS_CHECK_STACK_SIZE(realStackLimit, &stackDummy_)) {
   207         cx->bailoutRecord->setCause(ParallelBailoutOverRecursed);
   208         return false;
   209     }
   211     return InterruptCheckPar(cx);
   212 }
   214 bool
   215 jit::InterruptCheckPar(ForkJoinContext *cx)
   216 {
   217     JS_ASSERT(ForkJoinContext::current() == cx);
   218     bool result = cx->check();
   219     if (!result) {
   220         // Do not set the cause here.  Either it was set by this
   221         // thread already by some code that then triggered an abort,
   222         // or else we are just picking up an abort from some other
   223         // thread.  Either way we have nothing useful to contribute so
   224         // we might as well leave our bailout case unset.
   225         return false;
   226     }
   227     return true;
   228 }
   230 JSObject *
   231 jit::ExtendArrayPar(ForkJoinContext *cx, JSObject *array, uint32_t length)
   232 {
   233     JSObject::EnsureDenseResult res =
   234         array->ensureDenseElementsPreservePackedFlag(cx, 0, length);
   235     if (res != JSObject::ED_OK)
   236         return nullptr;
   237     return array;
   238 }
   240 bool
   241 jit::SetPropertyPar(ForkJoinContext *cx, HandleObject obj, HandlePropertyName name,
   242                     HandleValue value, bool strict, jsbytecode *pc)
   243 {
   244     JS_ASSERT(cx->isThreadLocal(obj));
   246     if (*pc == JSOP_SETALIASEDVAR) {
   247         // See comment in jit::SetProperty.
   248         Shape *shape = obj->nativeLookupPure(name);
   249         JS_ASSERT(shape && shape->hasSlot());
   250         return obj->nativeSetSlotIfHasType(shape, value);
   251     }
   253     // Fail early on hooks.
   254     if (obj->getOps()->setProperty)
   255         return TP_RETRY_SEQUENTIALLY;
   257     RootedValue v(cx, value);
   258     RootedId id(cx, NameToId(name));
   259     return baseops::SetPropertyHelper<ParallelExecution>(cx, obj, obj, id, baseops::Qualified, &v,
   260                                                          strict);
   261 }
   263 bool
   264 jit::SetElementPar(ForkJoinContext *cx, HandleObject obj, HandleValue index, HandleValue value,
   265                    bool strict)
   266 {
   267     RootedId id(cx);
   268     if (!ValueToIdPure(index, id.address()))
   269         return false;
   271     // SetObjectElementOperation, the sequential version, has several checks
   272     // for certain deoptimizing behaviors, such as marking having written to
   273     // holes and non-indexed element accesses. We don't do that here, as we
   274     // can't modify any TI state anyways. If we need to add a new type, we
   275     // would bail out.
   276     RootedValue v(cx, value);
   277     return baseops::SetPropertyHelper<ParallelExecution>(cx, obj, obj, id, baseops::Qualified, &v,
   278                                                          strict);
   279 }
   281 bool
   282 jit::SetDenseElementPar(ForkJoinContext *cx, HandleObject obj, int32_t index, HandleValue value,
   283                         bool strict)
   284 {
   285     RootedValue indexVal(cx, Int32Value(index));
   286     return SetElementPar(cx, obj, indexVal, value, strict);
   287 }
   289 JSString *
   290 jit::ConcatStringsPar(ForkJoinContext *cx, HandleString left, HandleString right)
   291 {
   292     return ConcatStrings<NoGC>(cx, left, right);
   293 }
   295 JSFlatString *
   296 jit::IntToStringPar(ForkJoinContext *cx, int i)
   297 {
   298     return Int32ToString<NoGC>(cx, i);
   299 }
   301 JSString *
   302 jit::DoubleToStringPar(ForkJoinContext *cx, double d)
   303 {
   304     return NumberToString<NoGC>(cx, d);
   305 }
   307 JSString *
   308 jit::PrimitiveToStringPar(ForkJoinContext *cx, HandleValue input)
   309 {
   310     // All other cases are handled in assembly.
   311     JS_ASSERT(input.isDouble() || input.isInt32());
   313     if (input.isInt32())
   314         return Int32ToString<NoGC>(cx, input.toInt32());
   316     return NumberToString<NoGC>(cx, input.toDouble());
   317 }
   319 bool
   320 jit::StringToNumberPar(ForkJoinContext *cx, JSString *str, double *out)
   321 {
   322     return StringToNumber(cx, str, out);
   323 }
   325 #define PAR_RELATIONAL_OP(OP, EXPECTED)                                         \
   326 do {                                                                            \
   327     /* Optimize for two int-tagged operands (typical loop control). */          \
   328     if (lhs.isInt32() && rhs.isInt32()) {                                       \
   329         *res = (lhs.toInt32() OP rhs.toInt32()) == EXPECTED;                    \
   330     } else if (lhs.isNumber() && rhs.isNumber()) {                              \
   331         double l = lhs.toNumber(), r = rhs.toNumber();                          \
   332         *res = (l OP r) == EXPECTED;                                            \
   333     } else if (lhs.isBoolean() && rhs.isBoolean()) {                            \
   334         bool l = lhs.toBoolean();                                               \
   335         bool r = rhs.toBoolean();                                               \
   336         *res = (l OP r) == EXPECTED;                                            \
   337     } else if (lhs.isBoolean() && rhs.isNumber()) {                             \
   338         bool l = lhs.toBoolean();                                               \
   339         double r = rhs.toNumber();                                              \
   340         *res = (l OP r) == EXPECTED;                                            \
   341     } else if (lhs.isNumber() && rhs.isBoolean()) {                             \
   342         double l = lhs.toNumber();                                              \
   343         bool r = rhs.toBoolean();                                               \
   344         *res = (l OP r) == EXPECTED;                                            \
   345     } else {                                                                    \
   346         int32_t vsZero;                                                         \
   347         if (!CompareMaybeStringsPar(cx, lhs, rhs, &vsZero))                  \
   348             return false;                                                       \
   349         *res = (vsZero OP 0) == EXPECTED;                                       \
   350     }                                                                           \
   351     return true;                                                                \
   352 } while(0)
   354 static bool
   355 CompareStringsPar(ForkJoinContext *cx, JSString *left, JSString *right, int32_t *res)
   356 {
   357     ScopedThreadSafeStringInspector leftInspector(left);
   358     ScopedThreadSafeStringInspector rightInspector(right);
   359     if (!leftInspector.ensureChars(cx) || !rightInspector.ensureChars(cx))
   360         return false;
   362     *res = CompareChars(leftInspector.chars(), left->length(),
   363                         rightInspector.chars(), right->length());
   364     return true;
   365 }
   367 static bool
   368 CompareMaybeStringsPar(ForkJoinContext *cx, HandleValue v1, HandleValue v2, int32_t *res)
   369 {
   370     if (!v1.isString())
   371         return false;
   372     if (!v2.isString())
   373         return false;
   374     return CompareStringsPar(cx, v1.toString(), v2.toString(), res);
   375 }
   377 template<bool Equal>
   378 bool
   379 LooselyEqualImplPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
   380 {
   381     PAR_RELATIONAL_OP(==, Equal);
   382 }
   384 bool
   385 js::jit::LooselyEqualPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
   386 {
   387     return LooselyEqualImplPar<true>(cx, lhs, rhs, res);
   388 }
   390 bool
   391 js::jit::LooselyUnequalPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
   392 {
   393     return LooselyEqualImplPar<false>(cx, lhs, rhs, res);
   394 }
   396 template<bool Equal>
   397 bool
   398 StrictlyEqualImplPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
   399 {
   400     if (lhs.isNumber()) {
   401         if (rhs.isNumber()) {
   402             *res = (lhs.toNumber() == rhs.toNumber()) == Equal;
   403             return true;
   404         }
   405     } else if (lhs.isBoolean()) {
   406         if (rhs.isBoolean()) {
   407             *res = (lhs.toBoolean() == rhs.toBoolean()) == Equal;
   408             return true;
   409         }
   410     } else if (lhs.isNull()) {
   411         if (rhs.isNull()) {
   412             *res = Equal;
   413             return true;
   414         }
   415     } else if (lhs.isUndefined()) {
   416         if (rhs.isUndefined()) {
   417             *res = Equal;
   418             return true;
   419         }
   420     } else if (lhs.isObject()) {
   421         if (rhs.isObject()) {
   422             *res = (lhs.toObjectOrNull() == rhs.toObjectOrNull()) == Equal;
   423             return true;
   424         }
   425     } else if (lhs.isString()) {
   426         if (rhs.isString())
   427             return LooselyEqualImplPar<Equal>(cx, lhs, rhs, res);
   428     }
   430     *res = false;
   431     return true;
   432 }
   434 bool
   435 js::jit::StrictlyEqualPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
   436 {
   437     return StrictlyEqualImplPar<true>(cx, lhs, rhs, res);
   438 }
   440 bool
   441 js::jit::StrictlyUnequalPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
   442 {
   443     return StrictlyEqualImplPar<false>(cx, lhs, rhs, res);
   444 }
   446 bool
   447 js::jit::LessThanPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
   448 {
   449     PAR_RELATIONAL_OP(<, true);
   450 }
   452 bool
   453 js::jit::LessThanOrEqualPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
   454 {
   455     PAR_RELATIONAL_OP(<=, true);
   456 }
   458 bool
   459 js::jit::GreaterThanPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
   460 {
   461     PAR_RELATIONAL_OP(>, true);
   462 }
   464 bool
   465 js::jit::GreaterThanOrEqualPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res)
   466 {
   467     PAR_RELATIONAL_OP(>=, true);
   468 }
   470 template<bool Equal>
   471 bool
   472 StringsEqualImplPar(ForkJoinContext *cx, HandleString lhs, HandleString rhs, bool *res)
   473 {
   474     int32_t vsZero;
   475     bool ret = CompareStringsPar(cx, lhs, rhs, &vsZero);
   476     if (ret != true)
   477         return ret;
   478     *res = (vsZero == 0) == Equal;
   479     return true;
   480 }
   482 bool
   483 js::jit::StringsEqualPar(ForkJoinContext *cx, HandleString v1, HandleString v2, bool *res)
   484 {
   485     return StringsEqualImplPar<true>(cx, v1, v2, res);
   486 }
   488 bool
   489 js::jit::StringsUnequalPar(ForkJoinContext *cx, HandleString v1, HandleString v2, bool *res)
   490 {
   491     return StringsEqualImplPar<false>(cx, v1, v2, res);
   492 }
   494 bool
   495 jit::BitNotPar(ForkJoinContext *cx, HandleValue in, int32_t *out)
   496 {
   497     if (in.isObject())
   498         return false;
   499     int i;
   500     if (!NonObjectToInt32(cx, in, &i))
   501         return false;
   502     *out = ~i;
   503     return true;
   504 }
   506 #define BIT_OP(OP)                                                      \
   507     JS_BEGIN_MACRO                                                      \
   508     int32_t left, right;                                                \
   509     if (lhs.isObject() || rhs.isObject())                               \
   510         return false;                                                   \
   511     if (!NonObjectToInt32(cx, lhs, &left) ||                            \
   512         !NonObjectToInt32(cx, rhs, &right))                             \
   513     {                                                                   \
   514         return false;                                                   \
   515     }                                                                   \
   516     *out = (OP);                                                        \
   517     return true;                                                        \
   518     JS_END_MACRO
   520 bool
   521 jit::BitXorPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out)
   522 {
   523     BIT_OP(left ^ right);
   524 }
   526 bool
   527 jit::BitOrPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out)
   528 {
   529     BIT_OP(left | right);
   530 }
   532 bool
   533 jit::BitAndPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out)
   534 {
   535     BIT_OP(left & right);
   536 }
   538 bool
   539 jit::BitLshPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out)
   540 {
   541     BIT_OP(uint32_t(left) << (right & 31));
   542 }
   544 bool
   545 jit::BitRshPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out)
   546 {
   547     BIT_OP(left >> (right & 31));
   548 }
   550 #undef BIT_OP
   552 bool
   553 jit::UrshValuesPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs,
   554                    MutableHandleValue out)
   555 {
   556     uint32_t left;
   557     int32_t right;
   558     if (lhs.isObject() || rhs.isObject())
   559         return false;
   560     if (!NonObjectToUint32(cx, lhs, &left) || !NonObjectToInt32(cx, rhs, &right))
   561         return false;
   562     left >>= right & 31;
   563     out.setNumber(uint32_t(left));
   564     return true;
   565 }
   567 void
   568 jit::AbortPar(ParallelBailoutCause cause, JSScript *outermostScript, JSScript *currentScript,
   569               jsbytecode *bytecode)
   570 {
   571     // Spew before asserts to help with diagnosing failures.
   572     Spew(SpewBailouts,
   573          "Parallel abort with cause %d in %p:%s:%d "
   574          "(%p:%s:%d at line %d)",
   575          cause,
   576          outermostScript, outermostScript->filename(), outermostScript->lineno(),
   577          currentScript, currentScript->filename(), currentScript->lineno(),
   578          (currentScript ? PCToLineNumber(currentScript, bytecode) : 0));
   580     JS_ASSERT(InParallelSection());
   581     JS_ASSERT(outermostScript != nullptr);
   582     JS_ASSERT(currentScript != nullptr);
   583     JS_ASSERT(outermostScript->hasParallelIonScript());
   585     ForkJoinContext *cx = ForkJoinContext::current();
   587     JS_ASSERT(cx->bailoutRecord->depth == 0);
   588     cx->bailoutRecord->setCause(cause, outermostScript, currentScript, bytecode);
   589 }
   591 void
   592 jit::PropagateAbortPar(JSScript *outermostScript, JSScript *currentScript)
   593 {
   594     Spew(SpewBailouts,
   595          "Propagate parallel abort via %p:%s:%d (%p:%s:%d)",
   596          outermostScript, outermostScript->filename(), outermostScript->lineno(),
   597          currentScript, currentScript->filename(), currentScript->lineno());
   599     JS_ASSERT(InParallelSection());
   600     JS_ASSERT(outermostScript->hasParallelIonScript());
   602     outermostScript->parallelIonScript()->setHasUncompiledCallTarget();
   604     ForkJoinContext *cx = ForkJoinContext::current();
   605     if (currentScript)
   606         cx->bailoutRecord->addTrace(currentScript, nullptr);
   607 }
   609 void
   610 jit::CallToUncompiledScriptPar(JSObject *obj)
   611 {
   612     JS_ASSERT(InParallelSection());
   614 #ifdef DEBUG
   615     static const int max_bound_function_unrolling = 5;
   617     if (!obj->is<JSFunction>()) {
   618         Spew(SpewBailouts, "Call to non-function");
   619         return;
   620     }
   622     JSFunction *func = &obj->as<JSFunction>();
   623     if (func->hasScript()) {
   624         JSScript *script = func->nonLazyScript();
   625         Spew(SpewBailouts, "Call to uncompiled script: %p:%s:%d",
   626              script, script->filename(), script->lineno());
   627     } else if (func->isInterpretedLazy()) {
   628         Spew(SpewBailouts, "Call to uncompiled lazy script");
   629     } else if (func->isBoundFunction()) {
   630         int depth = 0;
   631         JSFunction *target = &func->getBoundFunctionTarget()->as<JSFunction>();
   632         while (depth < max_bound_function_unrolling) {
   633             if (target->hasScript())
   634                 break;
   635             if (target->isBoundFunction())
   636                 target = &target->getBoundFunctionTarget()->as<JSFunction>();
   637             depth--;
   638         }
   639         if (target->hasScript()) {
   640             JSScript *script = target->nonLazyScript();
   641             Spew(SpewBailouts, "Call to bound function leading (depth: %d) to script: %p:%s:%d",
   642                  depth, script, script->filename(), script->lineno());
   643         } else {
   644             Spew(SpewBailouts, "Call to bound function (excessive depth: %d)", depth);
   645         }
   646     } else {
   647         JS_ASSERT(func->isNative());
   648         Spew(SpewBailouts, "Call to native function");
   649     }
   650 #endif
   651 }
   653 JSObject *
   654 jit::InitRestParameterPar(ForkJoinContext *cx, uint32_t length, Value *rest,
   655                           HandleObject templateObj, HandleObject res)
   656 {
   657     // In parallel execution, we should always have succeeded in allocation
   658     // before this point. We can do the allocation here like in the sequential
   659     // path, but duplicating the initGCThing logic is too tedious.
   660     JS_ASSERT(res);
   661     JS_ASSERT(res->is<ArrayObject>());
   662     JS_ASSERT(!res->getDenseInitializedLength());
   663     JS_ASSERT(res->type() == templateObj->type());
   665     if (length > 0) {
   666         JSObject::EnsureDenseResult edr =
   667             res->ensureDenseElementsPreservePackedFlag(cx, 0, length);
   668         if (edr != JSObject::ED_OK)
   669             return nullptr;
   670         res->initDenseElementsUnbarriered(0, rest, length);
   671         res->as<ArrayObject>().setLengthInt32(length);
   672     }
   674     return res;
   675 }

mercurial