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

mercurial