|
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/. */ |
|
6 |
|
7 #include "jit/ParallelFunctions.h" |
|
8 |
|
9 #include "builtin/TypedObject.h" |
|
10 #include "jit/arm/Simulator-arm.h" |
|
11 #include "vm/ArrayObject.h" |
|
12 |
|
13 #include "jsgcinlines.h" |
|
14 #include "jsobjinlines.h" |
|
15 |
|
16 using namespace js; |
|
17 using namespace jit; |
|
18 |
|
19 using parallel::Spew; |
|
20 using parallel::SpewOps; |
|
21 using parallel::SpewBailouts; |
|
22 using parallel::SpewBailoutIR; |
|
23 |
|
24 // Load the current thread context. |
|
25 ForkJoinContext * |
|
26 jit::ForkJoinContextPar() |
|
27 { |
|
28 return ForkJoinContext::current(); |
|
29 } |
|
30 |
|
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 } |
|
40 |
|
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. |
|
82 |
|
83 JS_ASSERT(ForkJoinContext::current() == cx); |
|
84 |
|
85 if (object->is<TypedObject>()) { |
|
86 TypedObject &typedObj = object->as<TypedObject>(); |
|
87 |
|
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; |
|
94 |
|
95 // Also check whether owner is thread-local. |
|
96 ArrayBufferObject &owner = typedObj.owner(); |
|
97 return cx->isThreadLocal(&owner); |
|
98 } |
|
99 |
|
100 // For other kinds of writable objects, must be thread-local. |
|
101 return cx->isThreadLocal(object); |
|
102 } |
|
103 |
|
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 } |
|
120 |
|
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 } |
|
129 |
|
130 static struct IonLIRTraceData seqTraceData; |
|
131 #endif |
|
132 |
|
133 void |
|
134 jit::TraceLIR(IonLIRTraceData *current) |
|
135 { |
|
136 #ifdef DEBUG |
|
137 static enum { NotSet, All, Bailouts } traceMode; |
|
138 |
|
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 |
|
148 |
|
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 } |
|
157 |
|
158 IonLIRTraceData *cached; |
|
159 if (current->execModeInt == 0) |
|
160 cached = &seqTraceData; |
|
161 else |
|
162 cached = &ForkJoinContext::current()->traceData; |
|
163 |
|
164 if (current->blockIndex == 0xDEADBEEF) { |
|
165 if (current->execModeInt == 0) |
|
166 printTrace("BAILOUT", cached); |
|
167 else |
|
168 SpewBailoutIR(cached); |
|
169 } |
|
170 |
|
171 memcpy(cached, current, sizeof(IonLIRTraceData)); |
|
172 |
|
173 if (traceMode == All) |
|
174 printTrace("Exec", cached); |
|
175 #endif |
|
176 } |
|
177 |
|
178 bool |
|
179 jit::CheckOverRecursedPar(ForkJoinContext *cx) |
|
180 { |
|
181 JS_ASSERT(ForkJoinContext::current() == cx); |
|
182 int stackDummy_; |
|
183 |
|
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. |
|
192 |
|
193 #ifdef JS_ARM_SIMULATOR |
|
194 if (Simulator::Current()->overRecursed()) { |
|
195 cx->bailoutRecord->setCause(ParallelBailoutOverRecursed); |
|
196 return false; |
|
197 } |
|
198 #endif |
|
199 |
|
200 uintptr_t realStackLimit; |
|
201 if (cx->isMainThread()) |
|
202 realStackLimit = GetNativeStackLimit(cx); |
|
203 else |
|
204 realStackLimit = cx->perThreadData->jitStackLimit; |
|
205 |
|
206 if (!JS_CHECK_STACK_SIZE(realStackLimit, &stackDummy_)) { |
|
207 cx->bailoutRecord->setCause(ParallelBailoutOverRecursed); |
|
208 return false; |
|
209 } |
|
210 |
|
211 return InterruptCheckPar(cx); |
|
212 } |
|
213 |
|
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 } |
|
229 |
|
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 } |
|
239 |
|
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)); |
|
245 |
|
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 } |
|
252 |
|
253 // Fail early on hooks. |
|
254 if (obj->getOps()->setProperty) |
|
255 return TP_RETRY_SEQUENTIALLY; |
|
256 |
|
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 } |
|
262 |
|
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; |
|
270 |
|
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 } |
|
280 |
|
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 } |
|
288 |
|
289 JSString * |
|
290 jit::ConcatStringsPar(ForkJoinContext *cx, HandleString left, HandleString right) |
|
291 { |
|
292 return ConcatStrings<NoGC>(cx, left, right); |
|
293 } |
|
294 |
|
295 JSFlatString * |
|
296 jit::IntToStringPar(ForkJoinContext *cx, int i) |
|
297 { |
|
298 return Int32ToString<NoGC>(cx, i); |
|
299 } |
|
300 |
|
301 JSString * |
|
302 jit::DoubleToStringPar(ForkJoinContext *cx, double d) |
|
303 { |
|
304 return NumberToString<NoGC>(cx, d); |
|
305 } |
|
306 |
|
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()); |
|
312 |
|
313 if (input.isInt32()) |
|
314 return Int32ToString<NoGC>(cx, input.toInt32()); |
|
315 |
|
316 return NumberToString<NoGC>(cx, input.toDouble()); |
|
317 } |
|
318 |
|
319 bool |
|
320 jit::StringToNumberPar(ForkJoinContext *cx, JSString *str, double *out) |
|
321 { |
|
322 return StringToNumber(cx, str, out); |
|
323 } |
|
324 |
|
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) |
|
353 |
|
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; |
|
361 |
|
362 *res = CompareChars(leftInspector.chars(), left->length(), |
|
363 rightInspector.chars(), right->length()); |
|
364 return true; |
|
365 } |
|
366 |
|
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 } |
|
376 |
|
377 template<bool Equal> |
|
378 bool |
|
379 LooselyEqualImplPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) |
|
380 { |
|
381 PAR_RELATIONAL_OP(==, Equal); |
|
382 } |
|
383 |
|
384 bool |
|
385 js::jit::LooselyEqualPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) |
|
386 { |
|
387 return LooselyEqualImplPar<true>(cx, lhs, rhs, res); |
|
388 } |
|
389 |
|
390 bool |
|
391 js::jit::LooselyUnequalPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) |
|
392 { |
|
393 return LooselyEqualImplPar<false>(cx, lhs, rhs, res); |
|
394 } |
|
395 |
|
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 } |
|
429 |
|
430 *res = false; |
|
431 return true; |
|
432 } |
|
433 |
|
434 bool |
|
435 js::jit::StrictlyEqualPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) |
|
436 { |
|
437 return StrictlyEqualImplPar<true>(cx, lhs, rhs, res); |
|
438 } |
|
439 |
|
440 bool |
|
441 js::jit::StrictlyUnequalPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) |
|
442 { |
|
443 return StrictlyEqualImplPar<false>(cx, lhs, rhs, res); |
|
444 } |
|
445 |
|
446 bool |
|
447 js::jit::LessThanPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) |
|
448 { |
|
449 PAR_RELATIONAL_OP(<, true); |
|
450 } |
|
451 |
|
452 bool |
|
453 js::jit::LessThanOrEqualPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) |
|
454 { |
|
455 PAR_RELATIONAL_OP(<=, true); |
|
456 } |
|
457 |
|
458 bool |
|
459 js::jit::GreaterThanPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) |
|
460 { |
|
461 PAR_RELATIONAL_OP(>, true); |
|
462 } |
|
463 |
|
464 bool |
|
465 js::jit::GreaterThanOrEqualPar(ForkJoinContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) |
|
466 { |
|
467 PAR_RELATIONAL_OP(>=, true); |
|
468 } |
|
469 |
|
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 } |
|
481 |
|
482 bool |
|
483 js::jit::StringsEqualPar(ForkJoinContext *cx, HandleString v1, HandleString v2, bool *res) |
|
484 { |
|
485 return StringsEqualImplPar<true>(cx, v1, v2, res); |
|
486 } |
|
487 |
|
488 bool |
|
489 js::jit::StringsUnequalPar(ForkJoinContext *cx, HandleString v1, HandleString v2, bool *res) |
|
490 { |
|
491 return StringsEqualImplPar<false>(cx, v1, v2, res); |
|
492 } |
|
493 |
|
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 } |
|
505 |
|
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 |
|
519 |
|
520 bool |
|
521 jit::BitXorPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out) |
|
522 { |
|
523 BIT_OP(left ^ right); |
|
524 } |
|
525 |
|
526 bool |
|
527 jit::BitOrPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out) |
|
528 { |
|
529 BIT_OP(left | right); |
|
530 } |
|
531 |
|
532 bool |
|
533 jit::BitAndPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out) |
|
534 { |
|
535 BIT_OP(left & right); |
|
536 } |
|
537 |
|
538 bool |
|
539 jit::BitLshPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out) |
|
540 { |
|
541 BIT_OP(uint32_t(left) << (right & 31)); |
|
542 } |
|
543 |
|
544 bool |
|
545 jit::BitRshPar(ForkJoinContext *cx, HandleValue lhs, HandleValue rhs, int32_t *out) |
|
546 { |
|
547 BIT_OP(left >> (right & 31)); |
|
548 } |
|
549 |
|
550 #undef BIT_OP |
|
551 |
|
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 } |
|
566 |
|
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)); |
|
579 |
|
580 JS_ASSERT(InParallelSection()); |
|
581 JS_ASSERT(outermostScript != nullptr); |
|
582 JS_ASSERT(currentScript != nullptr); |
|
583 JS_ASSERT(outermostScript->hasParallelIonScript()); |
|
584 |
|
585 ForkJoinContext *cx = ForkJoinContext::current(); |
|
586 |
|
587 JS_ASSERT(cx->bailoutRecord->depth == 0); |
|
588 cx->bailoutRecord->setCause(cause, outermostScript, currentScript, bytecode); |
|
589 } |
|
590 |
|
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()); |
|
598 |
|
599 JS_ASSERT(InParallelSection()); |
|
600 JS_ASSERT(outermostScript->hasParallelIonScript()); |
|
601 |
|
602 outermostScript->parallelIonScript()->setHasUncompiledCallTarget(); |
|
603 |
|
604 ForkJoinContext *cx = ForkJoinContext::current(); |
|
605 if (currentScript) |
|
606 cx->bailoutRecord->addTrace(currentScript, nullptr); |
|
607 } |
|
608 |
|
609 void |
|
610 jit::CallToUncompiledScriptPar(JSObject *obj) |
|
611 { |
|
612 JS_ASSERT(InParallelSection()); |
|
613 |
|
614 #ifdef DEBUG |
|
615 static const int max_bound_function_unrolling = 5; |
|
616 |
|
617 if (!obj->is<JSFunction>()) { |
|
618 Spew(SpewBailouts, "Call to non-function"); |
|
619 return; |
|
620 } |
|
621 |
|
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 } |
|
652 |
|
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()); |
|
664 |
|
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 } |
|
673 |
|
674 return res; |
|
675 } |