Sat, 03 Jan 2015 20:18:00 +0100
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.
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/CodeGenerator.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/Attributes.h"
11 #include "mozilla/DebugOnly.h"
13 #include "jslibmath.h"
14 #include "jsmath.h"
15 #include "jsnum.h"
16 #include "jsprf.h"
18 #include "builtin/Eval.h"
19 #include "builtin/TypedObject.h"
20 #ifdef JSGC_GENERATIONAL
21 # include "gc/Nursery.h"
22 #endif
23 #include "jit/IonCaches.h"
24 #include "jit/IonLinker.h"
25 #include "jit/IonOptimizationLevels.h"
26 #include "jit/IonSpewer.h"
27 #include "jit/MIRGenerator.h"
28 #include "jit/MoveEmitter.h"
29 #include "jit/ParallelFunctions.h"
30 #include "jit/ParallelSafetyAnalysis.h"
31 #include "jit/RangeAnalysis.h"
32 #include "vm/ForkJoin.h"
33 #include "vm/TraceLogging.h"
35 #include "jsboolinlines.h"
37 #include "jit/ExecutionMode-inl.h"
38 #include "jit/shared/CodeGenerator-shared-inl.h"
39 #include "vm/Interpreter-inl.h"
41 using namespace js;
42 using namespace js::jit;
44 using mozilla::DebugOnly;
45 using mozilla::FloatingPoint;
46 using mozilla::Maybe;
47 using mozilla::NegativeInfinity;
48 using mozilla::PositiveInfinity;
49 using JS::GenericNaN;
51 namespace js {
52 namespace jit {
54 // This out-of-line cache is used to do a double dispatch including it-self and
55 // the wrapped IonCache.
56 class OutOfLineUpdateCache :
57 public OutOfLineCodeBase<CodeGenerator>,
58 public IonCacheVisitor
59 {
60 private:
61 LInstruction *lir_;
62 size_t cacheIndex_;
63 AddCacheState state_;
65 public:
66 OutOfLineUpdateCache(LInstruction *lir, size_t cacheIndex)
67 : lir_(lir),
68 cacheIndex_(cacheIndex)
69 { }
71 void bind(MacroAssembler *masm) {
72 // The binding of the initial jump is done in
73 // CodeGenerator::visitOutOfLineCache.
74 }
76 size_t getCacheIndex() const {
77 return cacheIndex_;
78 }
79 LInstruction *lir() const {
80 return lir_;
81 }
82 AddCacheState &state() {
83 return state_;
84 }
86 bool accept(CodeGenerator *codegen) {
87 return codegen->visitOutOfLineCache(this);
88 }
90 // ICs' visit functions delegating the work to the CodeGen visit funtions.
91 #define VISIT_CACHE_FUNCTION(op) \
92 bool visit##op##IC(CodeGenerator *codegen) { \
93 CodeGenerator::DataPtr<op##IC> ic(codegen, getCacheIndex()); \
94 return codegen->visit##op##IC(this, ic); \
95 }
97 IONCACHE_KIND_LIST(VISIT_CACHE_FUNCTION)
98 #undef VISIT_CACHE_FUNCTION
99 };
101 // This function is declared here because it needs to instantiate an
102 // OutOfLineUpdateCache, but we want to keep it visible inside the
103 // CodeGeneratorShared such as we can specialize inline caches in function of
104 // the architecture.
105 bool
106 CodeGeneratorShared::addCache(LInstruction *lir, size_t cacheIndex)
107 {
108 if (cacheIndex == SIZE_MAX)
109 return false;
111 DataPtr<IonCache> cache(this, cacheIndex);
112 MInstruction *mir = lir->mirRaw()->toInstruction();
113 if (mir->resumePoint())
114 cache->setScriptedLocation(mir->block()->info().script(),
115 mir->resumePoint()->pc());
116 else
117 cache->setIdempotent();
119 OutOfLineUpdateCache *ool = new(alloc()) OutOfLineUpdateCache(lir, cacheIndex);
120 if (!addOutOfLineCode(ool))
121 return false;
123 // OOL-specific state depends on the type of cache.
124 cache->initializeAddCacheState(lir, &ool->state());
126 cache->emitInitialJump(masm, ool->state());
127 masm.bind(ool->rejoin());
129 return true;
130 }
132 bool
133 CodeGenerator::visitOutOfLineCache(OutOfLineUpdateCache *ool)
134 {
135 DataPtr<IonCache> cache(this, ool->getCacheIndex());
137 // Register the location of the OOL path in the IC.
138 cache->setFallbackLabel(masm.labelForPatch());
139 cache->bindInitialJump(masm, ool->state());
141 // Dispatch to ICs' accept functions.
142 return cache->accept(this, ool);
143 }
145 StringObject *
146 MNewStringObject::templateObj() const {
147 return &templateObj_->as<StringObject>();
148 }
150 CodeGenerator::CodeGenerator(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm)
151 : CodeGeneratorSpecific(gen, graph, masm)
152 , ionScriptLabels_(gen->alloc())
153 {
154 }
156 CodeGenerator::~CodeGenerator()
157 {
158 JS_ASSERT_IF(!gen->compilingAsmJS(), masm.numAsmJSAbsoluteLinks() == 0);
159 }
161 typedef bool (*StringToNumberFn)(ThreadSafeContext *, JSString *, double *);
162 typedef bool (*StringToNumberParFn)(ForkJoinContext *, JSString *, double *);
163 static const VMFunctionsModal StringToNumberInfo = VMFunctionsModal(
164 FunctionInfo<StringToNumberFn>(StringToNumber),
165 FunctionInfo<StringToNumberParFn>(StringToNumberPar));
167 bool
168 CodeGenerator::visitValueToInt32(LValueToInt32 *lir)
169 {
170 ValueOperand operand = ToValue(lir, LValueToInt32::Input);
171 Register output = ToRegister(lir->output());
172 FloatRegister temp = ToFloatRegister(lir->tempFloat());
174 MDefinition *input;
175 if (lir->mode() == LValueToInt32::NORMAL)
176 input = lir->mirNormal()->input();
177 else
178 input = lir->mirTruncate()->input();
180 Label fails;
181 if (lir->mode() == LValueToInt32::TRUNCATE) {
182 OutOfLineCode *oolDouble = oolTruncateDouble(temp, output);
183 if (!oolDouble)
184 return false;
186 // We can only handle strings in truncation contexts, like bitwise
187 // operations.
188 Label *stringEntry, *stringRejoin;
189 Register stringReg;
190 if (input->mightBeType(MIRType_String)) {
191 stringReg = ToRegister(lir->temp());
192 OutOfLineCode *oolString = oolCallVM(StringToNumberInfo, lir, (ArgList(), stringReg),
193 StoreFloatRegisterTo(temp));
194 if (!oolString)
195 return false;
196 stringEntry = oolString->entry();
197 stringRejoin = oolString->rejoin();
198 } else {
199 stringReg = InvalidReg;
200 stringEntry = nullptr;
201 stringRejoin = nullptr;
202 }
204 masm.truncateValueToInt32(operand, input, stringEntry, stringRejoin, oolDouble->entry(),
205 stringReg, temp, output, &fails);
206 masm.bind(oolDouble->rejoin());
207 } else {
208 masm.convertValueToInt32(operand, input, temp, output, &fails,
209 lir->mirNormal()->canBeNegativeZero(),
210 lir->mirNormal()->conversion());
211 }
213 return bailoutFrom(&fails, lir->snapshot());
214 }
216 bool
217 CodeGenerator::visitValueToDouble(LValueToDouble *lir)
218 {
219 MToDouble *mir = lir->mir();
220 ValueOperand operand = ToValue(lir, LValueToDouble::Input);
221 FloatRegister output = ToFloatRegister(lir->output());
223 Register tag = masm.splitTagForTest(operand);
225 Label isDouble, isInt32, isBool, isNull, isUndefined, done;
226 bool hasBoolean = false, hasNull = false, hasUndefined = false;
228 masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
229 masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
231 if (mir->conversion() != MToDouble::NumbersOnly) {
232 masm.branchTestBoolean(Assembler::Equal, tag, &isBool);
233 masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
234 hasBoolean = true;
235 hasUndefined = true;
236 if (mir->conversion() != MToDouble::NonNullNonStringPrimitives) {
237 masm.branchTestNull(Assembler::Equal, tag, &isNull);
238 hasNull = true;
239 }
240 }
242 if (!bailout(lir->snapshot()))
243 return false;
245 if (hasNull) {
246 masm.bind(&isNull);
247 masm.loadConstantDouble(0.0, output);
248 masm.jump(&done);
249 }
251 if (hasUndefined) {
252 masm.bind(&isUndefined);
253 masm.loadConstantDouble(GenericNaN(), output);
254 masm.jump(&done);
255 }
257 if (hasBoolean) {
258 masm.bind(&isBool);
259 masm.boolValueToDouble(operand, output);
260 masm.jump(&done);
261 }
263 masm.bind(&isInt32);
264 masm.int32ValueToDouble(operand, output);
265 masm.jump(&done);
267 masm.bind(&isDouble);
268 masm.unboxDouble(operand, output);
269 masm.bind(&done);
271 return true;
272 }
274 bool
275 CodeGenerator::visitValueToFloat32(LValueToFloat32 *lir)
276 {
277 MToFloat32 *mir = lir->mir();
278 ValueOperand operand = ToValue(lir, LValueToFloat32::Input);
279 FloatRegister output = ToFloatRegister(lir->output());
281 Register tag = masm.splitTagForTest(operand);
283 Label isDouble, isInt32, isBool, isNull, isUndefined, done;
284 bool hasBoolean = false, hasNull = false, hasUndefined = false;
286 masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
287 masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
289 if (mir->conversion() != MToFloat32::NumbersOnly) {
290 masm.branchTestBoolean(Assembler::Equal, tag, &isBool);
291 masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
292 hasBoolean = true;
293 hasUndefined = true;
294 if (mir->conversion() != MToFloat32::NonNullNonStringPrimitives) {
295 masm.branchTestNull(Assembler::Equal, tag, &isNull);
296 hasNull = true;
297 }
298 }
300 if (!bailout(lir->snapshot()))
301 return false;
303 if (hasNull) {
304 masm.bind(&isNull);
305 masm.loadConstantFloat32(0.0f, output);
306 masm.jump(&done);
307 }
309 if (hasUndefined) {
310 masm.bind(&isUndefined);
311 masm.loadConstantFloat32(float(GenericNaN()), output);
312 masm.jump(&done);
313 }
315 if (hasBoolean) {
316 masm.bind(&isBool);
317 masm.boolValueToFloat32(operand, output);
318 masm.jump(&done);
319 }
321 masm.bind(&isInt32);
322 masm.int32ValueToFloat32(operand, output);
323 masm.jump(&done);
325 masm.bind(&isDouble);
326 masm.unboxDouble(operand, output);
327 masm.convertDoubleToFloat32(output, output);
328 masm.bind(&done);
330 return true;
331 }
333 bool
334 CodeGenerator::visitInt32ToDouble(LInt32ToDouble *lir)
335 {
336 masm.convertInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
337 return true;
338 }
340 bool
341 CodeGenerator::visitFloat32ToDouble(LFloat32ToDouble *lir)
342 {
343 masm.convertFloat32ToDouble(ToFloatRegister(lir->input()), ToFloatRegister(lir->output()));
344 return true;
345 }
347 bool
348 CodeGenerator::visitDoubleToFloat32(LDoubleToFloat32 *lir)
349 {
350 masm.convertDoubleToFloat32(ToFloatRegister(lir->input()), ToFloatRegister(lir->output()));
351 return true;
352 }
354 bool
355 CodeGenerator::visitInt32ToFloat32(LInt32ToFloat32 *lir)
356 {
357 masm.convertInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output()));
358 return true;
359 }
361 bool
362 CodeGenerator::visitDoubleToInt32(LDoubleToInt32 *lir)
363 {
364 Label fail;
365 FloatRegister input = ToFloatRegister(lir->input());
366 Register output = ToRegister(lir->output());
367 masm.convertDoubleToInt32(input, output, &fail, lir->mir()->canBeNegativeZero());
368 if (!bailoutFrom(&fail, lir->snapshot()))
369 return false;
370 return true;
371 }
373 bool
374 CodeGenerator::visitFloat32ToInt32(LFloat32ToInt32 *lir)
375 {
376 Label fail;
377 FloatRegister input = ToFloatRegister(lir->input());
378 Register output = ToRegister(lir->output());
379 masm.convertFloat32ToInt32(input, output, &fail, lir->mir()->canBeNegativeZero());
380 if (!bailoutFrom(&fail, lir->snapshot()))
381 return false;
382 return true;
383 }
385 void
386 CodeGenerator::emitOOLTestObject(Register objreg,
387 Label *ifEmulatesUndefined,
388 Label *ifDoesntEmulateUndefined,
389 Register scratch)
390 {
391 saveVolatile(scratch);
392 masm.setupUnalignedABICall(1, scratch);
393 masm.passABIArg(objreg);
394 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::EmulatesUndefined));
395 masm.storeCallResult(scratch);
396 restoreVolatile(scratch);
398 masm.branchIfTrueBool(scratch, ifEmulatesUndefined);
399 masm.jump(ifDoesntEmulateUndefined);
400 }
402 // Base out-of-line code generator for all tests of the truthiness of an
403 // object, where the object might not be truthy. (Recall that per spec all
404 // objects are truthy, but we implement the JSCLASS_EMULATES_UNDEFINED class
405 // flag to permit objects to look like |undefined| in certain contexts,
406 // including in object truthiness testing.) We check truthiness inline except
407 // when we're testing it on a proxy (or if TI guarantees us that the specified
408 // object will never emulate |undefined|), in which case out-of-line code will
409 // call EmulatesUndefined for a conclusive answer.
410 class OutOfLineTestObject : public OutOfLineCodeBase<CodeGenerator>
411 {
412 Register objreg_;
413 Register scratch_;
415 Label *ifEmulatesUndefined_;
416 Label *ifDoesntEmulateUndefined_;
418 #ifdef DEBUG
419 bool initialized() { return ifEmulatesUndefined_ != nullptr; }
420 #endif
422 public:
423 OutOfLineTestObject()
424 #ifdef DEBUG
425 : ifEmulatesUndefined_(nullptr), ifDoesntEmulateUndefined_(nullptr)
426 #endif
427 { }
429 bool accept(CodeGenerator *codegen) MOZ_FINAL MOZ_OVERRIDE {
430 MOZ_ASSERT(initialized());
431 codegen->emitOOLTestObject(objreg_, ifEmulatesUndefined_, ifDoesntEmulateUndefined_,
432 scratch_);
433 return true;
434 }
436 // Specify the register where the object to be tested is found, labels to
437 // jump to if the object is truthy or falsy, and a scratch register for
438 // use in the out-of-line path.
439 void setInputAndTargets(Register objreg, Label *ifEmulatesUndefined, Label *ifDoesntEmulateUndefined,
440 Register scratch)
441 {
442 MOZ_ASSERT(!initialized());
443 MOZ_ASSERT(ifEmulatesUndefined);
444 objreg_ = objreg;
445 scratch_ = scratch;
446 ifEmulatesUndefined_ = ifEmulatesUndefined;
447 ifDoesntEmulateUndefined_ = ifDoesntEmulateUndefined;
448 }
449 };
451 // A subclass of OutOfLineTestObject containing two extra labels, for use when
452 // the ifTruthy/ifFalsy labels are needed in inline code as well as out-of-line
453 // code. The user should bind these labels in inline code, and specify them as
454 // targets via setInputAndTargets, as appropriate.
455 class OutOfLineTestObjectWithLabels : public OutOfLineTestObject
456 {
457 Label label1_;
458 Label label2_;
460 public:
461 OutOfLineTestObjectWithLabels() { }
463 Label *label1() { return &label1_; }
464 Label *label2() { return &label2_; }
465 };
467 void
468 CodeGenerator::testObjectEmulatesUndefinedKernel(Register objreg,
469 Label *ifEmulatesUndefined,
470 Label *ifDoesntEmulateUndefined,
471 Register scratch, OutOfLineTestObject *ool)
472 {
473 ool->setInputAndTargets(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined, scratch);
475 // Perform a fast-path check of the object's class flags if the object's
476 // not a proxy. Let out-of-line code handle the slow cases that require
477 // saving registers, making a function call, and restoring registers.
478 masm.branchTestObjectTruthy(false, objreg, scratch, ool->entry(), ifEmulatesUndefined);
479 }
481 void
482 CodeGenerator::branchTestObjectEmulatesUndefined(Register objreg,
483 Label *ifEmulatesUndefined,
484 Label *ifDoesntEmulateUndefined,
485 Register scratch, OutOfLineTestObject *ool)
486 {
487 MOZ_ASSERT(!ifDoesntEmulateUndefined->bound(),
488 "ifDoesntEmulateUndefined will be bound to the fallthrough path");
490 testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined,
491 scratch, ool);
492 masm.bind(ifDoesntEmulateUndefined);
493 }
495 void
496 CodeGenerator::testObjectEmulatesUndefined(Register objreg,
497 Label *ifEmulatesUndefined,
498 Label *ifDoesntEmulateUndefined,
499 Register scratch, OutOfLineTestObject *ool)
500 {
501 testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined,
502 scratch, ool);
503 masm.jump(ifDoesntEmulateUndefined);
504 }
506 void
507 CodeGenerator::testValueTruthyKernel(const ValueOperand &value,
508 const LDefinition *scratch1, const LDefinition *scratch2,
509 FloatRegister fr,
510 Label *ifTruthy, Label *ifFalsy,
511 OutOfLineTestObject *ool)
512 {
513 Register tag = masm.splitTagForTest(value);
515 // Eventually we will want some sort of type filter here. For now, just
516 // emit all easy cases. For speed we use the cached tag for all comparison,
517 // except for doubles, which we test last (as the operation can clobber the
518 // tag, which may be in ScratchReg).
519 masm.branchTestUndefined(Assembler::Equal, tag, ifFalsy);
520 masm.branchTestNull(Assembler::Equal, tag, ifFalsy);
522 Label notBoolean;
523 masm.branchTestBoolean(Assembler::NotEqual, tag, ¬Boolean);
524 masm.branchTestBooleanTruthy(false, value, ifFalsy);
525 masm.jump(ifTruthy);
526 masm.bind(¬Boolean);
528 Label notInt32;
529 masm.branchTestInt32(Assembler::NotEqual, tag, ¬Int32);
530 masm.branchTestInt32Truthy(false, value, ifFalsy);
531 masm.jump(ifTruthy);
532 masm.bind(¬Int32);
534 if (ool) {
535 Label notObject;
537 masm.branchTestObject(Assembler::NotEqual, tag, ¬Object);
539 Register objreg = masm.extractObject(value, ToRegister(scratch1));
540 testObjectEmulatesUndefined(objreg, ifFalsy, ifTruthy, ToRegister(scratch2), ool);
542 masm.bind(¬Object);
543 } else {
544 masm.branchTestObject(Assembler::Equal, tag, ifTruthy);
545 }
547 // Test if a string is non-empty.
548 Label notString;
549 masm.branchTestString(Assembler::NotEqual, tag, ¬String);
550 masm.branchTestStringTruthy(false, value, ifFalsy);
551 masm.jump(ifTruthy);
552 masm.bind(¬String);
554 // If we reach here the value is a double.
555 masm.unboxDouble(value, fr);
556 masm.branchTestDoubleTruthy(false, fr, ifFalsy);
558 // Fall through for truthy.
559 }
561 void
562 CodeGenerator::testValueTruthy(const ValueOperand &value,
563 const LDefinition *scratch1, const LDefinition *scratch2,
564 FloatRegister fr,
565 Label *ifTruthy, Label *ifFalsy,
566 OutOfLineTestObject *ool)
567 {
568 testValueTruthyKernel(value, scratch1, scratch2, fr, ifTruthy, ifFalsy, ool);
569 masm.jump(ifTruthy);
570 }
572 Label *
573 CodeGenerator::getJumpLabelForBranch(MBasicBlock *block)
574 {
575 if (!labelForBackedgeWithImplicitCheck(block))
576 return block->lir()->label();
578 // We need to use a patchable jump for this backedge, but want to treat
579 // this as a normal label target to simplify codegen. Efficiency isn't so
580 // important here as these tests are extremely unlikely to be used in loop
581 // backedges, so emit inline code for the patchable jump. Heap allocating
582 // the label allows it to be used by out of line blocks.
583 Label *res = GetIonContext()->temp->lifoAlloc()->new_<Label>();
584 Label after;
585 masm.jump(&after);
586 masm.bind(res);
587 jumpToBlock(block);
588 masm.bind(&after);
589 return res;
590 }
592 bool
593 CodeGenerator::visitTestOAndBranch(LTestOAndBranch *lir)
594 {
595 MOZ_ASSERT(lir->mir()->operandMightEmulateUndefined(),
596 "Objects which can't emulate undefined should have been constant-folded");
598 OutOfLineTestObject *ool = new(alloc()) OutOfLineTestObject();
599 if (!addOutOfLineCode(ool))
600 return false;
602 Label *truthy = getJumpLabelForBranch(lir->ifTruthy());
603 Label *falsy = getJumpLabelForBranch(lir->ifFalsy());
605 testObjectEmulatesUndefined(ToRegister(lir->input()), falsy, truthy,
606 ToRegister(lir->temp()), ool);
607 return true;
609 }
611 bool
612 CodeGenerator::visitTestVAndBranch(LTestVAndBranch *lir)
613 {
614 OutOfLineTestObject *ool = nullptr;
615 if (lir->mir()->operandMightEmulateUndefined()) {
616 ool = new(alloc()) OutOfLineTestObject();
617 if (!addOutOfLineCode(ool))
618 return false;
619 }
621 Label *truthy = getJumpLabelForBranch(lir->ifTruthy());
622 Label *falsy = getJumpLabelForBranch(lir->ifFalsy());
624 testValueTruthy(ToValue(lir, LTestVAndBranch::Input),
625 lir->temp1(), lir->temp2(),
626 ToFloatRegister(lir->tempFloat()),
627 truthy, falsy, ool);
628 return true;
629 }
631 bool
632 CodeGenerator::visitFunctionDispatch(LFunctionDispatch *lir)
633 {
634 MFunctionDispatch *mir = lir->mir();
635 Register input = ToRegister(lir->input());
636 Label *lastLabel;
637 size_t casesWithFallback;
639 // Determine if the last case is fallback or an ordinary case.
640 if (!mir->hasFallback()) {
641 JS_ASSERT(mir->numCases() > 0);
642 casesWithFallback = mir->numCases();
643 lastLabel = mir->getCaseBlock(mir->numCases() - 1)->lir()->label();
644 } else {
645 casesWithFallback = mir->numCases() + 1;
646 lastLabel = mir->getFallback()->lir()->label();
647 }
649 // Compare function pointers, except for the last case.
650 for (size_t i = 0; i < casesWithFallback - 1; i++) {
651 JS_ASSERT(i < mir->numCases());
652 JSFunction *func = mir->getCase(i);
653 LBlock *target = mir->getCaseBlock(i)->lir();
654 masm.branchPtr(Assembler::Equal, input, ImmGCPtr(func), target->label());
655 }
657 // Jump to the last case.
658 masm.jump(lastLabel);
660 return true;
661 }
663 bool
664 CodeGenerator::visitTypeObjectDispatch(LTypeObjectDispatch *lir)
665 {
666 MTypeObjectDispatch *mir = lir->mir();
667 Register input = ToRegister(lir->input());
668 Register temp = ToRegister(lir->temp());
670 // Hold the incoming TypeObject.
671 masm.loadPtr(Address(input, JSObject::offsetOfType()), temp);
673 // Compare TypeObjects.
674 InlinePropertyTable *propTable = mir->propTable();
675 for (size_t i = 0; i < mir->numCases(); i++) {
676 JSFunction *func = mir->getCase(i);
677 LBlock *target = mir->getCaseBlock(i)->lir();
679 DebugOnly<bool> found = false;
680 for (size_t j = 0; j < propTable->numEntries(); j++) {
681 if (propTable->getFunction(j) != func)
682 continue;
683 types::TypeObject *typeObj = propTable->getTypeObject(j);
684 masm.branchPtr(Assembler::Equal, temp, ImmGCPtr(typeObj), target->label());
685 found = true;
686 }
687 JS_ASSERT(found);
688 }
690 // Unknown function: jump to fallback block.
691 LBlock *fallback = mir->getFallback()->lir();
692 masm.jump(fallback->label());
693 return true;
694 }
696 bool
697 CodeGenerator::visitBooleanToString(LBooleanToString *lir)
698 {
699 Register input = ToRegister(lir->input());
700 Register output = ToRegister(lir->output());
701 const JSAtomState &names = GetIonContext()->runtime->names();
702 Label true_, done;
704 masm.branchTest32(Assembler::NonZero, input, input, &true_);
705 masm.movePtr(ImmGCPtr(names.false_), output);
706 masm.jump(&done);
708 masm.bind(&true_);
709 masm.movePtr(ImmGCPtr(names.true_), output);
711 masm.bind(&done);
713 return true;
714 }
716 void
717 CodeGenerator::emitIntToString(Register input, Register output, Label *ool)
718 {
719 masm.branch32(Assembler::AboveOrEqual, input, Imm32(StaticStrings::INT_STATIC_LIMIT), ool);
721 // Fast path for small integers.
722 masm.movePtr(ImmPtr(&GetIonContext()->runtime->staticStrings().intStaticTable), output);
723 masm.loadPtr(BaseIndex(output, input, ScalePointer), output);
724 }
726 typedef JSFlatString *(*IntToStringFn)(ThreadSafeContext *, int);
727 typedef JSFlatString *(*IntToStringParFn)(ForkJoinContext *, int);
728 static const VMFunctionsModal IntToStringInfo = VMFunctionsModal(
729 FunctionInfo<IntToStringFn>(Int32ToString<CanGC>),
730 FunctionInfo<IntToStringParFn>(IntToStringPar));
732 bool
733 CodeGenerator::visitIntToString(LIntToString *lir)
734 {
735 Register input = ToRegister(lir->input());
736 Register output = ToRegister(lir->output());
738 OutOfLineCode *ool = oolCallVM(IntToStringInfo, lir, (ArgList(), input),
739 StoreRegisterTo(output));
740 if (!ool)
741 return false;
743 emitIntToString(input, output, ool->entry());
745 masm.bind(ool->rejoin());
746 return true;
747 }
749 typedef JSString *(*DoubleToStringFn)(ThreadSafeContext *, double);
750 typedef JSString *(*DoubleToStringParFn)(ForkJoinContext *, double);
751 static const VMFunctionsModal DoubleToStringInfo = VMFunctionsModal(
752 FunctionInfo<DoubleToStringFn>(NumberToString<CanGC>),
753 FunctionInfo<DoubleToStringParFn>(DoubleToStringPar));
755 bool
756 CodeGenerator::visitDoubleToString(LDoubleToString *lir)
757 {
758 FloatRegister input = ToFloatRegister(lir->input());
759 Register temp = ToRegister(lir->tempInt());
760 Register output = ToRegister(lir->output());
762 OutOfLineCode *ool = oolCallVM(DoubleToStringInfo, lir, (ArgList(), input),
763 StoreRegisterTo(output));
764 if (!ool)
765 return false;
767 // Try double to integer conversion and run integer to string code.
768 masm.convertDoubleToInt32(input, temp, ool->entry(), true);
769 emitIntToString(temp, output, ool->entry());
771 masm.bind(ool->rejoin());
772 return true;
773 }
775 typedef JSString *(*PrimitiveToStringFn)(JSContext *, HandleValue);
776 typedef JSString *(*PrimitiveToStringParFn)(ForkJoinContext *, HandleValue);
777 static const VMFunctionsModal PrimitiveToStringInfo = VMFunctionsModal(
778 FunctionInfo<PrimitiveToStringFn>(ToStringSlow),
779 FunctionInfo<PrimitiveToStringParFn>(PrimitiveToStringPar));
781 bool
782 CodeGenerator::visitPrimitiveToString(LPrimitiveToString *lir)
783 {
784 ValueOperand input = ToValue(lir, LPrimitiveToString::Input);
785 Register output = ToRegister(lir->output());
787 OutOfLineCode *ool = oolCallVM(PrimitiveToStringInfo, lir, (ArgList(), input),
788 StoreRegisterTo(output));
789 if (!ool)
790 return false;
792 Label done;
793 Register tag = masm.splitTagForTest(input);
794 const JSAtomState &names = GetIonContext()->runtime->names();
796 // String
797 if (lir->mir()->input()->mightBeType(MIRType_String)) {
798 Label notString;
799 masm.branchTestString(Assembler::NotEqual, tag, ¬String);
800 masm.unboxString(input, output);
801 masm.jump(&done);
802 masm.bind(¬String);
803 }
805 // Integer
806 if (lir->mir()->input()->mightBeType(MIRType_Int32)) {
807 Label notInteger;
808 masm.branchTestInt32(Assembler::NotEqual, tag, ¬Integer);
809 Register unboxed = ToTempUnboxRegister(lir->tempToUnbox());
810 unboxed = masm.extractInt32(input, unboxed);
811 emitIntToString(unboxed, output, ool->entry());
812 masm.jump(&done);
813 masm.bind(¬Integer);
814 }
816 // Double
817 if (lir->mir()->input()->mightBeType(MIRType_Double)) {
818 // Note: no fastpath. Need two extra registers and can only convert doubles
819 // that fit integers and are smaller than StaticStrings::INT_STATIC_LIMIT.
820 masm.branchTestDouble(Assembler::Equal, tag, ool->entry());
821 }
823 // Undefined
824 if (lir->mir()->input()->mightBeType(MIRType_Undefined)) {
825 Label notUndefined;
826 masm.branchTestUndefined(Assembler::NotEqual, tag, ¬Undefined);
827 masm.movePtr(ImmGCPtr(names.undefined), output);
828 masm.jump(&done);
829 masm.bind(¬Undefined);
830 }
832 // Null
833 if (lir->mir()->input()->mightBeType(MIRType_Null)) {
834 Label notNull;
835 masm.branchTestNull(Assembler::NotEqual, tag, ¬Null);
836 masm.movePtr(ImmGCPtr(names.null), output);
837 masm.jump(&done);
838 masm.bind(¬Null);
839 }
841 // Boolean
842 if (lir->mir()->input()->mightBeType(MIRType_Boolean)) {
843 Label notBoolean, true_;
844 masm.branchTestBoolean(Assembler::NotEqual, tag, ¬Boolean);
845 masm.branchTestBooleanTruthy(true, input, &true_);
846 masm.movePtr(ImmGCPtr(names.false_), output);
847 masm.jump(&done);
848 masm.bind(&true_);
849 masm.movePtr(ImmGCPtr(names.true_), output);
850 masm.jump(&done);
851 masm.bind(¬Boolean);
852 }
854 #ifdef DEBUG
855 // Objects are not supported or we see a type that wasn't accounted for.
856 masm.assumeUnreachable("Unexpected type for MPrimitiveToString.");
857 #endif
859 masm.bind(&done);
860 masm.bind(ool->rejoin());
861 return true;
862 }
864 typedef JSObject *(*CloneRegExpObjectFn)(JSContext *, JSObject *);
865 static const VMFunction CloneRegExpObjectInfo =
866 FunctionInfo<CloneRegExpObjectFn>(CloneRegExpObject);
868 bool
869 CodeGenerator::visitRegExp(LRegExp *lir)
870 {
871 pushArg(ImmGCPtr(lir->mir()->source()));
872 return callVM(CloneRegExpObjectInfo, lir);
873 }
875 typedef bool (*RegExpExecRawFn)(JSContext *cx, HandleObject regexp,
876 HandleString input, MutableHandleValue output);
877 static const VMFunction RegExpExecRawInfo = FunctionInfo<RegExpExecRawFn>(regexp_exec_raw);
879 bool
880 CodeGenerator::visitRegExpExec(LRegExpExec *lir)
881 {
882 pushArg(ToRegister(lir->string()));
883 pushArg(ToRegister(lir->regexp()));
884 return callVM(RegExpExecRawInfo, lir);
885 }
887 typedef bool (*RegExpTestRawFn)(JSContext *cx, HandleObject regexp,
888 HandleString input, bool *result);
889 static const VMFunction RegExpTestRawInfo = FunctionInfo<RegExpTestRawFn>(regexp_test_raw);
891 bool
892 CodeGenerator::visitRegExpTest(LRegExpTest *lir)
893 {
894 pushArg(ToRegister(lir->string()));
895 pushArg(ToRegister(lir->regexp()));
896 return callVM(RegExpTestRawInfo, lir);
897 }
899 typedef JSString *(*RegExpReplaceFn)(JSContext *, HandleString, HandleObject, HandleString);
900 static const VMFunction RegExpReplaceInfo = FunctionInfo<RegExpReplaceFn>(RegExpReplace);
902 bool
903 CodeGenerator::visitRegExpReplace(LRegExpReplace *lir)
904 {
905 if (lir->replacement()->isConstant())
906 pushArg(ImmGCPtr(lir->replacement()->toConstant()->toString()));
907 else
908 pushArg(ToRegister(lir->replacement()));
910 pushArg(ToRegister(lir->pattern()));
912 if (lir->string()->isConstant())
913 pushArg(ImmGCPtr(lir->string()->toConstant()->toString()));
914 else
915 pushArg(ToRegister(lir->string()));
917 return callVM(RegExpReplaceInfo, lir);
918 }
920 typedef JSString *(*StringReplaceFn)(JSContext *, HandleString, HandleString, HandleString);
921 static const VMFunction StringReplaceInfo = FunctionInfo<StringReplaceFn>(StringReplace);
923 bool
924 CodeGenerator::visitStringReplace(LStringReplace *lir)
925 {
926 if (lir->replacement()->isConstant())
927 pushArg(ImmGCPtr(lir->replacement()->toConstant()->toString()));
928 else
929 pushArg(ToRegister(lir->replacement()));
931 if (lir->pattern()->isConstant())
932 pushArg(ImmGCPtr(lir->pattern()->toConstant()->toString()));
933 else
934 pushArg(ToRegister(lir->pattern()));
936 if (lir->string()->isConstant())
937 pushArg(ImmGCPtr(lir->string()->toConstant()->toString()));
938 else
939 pushArg(ToRegister(lir->string()));
941 return callVM(StringReplaceInfo, lir);
942 }
945 typedef JSObject *(*LambdaFn)(JSContext *, HandleFunction, HandleObject);
946 static const VMFunction LambdaInfo = FunctionInfo<LambdaFn>(js::Lambda);
948 bool
949 CodeGenerator::visitLambdaForSingleton(LLambdaForSingleton *lir)
950 {
951 pushArg(ToRegister(lir->scopeChain()));
952 pushArg(ImmGCPtr(lir->mir()->info().fun));
953 return callVM(LambdaInfo, lir);
954 }
956 bool
957 CodeGenerator::visitLambda(LLambda *lir)
958 {
959 Register scopeChain = ToRegister(lir->scopeChain());
960 Register output = ToRegister(lir->output());
961 Register tempReg = ToRegister(lir->temp());
962 const LambdaFunctionInfo &info = lir->mir()->info();
964 OutOfLineCode *ool = oolCallVM(LambdaInfo, lir, (ArgList(), ImmGCPtr(info.fun), scopeChain),
965 StoreRegisterTo(output));
966 if (!ool)
967 return false;
969 JS_ASSERT(!info.singletonType);
971 masm.newGCThing(output, tempReg, info.fun, ool->entry(), gc::DefaultHeap);
972 masm.initGCThing(output, tempReg, info.fun);
974 emitLambdaInit(output, scopeChain, info);
976 masm.bind(ool->rejoin());
977 return true;
978 }
980 typedef JSObject *(*LambdaArrowFn)(JSContext *, HandleFunction, HandleObject, HandleValue);
981 static const VMFunction LambdaArrowInfo = FunctionInfo<LambdaArrowFn>(js::LambdaArrow);
983 bool
984 CodeGenerator::visitLambdaArrow(LLambdaArrow *lir)
985 {
986 Register scopeChain = ToRegister(lir->scopeChain());
987 ValueOperand thisv = ToValue(lir, LLambdaArrow::ThisValue);
988 Register output = ToRegister(lir->output());
989 Register tempReg = ToRegister(lir->temp());
990 const LambdaFunctionInfo &info = lir->mir()->info();
992 OutOfLineCode *ool = oolCallVM(LambdaArrowInfo, lir,
993 (ArgList(), ImmGCPtr(info.fun), scopeChain, thisv),
994 StoreRegisterTo(output));
995 if (!ool)
996 return false;
998 MOZ_ASSERT(!info.useNewTypeForClone);
1000 if (info.singletonType) {
1001 // If the function has a singleton type, this instruction will only be
1002 // executed once so we don't bother inlining it.
1003 masm.jump(ool->entry());
1004 masm.bind(ool->rejoin());
1005 return true;
1006 }
1008 masm.newGCThing(output, tempReg, info.fun, ool->entry(), gc::DefaultHeap);
1009 masm.initGCThing(output, tempReg, info.fun);
1011 emitLambdaInit(output, scopeChain, info);
1013 // Initialize extended slots. Lexical |this| is stored in the first one.
1014 MOZ_ASSERT(info.flags & JSFunction::EXTENDED);
1015 static_assert(FunctionExtended::NUM_EXTENDED_SLOTS == 2, "All slots must be initialized");
1016 static_assert(FunctionExtended::ARROW_THIS_SLOT == 0, "|this| must be stored in first slot");
1017 masm.storeValue(thisv, Address(output, FunctionExtended::offsetOfExtendedSlot(0)));
1018 masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(1)));
1020 masm.bind(ool->rejoin());
1021 return true;
1022 }
1024 void
1025 CodeGenerator::emitLambdaInit(Register output, Register scopeChain,
1026 const LambdaFunctionInfo &info)
1027 {
1028 MOZ_ASSERT(!!(info.flags & JSFunction::ARROW) == !!(info.flags & JSFunction::EXTENDED));
1030 // Initialize nargs and flags. We do this with a single uint32 to avoid
1031 // 16-bit writes.
1032 union {
1033 struct S {
1034 uint16_t nargs;
1035 uint16_t flags;
1036 } s;
1037 uint32_t word;
1038 } u;
1039 u.s.nargs = info.fun->nargs();
1040 u.s.flags = info.flags;
1042 JS_ASSERT(JSFunction::offsetOfFlags() == JSFunction::offsetOfNargs() + 2);
1043 masm.store32(Imm32(u.word), Address(output, JSFunction::offsetOfNargs()));
1044 masm.storePtr(ImmGCPtr(info.scriptOrLazyScript),
1045 Address(output, JSFunction::offsetOfNativeOrScript()));
1046 masm.storePtr(scopeChain, Address(output, JSFunction::offsetOfEnvironment()));
1047 masm.storePtr(ImmGCPtr(info.fun->displayAtom()), Address(output, JSFunction::offsetOfAtom()));
1048 }
1050 bool
1051 CodeGenerator::visitLambdaPar(LLambdaPar *lir)
1052 {
1053 Register resultReg = ToRegister(lir->output());
1054 Register cxReg = ToRegister(lir->forkJoinContext());
1055 Register scopeChainReg = ToRegister(lir->scopeChain());
1056 Register tempReg1 = ToRegister(lir->getTemp0());
1057 Register tempReg2 = ToRegister(lir->getTemp1());
1058 const LambdaFunctionInfo &info = lir->mir()->info();
1060 JS_ASSERT(scopeChainReg != resultReg);
1062 emitAllocateGCThingPar(lir, resultReg, cxReg, tempReg1, tempReg2, info.fun);
1063 emitLambdaInit(resultReg, scopeChainReg, info);
1064 return true;
1065 }
1067 bool
1068 CodeGenerator::visitLabel(LLabel *lir)
1069 {
1070 return true;
1071 }
1073 bool
1074 CodeGenerator::visitNop(LNop *lir)
1075 {
1076 return true;
1077 }
1079 bool
1080 CodeGenerator::visitOsiPoint(LOsiPoint *lir)
1081 {
1082 // Note: markOsiPoint ensures enough space exists between the last
1083 // LOsiPoint and this one to patch adjacent call instructions.
1085 JS_ASSERT(masm.framePushed() == frameSize());
1087 uint32_t osiCallPointOffset;
1088 if (!markOsiPoint(lir, &osiCallPointOffset))
1089 return false;
1091 LSafepoint *safepoint = lir->associatedSafepoint();
1092 JS_ASSERT(!safepoint->osiCallPointOffset());
1093 safepoint->setOsiCallPointOffset(osiCallPointOffset);
1095 #ifdef DEBUG
1096 // There should be no movegroups or other instructions between
1097 // an instruction and its OsiPoint. This is necessary because
1098 // we use the OsiPoint's snapshot from within VM calls.
1099 for (LInstructionReverseIterator iter(current->rbegin(lir)); iter != current->rend(); iter++) {
1100 if (*iter == lir || iter->isNop())
1101 continue;
1102 JS_ASSERT(!iter->isMoveGroup());
1103 JS_ASSERT(iter->safepoint() == safepoint);
1104 break;
1105 }
1106 #endif
1108 #ifdef CHECK_OSIPOINT_REGISTERS
1109 if (shouldVerifyOsiPointRegs(safepoint))
1110 verifyOsiPointRegs(safepoint);
1111 #endif
1113 return true;
1114 }
1116 bool
1117 CodeGenerator::visitGoto(LGoto *lir)
1118 {
1119 jumpToBlock(lir->target());
1120 return true;
1121 }
1123 // Out-of-line path to execute any move groups between the start of a loop
1124 // header and its interrupt check, then invoke the interrupt handler.
1125 class OutOfLineInterruptCheckImplicit : public OutOfLineCodeBase<CodeGenerator>
1126 {
1127 public:
1128 LBlock *block;
1129 LInterruptCheckImplicit *lir;
1131 OutOfLineInterruptCheckImplicit(LBlock *block, LInterruptCheckImplicit *lir)
1132 : block(block), lir(lir)
1133 { }
1135 bool accept(CodeGenerator *codegen) {
1136 return codegen->visitOutOfLineInterruptCheckImplicit(this);
1137 }
1138 };
1140 bool
1141 CodeGenerator::visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit *ool)
1142 {
1143 #ifdef CHECK_OSIPOINT_REGISTERS
1144 // This is path is entered from the patched back-edge of the loop. This
1145 // means that the JitAtivation flags used for checking the validity of the
1146 // OSI points are not reseted by the path generated by generateBody, so we
1147 // have to reset it here.
1148 resetOsiPointRegs(ool->lir->safepoint());
1149 #endif
1151 LInstructionIterator iter = ool->block->begin();
1152 for (; iter != ool->block->end(); iter++) {
1153 if (iter->isLabel()) {
1154 // Nothing to do.
1155 } else if (iter->isMoveGroup()) {
1156 // Replay this move group that preceds the interrupt check at the
1157 // start of the loop header. Any incoming jumps here will be from
1158 // the backedge and will skip over the move group emitted inline.
1159 visitMoveGroup(iter->toMoveGroup());
1160 } else {
1161 break;
1162 }
1163 }
1164 JS_ASSERT(*iter == ool->lir);
1166 saveLive(ool->lir);
1167 if (!callVM(InterruptCheckInfo, ool->lir))
1168 return false;
1169 restoreLive(ool->lir);
1170 masm.jump(ool->rejoin());
1172 return true;
1173 }
1175 bool
1176 CodeGenerator::visitInterruptCheckImplicit(LInterruptCheckImplicit *lir)
1177 {
1178 OutOfLineInterruptCheckImplicit *ool = new(alloc()) OutOfLineInterruptCheckImplicit(current, lir);
1179 if (!addOutOfLineCode(ool))
1180 return false;
1182 lir->setOolEntry(ool->entry());
1183 masm.bind(ool->rejoin());
1184 return true;
1185 }
1187 bool
1188 CodeGenerator::visitTableSwitch(LTableSwitch *ins)
1189 {
1190 MTableSwitch *mir = ins->mir();
1191 Label *defaultcase = mir->getDefault()->lir()->label();
1192 const LAllocation *temp;
1194 if (mir->getOperand(0)->type() != MIRType_Int32) {
1195 temp = ins->tempInt()->output();
1197 // The input is a double, so try and convert it to an integer.
1198 // If it does not fit in an integer, take the default case.
1199 masm.convertDoubleToInt32(ToFloatRegister(ins->index()), ToRegister(temp), defaultcase, false);
1200 } else {
1201 temp = ins->index();
1202 }
1204 return emitTableSwitchDispatch(mir, ToRegister(temp), ToRegisterOrInvalid(ins->tempPointer()));
1205 }
1207 bool
1208 CodeGenerator::visitTableSwitchV(LTableSwitchV *ins)
1209 {
1210 MTableSwitch *mir = ins->mir();
1211 Label *defaultcase = mir->getDefault()->lir()->label();
1213 Register index = ToRegister(ins->tempInt());
1214 ValueOperand value = ToValue(ins, LTableSwitchV::InputValue);
1215 Register tag = masm.extractTag(value, index);
1216 masm.branchTestNumber(Assembler::NotEqual, tag, defaultcase);
1218 Label unboxInt, isInt;
1219 masm.branchTestInt32(Assembler::Equal, tag, &unboxInt);
1220 {
1221 FloatRegister floatIndex = ToFloatRegister(ins->tempFloat());
1222 masm.unboxDouble(value, floatIndex);
1223 masm.convertDoubleToInt32(floatIndex, index, defaultcase, false);
1224 masm.jump(&isInt);
1225 }
1227 masm.bind(&unboxInt);
1228 masm.unboxInt32(value, index);
1230 masm.bind(&isInt);
1232 return emitTableSwitchDispatch(mir, index, ToRegisterOrInvalid(ins->tempPointer()));
1233 }
1235 typedef JSObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleObject, NewObjectKind);
1236 static const VMFunction DeepCloneObjectLiteralInfo =
1237 FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral);
1239 bool
1240 CodeGenerator::visitCloneLiteral(LCloneLiteral *lir)
1241 {
1242 pushArg(ImmWord(js::MaybeSingletonObject));
1243 pushArg(ToRegister(lir->output()));
1244 return callVM(DeepCloneObjectLiteralInfo, lir);
1245 }
1247 bool
1248 CodeGenerator::visitParameter(LParameter *lir)
1249 {
1250 return true;
1251 }
1253 bool
1254 CodeGenerator::visitCallee(LCallee *lir)
1255 {
1256 // read number of actual arguments from the JS frame.
1257 Register callee = ToRegister(lir->output());
1258 Address ptr(StackPointer, frameSize() + IonJSFrameLayout::offsetOfCalleeToken());
1260 masm.loadPtr(ptr, callee);
1261 return true;
1262 }
1264 bool
1265 CodeGenerator::visitStart(LStart *lir)
1266 {
1267 return true;
1268 }
1270 bool
1271 CodeGenerator::visitReturn(LReturn *lir)
1272 {
1273 #if defined(JS_NUNBOX32)
1274 DebugOnly<LAllocation *> type = lir->getOperand(TYPE_INDEX);
1275 DebugOnly<LAllocation *> payload = lir->getOperand(PAYLOAD_INDEX);
1276 JS_ASSERT(ToRegister(type) == JSReturnReg_Type);
1277 JS_ASSERT(ToRegister(payload) == JSReturnReg_Data);
1278 #elif defined(JS_PUNBOX64)
1279 DebugOnly<LAllocation *> result = lir->getOperand(0);
1280 JS_ASSERT(ToRegister(result) == JSReturnReg);
1281 #endif
1282 // Don't emit a jump to the return label if this is the last block.
1283 if (current->mir() != *gen->graph().poBegin())
1284 masm.jump(&returnLabel_);
1285 return true;
1286 }
1288 bool
1289 CodeGenerator::visitOsrEntry(LOsrEntry *lir)
1290 {
1291 // Remember the OSR entry offset into the code buffer.
1292 masm.flushBuffer();
1293 setOsrEntryOffset(masm.size());
1295 #ifdef JS_TRACE_LOGGING
1296 if (gen->info().executionMode() == SequentialExecution) {
1297 if (!emitTracelogStopEvent(TraceLogger::Baseline))
1298 return false;
1299 if (!emitTracelogStartEvent(TraceLogger::IonMonkey))
1300 return false;
1301 }
1302 #endif
1304 // Allocate the full frame for this function.
1305 uint32_t size = frameSize();
1306 if (size != 0)
1307 masm.subPtr(Imm32(size), StackPointer);
1308 return true;
1309 }
1311 bool
1312 CodeGenerator::visitOsrScopeChain(LOsrScopeChain *lir)
1313 {
1314 const LAllocation *frame = lir->getOperand(0);
1315 const LDefinition *object = lir->getDef(0);
1317 const ptrdiff_t frameOffset = BaselineFrame::reverseOffsetOfScopeChain();
1319 masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object));
1320 return true;
1321 }
1323 bool
1324 CodeGenerator::visitOsrArgumentsObject(LOsrArgumentsObject *lir)
1325 {
1326 const LAllocation *frame = lir->getOperand(0);
1327 const LDefinition *object = lir->getDef(0);
1329 const ptrdiff_t frameOffset = BaselineFrame::reverseOffsetOfArgsObj();
1331 masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object));
1332 return true;
1333 }
1335 bool
1336 CodeGenerator::visitOsrValue(LOsrValue *value)
1337 {
1338 const LAllocation *frame = value->getOperand(0);
1339 const ValueOperand out = ToOutValue(value);
1341 const ptrdiff_t frameOffset = value->mir()->frameOffset();
1343 masm.loadValue(Address(ToRegister(frame), frameOffset), out);
1344 return true;
1345 }
1347 bool
1348 CodeGenerator::visitOsrReturnValue(LOsrReturnValue *lir)
1349 {
1350 const LAllocation *frame = lir->getOperand(0);
1351 const ValueOperand out = ToOutValue(lir);
1353 Address flags = Address(ToRegister(frame), BaselineFrame::reverseOffsetOfFlags());
1354 Address retval = Address(ToRegister(frame), BaselineFrame::reverseOffsetOfReturnValue());
1356 masm.moveValue(UndefinedValue(), out);
1358 Label done;
1359 masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL), &done);
1360 masm.loadValue(retval, out);
1361 masm.bind(&done);
1363 return true;
1364 }
1366 bool
1367 CodeGenerator::visitStackArgT(LStackArgT *lir)
1368 {
1369 const LAllocation *arg = lir->getArgument();
1370 MIRType argType = lir->type();
1371 uint32_t argslot = lir->argslot();
1372 JS_ASSERT(argslot - 1u < graph.argumentSlotCount());
1374 int32_t stack_offset = StackOffsetOfPassedArg(argslot);
1375 Address dest(StackPointer, stack_offset);
1377 if (arg->isFloatReg())
1378 masm.storeDouble(ToFloatRegister(arg), dest);
1379 else if (arg->isRegister())
1380 masm.storeValue(ValueTypeFromMIRType(argType), ToRegister(arg), dest);
1381 else
1382 masm.storeValue(*(arg->toConstant()), dest);
1384 uint32_t slot = StackOffsetToSlot(stack_offset);
1385 JS_ASSERT(slot - 1u < graph.totalSlotCount());
1386 return pushedArgumentSlots_.append(slot);
1387 }
1389 bool
1390 CodeGenerator::visitStackArgV(LStackArgV *lir)
1391 {
1392 ValueOperand val = ToValue(lir, 0);
1393 uint32_t argslot = lir->argslot();
1394 JS_ASSERT(argslot - 1u < graph.argumentSlotCount());
1396 int32_t stack_offset = StackOffsetOfPassedArg(argslot);
1398 masm.storeValue(val, Address(StackPointer, stack_offset));
1400 uint32_t slot = StackOffsetToSlot(stack_offset);
1401 JS_ASSERT(slot - 1u < graph.totalSlotCount());
1402 return pushedArgumentSlots_.append(slot);
1403 }
1405 bool
1406 CodeGenerator::visitMoveGroup(LMoveGroup *group)
1407 {
1408 if (!group->numMoves())
1409 return true;
1411 MoveResolver &resolver = masm.moveResolver();
1413 for (size_t i = 0; i < group->numMoves(); i++) {
1414 const LMove &move = group->getMove(i);
1416 const LAllocation *from = move.from();
1417 const LAllocation *to = move.to();
1418 LDefinition::Type type = move.type();
1420 // No bogus moves.
1421 JS_ASSERT(*from != *to);
1422 JS_ASSERT(!from->isConstant());
1424 MoveOp::Type moveType;
1425 switch (type) {
1426 case LDefinition::OBJECT:
1427 case LDefinition::SLOTS:
1428 #ifdef JS_NUNBOX32
1429 case LDefinition::TYPE:
1430 case LDefinition::PAYLOAD:
1431 #else
1432 case LDefinition::BOX:
1433 #endif
1434 case LDefinition::GENERAL: moveType = MoveOp::GENERAL; break;
1435 case LDefinition::INT32: moveType = MoveOp::INT32; break;
1436 case LDefinition::FLOAT32: moveType = MoveOp::FLOAT32; break;
1437 case LDefinition::DOUBLE: moveType = MoveOp::DOUBLE; break;
1438 default: MOZ_ASSUME_UNREACHABLE("Unexpected move type");
1439 }
1441 if (!resolver.addMove(toMoveOperand(from), toMoveOperand(to), moveType))
1442 return false;
1443 }
1445 if (!resolver.resolve())
1446 return false;
1448 MoveEmitter emitter(masm);
1449 emitter.emit(resolver);
1450 emitter.finish();
1452 return true;
1453 }
1455 bool
1456 CodeGenerator::visitInteger(LInteger *lir)
1457 {
1458 masm.move32(Imm32(lir->getValue()), ToRegister(lir->output()));
1459 return true;
1460 }
1462 bool
1463 CodeGenerator::visitPointer(LPointer *lir)
1464 {
1465 if (lir->kind() == LPointer::GC_THING)
1466 masm.movePtr(ImmGCPtr(lir->gcptr()), ToRegister(lir->output()));
1467 else
1468 masm.movePtr(ImmPtr(lir->ptr()), ToRegister(lir->output()));
1469 return true;
1470 }
1472 bool
1473 CodeGenerator::visitSlots(LSlots *lir)
1474 {
1475 Address slots(ToRegister(lir->object()), JSObject::offsetOfSlots());
1476 masm.loadPtr(slots, ToRegister(lir->output()));
1477 return true;
1478 }
1480 bool
1481 CodeGenerator::visitStoreSlotV(LStoreSlotV *store)
1482 {
1483 Register base = ToRegister(store->slots());
1484 int32_t offset = store->mir()->slot() * sizeof(Value);
1486 const ValueOperand value = ToValue(store, LStoreSlotV::Value);
1488 if (store->mir()->needsBarrier())
1489 emitPreBarrier(Address(base, offset), MIRType_Value);
1491 masm.storeValue(value, Address(base, offset));
1492 return true;
1493 }
1495 bool
1496 CodeGenerator::emitGetPropertyPolymorphic(LInstruction *ins, Register obj, Register scratch,
1497 const TypedOrValueRegister &output)
1498 {
1499 MGetPropertyPolymorphic *mir = ins->mirRaw()->toGetPropertyPolymorphic();
1500 JS_ASSERT(mir->numShapes() > 1);
1502 masm.loadObjShape(obj, scratch);
1504 Label done;
1505 for (size_t i = 0; i < mir->numShapes(); i++) {
1506 Label next;
1507 masm.branchPtr(Assembler::NotEqual, scratch, ImmGCPtr(mir->objShape(i)), &next);
1509 Shape *shape = mir->shape(i);
1510 if (shape->slot() < shape->numFixedSlots()) {
1511 // Fixed slot.
1512 masm.loadTypedOrValue(Address(obj, JSObject::getFixedSlotOffset(shape->slot())),
1513 output);
1514 } else {
1515 // Dynamic slot.
1516 uint32_t offset = (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value);
1517 masm.loadPtr(Address(obj, JSObject::offsetOfSlots()), scratch);
1518 masm.loadTypedOrValue(Address(scratch, offset), output);
1519 }
1521 masm.jump(&done);
1522 masm.bind(&next);
1523 }
1525 // Bailout if no shape matches.
1526 if (!bailout(ins->snapshot()))
1527 return false;
1529 masm.bind(&done);
1530 return true;
1531 }
1533 bool
1534 CodeGenerator::visitGetPropertyPolymorphicV(LGetPropertyPolymorphicV *ins)
1535 {
1536 Register obj = ToRegister(ins->obj());
1537 ValueOperand output = GetValueOutput(ins);
1538 return emitGetPropertyPolymorphic(ins, obj, output.scratchReg(), output);
1539 }
1541 bool
1542 CodeGenerator::visitGetPropertyPolymorphicT(LGetPropertyPolymorphicT *ins)
1543 {
1544 Register obj = ToRegister(ins->obj());
1545 TypedOrValueRegister output(ins->mir()->type(), ToAnyRegister(ins->output()));
1546 Register temp = (output.type() == MIRType_Double)
1547 ? ToRegister(ins->temp())
1548 : output.typedReg().gpr();
1549 return emitGetPropertyPolymorphic(ins, obj, temp, output);
1550 }
1552 bool
1553 CodeGenerator::emitSetPropertyPolymorphic(LInstruction *ins, Register obj, Register scratch,
1554 const ConstantOrRegister &value)
1555 {
1556 MSetPropertyPolymorphic *mir = ins->mirRaw()->toSetPropertyPolymorphic();
1557 JS_ASSERT(mir->numShapes() > 1);
1559 masm.loadObjShape(obj, scratch);
1561 Label done;
1562 for (size_t i = 0; i < mir->numShapes(); i++) {
1563 Label next;
1564 masm.branchPtr(Assembler::NotEqual, scratch, ImmGCPtr(mir->objShape(i)), &next);
1566 Shape *shape = mir->shape(i);
1567 if (shape->slot() < shape->numFixedSlots()) {
1568 // Fixed slot.
1569 Address addr(obj, JSObject::getFixedSlotOffset(shape->slot()));
1570 if (mir->needsBarrier())
1571 emitPreBarrier(addr, MIRType_Value);
1572 masm.storeConstantOrRegister(value, addr);
1573 } else {
1574 // Dynamic slot.
1575 masm.loadPtr(Address(obj, JSObject::offsetOfSlots()), scratch);
1576 Address addr(scratch, (shape->slot() - shape->numFixedSlots()) * sizeof(js::Value));
1577 if (mir->needsBarrier())
1578 emitPreBarrier(addr, MIRType_Value);
1579 masm.storeConstantOrRegister(value, addr);
1580 }
1582 masm.jump(&done);
1583 masm.bind(&next);
1584 }
1586 // Bailout if no shape matches.
1587 if (!bailout(ins->snapshot()))
1588 return false;
1590 masm.bind(&done);
1591 return true;
1592 }
1594 bool
1595 CodeGenerator::visitSetPropertyPolymorphicV(LSetPropertyPolymorphicV *ins)
1596 {
1597 Register obj = ToRegister(ins->obj());
1598 Register temp = ToRegister(ins->temp());
1599 ValueOperand value = ToValue(ins, LSetPropertyPolymorphicV::Value);
1600 return emitSetPropertyPolymorphic(ins, obj, temp, TypedOrValueRegister(value));
1601 }
1603 bool
1604 CodeGenerator::visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT *ins)
1605 {
1606 Register obj = ToRegister(ins->obj());
1607 Register temp = ToRegister(ins->temp());
1609 ConstantOrRegister value;
1610 if (ins->mir()->value()->isConstant())
1611 value = ConstantOrRegister(ins->mir()->value()->toConstant()->value());
1612 else
1613 value = TypedOrValueRegister(ins->mir()->value()->type(), ToAnyRegister(ins->value()));
1615 return emitSetPropertyPolymorphic(ins, obj, temp, value);
1616 }
1618 bool
1619 CodeGenerator::visitElements(LElements *lir)
1620 {
1621 Address elements(ToRegister(lir->object()), JSObject::offsetOfElements());
1622 masm.loadPtr(elements, ToRegister(lir->output()));
1623 return true;
1624 }
1626 typedef bool (*ConvertElementsToDoublesFn)(JSContext *, uintptr_t);
1627 static const VMFunction ConvertElementsToDoublesInfo =
1628 FunctionInfo<ConvertElementsToDoublesFn>(ObjectElements::ConvertElementsToDoubles);
1630 bool
1631 CodeGenerator::visitConvertElementsToDoubles(LConvertElementsToDoubles *lir)
1632 {
1633 Register elements = ToRegister(lir->elements());
1635 OutOfLineCode *ool = oolCallVM(ConvertElementsToDoublesInfo, lir,
1636 (ArgList(), elements), StoreNothing());
1637 if (!ool)
1638 return false;
1640 Address convertedAddress(elements, ObjectElements::offsetOfFlags());
1641 Imm32 bit(ObjectElements::CONVERT_DOUBLE_ELEMENTS);
1642 masm.branchTest32(Assembler::Zero, convertedAddress, bit, ool->entry());
1643 masm.bind(ool->rejoin());
1644 return true;
1645 }
1647 bool
1648 CodeGenerator::visitMaybeToDoubleElement(LMaybeToDoubleElement *lir)
1649 {
1650 Register elements = ToRegister(lir->elements());
1651 Register value = ToRegister(lir->value());
1652 ValueOperand out = ToOutValue(lir);
1654 FloatRegister temp = ToFloatRegister(lir->tempFloat());
1655 Label convert, done;
1657 // If the CONVERT_DOUBLE_ELEMENTS flag is set, convert the int32
1658 // value to double. Else, just box it.
1659 masm.branchTest32(Assembler::NonZero,
1660 Address(elements, ObjectElements::offsetOfFlags()),
1661 Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS),
1662 &convert);
1664 masm.tagValue(JSVAL_TYPE_INT32, value, out);
1665 masm.jump(&done);
1667 masm.bind(&convert);
1668 masm.convertInt32ToDouble(value, temp);
1669 masm.boxDouble(temp, out);
1671 masm.bind(&done);
1672 return true;
1673 }
1675 bool
1676 CodeGenerator::visitFunctionEnvironment(LFunctionEnvironment *lir)
1677 {
1678 Address environment(ToRegister(lir->function()), JSFunction::offsetOfEnvironment());
1679 masm.loadPtr(environment, ToRegister(lir->output()));
1680 return true;
1681 }
1683 bool
1684 CodeGenerator::visitForkJoinContext(LForkJoinContext *lir)
1685 {
1686 const Register tempReg = ToRegister(lir->getTempReg());
1688 masm.setupUnalignedABICall(0, tempReg);
1689 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ForkJoinContextPar));
1690 JS_ASSERT(ToRegister(lir->output()) == ReturnReg);
1691 return true;
1692 }
1694 bool
1695 CodeGenerator::visitGuardThreadExclusive(LGuardThreadExclusive *lir)
1696 {
1697 JS_ASSERT(gen->info().executionMode() == ParallelExecution);
1699 const Register tempReg = ToRegister(lir->getTempReg());
1700 masm.setupUnalignedABICall(2, tempReg);
1701 masm.passABIArg(ToRegister(lir->forkJoinContext()));
1702 masm.passABIArg(ToRegister(lir->object()));
1703 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParallelWriteGuard));
1705 OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutIllegalWrite, lir);
1706 if (!bail)
1707 return false;
1709 // branch to the OOL failure code if false is returned
1710 masm.branchIfFalseBool(ReturnReg, bail->entry());
1711 return true;
1712 }
1714 bool
1715 CodeGenerator::visitGuardObjectIdentity(LGuardObjectIdentity *guard)
1716 {
1717 Register obj = ToRegister(guard->input());
1719 Assembler::Condition cond =
1720 guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
1721 return bailoutCmpPtr(cond, obj, ImmGCPtr(guard->mir()->singleObject()), guard->snapshot());
1722 }
1724 bool
1725 CodeGenerator::visitTypeBarrierV(LTypeBarrierV *lir)
1726 {
1727 ValueOperand operand = ToValue(lir, LTypeBarrierV::Input);
1728 Register scratch = ToTempRegisterOrInvalid(lir->temp());
1730 Label miss;
1731 masm.guardTypeSet(operand, lir->mir()->resultTypeSet(), scratch, &miss);
1732 if (!bailoutFrom(&miss, lir->snapshot()))
1733 return false;
1734 return true;
1735 }
1737 bool
1738 CodeGenerator::visitTypeBarrierO(LTypeBarrierO *lir)
1739 {
1740 Register obj = ToRegister(lir->object());
1741 Register scratch = ToTempRegisterOrInvalid(lir->temp());
1743 Label miss;
1744 masm.guardObjectType(obj, lir->mir()->resultTypeSet(), scratch, &miss);
1745 if (!bailoutFrom(&miss, lir->snapshot()))
1746 return false;
1747 return true;
1748 }
1750 bool
1751 CodeGenerator::visitMonitorTypes(LMonitorTypes *lir)
1752 {
1753 ValueOperand operand = ToValue(lir, LMonitorTypes::Input);
1754 Register scratch = ToTempUnboxRegister(lir->temp());
1756 Label matched, miss;
1757 masm.guardTypeSet(operand, lir->mir()->typeSet(), scratch, &miss);
1758 if (!bailoutFrom(&miss, lir->snapshot()))
1759 return false;
1760 return true;
1761 }
1763 #ifdef JSGC_GENERATIONAL
1764 // Out-of-line path to update the store buffer.
1765 class OutOfLineCallPostWriteBarrier : public OutOfLineCodeBase<CodeGenerator>
1766 {
1767 LInstruction *lir_;
1768 const LAllocation *object_;
1770 public:
1771 OutOfLineCallPostWriteBarrier(LInstruction *lir, const LAllocation *object)
1772 : lir_(lir), object_(object)
1773 { }
1775 bool accept(CodeGenerator *codegen) {
1776 return codegen->visitOutOfLineCallPostWriteBarrier(this);
1777 }
1779 LInstruction *lir() const {
1780 return lir_;
1781 }
1782 const LAllocation *object() const {
1783 return object_;
1784 }
1785 };
1787 bool
1788 CodeGenerator::visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier *ool)
1789 {
1790 saveLiveVolatile(ool->lir());
1792 const LAllocation *obj = ool->object();
1794 GeneralRegisterSet regs = GeneralRegisterSet::Volatile();
1796 Register objreg;
1797 bool isGlobal = false;
1798 if (obj->isConstant()) {
1799 JSObject *object = &obj->toConstant()->toObject();
1800 isGlobal = object->is<GlobalObject>();
1801 objreg = regs.takeAny();
1802 masm.movePtr(ImmGCPtr(object), objreg);
1803 } else {
1804 objreg = ToRegister(obj);
1805 regs.takeUnchecked(objreg);
1806 }
1808 Register runtimereg = regs.takeAny();
1809 masm.mov(ImmPtr(GetIonContext()->runtime), runtimereg);
1811 void (*fun)(JSRuntime*, JSObject*) = isGlobal ? PostGlobalWriteBarrier : PostWriteBarrier;
1812 masm.setupUnalignedABICall(2, regs.takeAny());
1813 masm.passABIArg(runtimereg);
1814 masm.passABIArg(objreg);
1815 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, fun));
1817 restoreLiveVolatile(ool->lir());
1819 masm.jump(ool->rejoin());
1820 return true;
1821 }
1822 #endif
1824 bool
1825 CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO *lir)
1826 {
1827 #ifdef JSGC_GENERATIONAL
1828 OutOfLineCallPostWriteBarrier *ool = new(alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
1829 if (!addOutOfLineCode(ool))
1830 return false;
1832 Register temp = ToTempRegisterOrInvalid(lir->temp());
1834 if (lir->object()->isConstant()) {
1835 #ifdef DEBUG
1836 const Nursery &nursery = GetIonContext()->runtime->gcNursery();
1837 JS_ASSERT(!nursery.isInside(&lir->object()->toConstant()->toObject()));
1838 #endif
1839 } else {
1840 masm.branchPtrInNurseryRange(ToRegister(lir->object()), temp, ool->rejoin());
1841 }
1843 masm.branchPtrInNurseryRange(ToRegister(lir->value()), temp, ool->entry());
1845 masm.bind(ool->rejoin());
1846 #endif
1847 return true;
1848 }
1850 bool
1851 CodeGenerator::visitPostWriteBarrierV(LPostWriteBarrierV *lir)
1852 {
1853 #ifdef JSGC_GENERATIONAL
1854 OutOfLineCallPostWriteBarrier *ool = new(alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
1855 if (!addOutOfLineCode(ool))
1856 return false;
1858 Register temp = ToTempRegisterOrInvalid(lir->temp());
1860 if (lir->object()->isConstant()) {
1861 #ifdef DEBUG
1862 const Nursery &nursery = GetIonContext()->runtime->gcNursery();
1863 JS_ASSERT(!nursery.isInside(&lir->object()->toConstant()->toObject()));
1864 #endif
1865 } else {
1866 masm.branchPtrInNurseryRange(ToRegister(lir->object()), temp, ool->rejoin());
1867 }
1869 ValueOperand value = ToValue(lir, LPostWriteBarrierV::Input);
1870 masm.branchValueIsNurseryObject(value, temp, ool->entry());
1872 masm.bind(ool->rejoin());
1873 #endif
1874 return true;
1875 }
1877 bool
1878 CodeGenerator::visitCallNative(LCallNative *call)
1879 {
1880 JSFunction *target = call->getSingleTarget();
1881 JS_ASSERT(target);
1882 JS_ASSERT(target->isNative());
1884 int callargslot = call->argslot();
1885 int unusedStack = StackOffsetOfPassedArg(callargslot);
1887 // Registers used for callWithABI() argument-passing.
1888 const Register argContextReg = ToRegister(call->getArgContextReg());
1889 const Register argUintNReg = ToRegister(call->getArgUintNReg());
1890 const Register argVpReg = ToRegister(call->getArgVpReg());
1892 // Misc. temporary registers.
1893 const Register tempReg = ToRegister(call->getTempReg());
1895 DebugOnly<uint32_t> initialStack = masm.framePushed();
1897 masm.checkStackAlignment();
1899 // Sequential native functions have the signature:
1900 // bool (*)(JSContext *, unsigned, Value *vp)
1901 // and parallel native functions have the signature:
1902 // ParallelResult (*)(ForkJoinContext *, unsigned, Value *vp)
1903 // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
1904 // are the function arguments.
1906 // Allocate space for the outparam, moving the StackPointer to what will be &vp[1].
1907 masm.adjustStack(unusedStack);
1909 // Push a Value containing the callee object: natives are allowed to access their callee before
1910 // setitng the return value. The StackPointer is moved to &vp[0].
1911 masm.Push(ObjectValue(*target));
1913 // Preload arguments into registers.
1914 //
1915 // Note that for parallel execution, loadContext does an ABI call, so we
1916 // need to do this before we load the other argument registers, otherwise
1917 // we'll hose them.
1918 ExecutionMode executionMode = gen->info().executionMode();
1919 masm.loadContext(argContextReg, tempReg, executionMode);
1920 masm.move32(Imm32(call->numStackArgs()), argUintNReg);
1921 masm.movePtr(StackPointer, argVpReg);
1923 masm.Push(argUintNReg);
1925 // Construct native exit frame.
1926 uint32_t safepointOffset;
1927 if (!masm.buildFakeExitFrame(tempReg, &safepointOffset))
1928 return false;
1929 masm.enterFakeExitFrame(argContextReg, tempReg, executionMode);
1931 if (!markSafepointAt(safepointOffset, call))
1932 return false;
1934 // Construct and execute call.
1935 masm.setupUnalignedABICall(3, tempReg);
1936 masm.passABIArg(argContextReg);
1937 masm.passABIArg(argUintNReg);
1938 masm.passABIArg(argVpReg);
1940 switch (executionMode) {
1941 case SequentialExecution:
1942 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->native()));
1943 break;
1945 case ParallelExecution:
1946 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->parallelNative()));
1947 break;
1949 default:
1950 MOZ_ASSUME_UNREACHABLE("No such execution mode");
1951 }
1953 // Test for failure.
1954 masm.branchIfFalseBool(ReturnReg, masm.failureLabel(executionMode));
1956 // Load the outparam vp[0] into output register(s).
1957 masm.loadValue(Address(StackPointer, IonNativeExitFrameLayout::offsetOfResult()), JSReturnOperand);
1959 // The next instruction is removing the footer of the exit frame, so there
1960 // is no need for leaveFakeExitFrame.
1962 // Move the StackPointer back to its original location, unwinding the native exit frame.
1963 masm.adjustStack(IonNativeExitFrameLayout::Size() - unusedStack);
1964 JS_ASSERT(masm.framePushed() == initialStack);
1966 dropArguments(call->numStackArgs() + 1);
1967 return true;
1968 }
1970 bool
1971 CodeGenerator::visitCallDOMNative(LCallDOMNative *call)
1972 {
1973 JSFunction *target = call->getSingleTarget();
1974 JS_ASSERT(target);
1975 JS_ASSERT(target->isNative());
1976 JS_ASSERT(target->jitInfo());
1977 JS_ASSERT(call->mir()->isCallDOMNative());
1979 int callargslot = call->argslot();
1980 int unusedStack = StackOffsetOfPassedArg(callargslot);
1982 // Registers used for callWithABI() argument-passing.
1983 const Register argJSContext = ToRegister(call->getArgJSContext());
1984 const Register argObj = ToRegister(call->getArgObj());
1985 const Register argPrivate = ToRegister(call->getArgPrivate());
1986 const Register argArgs = ToRegister(call->getArgArgs());
1988 DebugOnly<uint32_t> initialStack = masm.framePushed();
1990 masm.checkStackAlignment();
1992 // DOM methods have the signature:
1993 // bool (*)(JSContext *, HandleObject, void *private, const JSJitMethodCallArgs& args)
1994 // Where args is initialized from an argc and a vp, vp[0] is space for an
1995 // outparam and the callee, vp[1] is |this|, and vp[2] onward are the
1996 // function arguments. Note that args stores the argv, not the vp, and
1997 // argv == vp + 2.
1999 // Nestle the stack up against the pushed arguments, leaving StackPointer at
2000 // &vp[1]
2001 masm.adjustStack(unusedStack);
2002 // argObj is filled with the extracted object, then returned.
2003 Register obj = masm.extractObject(Address(StackPointer, 0), argObj);
2004 JS_ASSERT(obj == argObj);
2006 // Push a Value containing the callee object: natives are allowed to access their callee before
2007 // setitng the return value. After this the StackPointer points to &vp[0].
2008 masm.Push(ObjectValue(*target));
2010 // Now compute the argv value. Since StackPointer is pointing to &vp[0] and
2011 // argv is &vp[2] we just need to add 2*sizeof(Value) to the current
2012 // StackPointer.
2013 JS_STATIC_ASSERT(JSJitMethodCallArgsTraits::offsetOfArgv == 0);
2014 JS_STATIC_ASSERT(JSJitMethodCallArgsTraits::offsetOfArgc ==
2015 IonDOMMethodExitFrameLayoutTraits::offsetOfArgcFromArgv);
2016 masm.computeEffectiveAddress(Address(StackPointer, 2 * sizeof(Value)), argArgs);
2018 // GetReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate()
2019 masm.loadPrivate(Address(obj, JSObject::getFixedSlotOffset(0)), argPrivate);
2021 // Push argc from the call instruction into what will become the IonExitFrame
2022 masm.Push(Imm32(call->numStackArgs()));
2024 // Push our argv onto the stack
2025 masm.Push(argArgs);
2026 // And store our JSJitMethodCallArgs* in argArgs.
2027 masm.movePtr(StackPointer, argArgs);
2029 // Push |this| object for passing HandleObject. We push after argc to
2030 // maintain the same sp-relative location of the object pointer with other
2031 // DOMExitFrames.
2032 masm.Push(argObj);
2033 masm.movePtr(StackPointer, argObj);
2035 // Construct native exit frame.
2036 uint32_t safepointOffset;
2037 if (!masm.buildFakeExitFrame(argJSContext, &safepointOffset))
2038 return false;
2039 masm.enterFakeExitFrame(ION_FRAME_DOMMETHOD);
2041 if (!markSafepointAt(safepointOffset, call))
2042 return false;
2044 // Construct and execute call.
2045 masm.setupUnalignedABICall(4, argJSContext);
2047 masm.loadJSContext(argJSContext);
2049 masm.passABIArg(argJSContext);
2050 masm.passABIArg(argObj);
2051 masm.passABIArg(argPrivate);
2052 masm.passABIArg(argArgs);
2053 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->jitInfo()->method));
2055 if (target->jitInfo()->isInfallible) {
2056 masm.loadValue(Address(StackPointer, IonDOMMethodExitFrameLayout::offsetOfResult()),
2057 JSReturnOperand);
2058 } else {
2059 // Test for failure.
2060 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
2062 // Load the outparam vp[0] into output register(s).
2063 masm.loadValue(Address(StackPointer, IonDOMMethodExitFrameLayout::offsetOfResult()),
2064 JSReturnOperand);
2065 }
2067 // The next instruction is removing the footer of the exit frame, so there
2068 // is no need for leaveFakeExitFrame.
2070 // Move the StackPointer back to its original location, unwinding the native exit frame.
2071 masm.adjustStack(IonDOMMethodExitFrameLayout::Size() - unusedStack);
2072 JS_ASSERT(masm.framePushed() == initialStack);
2074 dropArguments(call->numStackArgs() + 1);
2075 return true;
2076 }
2078 typedef bool (*GetIntrinsicValueFn)(JSContext *cx, HandlePropertyName, MutableHandleValue);
2079 static const VMFunction GetIntrinsicValueInfo =
2080 FunctionInfo<GetIntrinsicValueFn>(GetIntrinsicValue);
2082 bool
2083 CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue *lir)
2084 {
2085 pushArg(ImmGCPtr(lir->mir()->name()));
2086 return callVM(GetIntrinsicValueInfo, lir);
2087 }
2089 typedef bool (*InvokeFunctionFn)(JSContext *, HandleObject, uint32_t, Value *, Value *);
2090 static const VMFunction InvokeFunctionInfo = FunctionInfo<InvokeFunctionFn>(InvokeFunction);
2092 bool
2093 CodeGenerator::emitCallInvokeFunction(LInstruction *call, Register calleereg,
2094 uint32_t argc, uint32_t unusedStack)
2095 {
2096 // Nestle %esp up to the argument vector.
2097 // Each path must account for framePushed_ separately, for callVM to be valid.
2098 masm.freeStack(unusedStack);
2100 pushArg(StackPointer); // argv.
2101 pushArg(Imm32(argc)); // argc.
2102 pushArg(calleereg); // JSFunction *.
2104 if (!callVM(InvokeFunctionInfo, call))
2105 return false;
2107 // Un-nestle %esp from the argument vector. No prefix was pushed.
2108 masm.reserveStack(unusedStack);
2109 return true;
2110 }
2112 bool
2113 CodeGenerator::visitCallGeneric(LCallGeneric *call)
2114 {
2115 Register calleereg = ToRegister(call->getFunction());
2116 Register objreg = ToRegister(call->getTempObject());
2117 Register nargsreg = ToRegister(call->getNargsReg());
2118 uint32_t unusedStack = StackOffsetOfPassedArg(call->argslot());
2119 ExecutionMode executionMode = gen->info().executionMode();
2120 Label invoke, thunk, makeCall, end;
2122 // Known-target case is handled by LCallKnown.
2123 JS_ASSERT(!call->hasSingleTarget());
2125 // Generate an ArgumentsRectifier.
2126 JitCode *argumentsRectifier = gen->jitRuntime()->getArgumentsRectifier(executionMode);
2128 masm.checkStackAlignment();
2130 // Guard that calleereg is actually a function object.
2131 masm.loadObjClass(calleereg, nargsreg);
2132 masm.branchPtr(Assembler::NotEqual, nargsreg, ImmPtr(&JSFunction::class_), &invoke);
2134 // Guard that calleereg is an interpreted function with a JSScript.
2135 // If we are constructing, also ensure the callee is a constructor.
2136 if (call->mir()->isConstructing())
2137 masm.branchIfNotInterpretedConstructor(calleereg, nargsreg, &invoke);
2138 else
2139 masm.branchIfFunctionHasNoScript(calleereg, &invoke);
2141 // Knowing that calleereg is a non-native function, load the JSScript.
2142 masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
2144 // Load script jitcode.
2145 masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &invoke);
2147 // Nestle the StackPointer up to the argument vector.
2148 masm.freeStack(unusedStack);
2150 // Construct the IonFramePrefix.
2151 uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS);
2152 masm.Push(Imm32(call->numActualArgs()));
2153 masm.Push(calleereg);
2154 masm.Push(Imm32(descriptor));
2156 // Check whether the provided arguments satisfy target argc.
2157 masm.load16ZeroExtend(Address(calleereg, JSFunction::offsetOfNargs()), nargsreg);
2158 masm.branch32(Assembler::Above, nargsreg, Imm32(call->numStackArgs()), &thunk);
2159 masm.jump(&makeCall);
2161 // Argument fixed needed. Load the ArgumentsRectifier.
2162 masm.bind(&thunk);
2163 {
2164 JS_ASSERT(ArgumentsRectifierReg != objreg);
2165 masm.movePtr(ImmGCPtr(argumentsRectifier), objreg); // Necessary for GC marking.
2166 masm.loadPtr(Address(objreg, JitCode::offsetOfCode()), objreg);
2167 masm.move32(Imm32(call->numStackArgs()), ArgumentsRectifierReg);
2168 }
2170 // Finally call the function in objreg.
2171 masm.bind(&makeCall);
2172 uint32_t callOffset = masm.callIon(objreg);
2173 if (!markSafepointAt(callOffset, call))
2174 return false;
2176 // Increment to remove IonFramePrefix; decrement to fill FrameSizeClass.
2177 // The return address has already been removed from the Ion frame.
2178 int prefixGarbage = sizeof(IonJSFrameLayout) - sizeof(void *);
2179 masm.adjustStack(prefixGarbage - unusedStack);
2180 masm.jump(&end);
2182 // Handle uncompiled or native functions.
2183 masm.bind(&invoke);
2184 switch (executionMode) {
2185 case SequentialExecution:
2186 if (!emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack))
2187 return false;
2188 break;
2190 case ParallelExecution:
2191 if (!emitCallToUncompiledScriptPar(call, calleereg))
2192 return false;
2193 break;
2195 default:
2196 MOZ_ASSUME_UNREACHABLE("No such execution mode");
2197 }
2199 masm.bind(&end);
2201 // If the return value of the constructing function is Primitive,
2202 // replace the return value with the Object from CreateThis.
2203 if (call->mir()->isConstructing()) {
2204 Label notPrimitive;
2205 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, ¬Primitive);
2206 masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand);
2207 masm.bind(¬Primitive);
2208 }
2210 if (!checkForAbortPar(call))
2211 return false;
2213 dropArguments(call->numStackArgs() + 1);
2214 return true;
2215 }
2217 // Generates a call to CallToUncompiledScriptPar() and then bails out.
2218 // |calleeReg| should contain the JSFunction*.
2219 bool
2220 CodeGenerator::emitCallToUncompiledScriptPar(LInstruction *lir, Register calleeReg)
2221 {
2222 OutOfLineCode *bail = oolAbortPar(ParallelBailoutCalledToUncompiledScript, lir);
2223 if (!bail)
2224 return false;
2226 masm.movePtr(calleeReg, CallTempReg0);
2227 masm.setupUnalignedABICall(1, CallTempReg1);
2228 masm.passABIArg(CallTempReg0);
2229 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, CallToUncompiledScriptPar));
2230 masm.jump(bail->entry());
2231 return true;
2232 }
2234 bool
2235 CodeGenerator::visitCallKnown(LCallKnown *call)
2236 {
2237 Register calleereg = ToRegister(call->getFunction());
2238 Register objreg = ToRegister(call->getTempObject());
2239 uint32_t unusedStack = StackOffsetOfPassedArg(call->argslot());
2240 DebugOnly<JSFunction *> target = call->getSingleTarget();
2241 ExecutionMode executionMode = gen->info().executionMode();
2242 Label end, uncompiled;
2244 // Native single targets are handled by LCallNative.
2245 JS_ASSERT(!target->isNative());
2246 // Missing arguments must have been explicitly appended by the IonBuilder.
2247 JS_ASSERT(target->nargs() <= call->numStackArgs());
2249 JS_ASSERT_IF(call->mir()->isConstructing(), target->isInterpretedConstructor());
2251 masm.checkStackAlignment();
2253 // The calleereg is known to be a non-native function, but might point to
2254 // a LazyScript instead of a JSScript.
2255 masm.branchIfFunctionHasNoScript(calleereg, &uncompiled);
2257 // Knowing that calleereg is a non-native function, load the JSScript.
2258 masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
2260 // Load script jitcode.
2261 if (call->mir()->needsArgCheck())
2262 masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &uncompiled);
2263 else
2264 masm.loadBaselineOrIonNoArgCheck(objreg, objreg, executionMode, &uncompiled);
2266 // Nestle the StackPointer up to the argument vector.
2267 masm.freeStack(unusedStack);
2269 // Construct the IonFramePrefix.
2270 uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS);
2271 masm.Push(Imm32(call->numActualArgs()));
2272 masm.Push(calleereg);
2273 masm.Push(Imm32(descriptor));
2275 // Finally call the function in objreg.
2276 uint32_t callOffset = masm.callIon(objreg);
2277 if (!markSafepointAt(callOffset, call))
2278 return false;
2280 // Increment to remove IonFramePrefix; decrement to fill FrameSizeClass.
2281 // The return address has already been removed from the Ion frame.
2282 int prefixGarbage = sizeof(IonJSFrameLayout) - sizeof(void *);
2283 masm.adjustStack(prefixGarbage - unusedStack);
2284 masm.jump(&end);
2286 // Handle uncompiled functions.
2287 masm.bind(&uncompiled);
2288 switch (executionMode) {
2289 case SequentialExecution:
2290 if (!emitCallInvokeFunction(call, calleereg, call->numActualArgs(), unusedStack))
2291 return false;
2292 break;
2294 case ParallelExecution:
2295 if (!emitCallToUncompiledScriptPar(call, calleereg))
2296 return false;
2297 break;
2299 default:
2300 MOZ_ASSUME_UNREACHABLE("No such execution mode");
2301 }
2303 masm.bind(&end);
2305 if (!checkForAbortPar(call))
2306 return false;
2308 // If the return value of the constructing function is Primitive,
2309 // replace the return value with the Object from CreateThis.
2310 if (call->mir()->isConstructing()) {
2311 Label notPrimitive;
2312 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, ¬Primitive);
2313 masm.loadValue(Address(StackPointer, unusedStack), JSReturnOperand);
2314 masm.bind(¬Primitive);
2315 }
2317 dropArguments(call->numStackArgs() + 1);
2318 return true;
2319 }
2321 bool
2322 CodeGenerator::checkForAbortPar(LInstruction *lir)
2323 {
2324 // In parallel mode, if we call another ion-compiled function and
2325 // it returns JS_ION_ERROR, that indicates a bailout that we have
2326 // to propagate up the stack.
2327 ExecutionMode executionMode = gen->info().executionMode();
2328 if (executionMode == ParallelExecution) {
2329 OutOfLinePropagateAbortPar *bail = oolPropagateAbortPar(lir);
2330 if (!bail)
2331 return false;
2332 masm.branchTestMagic(Assembler::Equal, JSReturnOperand, bail->entry());
2333 }
2334 return true;
2335 }
2337 bool
2338 CodeGenerator::emitCallInvokeFunction(LApplyArgsGeneric *apply, Register extraStackSize)
2339 {
2340 Register objreg = ToRegister(apply->getTempObject());
2341 JS_ASSERT(objreg != extraStackSize);
2343 // Push the space used by the arguments.
2344 masm.movePtr(StackPointer, objreg);
2345 masm.Push(extraStackSize);
2347 pushArg(objreg); // argv.
2348 pushArg(ToRegister(apply->getArgc())); // argc.
2349 pushArg(ToRegister(apply->getFunction())); // JSFunction *.
2351 // This specialization og callVM restore the extraStackSize after the call.
2352 if (!callVM(InvokeFunctionInfo, apply, &extraStackSize))
2353 return false;
2355 masm.Pop(extraStackSize);
2356 return true;
2357 }
2359 // Do not bailout after the execution of this function since the stack no longer
2360 // correspond to what is expected by the snapshots.
2361 void
2362 CodeGenerator::emitPushArguments(LApplyArgsGeneric *apply, Register extraStackSpace)
2363 {
2364 // Holds the function nargs. Initially undefined.
2365 Register argcreg = ToRegister(apply->getArgc());
2367 Register copyreg = ToRegister(apply->getTempObject());
2368 size_t argvOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs();
2369 Label end;
2371 // Initialize the loop counter AND Compute the stack usage (if == 0)
2372 masm.movePtr(argcreg, extraStackSpace);
2373 masm.branchTestPtr(Assembler::Zero, argcreg, argcreg, &end);
2375 // Copy arguments.
2376 {
2377 Register count = extraStackSpace; // <- argcreg
2378 Label loop;
2379 masm.bind(&loop);
2381 // We remove sizeof(void*) from argvOffset because withtout it we target
2382 // the address after the memory area that we want to copy.
2383 BaseIndex disp(StackPointer, argcreg, ScaleFromElemWidth(sizeof(Value)), argvOffset - sizeof(void*));
2385 // Do not use Push here because other this account to 1 in the framePushed
2386 // instead of 0. These push are only counted by argcreg.
2387 masm.loadPtr(disp, copyreg);
2388 masm.push(copyreg);
2390 // Handle 32 bits architectures.
2391 if (sizeof(Value) == 2 * sizeof(void*)) {
2392 masm.loadPtr(disp, copyreg);
2393 masm.push(copyreg);
2394 }
2396 masm.decBranchPtr(Assembler::NonZero, count, Imm32(1), &loop);
2397 }
2399 // Compute the stack usage.
2400 masm.movePtr(argcreg, extraStackSpace);
2401 masm.lshiftPtr(Imm32::ShiftOf(ScaleFromElemWidth(sizeof(Value))), extraStackSpace);
2403 // Join with all arguments copied and the extra stack usage computed.
2404 masm.bind(&end);
2406 // Push |this|.
2407 masm.addPtr(Imm32(sizeof(Value)), extraStackSpace);
2408 masm.pushValue(ToValue(apply, LApplyArgsGeneric::ThisIndex));
2409 }
2411 void
2412 CodeGenerator::emitPopArguments(LApplyArgsGeneric *apply, Register extraStackSpace)
2413 {
2414 // Pop |this| and Arguments.
2415 masm.freeStack(extraStackSpace);
2416 }
2418 bool
2419 CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric *apply)
2420 {
2421 // Holds the function object.
2422 Register calleereg = ToRegister(apply->getFunction());
2424 // Temporary register for modifying the function object.
2425 Register objreg = ToRegister(apply->getTempObject());
2426 Register copyreg = ToRegister(apply->getTempCopy());
2428 // Holds the function nargs. Initially undefined.
2429 Register argcreg = ToRegister(apply->getArgc());
2431 // Unless already known, guard that calleereg is actually a function object.
2432 if (!apply->hasSingleTarget()) {
2433 masm.loadObjClass(calleereg, objreg);
2435 ImmPtr ptr = ImmPtr(&JSFunction::class_);
2436 if (!bailoutCmpPtr(Assembler::NotEqual, objreg, ptr, apply->snapshot()))
2437 return false;
2438 }
2440 // Copy the arguments of the current function.
2441 emitPushArguments(apply, copyreg);
2443 masm.checkStackAlignment();
2445 // If the function is known to be uncompilable, only emit the call to InvokeFunction.
2446 ExecutionMode executionMode = gen->info().executionMode();
2447 if (apply->hasSingleTarget()) {
2448 JSFunction *target = apply->getSingleTarget();
2449 if (target->isNative()) {
2450 if (!emitCallInvokeFunction(apply, copyreg))
2451 return false;
2452 emitPopArguments(apply, copyreg);
2453 return true;
2454 }
2455 }
2457 Label end, invoke;
2459 // Guard that calleereg is an interpreted function with a JSScript:
2460 if (!apply->hasSingleTarget()) {
2461 masm.branchIfFunctionHasNoScript(calleereg, &invoke);
2462 } else {
2463 // Native single targets are handled by LCallNative.
2464 JS_ASSERT(!apply->getSingleTarget()->isNative());
2465 }
2467 // Knowing that calleereg is a non-native function, load the JSScript.
2468 masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
2470 // Load script jitcode.
2471 masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &invoke);
2473 // Call with an Ion frame or a rectifier frame.
2474 {
2475 // Create the frame descriptor.
2476 unsigned pushed = masm.framePushed();
2477 masm.addPtr(Imm32(pushed), copyreg);
2478 masm.makeFrameDescriptor(copyreg, JitFrame_IonJS);
2480 masm.Push(argcreg);
2481 masm.Push(calleereg);
2482 masm.Push(copyreg); // descriptor
2484 Label underflow, rejoin;
2486 // Check whether the provided arguments satisfy target argc.
2487 if (!apply->hasSingleTarget()) {
2488 masm.load16ZeroExtend(Address(calleereg, JSFunction::offsetOfNargs()), copyreg);
2489 masm.branch32(Assembler::Below, argcreg, copyreg, &underflow);
2490 } else {
2491 masm.branch32(Assembler::Below, argcreg, Imm32(apply->getSingleTarget()->nargs()),
2492 &underflow);
2493 }
2495 // Skip the construction of the rectifier frame because we have no
2496 // underflow.
2497 masm.jump(&rejoin);
2499 // Argument fixup needed. Get ready to call the argumentsRectifier.
2500 {
2501 masm.bind(&underflow);
2503 // Hardcode the address of the argumentsRectifier code.
2504 JitCode *argumentsRectifier = gen->jitRuntime()->getArgumentsRectifier(executionMode);
2506 JS_ASSERT(ArgumentsRectifierReg != objreg);
2507 masm.movePtr(ImmGCPtr(argumentsRectifier), objreg); // Necessary for GC marking.
2508 masm.loadPtr(Address(objreg, JitCode::offsetOfCode()), objreg);
2509 masm.movePtr(argcreg, ArgumentsRectifierReg);
2510 }
2512 masm.bind(&rejoin);
2514 // Finally call the function in objreg, as assigned by one of the paths above.
2515 uint32_t callOffset = masm.callIon(objreg);
2516 if (!markSafepointAt(callOffset, apply))
2517 return false;
2519 // Recover the number of arguments from the frame descriptor.
2520 masm.loadPtr(Address(StackPointer, 0), copyreg);
2521 masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), copyreg);
2522 masm.subPtr(Imm32(pushed), copyreg);
2524 // Increment to remove IonFramePrefix; decrement to fill FrameSizeClass.
2525 // The return address has already been removed from the Ion frame.
2526 int prefixGarbage = sizeof(IonJSFrameLayout) - sizeof(void *);
2527 masm.adjustStack(prefixGarbage);
2528 masm.jump(&end);
2529 }
2531 // Handle uncompiled or native functions.
2532 {
2533 masm.bind(&invoke);
2534 if (!emitCallInvokeFunction(apply, copyreg))
2535 return false;
2536 }
2538 // Pop arguments and continue.
2539 masm.bind(&end);
2540 emitPopArguments(apply, copyreg);
2542 return true;
2543 }
2545 typedef bool (*ArraySpliceDenseFn)(JSContext *, HandleObject, uint32_t, uint32_t);
2546 static const VMFunction ArraySpliceDenseInfo = FunctionInfo<ArraySpliceDenseFn>(ArraySpliceDense);
2548 bool
2549 CodeGenerator::visitArraySplice(LArraySplice *lir)
2550 {
2551 pushArg(ToRegister(lir->getDeleteCount()));
2552 pushArg(ToRegister(lir->getStart()));
2553 pushArg(ToRegister(lir->getObject()));
2554 return callVM(ArraySpliceDenseInfo, lir);
2555 }
2558 bool
2559 CodeGenerator::visitBail(LBail *lir)
2560 {
2561 return bailout(lir->snapshot());
2562 }
2564 bool
2565 CodeGenerator::visitGetDynamicName(LGetDynamicName *lir)
2566 {
2567 Register scopeChain = ToRegister(lir->getScopeChain());
2568 Register name = ToRegister(lir->getName());
2569 Register temp1 = ToRegister(lir->temp1());
2570 Register temp2 = ToRegister(lir->temp2());
2571 Register temp3 = ToRegister(lir->temp3());
2573 masm.loadJSContext(temp3);
2575 /* Make space for the outparam. */
2576 masm.adjustStack(-int32_t(sizeof(Value)));
2577 masm.movePtr(StackPointer, temp2);
2579 masm.setupUnalignedABICall(4, temp1);
2580 masm.passABIArg(temp3);
2581 masm.passABIArg(scopeChain);
2582 masm.passABIArg(name);
2583 masm.passABIArg(temp2);
2584 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, GetDynamicName));
2586 const ValueOperand out = ToOutValue(lir);
2588 masm.loadValue(Address(StackPointer, 0), out);
2589 masm.adjustStack(sizeof(Value));
2591 Label undefined;
2592 masm.branchTestUndefined(Assembler::Equal, out, &undefined);
2593 return bailoutFrom(&undefined, lir->snapshot());
2594 }
2596 bool
2597 CodeGenerator::emitFilterArgumentsOrEval(LInstruction *lir, Register string,
2598 Register temp1, Register temp2)
2599 {
2600 masm.loadJSContext(temp2);
2602 masm.setupUnalignedABICall(2, temp1);
2603 masm.passABIArg(temp2);
2604 masm.passABIArg(string);
2605 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, FilterArgumentsOrEval));
2607 Label bail;
2608 masm.branchIfFalseBool(ReturnReg, &bail);
2609 return bailoutFrom(&bail, lir->snapshot());
2610 }
2612 bool
2613 CodeGenerator::visitFilterArgumentsOrEvalS(LFilterArgumentsOrEvalS *lir)
2614 {
2615 return emitFilterArgumentsOrEval(lir, ToRegister(lir->getString()),
2616 ToRegister(lir->temp1()),
2617 ToRegister(lir->temp2()));
2618 }
2620 bool
2621 CodeGenerator::visitFilterArgumentsOrEvalV(LFilterArgumentsOrEvalV *lir)
2622 {
2623 ValueOperand input = ToValue(lir, LFilterArgumentsOrEvalV::Input);
2625 // Act as nop on non-strings.
2626 Label done;
2627 masm.branchTestString(Assembler::NotEqual, input, &done);
2629 if (!emitFilterArgumentsOrEval(lir, masm.extractString(input, ToRegister(lir->temp3())),
2630 ToRegister(lir->temp1()), ToRegister(lir->temp2())))
2631 {
2632 return false;
2633 }
2635 masm.bind(&done);
2636 return true;
2637 }
2639 typedef bool (*DirectEvalSFn)(JSContext *, HandleObject, HandleScript, HandleValue, HandleString,
2640 jsbytecode *, MutableHandleValue);
2641 static const VMFunction DirectEvalStringInfo = FunctionInfo<DirectEvalSFn>(DirectEvalStringFromIon);
2643 bool
2644 CodeGenerator::visitCallDirectEvalS(LCallDirectEvalS *lir)
2645 {
2646 Register scopeChain = ToRegister(lir->getScopeChain());
2647 Register string = ToRegister(lir->getString());
2649 pushArg(ImmPtr(lir->mir()->pc()));
2650 pushArg(string);
2651 pushArg(ToValue(lir, LCallDirectEvalS::ThisValue));
2652 pushArg(ImmGCPtr(gen->info().script()));
2653 pushArg(scopeChain);
2655 return callVM(DirectEvalStringInfo, lir);
2656 }
2658 typedef bool (*DirectEvalVFn)(JSContext *, HandleObject, HandleScript, HandleValue, HandleValue,
2659 jsbytecode *, MutableHandleValue);
2660 static const VMFunction DirectEvalValueInfo = FunctionInfo<DirectEvalVFn>(DirectEvalValueFromIon);
2662 bool
2663 CodeGenerator::visitCallDirectEvalV(LCallDirectEvalV *lir)
2664 {
2665 Register scopeChain = ToRegister(lir->getScopeChain());
2667 pushArg(ImmPtr(lir->mir()->pc()));
2668 pushArg(ToValue(lir, LCallDirectEvalV::Argument));
2669 pushArg(ToValue(lir, LCallDirectEvalV::ThisValue));
2670 pushArg(ImmGCPtr(gen->info().script()));
2671 pushArg(scopeChain);
2673 return callVM(DirectEvalValueInfo, lir);
2674 }
2676 // Registers safe for use before generatePrologue().
2677 static const uint32_t EntryTempMask = Registers::TempMask & ~(1 << OsrFrameReg.code());
2679 bool
2680 CodeGenerator::generateArgumentsChecks(bool bailout)
2681 {
2682 // This function can be used the normal way to check the argument types,
2683 // before entering the function and bailout when arguments don't match.
2684 // For debug purpose, this is can also be used to force/check that the
2685 // arguments are correct. Upon fail it will hit a breakpoint.
2687 MIRGraph &mir = gen->graph();
2688 MResumePoint *rp = mir.entryResumePoint();
2690 // Reserve the amount of stack the actual frame will use. We have to undo
2691 // this before falling through to the method proper though, because the
2692 // monomorphic call case will bypass this entire path.
2693 masm.reserveStack(frameSize());
2695 // No registers are allocated yet, so it's safe to grab anything.
2696 Register temp = GeneralRegisterSet(EntryTempMask).getAny();
2698 CompileInfo &info = gen->info();
2700 Label miss;
2701 for (uint32_t i = info.startArgSlot(); i < info.endArgSlot(); i++) {
2702 // All initial parameters are guaranteed to be MParameters.
2703 MParameter *param = rp->getOperand(i)->toParameter();
2704 const types::TypeSet *types = param->resultTypeSet();
2705 if (!types || types->unknown())
2706 continue;
2708 // Calculate the offset on the stack of the argument.
2709 // (i - info.startArgSlot()) - Compute index of arg within arg vector.
2710 // ... * sizeof(Value) - Scale by value size.
2711 // ArgToStackOffset(...) - Compute displacement within arg vector.
2712 int32_t offset = ArgToStackOffset((i - info.startArgSlot()) * sizeof(Value));
2713 masm.guardTypeSet(Address(StackPointer, offset), types, temp, &miss);
2714 }
2716 if (miss.used()) {
2717 if (bailout) {
2718 if (!bailoutFrom(&miss, graph.entrySnapshot()))
2719 return false;
2720 } else {
2721 Label success;
2722 masm.jump(&success);
2723 masm.bind(&miss);
2724 masm.assumeUnreachable("Argument check fail.");
2725 masm.bind(&success);
2726 }
2727 }
2729 masm.freeStack(frameSize());
2731 return true;
2732 }
2734 // Out-of-line path to report over-recursed error and fail.
2735 class CheckOverRecursedFailure : public OutOfLineCodeBase<CodeGenerator>
2736 {
2737 LCheckOverRecursed *lir_;
2739 public:
2740 CheckOverRecursedFailure(LCheckOverRecursed *lir)
2741 : lir_(lir)
2742 { }
2744 bool accept(CodeGenerator *codegen) {
2745 return codegen->visitCheckOverRecursedFailure(this);
2746 }
2748 LCheckOverRecursed *lir() const {
2749 return lir_;
2750 }
2751 };
2753 bool
2754 CodeGenerator::visitCheckOverRecursed(LCheckOverRecursed *lir)
2755 {
2756 // If we don't push anything on the stack, skip the check.
2757 if (omitOverRecursedCheck())
2758 return true;
2760 // Ensure that this frame will not cross the stack limit.
2761 // This is a weak check, justified by Ion using the C stack: we must always
2762 // be some distance away from the actual limit, since if the limit is
2763 // crossed, an error must be thrown, which requires more frames.
2764 //
2765 // It must always be possible to trespass past the stack limit.
2766 // Ion may legally place frames very close to the limit. Calling additional
2767 // C functions may then violate the limit without any checking.
2769 // Since Ion frames exist on the C stack, the stack limit may be
2770 // dynamically set by JS_SetThreadStackLimit() and JS_SetNativeStackQuota().
2771 const void *limitAddr = GetIonContext()->runtime->addressOfJitStackLimit();
2773 CheckOverRecursedFailure *ool = new(alloc()) CheckOverRecursedFailure(lir);
2774 if (!addOutOfLineCode(ool))
2775 return false;
2777 // Conditional forward (unlikely) branch to failure.
2778 masm.branchPtr(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr), StackPointer, ool->entry());
2779 masm.bind(ool->rejoin());
2781 return true;
2782 }
2784 typedef bool (*DefVarOrConstFn)(JSContext *, HandlePropertyName, unsigned, HandleObject);
2785 static const VMFunction DefVarOrConstInfo =
2786 FunctionInfo<DefVarOrConstFn>(DefVarOrConst);
2788 bool
2789 CodeGenerator::visitDefVar(LDefVar *lir)
2790 {
2791 Register scopeChain = ToRegister(lir->scopeChain());
2793 pushArg(scopeChain); // JSObject *
2794 pushArg(Imm32(lir->mir()->attrs())); // unsigned
2795 pushArg(ImmGCPtr(lir->mir()->name())); // PropertyName *
2797 if (!callVM(DefVarOrConstInfo, lir))
2798 return false;
2800 return true;
2801 }
2803 typedef bool (*DefFunOperationFn)(JSContext *, HandleScript, HandleObject, HandleFunction);
2804 static const VMFunction DefFunOperationInfo = FunctionInfo<DefFunOperationFn>(DefFunOperation);
2806 bool
2807 CodeGenerator::visitDefFun(LDefFun *lir)
2808 {
2809 Register scopeChain = ToRegister(lir->scopeChain());
2811 pushArg(ImmGCPtr(lir->mir()->fun()));
2812 pushArg(scopeChain);
2813 pushArg(ImmGCPtr(current->mir()->info().script()));
2815 return callVM(DefFunOperationInfo, lir);
2816 }
2818 typedef bool (*ReportOverRecursedFn)(JSContext *);
2819 static const VMFunction CheckOverRecursedInfo =
2820 FunctionInfo<ReportOverRecursedFn>(CheckOverRecursed);
2822 bool
2823 CodeGenerator::visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool)
2824 {
2825 // The OOL path is hit if the recursion depth has been exceeded.
2826 // Throw an InternalError for over-recursion.
2828 // LFunctionEnvironment can appear before LCheckOverRecursed, so we have
2829 // to save all live registers to avoid crashes if CheckOverRecursed triggers
2830 // a GC.
2831 saveLive(ool->lir());
2833 if (!callVM(CheckOverRecursedInfo, ool->lir()))
2834 return false;
2836 restoreLive(ool->lir());
2837 masm.jump(ool->rejoin());
2838 return true;
2839 }
2841 // Out-of-line path to report over-recursed error and fail.
2842 class CheckOverRecursedFailurePar : public OutOfLineCodeBase<CodeGenerator>
2843 {
2844 LCheckOverRecursedPar *lir_;
2846 public:
2847 CheckOverRecursedFailurePar(LCheckOverRecursedPar *lir)
2848 : lir_(lir)
2849 { }
2851 bool accept(CodeGenerator *codegen) {
2852 return codegen->visitCheckOverRecursedFailurePar(this);
2853 }
2855 LCheckOverRecursedPar *lir() const {
2856 return lir_;
2857 }
2858 };
2860 bool
2861 CodeGenerator::visitCheckOverRecursedPar(LCheckOverRecursedPar *lir)
2862 {
2863 // See above: unlike visitCheckOverRecursed(), this code runs in
2864 // parallel mode and hence uses the jitStackLimit from the current
2865 // thread state. Also, we must check the interrupt flags because
2866 // on interrupt or abort, only the stack limit for the main thread
2867 // is reset, not the worker threads. See comment in vm/ForkJoin.h
2868 // for more details.
2870 Register cxReg = ToRegister(lir->forkJoinContext());
2871 Register tempReg = ToRegister(lir->getTempReg());
2873 masm.loadPtr(Address(cxReg, offsetof(ForkJoinContext, perThreadData)), tempReg);
2874 masm.loadPtr(Address(tempReg, offsetof(PerThreadData, jitStackLimit)), tempReg);
2876 // Conditional forward (unlikely) branch to failure.
2877 CheckOverRecursedFailurePar *ool = new(alloc()) CheckOverRecursedFailurePar(lir);
2878 if (!addOutOfLineCode(ool))
2879 return false;
2880 masm.branchPtr(Assembler::BelowOrEqual, StackPointer, tempReg, ool->entry());
2881 masm.checkInterruptFlagPar(tempReg, ool->entry());
2882 masm.bind(ool->rejoin());
2884 return true;
2885 }
2887 bool
2888 CodeGenerator::visitCheckOverRecursedFailurePar(CheckOverRecursedFailurePar *ool)
2889 {
2890 OutOfLinePropagateAbortPar *bail = oolPropagateAbortPar(ool->lir());
2891 if (!bail)
2892 return false;
2894 // Avoid saving/restoring the temp register since we will put the
2895 // ReturnReg into it below and we don't want to clobber that
2896 // during PopRegsInMask():
2897 LCheckOverRecursedPar *lir = ool->lir();
2898 Register tempReg = ToRegister(lir->getTempReg());
2899 RegisterSet saveSet(lir->safepoint()->liveRegs());
2900 saveSet.takeUnchecked(tempReg);
2902 masm.PushRegsInMask(saveSet);
2903 masm.movePtr(ToRegister(lir->forkJoinContext()), CallTempReg0);
2904 masm.setupUnalignedABICall(1, CallTempReg1);
2905 masm.passABIArg(CallTempReg0);
2906 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, CheckOverRecursedPar));
2907 masm.movePtr(ReturnReg, tempReg);
2908 masm.PopRegsInMask(saveSet);
2909 masm.branchIfFalseBool(tempReg, bail->entry());
2910 masm.jump(ool->rejoin());
2912 return true;
2913 }
2915 // Out-of-line path to report over-recursed error and fail.
2916 class OutOfLineInterruptCheckPar : public OutOfLineCodeBase<CodeGenerator>
2917 {
2918 public:
2919 LInterruptCheckPar *const lir;
2921 OutOfLineInterruptCheckPar(LInterruptCheckPar *lir)
2922 : lir(lir)
2923 { }
2925 bool accept(CodeGenerator *codegen) {
2926 return codegen->visitOutOfLineInterruptCheckPar(this);
2927 }
2928 };
2930 bool
2931 CodeGenerator::visitInterruptCheckPar(LInterruptCheckPar *lir)
2932 {
2933 // First check for cx->shared->interrupt_.
2934 OutOfLineInterruptCheckPar *ool = new(alloc()) OutOfLineInterruptCheckPar(lir);
2935 if (!addOutOfLineCode(ool))
2936 return false;
2938 Register tempReg = ToRegister(lir->getTempReg());
2939 masm.checkInterruptFlagPar(tempReg, ool->entry());
2940 masm.bind(ool->rejoin());
2941 return true;
2942 }
2944 bool
2945 CodeGenerator::visitOutOfLineInterruptCheckPar(OutOfLineInterruptCheckPar *ool)
2946 {
2947 OutOfLinePropagateAbortPar *bail = oolPropagateAbortPar(ool->lir);
2948 if (!bail)
2949 return false;
2951 // Avoid saving/restoring the temp register since we will put the
2952 // ReturnReg into it below and we don't want to clobber that
2953 // during PopRegsInMask():
2954 LInterruptCheckPar *lir = ool->lir;
2955 Register tempReg = ToRegister(lir->getTempReg());
2956 RegisterSet saveSet(lir->safepoint()->liveRegs());
2957 saveSet.takeUnchecked(tempReg);
2959 masm.PushRegsInMask(saveSet);
2960 masm.movePtr(ToRegister(ool->lir->forkJoinContext()), CallTempReg0);
2961 masm.setupUnalignedABICall(1, CallTempReg1);
2962 masm.passABIArg(CallTempReg0);
2963 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, InterruptCheckPar));
2964 masm.movePtr(ReturnReg, tempReg);
2965 masm.PopRegsInMask(saveSet);
2966 masm.branchIfFalseBool(tempReg, bail->entry());
2967 masm.jump(ool->rejoin());
2969 return true;
2970 }
2972 IonScriptCounts *
2973 CodeGenerator::maybeCreateScriptCounts()
2974 {
2975 // If scripts are being profiled, create a new IonScriptCounts and attach
2976 // it to the script. This must be done on the main thread.
2977 JSContext *cx = GetIonContext()->cx;
2978 if (!cx || !cx->runtime()->profilingScripts)
2979 return nullptr;
2981 CompileInfo *outerInfo = &gen->info();
2982 JSScript *script = outerInfo->script();
2983 if (!script)
2984 return nullptr;
2986 if (!script->hasScriptCounts() && !script->initScriptCounts(cx))
2987 return nullptr;
2989 IonScriptCounts *counts = js_new<IonScriptCounts>();
2990 if (!counts || !counts->init(graph.numBlocks())) {
2991 js_delete(counts);
2992 return nullptr;
2993 }
2995 for (size_t i = 0; i < graph.numBlocks(); i++) {
2996 MBasicBlock *block = graph.getBlock(i)->mir();
2998 // Find a PC offset in the outermost script to use. If this block
2999 // is from an inlined script, find a location in the outer script
3000 // to associate information about the inlining with.
3001 MResumePoint *resume = block->entryResumePoint();
3002 while (resume->caller())
3003 resume = resume->caller();
3005 uint32_t offset = script->pcToOffset(resume->pc());
3006 if (!counts->block(i).init(block->id(), offset, block->numSuccessors())) {
3007 js_delete(counts);
3008 return nullptr;
3009 }
3011 for (size_t j = 0; j < block->numSuccessors(); j++)
3012 counts->block(i).setSuccessor(j, block->getSuccessor(j)->id());
3013 }
3015 script->addIonCounts(counts);
3016 return counts;
3017 }
3019 // Structure for managing the state tracked for a block by script counters.
3020 struct ScriptCountBlockState
3021 {
3022 IonBlockCounts █
3023 MacroAssembler &masm;
3025 Sprinter printer;
3027 public:
3028 ScriptCountBlockState(IonBlockCounts *block, MacroAssembler *masm)
3029 : block(*block), masm(*masm), printer(GetIonContext()->cx)
3030 {
3031 }
3033 bool init()
3034 {
3035 if (!printer.init())
3036 return false;
3038 // Bump the hit count for the block at the start. This code is not
3039 // included in either the text for the block or the instruction byte
3040 // counts.
3041 masm.inc64(AbsoluteAddress(block.addressOfHitCount()));
3043 // Collect human readable assembly for the code generated in the block.
3044 masm.setPrinter(&printer);
3046 return true;
3047 }
3049 void visitInstruction(LInstruction *ins)
3050 {
3051 // Prefix stream of assembly instructions with their LIR instruction
3052 // name and any associated high level info.
3053 if (const char *extra = ins->extraName())
3054 printer.printf("[%s:%s]\n", ins->opName(), extra);
3055 else
3056 printer.printf("[%s]\n", ins->opName());
3057 }
3059 ~ScriptCountBlockState()
3060 {
3061 masm.setPrinter(nullptr);
3063 block.setCode(printer.string());
3064 }
3065 };
3067 #ifdef DEBUG
3068 bool
3069 CodeGenerator::branchIfInvalidated(Register temp, Label *invalidated)
3070 {
3071 CodeOffsetLabel label = masm.movWithPatch(ImmWord(uintptr_t(-1)), temp);
3072 if (!ionScriptLabels_.append(label))
3073 return false;
3075 // If IonScript::refcount != 0, the script has been invalidated.
3076 masm.branch32(Assembler::NotEqual,
3077 Address(temp, IonScript::offsetOfRefcount()),
3078 Imm32(0),
3079 invalidated);
3080 return true;
3081 }
3083 bool
3084 CodeGenerator::emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mir)
3085 {
3086 if (lir->numDefs() == 0)
3087 return true;
3089 JS_ASSERT(lir->numDefs() == 1);
3090 Register output = ToRegister(lir->getDef(0));
3092 GeneralRegisterSet regs(GeneralRegisterSet::All());
3093 regs.take(output);
3095 Register temp = regs.takeAny();
3096 masm.push(temp);
3098 // Don't check if the script has been invalidated. In that case invalid
3099 // types are expected (until we reach the OsiPoint and bailout).
3100 Label done;
3101 if (!branchIfInvalidated(temp, &done))
3102 return false;
3104 if (mir->type() == MIRType_Object &&
3105 mir->resultTypeSet() &&
3106 !mir->resultTypeSet()->unknownObject())
3107 {
3108 // We have a result TypeSet, assert this object is in it.
3109 Label miss, ok;
3110 if (mir->resultTypeSet()->getObjectCount() > 0)
3111 masm.guardObjectType(output, mir->resultTypeSet(), temp, &miss);
3112 else
3113 masm.jump(&miss);
3114 masm.jump(&ok);
3116 masm.bind(&miss);
3117 masm.assumeUnreachable("MIR instruction returned object with unexpected type");
3119 masm.bind(&ok);
3120 }
3122 // Check that we have a valid GC pointer.
3123 if (gen->info().executionMode() != ParallelExecution) {
3124 saveVolatile();
3125 masm.setupUnalignedABICall(2, temp);
3126 masm.loadJSContext(temp);
3127 masm.passABIArg(temp);
3128 masm.passABIArg(output);
3129 masm.callWithABINoProfiling(mir->type() == MIRType_Object
3130 ? JS_FUNC_TO_DATA_PTR(void *, AssertValidObjectPtr)
3131 : JS_FUNC_TO_DATA_PTR(void *, AssertValidStringPtr));
3132 restoreVolatile();
3133 }
3135 masm.bind(&done);
3136 masm.pop(temp);
3137 return true;
3138 }
3140 bool
3141 CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir)
3142 {
3143 if (lir->numDefs() == 0)
3144 return true;
3146 JS_ASSERT(lir->numDefs() == BOX_PIECES);
3147 if (!lir->getDef(0)->output()->isRegister())
3148 return true;
3150 ValueOperand output = ToOutValue(lir);
3152 GeneralRegisterSet regs(GeneralRegisterSet::All());
3153 regs.take(output);
3155 Register temp1 = regs.takeAny();
3156 Register temp2 = regs.takeAny();
3157 masm.push(temp1);
3158 masm.push(temp2);
3160 // Don't check if the script has been invalidated. In that case invalid
3161 // types are expected (until we reach the OsiPoint and bailout).
3162 Label done;
3163 if (!branchIfInvalidated(temp1, &done))
3164 return false;
3166 if (mir->resultTypeSet() && !mir->resultTypeSet()->unknown()) {
3167 // We have a result TypeSet, assert this value is in it.
3168 Label miss, ok;
3169 masm.guardTypeSet(output, mir->resultTypeSet(), temp1, &miss);
3170 masm.jump(&ok);
3172 masm.bind(&miss);
3173 masm.assumeUnreachable("MIR instruction returned value with unexpected type");
3175 masm.bind(&ok);
3176 }
3178 // Check that we have a valid GC pointer.
3179 if (gen->info().executionMode() != ParallelExecution) {
3180 saveVolatile();
3182 masm.pushValue(output);
3183 masm.movePtr(StackPointer, temp1);
3185 masm.setupUnalignedABICall(2, temp2);
3186 masm.loadJSContext(temp2);
3187 masm.passABIArg(temp2);
3188 masm.passABIArg(temp1);
3189 masm.callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, AssertValidValue));
3190 masm.popValue(output);
3191 restoreVolatile();
3192 }
3194 masm.bind(&done);
3195 masm.pop(temp2);
3196 masm.pop(temp1);
3197 return true;
3198 }
3200 bool
3201 CodeGenerator::emitDebugResultChecks(LInstruction *ins)
3202 {
3203 // In debug builds, check that LIR instructions return valid values.
3205 MDefinition *mir = ins->mirRaw();
3206 if (!mir)
3207 return true;
3209 switch (mir->type()) {
3210 case MIRType_Object:
3211 case MIRType_String:
3212 return emitObjectOrStringResultChecks(ins, mir);
3213 case MIRType_Value:
3214 return emitValueResultChecks(ins, mir);
3215 default:
3216 return true;
3217 }
3218 }
3219 #endif
3221 bool
3222 CodeGenerator::generateBody()
3223 {
3224 IonScriptCounts *counts = maybeCreateScriptCounts();
3226 #if defined(JS_ION_PERF)
3227 PerfSpewer *perfSpewer = &perfSpewer_;
3228 if (gen->compilingAsmJS())
3229 perfSpewer = &gen->perfSpewer();
3230 #endif
3232 for (size_t i = 0; i < graph.numBlocks(); i++) {
3233 current = graph.getBlock(i);
3234 masm.bind(current->label());
3236 mozilla::Maybe<ScriptCountBlockState> blockCounts;
3237 if (counts) {
3238 blockCounts.construct(&counts->block(i), &masm);
3239 if (!blockCounts.ref().init())
3240 return false;
3241 }
3243 #if defined(JS_ION_PERF)
3244 perfSpewer->startBasicBlock(current->mir(), masm);
3245 #endif
3247 for (LInstructionIterator iter = current->begin(); iter != current->end(); iter++) {
3248 IonSpewStart(IonSpew_Codegen, "instruction %s", iter->opName());
3249 #ifdef DEBUG
3250 if (const char *extra = iter->extraName())
3251 IonSpewCont(IonSpew_Codegen, ":%s", extra);
3252 #endif
3253 IonSpewFin(IonSpew_Codegen);
3255 if (counts)
3256 blockCounts.ref().visitInstruction(*iter);
3258 if (iter->safepoint() && pushedArgumentSlots_.length()) {
3259 if (!markArgumentSlots(iter->safepoint()))
3260 return false;
3261 }
3263 #ifdef CHECK_OSIPOINT_REGISTERS
3264 if (iter->safepoint())
3265 resetOsiPointRegs(iter->safepoint());
3266 #endif
3268 if (!callTraceLIR(i, *iter))
3269 return false;
3271 if (!iter->accept(this))
3272 return false;
3274 #ifdef DEBUG
3275 if (!emitDebugResultChecks(*iter))
3276 return false;
3277 #endif
3278 }
3279 if (masm.oom())
3280 return false;
3282 #if defined(JS_ION_PERF)
3283 perfSpewer->endBasicBlock(masm);
3284 #endif
3285 }
3287 JS_ASSERT(pushedArgumentSlots_.empty());
3288 return true;
3289 }
3291 // Out-of-line object allocation for LNewArray.
3292 class OutOfLineNewArray : public OutOfLineCodeBase<CodeGenerator>
3293 {
3294 LNewArray *lir_;
3296 public:
3297 OutOfLineNewArray(LNewArray *lir)
3298 : lir_(lir)
3299 { }
3301 bool accept(CodeGenerator *codegen) {
3302 return codegen->visitOutOfLineNewArray(this);
3303 }
3305 LNewArray *lir() const {
3306 return lir_;
3307 }
3308 };
3310 typedef JSObject *(*NewInitArrayFn)(JSContext *, uint32_t, types::TypeObject *);
3311 static const VMFunction NewInitArrayInfo =
3312 FunctionInfo<NewInitArrayFn>(NewInitArray);
3314 bool
3315 CodeGenerator::visitNewArrayCallVM(LNewArray *lir)
3316 {
3317 JS_ASSERT(gen->info().executionMode() == SequentialExecution);
3319 Register objReg = ToRegister(lir->output());
3321 JS_ASSERT(!lir->isCall());
3322 saveLive(lir);
3324 JSObject *templateObject = lir->mir()->templateObject();
3325 types::TypeObject *type =
3326 templateObject->hasSingletonType() ? nullptr : templateObject->type();
3328 pushArg(ImmGCPtr(type));
3329 pushArg(Imm32(lir->mir()->count()));
3331 if (!callVM(NewInitArrayInfo, lir))
3332 return false;
3334 if (ReturnReg != objReg)
3335 masm.movePtr(ReturnReg, objReg);
3337 restoreLive(lir);
3339 return true;
3340 }
3342 typedef JSObject *(*NewDerivedTypedObjectFn)(JSContext *,
3343 HandleObject type,
3344 HandleObject owner,
3345 int32_t offset);
3346 static const VMFunction CreateDerivedTypedObjInfo =
3347 FunctionInfo<NewDerivedTypedObjectFn>(CreateDerivedTypedObj);
3349 bool
3350 CodeGenerator::visitNewDerivedTypedObject(LNewDerivedTypedObject *lir)
3351 {
3352 // Not yet made safe for par exec:
3353 JS_ASSERT(gen->info().executionMode() == SequentialExecution);
3355 pushArg(ToRegister(lir->offset()));
3356 pushArg(ToRegister(lir->owner()));
3357 pushArg(ToRegister(lir->type()));
3358 return callVM(CreateDerivedTypedObjInfo, lir);
3359 }
3361 bool
3362 CodeGenerator::visitNewSlots(LNewSlots *lir)
3363 {
3364 Register temp1 = ToRegister(lir->temp1());
3365 Register temp2 = ToRegister(lir->temp2());
3366 Register temp3 = ToRegister(lir->temp3());
3367 Register output = ToRegister(lir->output());
3369 masm.mov(ImmPtr(GetIonContext()->runtime), temp1);
3370 masm.mov(ImmWord(lir->mir()->nslots()), temp2);
3372 masm.setupUnalignedABICall(2, temp3);
3373 masm.passABIArg(temp1);
3374 masm.passABIArg(temp2);
3375 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NewSlots));
3377 if (!bailoutTestPtr(Assembler::Zero, output, output, lir->snapshot()))
3378 return false;
3380 return true;
3381 }
3383 bool CodeGenerator::visitAtan2D(LAtan2D *lir)
3384 {
3385 Register temp = ToRegister(lir->temp());
3386 FloatRegister y = ToFloatRegister(lir->y());
3387 FloatRegister x = ToFloatRegister(lir->x());
3389 masm.setupUnalignedABICall(2, temp);
3390 masm.passABIArg(y, MoveOp::DOUBLE);
3391 masm.passABIArg(x, MoveOp::DOUBLE);
3392 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaAtan2), MoveOp::DOUBLE);
3394 JS_ASSERT(ToFloatRegister(lir->output()) == ReturnFloatReg);
3395 return true;
3396 }
3398 bool CodeGenerator::visitHypot(LHypot *lir)
3399 {
3400 Register temp = ToRegister(lir->temp());
3401 FloatRegister x = ToFloatRegister(lir->x());
3402 FloatRegister y = ToFloatRegister(lir->y());
3404 masm.setupUnalignedABICall(2, temp);
3405 masm.passABIArg(x, MoveOp::DOUBLE);
3406 masm.passABIArg(y, MoveOp::DOUBLE);
3407 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaHypot), MoveOp::DOUBLE);
3409 JS_ASSERT(ToFloatRegister(lir->output()) == ReturnFloatReg);
3410 return true;
3411 }
3413 bool
3414 CodeGenerator::visitNewArray(LNewArray *lir)
3415 {
3416 JS_ASSERT(gen->info().executionMode() == SequentialExecution);
3417 Register objReg = ToRegister(lir->output());
3418 Register tempReg = ToRegister(lir->temp());
3419 JSObject *templateObject = lir->mir()->templateObject();
3420 DebugOnly<uint32_t> count = lir->mir()->count();
3422 JS_ASSERT(count < JSObject::NELEMENTS_LIMIT);
3424 if (lir->mir()->shouldUseVM())
3425 return visitNewArrayCallVM(lir);
3427 OutOfLineNewArray *ool = new(alloc()) OutOfLineNewArray(lir);
3428 if (!addOutOfLineCode(ool))
3429 return false;
3431 masm.newGCThing(objReg, tempReg, templateObject, ool->entry(), lir->mir()->initialHeap());
3432 masm.initGCThing(objReg, tempReg, templateObject);
3434 masm.bind(ool->rejoin());
3435 return true;
3436 }
3438 bool
3439 CodeGenerator::visitOutOfLineNewArray(OutOfLineNewArray *ool)
3440 {
3441 if (!visitNewArrayCallVM(ool->lir()))
3442 return false;
3443 masm.jump(ool->rejoin());
3444 return true;
3445 }
3447 // Out-of-line object allocation for JSOP_NEWOBJECT.
3448 class OutOfLineNewObject : public OutOfLineCodeBase<CodeGenerator>
3449 {
3450 LNewObject *lir_;
3452 public:
3453 OutOfLineNewObject(LNewObject *lir)
3454 : lir_(lir)
3455 { }
3457 bool accept(CodeGenerator *codegen) {
3458 return codegen->visitOutOfLineNewObject(this);
3459 }
3461 LNewObject *lir() const {
3462 return lir_;
3463 }
3464 };
3466 typedef JSObject *(*NewInitObjectFn)(JSContext *, HandleObject);
3467 static const VMFunction NewInitObjectInfo = FunctionInfo<NewInitObjectFn>(NewInitObject);
3469 typedef JSObject *(*NewInitObjectWithClassPrototypeFn)(JSContext *, HandleObject);
3470 static const VMFunction NewInitObjectWithClassPrototypeInfo =
3471 FunctionInfo<NewInitObjectWithClassPrototypeFn>(NewInitObjectWithClassPrototype);
3473 bool
3474 CodeGenerator::visitNewObjectVMCall(LNewObject *lir)
3475 {
3476 JS_ASSERT(gen->info().executionMode() == SequentialExecution);
3478 Register objReg = ToRegister(lir->output());
3480 JS_ASSERT(!lir->isCall());
3481 saveLive(lir);
3483 pushArg(ImmGCPtr(lir->mir()->templateObject()));
3485 // If we're making a new object with a class prototype (that is, an object
3486 // that derives its class from its prototype instead of being
3487 // JSObject::class_'d) from self-hosted code, we need a different init
3488 // function.
3489 if (lir->mir()->templateObjectIsClassPrototype()) {
3490 if (!callVM(NewInitObjectWithClassPrototypeInfo, lir))
3491 return false;
3492 } else if (!callVM(NewInitObjectInfo, lir)) {
3493 return false;
3494 }
3496 if (ReturnReg != objReg)
3497 masm.movePtr(ReturnReg, objReg);
3499 restoreLive(lir);
3500 return true;
3501 }
3503 bool
3504 CodeGenerator::visitNewObject(LNewObject *lir)
3505 {
3506 JS_ASSERT(gen->info().executionMode() == SequentialExecution);
3507 Register objReg = ToRegister(lir->output());
3508 Register tempReg = ToRegister(lir->temp());
3509 JSObject *templateObject = lir->mir()->templateObject();
3511 if (lir->mir()->shouldUseVM())
3512 return visitNewObjectVMCall(lir);
3514 OutOfLineNewObject *ool = new(alloc()) OutOfLineNewObject(lir);
3515 if (!addOutOfLineCode(ool))
3516 return false;
3518 masm.newGCThing(objReg, tempReg, templateObject, ool->entry(), lir->mir()->initialHeap());
3519 masm.initGCThing(objReg, tempReg, templateObject);
3521 masm.bind(ool->rejoin());
3522 return true;
3523 }
3525 bool
3526 CodeGenerator::visitOutOfLineNewObject(OutOfLineNewObject *ool)
3527 {
3528 if (!visitNewObjectVMCall(ool->lir()))
3529 return false;
3530 masm.jump(ool->rejoin());
3531 return true;
3532 }
3534 typedef js::DeclEnvObject *(*NewDeclEnvObjectFn)(JSContext *, HandleFunction, gc::InitialHeap);
3535 static const VMFunction NewDeclEnvObjectInfo =
3536 FunctionInfo<NewDeclEnvObjectFn>(DeclEnvObject::createTemplateObject);
3538 bool
3539 CodeGenerator::visitNewDeclEnvObject(LNewDeclEnvObject *lir)
3540 {
3541 Register objReg = ToRegister(lir->output());
3542 Register tempReg = ToRegister(lir->temp());
3543 JSObject *templateObj = lir->mir()->templateObj();
3544 CompileInfo &info = lir->mir()->block()->info();
3546 // If we have a template object, we can inline call object creation.
3547 OutOfLineCode *ool = oolCallVM(NewDeclEnvObjectInfo, lir,
3548 (ArgList(), ImmGCPtr(info.funMaybeLazy()),
3549 Imm32(gc::DefaultHeap)),
3550 StoreRegisterTo(objReg));
3551 if (!ool)
3552 return false;
3554 masm.newGCThing(objReg, tempReg, templateObj, ool->entry(), gc::DefaultHeap);
3555 masm.initGCThing(objReg, tempReg, templateObj);
3556 masm.bind(ool->rejoin());
3557 return true;
3558 }
3560 typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleShape, HandleTypeObject, HeapSlot *);
3561 static const VMFunction NewCallObjectInfo =
3562 FunctionInfo<NewCallObjectFn>(NewCallObject);
3564 bool
3565 CodeGenerator::visitNewCallObject(LNewCallObject *lir)
3566 {
3567 Register objReg = ToRegister(lir->output());
3568 Register tempReg = ToRegister(lir->temp());
3570 JSObject *templateObj = lir->mir()->templateObject();
3572 OutOfLineCode *ool;
3573 if (lir->slots()->isRegister()) {
3574 ool = oolCallVM(NewCallObjectInfo, lir,
3575 (ArgList(), ImmGCPtr(templateObj->lastProperty()),
3576 ImmGCPtr(templateObj->type()),
3577 ToRegister(lir->slots())),
3578 StoreRegisterTo(objReg));
3579 } else {
3580 ool = oolCallVM(NewCallObjectInfo, lir,
3581 (ArgList(), ImmGCPtr(templateObj->lastProperty()),
3582 ImmGCPtr(templateObj->type()),
3583 ImmPtr(nullptr)),
3584 StoreRegisterTo(objReg));
3585 }
3586 if (!ool)
3587 return false;
3589 #ifdef JSGC_GENERATIONAL
3590 if (templateObj->hasDynamicSlots()) {
3591 // Slot initialization is unbarriered in this case, so we must either
3592 // allocate in the nursery or bail if that is not possible.
3593 masm.jump(ool->entry());
3594 } else
3595 #endif
3596 {
3597 // Inline call object creation, using the OOL path only for tricky cases.
3598 masm.newGCThing(objReg, tempReg, templateObj, ool->entry(), gc::DefaultHeap);
3599 masm.initGCThing(objReg, tempReg, templateObj);
3600 }
3602 if (lir->slots()->isRegister())
3603 masm.storePtr(ToRegister(lir->slots()), Address(objReg, JSObject::offsetOfSlots()));
3605 masm.bind(ool->rejoin());
3606 return true;
3607 }
3609 typedef JSObject *(*NewSingletonCallObjectFn)(JSContext *, HandleShape, HeapSlot *);
3610 static const VMFunction NewSingletonCallObjectInfo =
3611 FunctionInfo<NewSingletonCallObjectFn>(NewSingletonCallObject);
3613 bool
3614 CodeGenerator::visitNewSingletonCallObject(LNewSingletonCallObject *lir)
3615 {
3616 Register objReg = ToRegister(lir->output());
3618 JSObject *templateObj = lir->mir()->templateObject();
3620 OutOfLineCode *ool;
3621 if (lir->slots()->isRegister()) {
3622 ool = oolCallVM(NewSingletonCallObjectInfo, lir,
3623 (ArgList(), ImmGCPtr(templateObj->lastProperty()),
3624 ToRegister(lir->slots())),
3625 StoreRegisterTo(objReg));
3626 } else {
3627 ool = oolCallVM(NewSingletonCallObjectInfo, lir,
3628 (ArgList(), ImmGCPtr(templateObj->lastProperty()),
3629 ImmPtr(nullptr)),
3630 StoreRegisterTo(objReg));
3631 }
3632 if (!ool)
3633 return false;
3635 // Objects can only be given singleton types in VM calls. We make the call
3636 // out of line to not bloat inline code, even if (naively) this seems like
3637 // extra work.
3638 masm.jump(ool->entry());
3639 masm.bind(ool->rejoin());
3641 return true;
3642 }
3644 bool
3645 CodeGenerator::visitNewCallObjectPar(LNewCallObjectPar *lir)
3646 {
3647 Register resultReg = ToRegister(lir->output());
3648 Register cxReg = ToRegister(lir->forkJoinContext());
3649 Register tempReg1 = ToRegister(lir->getTemp0());
3650 Register tempReg2 = ToRegister(lir->getTemp1());
3651 JSObject *templateObj = lir->mir()->templateObj();
3653 emitAllocateGCThingPar(lir, resultReg, cxReg, tempReg1, tempReg2, templateObj);
3655 // NB: !lir->slots()->isRegister() implies that there is no slots
3656 // array at all, and the memory is already zeroed when copying
3657 // from the template object
3659 if (lir->slots()->isRegister()) {
3660 Register slotsReg = ToRegister(lir->slots());
3661 JS_ASSERT(slotsReg != resultReg);
3662 masm.storePtr(slotsReg, Address(resultReg, JSObject::offsetOfSlots()));
3663 }
3665 return true;
3666 }
3668 bool
3669 CodeGenerator::visitNewDenseArrayPar(LNewDenseArrayPar *lir)
3670 {
3671 Register cxReg = ToRegister(lir->forkJoinContext());
3672 Register lengthReg = ToRegister(lir->length());
3673 Register tempReg0 = ToRegister(lir->getTemp0());
3674 Register tempReg1 = ToRegister(lir->getTemp1());
3675 Register tempReg2 = ToRegister(lir->getTemp2());
3676 JSObject *templateObj = lir->mir()->templateObject();
3678 // Allocate the array into tempReg2. Don't use resultReg because it
3679 // may alias cxReg etc.
3680 emitAllocateGCThingPar(lir, tempReg2, cxReg, tempReg0, tempReg1, templateObj);
3682 // Invoke a C helper to allocate the elements. For convenience,
3683 // this helper also returns the array back to us, or nullptr, which
3684 // obviates the need to preserve the register across the call. In
3685 // reality, we should probably just have the C helper also
3686 // *allocate* the array, but that would require that it initialize
3687 // the various fields of the object, and I didn't want to
3688 // duplicate the code in initGCThing() that already does such an
3689 // admirable job.
3690 masm.setupUnalignedABICall(3, tempReg0);
3691 masm.passABIArg(cxReg);
3692 masm.passABIArg(tempReg2);
3693 masm.passABIArg(lengthReg);
3694 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ExtendArrayPar));
3696 Register resultReg = ToRegister(lir->output());
3697 JS_ASSERT(resultReg == ReturnReg);
3698 OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutOutOfMemory, lir);
3699 if (!bail)
3700 return false;
3701 masm.branchTestPtr(Assembler::Zero, resultReg, resultReg, bail->entry());
3703 return true;
3704 }
3706 typedef JSObject *(*NewStringObjectFn)(JSContext *, HandleString);
3707 static const VMFunction NewStringObjectInfo = FunctionInfo<NewStringObjectFn>(NewStringObject);
3709 bool
3710 CodeGenerator::visitNewStringObject(LNewStringObject *lir)
3711 {
3712 Register input = ToRegister(lir->input());
3713 Register output = ToRegister(lir->output());
3714 Register temp = ToRegister(lir->temp());
3716 StringObject *templateObj = lir->mir()->templateObj();
3718 OutOfLineCode *ool = oolCallVM(NewStringObjectInfo, lir, (ArgList(), input),
3719 StoreRegisterTo(output));
3720 if (!ool)
3721 return false;
3723 masm.newGCThing(output, temp, templateObj, ool->entry(), gc::DefaultHeap);
3724 masm.initGCThing(output, temp, templateObj);
3726 masm.loadStringLength(input, temp);
3728 masm.storeValue(JSVAL_TYPE_STRING, input, Address(output, StringObject::offsetOfPrimitiveValue()));
3729 masm.storeValue(JSVAL_TYPE_INT32, temp, Address(output, StringObject::offsetOfLength()));
3731 masm.bind(ool->rejoin());
3732 return true;
3733 }
3735 bool
3736 CodeGenerator::visitNewPar(LNewPar *lir)
3737 {
3738 Register objReg = ToRegister(lir->output());
3739 Register cxReg = ToRegister(lir->forkJoinContext());
3740 Register tempReg1 = ToRegister(lir->getTemp0());
3741 Register tempReg2 = ToRegister(lir->getTemp1());
3742 JSObject *templateObject = lir->mir()->templateObject();
3743 emitAllocateGCThingPar(lir, objReg, cxReg, tempReg1, tempReg2, templateObject);
3744 return true;
3745 }
3747 class OutOfLineNewGCThingPar : public OutOfLineCodeBase<CodeGenerator>
3748 {
3749 public:
3750 LInstruction *lir;
3751 gc::AllocKind allocKind;
3752 Register objReg;
3753 Register cxReg;
3755 OutOfLineNewGCThingPar(LInstruction *lir, gc::AllocKind allocKind, Register objReg,
3756 Register cxReg)
3757 : lir(lir), allocKind(allocKind), objReg(objReg), cxReg(cxReg)
3758 {}
3760 bool accept(CodeGenerator *codegen) {
3761 return codegen->visitOutOfLineNewGCThingPar(this);
3762 }
3763 };
3765 bool
3766 CodeGenerator::emitAllocateGCThingPar(LInstruction *lir, Register objReg, Register cxReg,
3767 Register tempReg1, Register tempReg2, JSObject *templateObj)
3768 {
3769 gc::AllocKind allocKind = templateObj->tenuredGetAllocKind();
3770 OutOfLineNewGCThingPar *ool = new(alloc()) OutOfLineNewGCThingPar(lir, allocKind, objReg, cxReg);
3771 if (!ool || !addOutOfLineCode(ool))
3772 return false;
3774 masm.newGCThingPar(objReg, cxReg, tempReg1, tempReg2, templateObj, ool->entry());
3775 masm.bind(ool->rejoin());
3776 masm.initGCThing(objReg, tempReg1, templateObj);
3777 return true;
3778 }
3780 bool
3781 CodeGenerator::visitOutOfLineNewGCThingPar(OutOfLineNewGCThingPar *ool)
3782 {
3783 // As a fallback for allocation in par. exec. mode, we invoke the
3784 // C helper NewGCThingPar(), which calls into the GC code. If it
3785 // returns nullptr, we bail. If returns non-nullptr, we rejoin the
3786 // original instruction.
3787 Register out = ool->objReg;
3789 saveVolatile(out);
3790 masm.setupUnalignedABICall(2, out);
3791 masm.passABIArg(ool->cxReg);
3792 masm.move32(Imm32(ool->allocKind), out);
3793 masm.passABIArg(out);
3794 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NewGCThingPar));
3795 masm.storeCallResult(out);
3796 restoreVolatile(out);
3798 OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutOutOfMemory, ool->lir);
3799 if (!bail)
3800 return false;
3801 masm.branchTestPtr(Assembler::Zero, out, out, bail->entry());
3802 masm.jump(ool->rejoin());
3803 return true;
3804 }
3806 bool
3807 CodeGenerator::visitAbortPar(LAbortPar *lir)
3808 {
3809 OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutUnsupported, lir);
3810 if (!bail)
3811 return false;
3812 masm.jump(bail->entry());
3813 return true;
3814 }
3816 typedef bool(*InitElemFn)(JSContext *cx, HandleObject obj,
3817 HandleValue id, HandleValue value);
3818 static const VMFunction InitElemInfo =
3819 FunctionInfo<InitElemFn>(InitElemOperation);
3821 bool
3822 CodeGenerator::visitInitElem(LInitElem *lir)
3823 {
3824 Register objReg = ToRegister(lir->getObject());
3826 pushArg(ToValue(lir, LInitElem::ValueIndex));
3827 pushArg(ToValue(lir, LInitElem::IdIndex));
3828 pushArg(objReg);
3830 return callVM(InitElemInfo, lir);
3831 }
3833 typedef bool (*InitElemGetterSetterFn)(JSContext *, jsbytecode *, HandleObject, HandleValue,
3834 HandleObject);
3835 static const VMFunction InitElemGetterSetterInfo =
3836 FunctionInfo<InitElemGetterSetterFn>(InitGetterSetterOperation);
3838 bool
3839 CodeGenerator::visitInitElemGetterSetter(LInitElemGetterSetter *lir)
3840 {
3841 Register obj = ToRegister(lir->object());
3842 Register value = ToRegister(lir->value());
3844 pushArg(value);
3845 pushArg(ToValue(lir, LInitElemGetterSetter::IdIndex));
3846 pushArg(obj);
3847 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
3849 return callVM(InitElemGetterSetterInfo, lir);
3850 }
3852 typedef bool(*MutatePrototypeFn)(JSContext *cx, HandleObject obj, HandleValue value);
3853 static const VMFunction MutatePrototypeInfo =
3854 FunctionInfo<MutatePrototypeFn>(MutatePrototype);
3856 bool
3857 CodeGenerator::visitMutateProto(LMutateProto *lir)
3858 {
3859 Register objReg = ToRegister(lir->getObject());
3861 pushArg(ToValue(lir, LMutateProto::ValueIndex));
3862 pushArg(objReg);
3864 return callVM(MutatePrototypeInfo, lir);
3865 }
3867 typedef bool(*InitPropFn)(JSContext *cx, HandleObject obj,
3868 HandlePropertyName name, HandleValue value);
3869 static const VMFunction InitPropInfo =
3870 FunctionInfo<InitPropFn>(InitProp);
3872 bool
3873 CodeGenerator::visitInitProp(LInitProp *lir)
3874 {
3875 Register objReg = ToRegister(lir->getObject());
3877 pushArg(ToValue(lir, LInitProp::ValueIndex));
3878 pushArg(ImmGCPtr(lir->mir()->propertyName()));
3879 pushArg(objReg);
3881 return callVM(InitPropInfo, lir);
3882 }
3884 typedef bool(*InitPropGetterSetterFn)(JSContext *, jsbytecode *, HandleObject, HandlePropertyName,
3885 HandleObject);
3886 static const VMFunction InitPropGetterSetterInfo =
3887 FunctionInfo<InitPropGetterSetterFn>(InitGetterSetterOperation);
3889 bool
3890 CodeGenerator::visitInitPropGetterSetter(LInitPropGetterSetter *lir)
3891 {
3892 Register obj = ToRegister(lir->object());
3893 Register value = ToRegister(lir->value());
3895 pushArg(value);
3896 pushArg(ImmGCPtr(lir->mir()->name()));
3897 pushArg(obj);
3898 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
3900 return callVM(InitPropGetterSetterInfo, lir);
3901 }
3903 typedef bool (*CreateThisFn)(JSContext *cx, HandleObject callee, MutableHandleValue rval);
3904 static const VMFunction CreateThisInfoCodeGen = FunctionInfo<CreateThisFn>(CreateThis);
3906 bool
3907 CodeGenerator::visitCreateThis(LCreateThis *lir)
3908 {
3909 const LAllocation *callee = lir->getCallee();
3911 if (callee->isConstant())
3912 pushArg(ImmGCPtr(&callee->toConstant()->toObject()));
3913 else
3914 pushArg(ToRegister(callee));
3916 return callVM(CreateThisInfoCodeGen, lir);
3917 }
3919 static JSObject *
3920 CreateThisForFunctionWithProtoWrapper(JSContext *cx, js::HandleObject callee, JSObject *proto)
3921 {
3922 return CreateThisForFunctionWithProto(cx, callee, proto);
3923 }
3925 typedef JSObject *(*CreateThisWithProtoFn)(JSContext *cx, HandleObject callee, JSObject *proto);
3926 static const VMFunction CreateThisWithProtoInfo =
3927 FunctionInfo<CreateThisWithProtoFn>(CreateThisForFunctionWithProtoWrapper);
3929 bool
3930 CodeGenerator::visitCreateThisWithProto(LCreateThisWithProto *lir)
3931 {
3932 const LAllocation *callee = lir->getCallee();
3933 const LAllocation *proto = lir->getPrototype();
3935 if (proto->isConstant())
3936 pushArg(ImmGCPtr(&proto->toConstant()->toObject()));
3937 else
3938 pushArg(ToRegister(proto));
3940 if (callee->isConstant())
3941 pushArg(ImmGCPtr(&callee->toConstant()->toObject()));
3942 else
3943 pushArg(ToRegister(callee));
3945 return callVM(CreateThisWithProtoInfo, lir);
3946 }
3948 typedef JSObject *(*NewGCObjectFn)(JSContext *cx, gc::AllocKind allocKind,
3949 gc::InitialHeap initialHeap);
3950 static const VMFunction NewGCObjectInfo =
3951 FunctionInfo<NewGCObjectFn>(js::jit::NewGCObject);
3953 bool
3954 CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate *lir)
3955 {
3956 JSObject *templateObject = lir->mir()->templateObject();
3957 gc::AllocKind allocKind = templateObject->tenuredGetAllocKind();
3958 gc::InitialHeap initialHeap = lir->mir()->initialHeap();
3959 Register objReg = ToRegister(lir->output());
3960 Register tempReg = ToRegister(lir->temp());
3962 OutOfLineCode *ool = oolCallVM(NewGCObjectInfo, lir,
3963 (ArgList(), Imm32(allocKind), Imm32(initialHeap)),
3964 StoreRegisterTo(objReg));
3965 if (!ool)
3966 return false;
3968 // Allocate. If the FreeList is empty, call to VM, which may GC.
3969 masm.newGCThing(objReg, tempReg, templateObject, ool->entry(), lir->mir()->initialHeap());
3971 // Initialize based on the templateObject.
3972 masm.bind(ool->rejoin());
3973 masm.initGCThing(objReg, tempReg, templateObject);
3975 return true;
3976 }
3978 typedef JSObject *(*NewIonArgumentsObjectFn)(JSContext *cx, IonJSFrameLayout *frame, HandleObject);
3979 static const VMFunction NewIonArgumentsObjectInfo =
3980 FunctionInfo<NewIonArgumentsObjectFn>((NewIonArgumentsObjectFn) ArgumentsObject::createForIon);
3982 bool
3983 CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject *lir)
3984 {
3985 // This should be getting constructed in the first block only, and not any OSR entry blocks.
3986 JS_ASSERT(lir->mir()->block()->id() == 0);
3988 const LAllocation *callObj = lir->getCallObject();
3989 Register temp = ToRegister(lir->getTemp(0));
3991 masm.movePtr(StackPointer, temp);
3992 masm.addPtr(Imm32(frameSize()), temp);
3994 pushArg(ToRegister(callObj));
3995 pushArg(temp);
3996 return callVM(NewIonArgumentsObjectInfo, lir);
3997 }
3999 bool
4000 CodeGenerator::visitGetArgumentsObjectArg(LGetArgumentsObjectArg *lir)
4001 {
4002 Register temp = ToRegister(lir->getTemp(0));
4003 Register argsObj = ToRegister(lir->getArgsObject());
4004 ValueOperand out = ToOutValue(lir);
4006 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), temp);
4007 Address argAddr(temp, ArgumentsData::offsetOfArgs() + lir->mir()->argno() * sizeof(Value));
4008 masm.loadValue(argAddr, out);
4009 #ifdef DEBUG
4010 Label success;
4011 masm.branchTestMagic(Assembler::NotEqual, out, &success);
4012 masm.assumeUnreachable("Result from ArgumentObject shouldn't be JSVAL_TYPE_MAGIC.");
4013 masm.bind(&success);
4014 #endif
4015 return true;
4016 }
4018 bool
4019 CodeGenerator::visitSetArgumentsObjectArg(LSetArgumentsObjectArg *lir)
4020 {
4021 Register temp = ToRegister(lir->getTemp(0));
4022 Register argsObj = ToRegister(lir->getArgsObject());
4023 ValueOperand value = ToValue(lir, LSetArgumentsObjectArg::ValueIndex);
4025 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), temp);
4026 Address argAddr(temp, ArgumentsData::offsetOfArgs() + lir->mir()->argno() * sizeof(Value));
4027 emitPreBarrier(argAddr, MIRType_Value);
4028 #ifdef DEBUG
4029 Label success;
4030 masm.branchTestMagic(Assembler::NotEqual, argAddr, &success);
4031 masm.assumeUnreachable("Result in ArgumentObject shouldn't be JSVAL_TYPE_MAGIC.");
4032 masm.bind(&success);
4033 #endif
4034 masm.storeValue(value, argAddr);
4035 return true;
4036 }
4038 bool
4039 CodeGenerator::visitReturnFromCtor(LReturnFromCtor *lir)
4040 {
4041 ValueOperand value = ToValue(lir, LReturnFromCtor::ValueIndex);
4042 Register obj = ToRegister(lir->getObject());
4043 Register output = ToRegister(lir->output());
4045 Label valueIsObject, end;
4047 masm.branchTestObject(Assembler::Equal, value, &valueIsObject);
4049 // Value is not an object. Return that other object.
4050 masm.movePtr(obj, output);
4051 masm.jump(&end);
4053 // Value is an object. Return unbox(Value).
4054 masm.bind(&valueIsObject);
4055 Register payload = masm.extractObject(value, output);
4056 if (payload != output)
4057 masm.movePtr(payload, output);
4059 masm.bind(&end);
4060 return true;
4061 }
4063 typedef JSObject *(*BoxNonStrictThisFn)(JSContext *, HandleValue);
4064 static const VMFunction BoxNonStrictThisInfo = FunctionInfo<BoxNonStrictThisFn>(BoxNonStrictThis);
4066 bool
4067 CodeGenerator::visitComputeThis(LComputeThis *lir)
4068 {
4069 ValueOperand value = ToValue(lir, LComputeThis::ValueIndex);
4070 Register output = ToRegister(lir->output());
4072 OutOfLineCode *ool = oolCallVM(BoxNonStrictThisInfo, lir, (ArgList(), value),
4073 StoreRegisterTo(output));
4074 if (!ool)
4075 return false;
4077 masm.branchTestObject(Assembler::NotEqual, value, ool->entry());
4078 masm.unboxObject(value, output);
4080 masm.bind(ool->rejoin());
4081 return true;
4082 }
4084 bool
4085 CodeGenerator::visitLoadArrowThis(LLoadArrowThis *lir)
4086 {
4087 Register callee = ToRegister(lir->callee());
4088 ValueOperand output = ToOutValue(lir);
4089 masm.loadValue(Address(callee, FunctionExtended::offsetOfArrowThisSlot()), output);
4090 return true;
4091 }
4093 bool
4094 CodeGenerator::visitArrayLength(LArrayLength *lir)
4095 {
4096 Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength());
4097 masm.load32(length, ToRegister(lir->output()));
4098 return true;
4099 }
4101 bool
4102 CodeGenerator::visitSetArrayLength(LSetArrayLength *lir)
4103 {
4104 Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength());
4105 Int32Key newLength = ToInt32Key(lir->index());
4107 masm.bumpKey(&newLength, 1);
4108 masm.storeKey(newLength, length);
4109 // Restore register value if it is used/captured after.
4110 masm.bumpKey(&newLength, -1);
4111 return true;
4112 }
4114 bool
4115 CodeGenerator::visitTypedArrayLength(LTypedArrayLength *lir)
4116 {
4117 Register obj = ToRegister(lir->object());
4118 Register out = ToRegister(lir->output());
4119 masm.unboxInt32(Address(obj, TypedArrayObject::lengthOffset()), out);
4120 return true;
4121 }
4123 bool
4124 CodeGenerator::visitTypedArrayElements(LTypedArrayElements *lir)
4125 {
4126 Register obj = ToRegister(lir->object());
4127 Register out = ToRegister(lir->output());
4128 masm.loadPtr(Address(obj, TypedArrayObject::dataOffset()), out);
4129 return true;
4130 }
4132 bool
4133 CodeGenerator::visitNeuterCheck(LNeuterCheck *lir)
4134 {
4135 Register obj = ToRegister(lir->object());
4136 Register temp = ToRegister(lir->temp());
4138 masm.extractObject(Address(obj, TypedObject::offsetOfOwnerSlot()), temp);
4139 masm.unboxInt32(Address(temp, ArrayBufferObject::flagsOffset()), temp);
4141 Imm32 flag(ArrayBufferObject::neuteredFlag());
4142 if (!bailoutTest32(Assembler::NonZero, temp, flag, lir->snapshot()))
4143 return false;
4145 return true;
4146 }
4148 bool
4149 CodeGenerator::visitTypedObjectElements(LTypedObjectElements *lir)
4150 {
4151 Register obj = ToRegister(lir->object());
4152 Register out = ToRegister(lir->output());
4153 masm.loadPtr(Address(obj, TypedObject::offsetOfDataSlot()), out);
4154 return true;
4155 }
4157 bool
4158 CodeGenerator::visitSetTypedObjectOffset(LSetTypedObjectOffset *lir)
4159 {
4160 Register object = ToRegister(lir->object());
4161 Register offset = ToRegister(lir->offset());
4162 Register temp0 = ToRegister(lir->temp0());
4164 // `offset` is an absolute offset into the base buffer. One way
4165 // to implement this instruction would be load the base address
4166 // from the buffer and add `offset`. But that'd be an extra load.
4167 // We can instead load the current base pointer and current
4168 // offset, compute the difference with `offset`, and then adjust
4169 // the current base pointer. This is two loads but to adjacent
4170 // fields in the same object, which should come in the same cache
4171 // line.
4172 //
4173 // The C code I would probably write is the following:
4174 //
4175 // void SetTypedObjectOffset(TypedObject *obj, int32_t offset) {
4176 // int32_t temp0 = obj->byteOffset;
4177 // obj->pointer = obj->pointer - temp0 + offset;
4178 // obj->byteOffset = offset;
4179 // }
4180 //
4181 // But what we actually compute is more like this, because it
4182 // saves us a temporary to do it this way:
4183 //
4184 // void SetTypedObjectOffset(TypedObject *obj, int32_t offset) {
4185 // int32_t temp0 = obj->byteOffset;
4186 // obj->pointer = obj->pointer - (temp0 - offset);
4187 // obj->byteOffset = offset;
4188 // }
4190 // temp0 = typedObj->byteOffset;
4191 masm.unboxInt32(Address(object, TypedObject::offsetOfByteOffsetSlot()), temp0);
4193 // temp0 -= offset;
4194 masm.subPtr(offset, temp0);
4196 // obj->pointer -= temp0;
4197 masm.subPtr(temp0, Address(object, TypedObject::offsetOfDataSlot()));
4199 // obj->byteOffset = offset;
4200 masm.storeValue(JSVAL_TYPE_INT32, offset,
4201 Address(object, TypedObject::offsetOfByteOffsetSlot()));
4203 return true;
4204 }
4206 bool
4207 CodeGenerator::visitStringLength(LStringLength *lir)
4208 {
4209 Register input = ToRegister(lir->string());
4210 Register output = ToRegister(lir->output());
4212 masm.loadStringLength(input, output);
4213 return true;
4214 }
4216 bool
4217 CodeGenerator::visitMinMaxI(LMinMaxI *ins)
4218 {
4219 Register first = ToRegister(ins->first());
4220 Register output = ToRegister(ins->output());
4222 JS_ASSERT(first == output);
4224 Label done;
4225 Assembler::Condition cond = ins->mir()->isMax()
4226 ? Assembler::GreaterThan
4227 : Assembler::LessThan;
4229 if (ins->second()->isConstant()) {
4230 masm.branch32(cond, first, Imm32(ToInt32(ins->second())), &done);
4231 masm.move32(Imm32(ToInt32(ins->second())), output);
4232 } else {
4233 masm.branch32(cond, first, ToRegister(ins->second()), &done);
4234 masm.move32(ToRegister(ins->second()), output);
4235 }
4237 masm.bind(&done);
4238 return true;
4239 }
4241 bool
4242 CodeGenerator::visitAbsI(LAbsI *ins)
4243 {
4244 Register input = ToRegister(ins->input());
4245 Label positive;
4247 JS_ASSERT(input == ToRegister(ins->output()));
4248 masm.branchTest32(Assembler::NotSigned, input, input, &positive);
4249 masm.neg32(input);
4250 #ifdef JS_CODEGEN_MIPS
4251 LSnapshot *snapshot = ins->snapshot();
4252 if (snapshot && !bailoutCmp32(Assembler::Equal, input, Imm32(INT32_MIN), snapshot))
4253 return false;
4254 #else
4255 if (ins->snapshot() && !bailoutIf(Assembler::Overflow, ins->snapshot()))
4256 return false;
4257 #endif
4258 masm.bind(&positive);
4260 return true;
4261 }
4263 bool
4264 CodeGenerator::visitPowI(LPowI *ins)
4265 {
4266 FloatRegister value = ToFloatRegister(ins->value());
4267 Register power = ToRegister(ins->power());
4268 Register temp = ToRegister(ins->temp());
4270 JS_ASSERT(power != temp);
4272 // In all implementations, setupUnalignedABICall() relinquishes use of
4273 // its scratch register. We can therefore save an input register by
4274 // reusing the scratch register to pass constants to callWithABI.
4275 masm.setupUnalignedABICall(2, temp);
4276 masm.passABIArg(value, MoveOp::DOUBLE);
4277 masm.passABIArg(power);
4279 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::powi), MoveOp::DOUBLE);
4280 JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
4282 return true;
4283 }
4285 bool
4286 CodeGenerator::visitPowD(LPowD *ins)
4287 {
4288 FloatRegister value = ToFloatRegister(ins->value());
4289 FloatRegister power = ToFloatRegister(ins->power());
4290 Register temp = ToRegister(ins->temp());
4292 masm.setupUnalignedABICall(2, temp);
4293 masm.passABIArg(value, MoveOp::DOUBLE);
4294 masm.passABIArg(power, MoveOp::DOUBLE);
4295 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaPow), MoveOp::DOUBLE);
4297 JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
4298 return true;
4299 }
4301 bool
4302 CodeGenerator::visitRandom(LRandom *ins)
4303 {
4304 Register temp = ToRegister(ins->temp());
4305 Register temp2 = ToRegister(ins->temp2());
4307 masm.loadJSContext(temp);
4309 masm.setupUnalignedABICall(1, temp2);
4310 masm.passABIArg(temp);
4311 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, math_random_no_outparam), MoveOp::DOUBLE);
4313 JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
4314 return true;
4315 }
4317 bool
4318 CodeGenerator::visitMathFunctionD(LMathFunctionD *ins)
4319 {
4320 Register temp = ToRegister(ins->temp());
4321 FloatRegister input = ToFloatRegister(ins->input());
4322 JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
4324 const MathCache *mathCache = ins->mir()->cache();
4326 masm.setupUnalignedABICall(mathCache ? 2 : 1, temp);
4327 if (mathCache) {
4328 masm.movePtr(ImmPtr(mathCache), temp);
4329 masm.passABIArg(temp);
4330 }
4331 masm.passABIArg(input, MoveOp::DOUBLE);
4333 # define MAYBE_CACHED(fcn) (mathCache ? (void*)fcn ## _impl : (void*)fcn ## _uncached)
4335 void *funptr = nullptr;
4336 switch (ins->mir()->function()) {
4337 case MMathFunction::Log:
4338 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_log));
4339 break;
4340 case MMathFunction::Sin:
4341 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_sin));
4342 break;
4343 case MMathFunction::Cos:
4344 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_cos));
4345 break;
4346 case MMathFunction::Exp:
4347 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_exp));
4348 break;
4349 case MMathFunction::Tan:
4350 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_tan));
4351 break;
4352 case MMathFunction::ATan:
4353 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_atan));
4354 break;
4355 case MMathFunction::ASin:
4356 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_asin));
4357 break;
4358 case MMathFunction::ACos:
4359 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_acos));
4360 break;
4361 case MMathFunction::Log10:
4362 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_log10));
4363 break;
4364 case MMathFunction::Log2:
4365 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_log2));
4366 break;
4367 case MMathFunction::Log1P:
4368 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_log1p));
4369 break;
4370 case MMathFunction::ExpM1:
4371 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_expm1));
4372 break;
4373 case MMathFunction::CosH:
4374 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_cosh));
4375 break;
4376 case MMathFunction::SinH:
4377 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_sinh));
4378 break;
4379 case MMathFunction::TanH:
4380 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_tanh));
4381 break;
4382 case MMathFunction::ACosH:
4383 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_acosh));
4384 break;
4385 case MMathFunction::ASinH:
4386 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_asinh));
4387 break;
4388 case MMathFunction::ATanH:
4389 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_atanh));
4390 break;
4391 case MMathFunction::Sign:
4392 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_sign));
4393 break;
4394 case MMathFunction::Trunc:
4395 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_trunc));
4396 break;
4397 case MMathFunction::Cbrt:
4398 funptr = JS_FUNC_TO_DATA_PTR(void *, MAYBE_CACHED(js::math_cbrt));
4399 break;
4400 case MMathFunction::Floor:
4401 funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_floor_impl);
4402 break;
4403 case MMathFunction::Ceil:
4404 funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_ceil_impl);
4405 break;
4406 case MMathFunction::Round:
4407 funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_round_impl);
4408 break;
4409 default:
4410 MOZ_ASSUME_UNREACHABLE("Unknown math function");
4411 }
4413 # undef MAYBE_CACHED
4415 masm.callWithABI(funptr, MoveOp::DOUBLE);
4416 return true;
4417 }
4419 bool
4420 CodeGenerator::visitMathFunctionF(LMathFunctionF *ins)
4421 {
4422 Register temp = ToRegister(ins->temp());
4423 FloatRegister input = ToFloatRegister(ins->input());
4424 JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
4426 masm.setupUnalignedABICall(1, temp);
4427 masm.passABIArg(input, MoveOp::FLOAT32);
4429 void *funptr = nullptr;
4430 switch (ins->mir()->function()) {
4431 case MMathFunction::Floor: funptr = JS_FUNC_TO_DATA_PTR(void *, floorf); break;
4432 case MMathFunction::Round: funptr = JS_FUNC_TO_DATA_PTR(void *, math_roundf_impl); break;
4433 case MMathFunction::Ceil: funptr = JS_FUNC_TO_DATA_PTR(void *, ceilf); break;
4434 default:
4435 MOZ_ASSUME_UNREACHABLE("Unknown or unsupported float32 math function");
4436 }
4438 masm.callWithABI(funptr, MoveOp::FLOAT32);
4439 return true;
4440 }
4442 bool
4443 CodeGenerator::visitModD(LModD *ins)
4444 {
4445 FloatRegister lhs = ToFloatRegister(ins->lhs());
4446 FloatRegister rhs = ToFloatRegister(ins->rhs());
4447 Register temp = ToRegister(ins->temp());
4449 JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg);
4451 masm.setupUnalignedABICall(2, temp);
4452 masm.passABIArg(lhs, MoveOp::DOUBLE);
4453 masm.passABIArg(rhs, MoveOp::DOUBLE);
4455 if (gen->compilingAsmJS())
4456 masm.callWithABI(AsmJSImm_ModD, MoveOp::DOUBLE);
4457 else
4458 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NumberMod), MoveOp::DOUBLE);
4459 return true;
4460 }
4462 typedef bool (*BinaryFn)(JSContext *, MutableHandleValue, MutableHandleValue, MutableHandleValue);
4463 typedef bool (*BinaryParFn)(ForkJoinContext *, HandleValue, HandleValue, MutableHandleValue);
4465 static const VMFunction AddInfo = FunctionInfo<BinaryFn>(js::AddValues);
4466 static const VMFunction SubInfo = FunctionInfo<BinaryFn>(js::SubValues);
4467 static const VMFunction MulInfo = FunctionInfo<BinaryFn>(js::MulValues);
4468 static const VMFunction DivInfo = FunctionInfo<BinaryFn>(js::DivValues);
4469 static const VMFunction ModInfo = FunctionInfo<BinaryFn>(js::ModValues);
4470 static const VMFunctionsModal UrshInfo = VMFunctionsModal(
4471 FunctionInfo<BinaryFn>(js::UrshValues),
4472 FunctionInfo<BinaryParFn>(UrshValuesPar));
4474 bool
4475 CodeGenerator::visitBinaryV(LBinaryV *lir)
4476 {
4477 pushArg(ToValue(lir, LBinaryV::RhsInput));
4478 pushArg(ToValue(lir, LBinaryV::LhsInput));
4480 switch (lir->jsop()) {
4481 case JSOP_ADD:
4482 return callVM(AddInfo, lir);
4484 case JSOP_SUB:
4485 return callVM(SubInfo, lir);
4487 case JSOP_MUL:
4488 return callVM(MulInfo, lir);
4490 case JSOP_DIV:
4491 return callVM(DivInfo, lir);
4493 case JSOP_MOD:
4494 return callVM(ModInfo, lir);
4496 case JSOP_URSH:
4497 return callVM(UrshInfo, lir);
4499 default:
4500 MOZ_ASSUME_UNREACHABLE("Unexpected binary op");
4501 }
4502 }
4504 typedef bool (*StringCompareFn)(JSContext *, HandleString, HandleString, bool *);
4505 typedef bool (*StringCompareParFn)(ForkJoinContext *, HandleString, HandleString, bool *);
4506 static const VMFunctionsModal StringsEqualInfo = VMFunctionsModal(
4507 FunctionInfo<StringCompareFn>(jit::StringsEqual<true>),
4508 FunctionInfo<StringCompareParFn>(jit::StringsEqualPar));
4509 static const VMFunctionsModal StringsNotEqualInfo = VMFunctionsModal(
4510 FunctionInfo<StringCompareFn>(jit::StringsEqual<false>),
4511 FunctionInfo<StringCompareParFn>(jit::StringsUnequalPar));
4513 bool
4514 CodeGenerator::emitCompareS(LInstruction *lir, JSOp op, Register left, Register right,
4515 Register output, Register temp)
4516 {
4517 JS_ASSERT(lir->isCompareS() || lir->isCompareStrictS());
4519 OutOfLineCode *ool = nullptr;
4521 if (op == JSOP_EQ || op == JSOP_STRICTEQ) {
4522 ool = oolCallVM(StringsEqualInfo, lir, (ArgList(), left, right), StoreRegisterTo(output));
4523 } else {
4524 JS_ASSERT(op == JSOP_NE || op == JSOP_STRICTNE);
4525 ool = oolCallVM(StringsNotEqualInfo, lir, (ArgList(), left, right), StoreRegisterTo(output));
4526 }
4527 if (!ool)
4528 return false;
4530 masm.compareStrings(op, left, right, output, temp, ool->entry());
4532 masm.bind(ool->rejoin());
4533 return true;
4534 }
4536 bool
4537 CodeGenerator::visitCompareStrictS(LCompareStrictS *lir)
4538 {
4539 JSOp op = lir->mir()->jsop();
4540 JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
4542 const ValueOperand leftV = ToValue(lir, LCompareStrictS::Lhs);
4543 Register right = ToRegister(lir->right());
4544 Register output = ToRegister(lir->output());
4545 Register temp = ToRegister(lir->temp());
4546 Register tempToUnbox = ToTempUnboxRegister(lir->tempToUnbox());
4548 Label string, done;
4550 masm.branchTestString(Assembler::Equal, leftV, &string);
4551 masm.move32(Imm32(op == JSOP_STRICTNE), output);
4552 masm.jump(&done);
4554 masm.bind(&string);
4555 Register left = masm.extractString(leftV, tempToUnbox);
4556 if (!emitCompareS(lir, op, left, right, output, temp))
4557 return false;
4559 masm.bind(&done);
4561 return true;
4562 }
4564 bool
4565 CodeGenerator::visitCompareS(LCompareS *lir)
4566 {
4567 JSOp op = lir->mir()->jsop();
4568 Register left = ToRegister(lir->left());
4569 Register right = ToRegister(lir->right());
4570 Register output = ToRegister(lir->output());
4571 Register temp = ToRegister(lir->temp());
4573 return emitCompareS(lir, op, left, right, output, temp);
4574 }
4576 typedef bool (*CompareFn)(JSContext *, MutableHandleValue, MutableHandleValue, bool *);
4577 typedef bool (*CompareParFn)(ForkJoinContext *, MutableHandleValue, MutableHandleValue, bool *);
4578 static const VMFunctionsModal EqInfo = VMFunctionsModal(
4579 FunctionInfo<CompareFn>(jit::LooselyEqual<true>),
4580 FunctionInfo<CompareParFn>(jit::LooselyEqualPar));
4581 static const VMFunctionsModal NeInfo = VMFunctionsModal(
4582 FunctionInfo<CompareFn>(jit::LooselyEqual<false>),
4583 FunctionInfo<CompareParFn>(jit::LooselyUnequalPar));
4584 static const VMFunctionsModal StrictEqInfo = VMFunctionsModal(
4585 FunctionInfo<CompareFn>(jit::StrictlyEqual<true>),
4586 FunctionInfo<CompareParFn>(jit::StrictlyEqualPar));
4587 static const VMFunctionsModal StrictNeInfo = VMFunctionsModal(
4588 FunctionInfo<CompareFn>(jit::StrictlyEqual<false>),
4589 FunctionInfo<CompareParFn>(jit::StrictlyUnequalPar));
4590 static const VMFunctionsModal LtInfo = VMFunctionsModal(
4591 FunctionInfo<CompareFn>(jit::LessThan),
4592 FunctionInfo<CompareParFn>(jit::LessThanPar));
4593 static const VMFunctionsModal LeInfo = VMFunctionsModal(
4594 FunctionInfo<CompareFn>(jit::LessThanOrEqual),
4595 FunctionInfo<CompareParFn>(jit::LessThanOrEqualPar));
4596 static const VMFunctionsModal GtInfo = VMFunctionsModal(
4597 FunctionInfo<CompareFn>(jit::GreaterThan),
4598 FunctionInfo<CompareParFn>(jit::GreaterThanPar));
4599 static const VMFunctionsModal GeInfo = VMFunctionsModal(
4600 FunctionInfo<CompareFn>(jit::GreaterThanOrEqual),
4601 FunctionInfo<CompareParFn>(jit::GreaterThanOrEqualPar));
4603 bool
4604 CodeGenerator::visitCompareVM(LCompareVM *lir)
4605 {
4606 pushArg(ToValue(lir, LBinaryV::RhsInput));
4607 pushArg(ToValue(lir, LBinaryV::LhsInput));
4609 switch (lir->mir()->jsop()) {
4610 case JSOP_EQ:
4611 return callVM(EqInfo, lir);
4613 case JSOP_NE:
4614 return callVM(NeInfo, lir);
4616 case JSOP_STRICTEQ:
4617 return callVM(StrictEqInfo, lir);
4619 case JSOP_STRICTNE:
4620 return callVM(StrictNeInfo, lir);
4622 case JSOP_LT:
4623 return callVM(LtInfo, lir);
4625 case JSOP_LE:
4626 return callVM(LeInfo, lir);
4628 case JSOP_GT:
4629 return callVM(GtInfo, lir);
4631 case JSOP_GE:
4632 return callVM(GeInfo, lir);
4634 default:
4635 MOZ_ASSUME_UNREACHABLE("Unexpected compare op");
4636 }
4637 }
4639 bool
4640 CodeGenerator::visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir)
4641 {
4642 JSOp op = lir->mir()->jsop();
4643 MCompare::CompareType compareType = lir->mir()->compareType();
4644 JS_ASSERT(compareType == MCompare::Compare_Undefined ||
4645 compareType == MCompare::Compare_Null);
4647 const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefined::Value);
4648 Register output = ToRegister(lir->output());
4650 if (op == JSOP_EQ || op == JSOP_NE) {
4651 MOZ_ASSERT(lir->mir()->lhs()->type() != MIRType_Object ||
4652 lir->mir()->operandMightEmulateUndefined(),
4653 "Operands which can't emulate undefined should have been folded");
4655 OutOfLineTestObjectWithLabels *ool = nullptr;
4656 Maybe<Label> label1, label2;
4657 Label *nullOrLikeUndefined;
4658 Label *notNullOrLikeUndefined;
4659 if (lir->mir()->operandMightEmulateUndefined()) {
4660 ool = new(alloc()) OutOfLineTestObjectWithLabels();
4661 if (!addOutOfLineCode(ool))
4662 return false;
4663 nullOrLikeUndefined = ool->label1();
4664 notNullOrLikeUndefined = ool->label2();
4665 } else {
4666 label1.construct();
4667 label2.construct();
4668 nullOrLikeUndefined = label1.addr();
4669 notNullOrLikeUndefined = label2.addr();
4670 }
4672 Register tag = masm.splitTagForTest(value);
4674 masm.branchTestNull(Assembler::Equal, tag, nullOrLikeUndefined);
4675 masm.branchTestUndefined(Assembler::Equal, tag, nullOrLikeUndefined);
4677 if (ool) {
4678 // Check whether it's a truthy object or a falsy object that emulates
4679 // undefined.
4680 masm.branchTestObject(Assembler::NotEqual, tag, notNullOrLikeUndefined);
4682 Register objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox()));
4683 branchTestObjectEmulatesUndefined(objreg, nullOrLikeUndefined, notNullOrLikeUndefined,
4684 ToRegister(lir->temp()), ool);
4685 // fall through
4686 }
4688 Label done;
4690 // It's not null or undefined, and if it's an object it doesn't
4691 // emulate undefined, so it's not like undefined.
4692 masm.move32(Imm32(op == JSOP_NE), output);
4693 masm.jump(&done);
4695 masm.bind(nullOrLikeUndefined);
4696 masm.move32(Imm32(op == JSOP_EQ), output);
4698 // Both branches meet here.
4699 masm.bind(&done);
4700 return true;
4701 }
4703 JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
4705 Assembler::Condition cond = JSOpToCondition(compareType, op);
4706 if (compareType == MCompare::Compare_Null)
4707 masm.testNullSet(cond, value, output);
4708 else
4709 masm.testUndefinedSet(cond, value, output);
4711 return true;
4712 }
4714 bool
4715 CodeGenerator::visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBranch *lir)
4716 {
4717 JSOp op = lir->cmpMir()->jsop();
4718 MCompare::CompareType compareType = lir->cmpMir()->compareType();
4719 JS_ASSERT(compareType == MCompare::Compare_Undefined ||
4720 compareType == MCompare::Compare_Null);
4722 const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedAndBranch::Value);
4724 if (op == JSOP_EQ || op == JSOP_NE) {
4725 MBasicBlock *ifTrue;
4726 MBasicBlock *ifFalse;
4728 if (op == JSOP_EQ) {
4729 ifTrue = lir->ifTrue();
4730 ifFalse = lir->ifFalse();
4731 } else {
4732 // Swap branches.
4733 ifTrue = lir->ifFalse();
4734 ifFalse = lir->ifTrue();
4735 op = JSOP_EQ;
4736 }
4738 MOZ_ASSERT(lir->cmpMir()->lhs()->type() != MIRType_Object ||
4739 lir->cmpMir()->operandMightEmulateUndefined(),
4740 "Operands which can't emulate undefined should have been folded");
4742 OutOfLineTestObject *ool = nullptr;
4743 if (lir->cmpMir()->operandMightEmulateUndefined()) {
4744 ool = new(alloc()) OutOfLineTestObject();
4745 if (!addOutOfLineCode(ool))
4746 return false;
4747 }
4749 Register tag = masm.splitTagForTest(value);
4751 Label *ifTrueLabel = getJumpLabelForBranch(ifTrue);
4752 Label *ifFalseLabel = getJumpLabelForBranch(ifFalse);
4754 masm.branchTestNull(Assembler::Equal, tag, ifTrueLabel);
4755 masm.branchTestUndefined(Assembler::Equal, tag, ifTrueLabel);
4757 if (ool) {
4758 masm.branchTestObject(Assembler::NotEqual, tag, ifFalseLabel);
4760 // Objects that emulate undefined are loosely equal to null/undefined.
4761 Register objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox()));
4762 Register scratch = ToRegister(lir->temp());
4763 testObjectEmulatesUndefined(objreg, ifTrueLabel, ifFalseLabel, scratch, ool);
4764 } else {
4765 masm.jump(ifFalseLabel);
4766 }
4767 return true;
4768 }
4770 JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
4772 Assembler::Condition cond = JSOpToCondition(compareType, op);
4773 if (compareType == MCompare::Compare_Null)
4774 testNullEmitBranch(cond, value, lir->ifTrue(), lir->ifFalse());
4775 else
4776 testUndefinedEmitBranch(cond, value, lir->ifTrue(), lir->ifFalse());
4778 return true;
4779 }
4781 bool
4782 CodeGenerator::visitEmulatesUndefined(LEmulatesUndefined *lir)
4783 {
4784 MOZ_ASSERT(lir->mir()->compareType() == MCompare::Compare_Undefined ||
4785 lir->mir()->compareType() == MCompare::Compare_Null);
4786 MOZ_ASSERT(lir->mir()->lhs()->type() == MIRType_Object);
4787 MOZ_ASSERT(lir->mir()->operandMightEmulateUndefined(),
4788 "If the object couldn't emulate undefined, this should have been folded.");
4790 JSOp op = lir->mir()->jsop();
4791 MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE, "Strict equality should have been folded");
4793 OutOfLineTestObjectWithLabels *ool = new(alloc()) OutOfLineTestObjectWithLabels();
4794 if (!addOutOfLineCode(ool))
4795 return false;
4797 Label *emulatesUndefined = ool->label1();
4798 Label *doesntEmulateUndefined = ool->label2();
4800 Register objreg = ToRegister(lir->input());
4801 Register output = ToRegister(lir->output());
4802 branchTestObjectEmulatesUndefined(objreg, emulatesUndefined, doesntEmulateUndefined,
4803 output, ool);
4805 Label done;
4807 masm.move32(Imm32(op == JSOP_NE), output);
4808 masm.jump(&done);
4810 masm.bind(emulatesUndefined);
4811 masm.move32(Imm32(op == JSOP_EQ), output);
4812 masm.bind(&done);
4813 return true;
4814 }
4816 bool
4817 CodeGenerator::visitEmulatesUndefinedAndBranch(LEmulatesUndefinedAndBranch *lir)
4818 {
4819 MOZ_ASSERT(lir->cmpMir()->compareType() == MCompare::Compare_Undefined ||
4820 lir->cmpMir()->compareType() == MCompare::Compare_Null);
4821 MOZ_ASSERT(lir->cmpMir()->operandMightEmulateUndefined(),
4822 "Operands which can't emulate undefined should have been folded");
4824 JSOp op = lir->cmpMir()->jsop();
4825 MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE, "Strict equality should have been folded");
4827 OutOfLineTestObject *ool = new(alloc()) OutOfLineTestObject();
4828 if (!addOutOfLineCode(ool))
4829 return false;
4831 Label *equal;
4832 Label *unequal;
4834 {
4835 MBasicBlock *ifTrue;
4836 MBasicBlock *ifFalse;
4838 if (op == JSOP_EQ) {
4839 ifTrue = lir->ifTrue();
4840 ifFalse = lir->ifFalse();
4841 } else {
4842 // Swap branches.
4843 ifTrue = lir->ifFalse();
4844 ifFalse = lir->ifTrue();
4845 op = JSOP_EQ;
4846 }
4848 equal = getJumpLabelForBranch(ifTrue);
4849 unequal = getJumpLabelForBranch(ifFalse);
4850 }
4852 Register objreg = ToRegister(lir->input());
4854 testObjectEmulatesUndefined(objreg, equal, unequal, ToRegister(lir->temp()), ool);
4855 return true;
4856 }
4858 typedef JSString *(*ConcatStringsFn)(ThreadSafeContext *, HandleString, HandleString);
4859 typedef JSString *(*ConcatStringsParFn)(ForkJoinContext *, HandleString, HandleString);
4860 static const VMFunctionsModal ConcatStringsInfo = VMFunctionsModal(
4861 FunctionInfo<ConcatStringsFn>(ConcatStrings<CanGC>),
4862 FunctionInfo<ConcatStringsParFn>(ConcatStringsPar));
4864 bool
4865 CodeGenerator::emitConcat(LInstruction *lir, Register lhs, Register rhs, Register output)
4866 {
4867 OutOfLineCode *ool = oolCallVM(ConcatStringsInfo, lir, (ArgList(), lhs, rhs),
4868 StoreRegisterTo(output));
4869 if (!ool)
4870 return false;
4872 ExecutionMode mode = gen->info().executionMode();
4873 JitCode *stringConcatStub = gen->compartment->jitCompartment()->stringConcatStub(mode);
4874 masm.call(stringConcatStub);
4875 masm.branchTestPtr(Assembler::Zero, output, output, ool->entry());
4877 masm.bind(ool->rejoin());
4878 return true;
4879 }
4881 bool
4882 CodeGenerator::visitConcat(LConcat *lir)
4883 {
4884 Register lhs = ToRegister(lir->lhs());
4885 Register rhs = ToRegister(lir->rhs());
4887 Register output = ToRegister(lir->output());
4889 JS_ASSERT(lhs == CallTempReg0);
4890 JS_ASSERT(rhs == CallTempReg1);
4891 JS_ASSERT(ToRegister(lir->temp1()) == CallTempReg0);
4892 JS_ASSERT(ToRegister(lir->temp2()) == CallTempReg1);
4893 JS_ASSERT(ToRegister(lir->temp3()) == CallTempReg2);
4894 JS_ASSERT(ToRegister(lir->temp4()) == CallTempReg3);
4895 JS_ASSERT(ToRegister(lir->temp5()) == CallTempReg4);
4896 JS_ASSERT(output == CallTempReg5);
4898 return emitConcat(lir, lhs, rhs, output);
4899 }
4901 bool
4902 CodeGenerator::visitConcatPar(LConcatPar *lir)
4903 {
4904 DebugOnly<Register> cx = ToRegister(lir->forkJoinContext());
4905 Register lhs = ToRegister(lir->lhs());
4906 Register rhs = ToRegister(lir->rhs());
4907 Register output = ToRegister(lir->output());
4909 JS_ASSERT(lhs == CallTempReg0);
4910 JS_ASSERT(rhs == CallTempReg1);
4911 JS_ASSERT((Register)cx == CallTempReg4);
4912 JS_ASSERT(ToRegister(lir->temp1()) == CallTempReg0);
4913 JS_ASSERT(ToRegister(lir->temp2()) == CallTempReg1);
4914 JS_ASSERT(ToRegister(lir->temp3()) == CallTempReg2);
4915 JS_ASSERT(ToRegister(lir->temp4()) == CallTempReg3);
4916 JS_ASSERT(output == CallTempReg5);
4918 return emitConcat(lir, lhs, rhs, output);
4919 }
4921 static void
4922 CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len, Register scratch)
4923 {
4924 // Copy |len| jschars from |from| to |to|. Assumes len > 0 (checked below in
4925 // debug builds), and when done |to| must point to the next available char.
4927 #ifdef DEBUG
4928 Label ok;
4929 masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok);
4930 masm.assumeUnreachable("Length should be greater than 0.");
4931 masm.bind(&ok);
4932 #endif
4934 JS_STATIC_ASSERT(sizeof(jschar) == 2);
4936 Label start;
4937 masm.bind(&start);
4938 masm.load16ZeroExtend(Address(from, 0), scratch);
4939 masm.store16(scratch, Address(to, 0));
4940 masm.addPtr(Imm32(2), from);
4941 masm.addPtr(Imm32(2), to);
4942 masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start);
4943 }
4945 JitCode *
4946 JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
4947 {
4948 MacroAssembler masm(cx);
4950 Register lhs = CallTempReg0;
4951 Register rhs = CallTempReg1;
4952 Register temp1 = CallTempReg2;
4953 Register temp2 = CallTempReg3;
4954 Register temp3 = CallTempReg4;
4955 Register output = CallTempReg5;
4957 // In parallel execution, we pass in the ForkJoinContext in CallTempReg4, as
4958 // by the time we need to use the temp3 we no longer have need of the
4959 // cx.
4960 Register forkJoinContext = CallTempReg4;
4962 Label failure, failurePopTemps;
4964 // If lhs is empty, return rhs.
4965 Label leftEmpty;
4966 masm.loadStringLength(lhs, temp1);
4967 masm.branchTest32(Assembler::Zero, temp1, temp1, &leftEmpty);
4969 // If rhs is empty, return lhs.
4970 Label rightEmpty;
4971 masm.loadStringLength(rhs, temp2);
4972 masm.branchTest32(Assembler::Zero, temp2, temp2, &rightEmpty);
4974 masm.add32(temp1, temp2);
4976 // Check if we can use a JSFatInlineString.
4977 Label isFatInline;
4978 masm.branch32(Assembler::BelowOrEqual, temp2, Imm32(JSFatInlineString::MAX_FAT_INLINE_LENGTH),
4979 &isFatInline);
4981 // Ensure result length <= JSString::MAX_LENGTH.
4982 masm.branch32(Assembler::Above, temp2, Imm32(JSString::MAX_LENGTH), &failure);
4984 // Allocate a new rope.
4985 switch (mode) {
4986 case SequentialExecution:
4987 masm.newGCString(output, temp3, &failure);
4988 break;
4989 case ParallelExecution:
4990 masm.push(temp1);
4991 masm.push(temp2);
4992 masm.newGCStringPar(output, forkJoinContext, temp1, temp2, &failurePopTemps);
4993 masm.pop(temp2);
4994 masm.pop(temp1);
4995 break;
4996 default:
4997 MOZ_ASSUME_UNREACHABLE("No such execution mode");
4998 }
5000 // Store lengthAndFlags.
5001 JS_STATIC_ASSERT(JSString::ROPE_FLAGS == 0);
5002 masm.lshiftPtr(Imm32(JSString::LENGTH_SHIFT), temp2);
5003 masm.storePtr(temp2, Address(output, JSString::offsetOfLengthAndFlags()));
5005 // Store left and right nodes.
5006 masm.storePtr(lhs, Address(output, JSRope::offsetOfLeft()));
5007 masm.storePtr(rhs, Address(output, JSRope::offsetOfRight()));
5008 masm.ret();
5010 masm.bind(&leftEmpty);
5011 masm.mov(rhs, output);
5012 masm.ret();
5014 masm.bind(&rightEmpty);
5015 masm.mov(lhs, output);
5016 masm.ret();
5018 masm.bind(&isFatInline);
5020 // State: lhs length in temp1, result length in temp2.
5022 // Ensure both strings are linear (flags != 0).
5023 JS_STATIC_ASSERT(JSString::ROPE_FLAGS == 0);
5024 masm.branchTestPtr(Assembler::Zero, Address(lhs, JSString::offsetOfLengthAndFlags()),
5025 Imm32(JSString::FLAGS_MASK), &failure);
5026 masm.branchTestPtr(Assembler::Zero, Address(rhs, JSString::offsetOfLengthAndFlags()),
5027 Imm32(JSString::FLAGS_MASK), &failure);
5029 // Allocate a JSFatInlineString.
5030 switch (mode) {
5031 case SequentialExecution:
5032 masm.newGCFatInlineString(output, temp3, &failure);
5033 break;
5034 case ParallelExecution:
5035 masm.push(temp1);
5036 masm.push(temp2);
5037 masm.newGCFatInlineStringPar(output, forkJoinContext, temp1, temp2, &failurePopTemps);
5038 masm.pop(temp2);
5039 masm.pop(temp1);
5040 break;
5041 default:
5042 MOZ_ASSUME_UNREACHABLE("No such execution mode");
5043 }
5045 // Set lengthAndFlags.
5046 masm.lshiftPtr(Imm32(JSString::LENGTH_SHIFT), temp2);
5047 masm.orPtr(Imm32(JSString::FIXED_FLAGS), temp2);
5048 masm.storePtr(temp2, Address(output, JSString::offsetOfLengthAndFlags()));
5050 // Set chars pointer, keep in temp2 for copy loop below.
5051 masm.computeEffectiveAddress(Address(output, JSFatInlineString::offsetOfInlineStorage()), temp2);
5052 masm.storePtr(temp2, Address(output, JSFatInlineString::offsetOfChars()));
5054 {
5055 // We use temp3 in this block, which in parallel execution also holds
5056 // a live ForkJoinContext pointer. If we are compiling for parallel
5057 // execution, be sure to save and restore the ForkJoinContext.
5058 if (mode == ParallelExecution)
5059 masm.push(temp3);
5061 // Copy lhs chars. Temp1 still holds the lhs length. Note that this
5062 // advances temp2 to point to the next char. Note that this also
5063 // repurposes the lhs register.
5064 masm.loadPtr(Address(lhs, JSString::offsetOfChars()), lhs);
5065 CopyStringChars(masm, temp2, lhs, temp1, temp3);
5067 // Copy rhs chars.
5068 masm.loadStringLength(rhs, temp1);
5069 masm.loadPtr(Address(rhs, JSString::offsetOfChars()), rhs);
5070 CopyStringChars(masm, temp2, rhs, temp1, temp3);
5072 if (mode == ParallelExecution)
5073 masm.pop(temp3);
5074 }
5076 // Null-terminate.
5077 masm.store16(Imm32(0), Address(temp2, 0));
5078 masm.ret();
5080 masm.bind(&failurePopTemps);
5081 masm.pop(temp2);
5082 masm.pop(temp1);
5084 masm.bind(&failure);
5085 masm.movePtr(ImmPtr(nullptr), output);
5086 masm.ret();
5088 Linker linker(masm);
5089 AutoFlushICache afc("StringConcatStub");
5090 JitCode *code = linker.newCode<CanGC>(cx, JSC::OTHER_CODE);
5092 #ifdef JS_ION_PERF
5093 writePerfSpewerJitCodeProfile(code, "StringConcatStub");
5094 #endif
5096 return code;
5097 }
5099 typedef bool (*CharCodeAtFn)(JSContext *, HandleString, int32_t, uint32_t *);
5100 static const VMFunction CharCodeAtInfo = FunctionInfo<CharCodeAtFn>(jit::CharCodeAt);
5102 bool
5103 CodeGenerator::visitCharCodeAt(LCharCodeAt *lir)
5104 {
5105 Register str = ToRegister(lir->str());
5106 Register index = ToRegister(lir->index());
5107 Register output = ToRegister(lir->output());
5109 OutOfLineCode *ool = oolCallVM(CharCodeAtInfo, lir, (ArgList(), str, index), StoreRegisterTo(output));
5110 if (!ool)
5111 return false;
5113 Address lengthAndFlagsAddr(str, JSString::offsetOfLengthAndFlags());
5114 masm.branchTest32(Assembler::Zero, lengthAndFlagsAddr, Imm32(JSString::FLAGS_MASK), ool->entry());
5116 // getChars
5117 Address charsAddr(str, JSString::offsetOfChars());
5118 masm.loadPtr(charsAddr, output);
5119 masm.load16ZeroExtend(BaseIndex(output, index, TimesTwo, 0), output);
5121 masm.bind(ool->rejoin());
5122 return true;
5123 }
5125 typedef JSFlatString *(*StringFromCharCodeFn)(JSContext *, int32_t);
5126 static const VMFunction StringFromCharCodeInfo = FunctionInfo<StringFromCharCodeFn>(jit::StringFromCharCode);
5128 bool
5129 CodeGenerator::visitFromCharCode(LFromCharCode *lir)
5130 {
5131 Register code = ToRegister(lir->code());
5132 Register output = ToRegister(lir->output());
5134 OutOfLineCode *ool = oolCallVM(StringFromCharCodeInfo, lir, (ArgList(), code), StoreRegisterTo(output));
5135 if (!ool)
5136 return false;
5138 // OOL path if code >= UNIT_STATIC_LIMIT.
5139 masm.branch32(Assembler::AboveOrEqual, code, Imm32(StaticStrings::UNIT_STATIC_LIMIT),
5140 ool->entry());
5142 masm.movePtr(ImmPtr(&GetIonContext()->runtime->staticStrings().unitStaticTable), output);
5143 masm.loadPtr(BaseIndex(output, code, ScalePointer), output);
5145 masm.bind(ool->rejoin());
5146 return true;
5147 }
5149 typedef JSObject *(*StringSplitFn)(JSContext *, HandleTypeObject, HandleString, HandleString);
5150 static const VMFunction StringSplitInfo = FunctionInfo<StringSplitFn>(js::str_split_string);
5152 bool
5153 CodeGenerator::visitStringSplit(LStringSplit *lir)
5154 {
5155 pushArg(ToRegister(lir->separator()));
5156 pushArg(ToRegister(lir->string()));
5157 pushArg(ImmGCPtr(lir->mir()->typeObject()));
5159 return callVM(StringSplitInfo, lir);
5160 }
5162 bool
5163 CodeGenerator::visitInitializedLength(LInitializedLength *lir)
5164 {
5165 Address initLength(ToRegister(lir->elements()), ObjectElements::offsetOfInitializedLength());
5166 masm.load32(initLength, ToRegister(lir->output()));
5167 return true;
5168 }
5170 bool
5171 CodeGenerator::visitSetInitializedLength(LSetInitializedLength *lir)
5172 {
5173 Address initLength(ToRegister(lir->elements()), ObjectElements::offsetOfInitializedLength());
5174 Int32Key index = ToInt32Key(lir->index());
5176 masm.bumpKey(&index, 1);
5177 masm.storeKey(index, initLength);
5178 // Restore register value if it is used/captured after.
5179 masm.bumpKey(&index, -1);
5180 return true;
5181 }
5183 bool
5184 CodeGenerator::visitNotO(LNotO *lir)
5185 {
5186 MOZ_ASSERT(lir->mir()->operandMightEmulateUndefined(),
5187 "This should be constant-folded if the object can't emulate undefined.");
5189 OutOfLineTestObjectWithLabels *ool = new(alloc()) OutOfLineTestObjectWithLabels();
5190 if (!addOutOfLineCode(ool))
5191 return false;
5193 Label *ifEmulatesUndefined = ool->label1();
5194 Label *ifDoesntEmulateUndefined = ool->label2();
5196 Register objreg = ToRegister(lir->input());
5197 Register output = ToRegister(lir->output());
5198 branchTestObjectEmulatesUndefined(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined,
5199 output, ool);
5200 // fall through
5202 Label join;
5204 masm.move32(Imm32(0), output);
5205 masm.jump(&join);
5207 masm.bind(ifEmulatesUndefined);
5208 masm.move32(Imm32(1), output);
5210 masm.bind(&join);
5211 return true;
5212 }
5214 bool
5215 CodeGenerator::visitNotV(LNotV *lir)
5216 {
5217 Maybe<Label> ifTruthyLabel, ifFalsyLabel;
5218 Label *ifTruthy;
5219 Label *ifFalsy;
5221 OutOfLineTestObjectWithLabels *ool = nullptr;
5222 if (lir->mir()->operandMightEmulateUndefined()) {
5223 ool = new(alloc()) OutOfLineTestObjectWithLabels();
5224 if (!addOutOfLineCode(ool))
5225 return false;
5226 ifTruthy = ool->label1();
5227 ifFalsy = ool->label2();
5228 } else {
5229 ifTruthyLabel.construct();
5230 ifFalsyLabel.construct();
5231 ifTruthy = ifTruthyLabel.addr();
5232 ifFalsy = ifFalsyLabel.addr();
5233 }
5235 testValueTruthyKernel(ToValue(lir, LNotV::Input), lir->temp1(), lir->temp2(),
5236 ToFloatRegister(lir->tempFloat()),
5237 ifTruthy, ifFalsy, ool);
5239 Label join;
5240 Register output = ToRegister(lir->output());
5242 // Note that the testValueTruthyKernel call above may choose to fall through
5243 // to ifTruthy instead of branching there.
5244 masm.bind(ifTruthy);
5245 masm.move32(Imm32(0), output);
5246 masm.jump(&join);
5248 masm.bind(ifFalsy);
5249 masm.move32(Imm32(1), output);
5251 // both branches meet here.
5252 masm.bind(&join);
5253 return true;
5254 }
5256 bool
5257 CodeGenerator::visitBoundsCheck(LBoundsCheck *lir)
5258 {
5259 if (lir->index()->isConstant()) {
5260 // Use uint32 so that the comparison is unsigned.
5261 uint32_t index = ToInt32(lir->index());
5262 if (lir->length()->isConstant()) {
5263 uint32_t length = ToInt32(lir->length());
5264 if (index < length)
5265 return true;
5266 return bailout(lir->snapshot());
5267 }
5268 return bailoutCmp32(Assembler::BelowOrEqual, ToOperand(lir->length()), Imm32(index),
5269 lir->snapshot());
5270 }
5271 if (lir->length()->isConstant()) {
5272 return bailoutCmp32(Assembler::AboveOrEqual, ToRegister(lir->index()),
5273 Imm32(ToInt32(lir->length())), lir->snapshot());
5274 }
5275 return bailoutCmp32(Assembler::BelowOrEqual, ToOperand(lir->length()),
5276 ToRegister(lir->index()), lir->snapshot());
5277 }
5279 bool
5280 CodeGenerator::visitBoundsCheckRange(LBoundsCheckRange *lir)
5281 {
5282 int32_t min = lir->mir()->minimum();
5283 int32_t max = lir->mir()->maximum();
5284 JS_ASSERT(max >= min);
5286 Register temp = ToRegister(lir->getTemp(0));
5287 if (lir->index()->isConstant()) {
5288 int32_t nmin, nmax;
5289 int32_t index = ToInt32(lir->index());
5290 if (SafeAdd(index, min, &nmin) && SafeAdd(index, max, &nmax) && nmin >= 0) {
5291 return bailoutCmp32(Assembler::BelowOrEqual, ToOperand(lir->length()), Imm32(nmax),
5292 lir->snapshot());
5293 }
5294 masm.mov(ImmWord(index), temp);
5295 } else {
5296 masm.mov(ToRegister(lir->index()), temp);
5297 }
5299 // If the minimum and maximum differ then do an underflow check first.
5300 // If the two are the same then doing an unsigned comparison on the
5301 // length will also catch a negative index.
5302 if (min != max) {
5303 if (min != 0) {
5304 Label bail;
5305 masm.branchAdd32(Assembler::Overflow, Imm32(min), temp, &bail);
5306 if (!bailoutFrom(&bail, lir->snapshot()))
5307 return false;
5308 }
5310 if (!bailoutCmp32(Assembler::LessThan, temp, Imm32(0), lir->snapshot()))
5311 return false;
5313 if (min != 0) {
5314 int32_t diff;
5315 if (SafeSub(max, min, &diff))
5316 max = diff;
5317 else
5318 masm.sub32(Imm32(min), temp);
5319 }
5320 }
5322 // Compute the maximum possible index. No overflow check is needed when
5323 // max > 0. We can only wraparound to a negative number, which will test as
5324 // larger than all nonnegative numbers in the unsigned comparison, and the
5325 // length is required to be nonnegative (else testing a negative length
5326 // would succeed on any nonnegative index).
5327 if (max != 0) {
5328 if (max < 0) {
5329 Label bail;
5330 masm.branchAdd32(Assembler::Overflow, Imm32(max), temp, &bail);
5331 if (!bailoutFrom(&bail, lir->snapshot()))
5332 return false;
5333 } else {
5334 masm.add32(Imm32(max), temp);
5335 }
5336 }
5338 return bailoutCmp32(Assembler::BelowOrEqual, ToOperand(lir->length()), temp, lir->snapshot());
5339 }
5341 bool
5342 CodeGenerator::visitBoundsCheckLower(LBoundsCheckLower *lir)
5343 {
5344 int32_t min = lir->mir()->minimum();
5345 return bailoutCmp32(Assembler::LessThan, ToRegister(lir->index()), Imm32(min),
5346 lir->snapshot());
5347 }
5349 class OutOfLineStoreElementHole : public OutOfLineCodeBase<CodeGenerator>
5350 {
5351 LInstruction *ins_;
5352 Label rejoinStore_;
5354 public:
5355 OutOfLineStoreElementHole(LInstruction *ins)
5356 : ins_(ins)
5357 {
5358 JS_ASSERT(ins->isStoreElementHoleV() || ins->isStoreElementHoleT());
5359 }
5361 bool accept(CodeGenerator *codegen) {
5362 return codegen->visitOutOfLineStoreElementHole(this);
5363 }
5364 LInstruction *ins() const {
5365 return ins_;
5366 }
5367 Label *rejoinStore() {
5368 return &rejoinStore_;
5369 }
5370 };
5372 bool
5373 CodeGenerator::emitStoreHoleCheck(Register elements, const LAllocation *index, LSnapshot *snapshot)
5374 {
5375 Label bail;
5376 if (index->isConstant()) {
5377 masm.branchTestMagic(Assembler::Equal,
5378 Address(elements, ToInt32(index) * sizeof(js::Value)), &bail);
5379 } else {
5380 masm.branchTestMagic(Assembler::Equal,
5381 BaseIndex(elements, ToRegister(index), TimesEight), &bail);
5382 }
5383 return bailoutFrom(&bail, snapshot);
5384 }
5386 bool
5387 CodeGenerator::visitStoreElementT(LStoreElementT *store)
5388 {
5389 Register elements = ToRegister(store->elements());
5390 const LAllocation *index = store->index();
5392 if (store->mir()->needsBarrier())
5393 emitPreBarrier(elements, index, store->mir()->elementType());
5395 if (store->mir()->needsHoleCheck() && !emitStoreHoleCheck(elements, index, store->snapshot()))
5396 return false;
5398 storeElementTyped(store->value(), store->mir()->value()->type(), store->mir()->elementType(),
5399 elements, index);
5400 return true;
5401 }
5403 bool
5404 CodeGenerator::visitStoreElementV(LStoreElementV *lir)
5405 {
5406 const ValueOperand value = ToValue(lir, LStoreElementV::Value);
5407 Register elements = ToRegister(lir->elements());
5408 const LAllocation *index = lir->index();
5410 if (lir->mir()->needsBarrier())
5411 emitPreBarrier(elements, index, MIRType_Value);
5413 if (lir->mir()->needsHoleCheck() && !emitStoreHoleCheck(elements, index, lir->snapshot()))
5414 return false;
5416 if (lir->index()->isConstant())
5417 masm.storeValue(value, Address(elements, ToInt32(lir->index()) * sizeof(js::Value)));
5418 else
5419 masm.storeValue(value, BaseIndex(elements, ToRegister(lir->index()), TimesEight));
5420 return true;
5421 }
5423 bool
5424 CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT *lir)
5425 {
5426 OutOfLineStoreElementHole *ool = new(alloc()) OutOfLineStoreElementHole(lir);
5427 if (!addOutOfLineCode(ool))
5428 return false;
5430 Register elements = ToRegister(lir->elements());
5431 const LAllocation *index = lir->index();
5433 // OOL path if index >= initializedLength.
5434 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
5435 masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry());
5437 if (lir->mir()->needsBarrier())
5438 emitPreBarrier(elements, index, lir->mir()->elementType());
5440 masm.bind(ool->rejoinStore());
5441 storeElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(),
5442 elements, index);
5444 masm.bind(ool->rejoin());
5445 return true;
5446 }
5448 bool
5449 CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV *lir)
5450 {
5451 OutOfLineStoreElementHole *ool = new(alloc()) OutOfLineStoreElementHole(lir);
5452 if (!addOutOfLineCode(ool))
5453 return false;
5455 Register elements = ToRegister(lir->elements());
5456 const LAllocation *index = lir->index();
5457 const ValueOperand value = ToValue(lir, LStoreElementHoleV::Value);
5459 // OOL path if index >= initializedLength.
5460 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
5461 masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry());
5463 if (lir->mir()->needsBarrier())
5464 emitPreBarrier(elements, index, lir->mir()->elementType());
5466 masm.bind(ool->rejoinStore());
5467 if (lir->index()->isConstant())
5468 masm.storeValue(value, Address(elements, ToInt32(lir->index()) * sizeof(js::Value)));
5469 else
5470 masm.storeValue(value, BaseIndex(elements, ToRegister(lir->index()), TimesEight));
5472 masm.bind(ool->rejoin());
5473 return true;
5474 }
5476 typedef bool (*SetDenseElementFn)(JSContext *, HandleObject, int32_t, HandleValue,
5477 bool strict);
5478 typedef bool (*SetDenseElementParFn)(ForkJoinContext *, HandleObject, int32_t, HandleValue, bool);
5479 static const VMFunctionsModal SetDenseElementInfo = VMFunctionsModal(
5480 FunctionInfo<SetDenseElementFn>(SetDenseElement),
5481 FunctionInfo<SetDenseElementParFn>(SetDenseElementPar));
5483 bool
5484 CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole *ool)
5485 {
5486 Register object, elements;
5487 LInstruction *ins = ool->ins();
5488 const LAllocation *index;
5489 MIRType valueType;
5490 ConstantOrRegister value;
5492 if (ins->isStoreElementHoleV()) {
5493 LStoreElementHoleV *store = ins->toStoreElementHoleV();
5494 object = ToRegister(store->object());
5495 elements = ToRegister(store->elements());
5496 index = store->index();
5497 valueType = store->mir()->value()->type();
5498 value = TypedOrValueRegister(ToValue(store, LStoreElementHoleV::Value));
5499 } else {
5500 LStoreElementHoleT *store = ins->toStoreElementHoleT();
5501 object = ToRegister(store->object());
5502 elements = ToRegister(store->elements());
5503 index = store->index();
5504 valueType = store->mir()->value()->type();
5505 if (store->value()->isConstant())
5506 value = ConstantOrRegister(*store->value()->toConstant());
5507 else
5508 value = TypedOrValueRegister(valueType, ToAnyRegister(store->value()));
5509 }
5511 // If index == initializedLength, try to bump the initialized length inline.
5512 // If index > initializedLength, call a stub. Note that this relies on the
5513 // condition flags sticking from the incoming branch.
5514 Label callStub;
5515 #ifdef JS_CODEGEN_MIPS
5516 // Had to reimplement for MIPS because there are no flags.
5517 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
5518 masm.branchKey(Assembler::NotEqual, initLength, ToInt32Key(index), &callStub);
5519 #else
5520 masm.j(Assembler::NotEqual, &callStub);
5521 #endif
5523 Int32Key key = ToInt32Key(index);
5525 // Check array capacity.
5526 masm.branchKey(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()),
5527 key, &callStub);
5529 // Update initialized length. The capacity guard above ensures this won't overflow,
5530 // due to NELEMENTS_LIMIT.
5531 masm.bumpKey(&key, 1);
5532 masm.storeKey(key, Address(elements, ObjectElements::offsetOfInitializedLength()));
5534 // Update length if length < initializedLength.
5535 Label dontUpdate;
5536 masm.branchKey(Assembler::AboveOrEqual, Address(elements, ObjectElements::offsetOfLength()),
5537 key, &dontUpdate);
5538 masm.storeKey(key, Address(elements, ObjectElements::offsetOfLength()));
5539 masm.bind(&dontUpdate);
5541 masm.bumpKey(&key, -1);
5543 if (ins->isStoreElementHoleT() && valueType != MIRType_Double) {
5544 // The inline path for StoreElementHoleT does not always store the type tag,
5545 // so we do the store on the OOL path. We use MIRType_None for the element type
5546 // so that storeElementTyped will always store the type tag.
5547 storeElementTyped(ins->toStoreElementHoleT()->value(), valueType, MIRType_None, elements,
5548 index);
5549 masm.jump(ool->rejoin());
5550 } else {
5551 // Jump to the inline path where we will store the value.
5552 masm.jump(ool->rejoinStore());
5553 }
5555 masm.bind(&callStub);
5556 saveLive(ins);
5558 pushArg(Imm32(current->mir()->strict()));
5559 pushArg(value);
5560 if (index->isConstant())
5561 pushArg(Imm32(ToInt32(index)));
5562 else
5563 pushArg(ToRegister(index));
5564 pushArg(object);
5565 if (!callVM(SetDenseElementInfo, ins))
5566 return false;
5568 restoreLive(ins);
5569 masm.jump(ool->rejoin());
5570 return true;
5571 }
5573 typedef bool (*ArrayPopShiftFn)(JSContext *, HandleObject, MutableHandleValue);
5574 static const VMFunction ArrayPopDenseInfo = FunctionInfo<ArrayPopShiftFn>(jit::ArrayPopDense);
5575 static const VMFunction ArrayShiftDenseInfo = FunctionInfo<ArrayPopShiftFn>(jit::ArrayShiftDense);
5577 bool
5578 CodeGenerator::emitArrayPopShift(LInstruction *lir, const MArrayPopShift *mir, Register obj,
5579 Register elementsTemp, Register lengthTemp, TypedOrValueRegister out)
5580 {
5581 OutOfLineCode *ool;
5583 if (mir->mode() == MArrayPopShift::Pop) {
5584 ool = oolCallVM(ArrayPopDenseInfo, lir, (ArgList(), obj), StoreValueTo(out));
5585 if (!ool)
5586 return false;
5587 } else {
5588 JS_ASSERT(mir->mode() == MArrayPopShift::Shift);
5589 ool = oolCallVM(ArrayShiftDenseInfo, lir, (ArgList(), obj), StoreValueTo(out));
5590 if (!ool)
5591 return false;
5592 }
5594 // VM call if a write barrier is necessary.
5595 masm.branchTestNeedsBarrier(Assembler::NonZero, lengthTemp, ool->entry());
5597 // Load elements and length.
5598 masm.loadPtr(Address(obj, JSObject::offsetOfElements()), elementsTemp);
5599 masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp);
5601 // VM call if length != initializedLength.
5602 Int32Key key = Int32Key(lengthTemp);
5603 Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
5604 masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
5606 // Test for length != 0. On zero length either take a VM call or generate
5607 // an undefined value, depending on whether the call is known to produce
5608 // undefined.
5609 Label done;
5610 if (mir->maybeUndefined()) {
5611 Label notEmpty;
5612 masm.branchTest32(Assembler::NonZero, lengthTemp, lengthTemp, ¬Empty);
5613 masm.moveValue(UndefinedValue(), out.valueReg());
5614 masm.jump(&done);
5615 masm.bind(¬Empty);
5616 } else {
5617 masm.branchTest32(Assembler::Zero, lengthTemp, lengthTemp, ool->entry());
5618 }
5620 masm.bumpKey(&key, -1);
5622 if (mir->mode() == MArrayPopShift::Pop) {
5623 masm.loadElementTypedOrValue(BaseIndex(elementsTemp, lengthTemp, TimesEight), out,
5624 mir->needsHoleCheck(), ool->entry());
5625 } else {
5626 JS_ASSERT(mir->mode() == MArrayPopShift::Shift);
5627 masm.loadElementTypedOrValue(Address(elementsTemp, 0), out, mir->needsHoleCheck(),
5628 ool->entry());
5629 }
5631 // Handle the failure case when the array length is non-writable in the
5632 // OOL path. (Unlike in the adding-an-element cases, we can't rely on the
5633 // capacity <= length invariant for such arrays to avoid an explicit
5634 // check.)
5635 Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
5636 Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
5637 masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
5639 // Now adjust length and initializedLength.
5640 masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength()));
5641 masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
5643 if (mir->mode() == MArrayPopShift::Shift) {
5644 // Don't save the temp registers.
5645 RegisterSet temps;
5646 temps.add(elementsTemp);
5647 temps.add(lengthTemp);
5649 saveVolatile(temps);
5650 masm.setupUnalignedABICall(1, lengthTemp);
5651 masm.passABIArg(obj);
5652 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ArrayShiftMoveElements));
5653 restoreVolatile(temps);
5654 }
5656 masm.bind(&done);
5657 masm.bind(ool->rejoin());
5658 return true;
5659 }
5661 bool
5662 CodeGenerator::visitArrayPopShiftV(LArrayPopShiftV *lir)
5663 {
5664 Register obj = ToRegister(lir->object());
5665 Register elements = ToRegister(lir->temp0());
5666 Register length = ToRegister(lir->temp1());
5667 TypedOrValueRegister out(ToOutValue(lir));
5668 return emitArrayPopShift(lir, lir->mir(), obj, elements, length, out);
5669 }
5671 bool
5672 CodeGenerator::visitArrayPopShiftT(LArrayPopShiftT *lir)
5673 {
5674 Register obj = ToRegister(lir->object());
5675 Register elements = ToRegister(lir->temp0());
5676 Register length = ToRegister(lir->temp1());
5677 TypedOrValueRegister out(lir->mir()->type(), ToAnyRegister(lir->output()));
5678 return emitArrayPopShift(lir, lir->mir(), obj, elements, length, out);
5679 }
5681 typedef bool (*ArrayPushDenseFn)(JSContext *, HandleObject, HandleValue, uint32_t *);
5682 static const VMFunction ArrayPushDenseInfo =
5683 FunctionInfo<ArrayPushDenseFn>(jit::ArrayPushDense);
5685 bool
5686 CodeGenerator::emitArrayPush(LInstruction *lir, const MArrayPush *mir, Register obj,
5687 ConstantOrRegister value, Register elementsTemp, Register length)
5688 {
5689 OutOfLineCode *ool = oolCallVM(ArrayPushDenseInfo, lir, (ArgList(), obj, value), StoreRegisterTo(length));
5690 if (!ool)
5691 return false;
5693 // Load elements and length.
5694 masm.loadPtr(Address(obj, JSObject::offsetOfElements()), elementsTemp);
5695 masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length);
5697 Int32Key key = Int32Key(length);
5698 Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
5699 Address capacity(elementsTemp, ObjectElements::offsetOfCapacity());
5701 // Guard length == initializedLength.
5702 masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
5704 // Guard length < capacity.
5705 masm.branchKey(Assembler::BelowOrEqual, capacity, key, ool->entry());
5707 masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight));
5709 masm.bumpKey(&key, 1);
5710 masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength()));
5711 masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
5713 masm.bind(ool->rejoin());
5714 return true;
5715 }
5717 bool
5718 CodeGenerator::visitArrayPushV(LArrayPushV *lir)
5719 {
5720 Register obj = ToRegister(lir->object());
5721 Register elementsTemp = ToRegister(lir->temp());
5722 Register length = ToRegister(lir->output());
5723 ConstantOrRegister value = TypedOrValueRegister(ToValue(lir, LArrayPushV::Value));
5724 return emitArrayPush(lir, lir->mir(), obj, value, elementsTemp, length);
5725 }
5727 bool
5728 CodeGenerator::visitArrayPushT(LArrayPushT *lir)
5729 {
5730 Register obj = ToRegister(lir->object());
5731 Register elementsTemp = ToRegister(lir->temp());
5732 Register length = ToRegister(lir->output());
5733 ConstantOrRegister value;
5734 if (lir->value()->isConstant())
5735 value = ConstantOrRegister(*lir->value()->toConstant());
5736 else
5737 value = TypedOrValueRegister(lir->mir()->value()->type(), ToAnyRegister(lir->value()));
5738 return emitArrayPush(lir, lir->mir(), obj, value, elementsTemp, length);
5739 }
5741 typedef JSObject *(*ArrayConcatDenseFn)(JSContext *, HandleObject, HandleObject, HandleObject);
5742 static const VMFunction ArrayConcatDenseInfo = FunctionInfo<ArrayConcatDenseFn>(ArrayConcatDense);
5744 bool
5745 CodeGenerator::visitArrayConcat(LArrayConcat *lir)
5746 {
5747 Register lhs = ToRegister(lir->lhs());
5748 Register rhs = ToRegister(lir->rhs());
5749 Register temp1 = ToRegister(lir->temp1());
5750 Register temp2 = ToRegister(lir->temp2());
5752 // If 'length == initializedLength' for both arrays we try to allocate an object
5753 // inline and pass it to the stub. Else, we just pass nullptr and the stub falls
5754 // back to a slow path.
5755 Label fail, call;
5756 masm.loadPtr(Address(lhs, JSObject::offsetOfElements()), temp1);
5757 masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
5758 masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
5760 masm.loadPtr(Address(rhs, JSObject::offsetOfElements()), temp1);
5761 masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
5762 masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
5764 // Try to allocate an object.
5765 JSObject *templateObj = lir->mir()->templateObj();
5766 masm.newGCThing(temp1, temp2, templateObj, &fail, lir->mir()->initialHeap());
5767 masm.initGCThing(temp1, temp2, templateObj);
5768 masm.jump(&call);
5769 {
5770 masm.bind(&fail);
5771 masm.movePtr(ImmPtr(nullptr), temp1);
5772 }
5773 masm.bind(&call);
5775 pushArg(temp1);
5776 pushArg(ToRegister(lir->rhs()));
5777 pushArg(ToRegister(lir->lhs()));
5778 return callVM(ArrayConcatDenseInfo, lir);
5779 }
5781 typedef JSObject *(*GetIteratorObjectFn)(JSContext *, HandleObject, uint32_t);
5782 static const VMFunction GetIteratorObjectInfo = FunctionInfo<GetIteratorObjectFn>(GetIteratorObject);
5784 bool
5785 CodeGenerator::visitCallIteratorStart(LCallIteratorStart *lir)
5786 {
5787 pushArg(Imm32(lir->mir()->flags()));
5788 pushArg(ToRegister(lir->object()));
5789 return callVM(GetIteratorObjectInfo, lir);
5790 }
5792 bool
5793 CodeGenerator::visitIteratorStart(LIteratorStart *lir)
5794 {
5795 const Register obj = ToRegister(lir->object());
5796 const Register output = ToRegister(lir->output());
5798 uint32_t flags = lir->mir()->flags();
5800 OutOfLineCode *ool = oolCallVM(GetIteratorObjectInfo, lir,
5801 (ArgList(), obj, Imm32(flags)), StoreRegisterTo(output));
5802 if (!ool)
5803 return false;
5805 const Register temp1 = ToRegister(lir->temp1());
5806 const Register temp2 = ToRegister(lir->temp2());
5807 const Register niTemp = ToRegister(lir->temp3()); // Holds the NativeIterator object.
5809 // Iterators other than for-in should use LCallIteratorStart.
5810 JS_ASSERT(flags == JSITER_ENUMERATE);
5812 // Fetch the most recent iterator and ensure it's not nullptr.
5813 masm.loadPtr(AbsoluteAddress(GetIonContext()->runtime->addressOfLastCachedNativeIterator()), output);
5814 masm.branchTestPtr(Assembler::Zero, output, output, ool->entry());
5816 // Load NativeIterator.
5817 masm.loadObjPrivate(output, JSObject::ITER_CLASS_NFIXED_SLOTS, niTemp);
5819 // Ensure the |active| and |unreusable| bits are not set.
5820 masm.branchTest32(Assembler::NonZero, Address(niTemp, offsetof(NativeIterator, flags)),
5821 Imm32(JSITER_ACTIVE|JSITER_UNREUSABLE), ool->entry());
5823 // Load the iterator's shape array.
5824 masm.loadPtr(Address(niTemp, offsetof(NativeIterator, shapes_array)), temp2);
5826 // Compare shape of object with the first shape.
5827 masm.loadObjShape(obj, temp1);
5828 masm.branchPtr(Assembler::NotEqual, Address(temp2, 0), temp1, ool->entry());
5830 // Compare shape of object's prototype with the second shape.
5831 masm.loadObjProto(obj, temp1);
5832 masm.loadObjShape(temp1, temp1);
5833 masm.branchPtr(Assembler::NotEqual, Address(temp2, sizeof(Shape *)), temp1, ool->entry());
5835 // Ensure the object's prototype's prototype is nullptr. The last native
5836 // iterator will always have a prototype chain length of one (i.e. it must
5837 // be a plain ), so we do not need to generate a loop here.
5838 masm.loadObjProto(obj, temp1);
5839 masm.loadObjProto(temp1, temp1);
5840 masm.branchTestPtr(Assembler::NonZero, temp1, temp1, ool->entry());
5842 // Ensure the object does not have any elements. The presence of dense
5843 // elements is not captured by the shape tests above.
5844 masm.branchPtr(Assembler::NotEqual,
5845 Address(obj, JSObject::offsetOfElements()),
5846 ImmPtr(js::emptyObjectElements),
5847 ool->entry());
5849 // Write barrier for stores to the iterator. We only need to take a write
5850 // barrier if NativeIterator::obj is actually going to change.
5851 {
5852 #ifdef JSGC_GENERATIONAL
5853 // Bug 867815: When using a nursery, we unconditionally take this out-
5854 // of-line so that we do not have to post-barrier the store to
5855 // NativeIter::obj. This just needs JIT support for the Cell* buffer.
5856 Address objAddr(niTemp, offsetof(NativeIterator, obj));
5857 masm.branchPtr(Assembler::NotEqual, objAddr, obj, ool->entry());
5858 #else
5859 Label noBarrier;
5860 masm.branchTestNeedsBarrier(Assembler::Zero, temp1, &noBarrier);
5862 Address objAddr(niTemp, offsetof(NativeIterator, obj));
5863 masm.branchPtr(Assembler::NotEqual, objAddr, obj, ool->entry());
5865 masm.bind(&noBarrier);
5866 #endif // !JSGC_GENERATIONAL
5867 }
5869 // Mark iterator as active.
5870 masm.storePtr(obj, Address(niTemp, offsetof(NativeIterator, obj)));
5871 masm.or32(Imm32(JSITER_ACTIVE), Address(niTemp, offsetof(NativeIterator, flags)));
5873 // Chain onto the active iterator stack.
5874 masm.loadPtr(AbsoluteAddress(gen->compartment->addressOfEnumerators()), temp1);
5876 // ni->next = list
5877 masm.storePtr(temp1, Address(niTemp, NativeIterator::offsetOfNext()));
5879 // ni->prev = list->prev
5880 masm.loadPtr(Address(temp1, NativeIterator::offsetOfPrev()), temp2);
5881 masm.storePtr(temp2, Address(niTemp, NativeIterator::offsetOfPrev()));
5883 // list->prev->next = ni
5884 masm.storePtr(niTemp, Address(temp2, NativeIterator::offsetOfNext()));
5886 // list->prev = ni
5887 masm.storePtr(niTemp, Address(temp1, NativeIterator::offsetOfPrev()));
5889 masm.bind(ool->rejoin());
5890 return true;
5891 }
5893 static void
5894 LoadNativeIterator(MacroAssembler &masm, Register obj, Register dest, Label *failures)
5895 {
5896 JS_ASSERT(obj != dest);
5898 // Test class.
5899 masm.branchTestObjClass(Assembler::NotEqual, obj, dest, &PropertyIteratorObject::class_, failures);
5901 // Load NativeIterator object.
5902 masm.loadObjPrivate(obj, JSObject::ITER_CLASS_NFIXED_SLOTS, dest);
5903 }
5905 typedef bool (*IteratorNextFn)(JSContext *, HandleObject, MutableHandleValue);
5906 static const VMFunction IteratorNextInfo = FunctionInfo<IteratorNextFn>(js_IteratorNext);
5908 bool
5909 CodeGenerator::visitIteratorNext(LIteratorNext *lir)
5910 {
5911 const Register obj = ToRegister(lir->object());
5912 const Register temp = ToRegister(lir->temp());
5913 const ValueOperand output = ToOutValue(lir);
5915 OutOfLineCode *ool = oolCallVM(IteratorNextInfo, lir, (ArgList(), obj), StoreValueTo(output));
5916 if (!ool)
5917 return false;
5919 LoadNativeIterator(masm, obj, temp, ool->entry());
5921 masm.branchTest32(Assembler::NonZero, Address(temp, offsetof(NativeIterator, flags)),
5922 Imm32(JSITER_FOREACH), ool->entry());
5924 // Get cursor, next string.
5925 masm.loadPtr(Address(temp, offsetof(NativeIterator, props_cursor)), output.scratchReg());
5926 masm.loadPtr(Address(output.scratchReg(), 0), output.scratchReg());
5927 masm.tagValue(JSVAL_TYPE_STRING, output.scratchReg(), output);
5929 // Increase the cursor.
5930 masm.addPtr(Imm32(sizeof(JSString *)), Address(temp, offsetof(NativeIterator, props_cursor)));
5932 masm.bind(ool->rejoin());
5933 return true;
5934 }
5936 typedef bool (*IteratorMoreFn)(JSContext *, HandleObject, bool *);
5937 static const VMFunction IteratorMoreInfo = FunctionInfo<IteratorMoreFn>(jit::IteratorMore);
5939 bool
5940 CodeGenerator::visitIteratorMore(LIteratorMore *lir)
5941 {
5942 const Register obj = ToRegister(lir->object());
5943 const Register output = ToRegister(lir->output());
5944 const Register temp = ToRegister(lir->temp());
5946 OutOfLineCode *ool = oolCallVM(IteratorMoreInfo, lir,
5947 (ArgList(), obj), StoreRegisterTo(output));
5948 if (!ool)
5949 return false;
5951 LoadNativeIterator(masm, obj, output, ool->entry());
5953 masm.branchTest32(Assembler::NonZero, Address(output, offsetof(NativeIterator, flags)),
5954 Imm32(JSITER_FOREACH), ool->entry());
5956 // Set output to true if props_cursor < props_end.
5957 masm.loadPtr(Address(output, offsetof(NativeIterator, props_end)), temp);
5958 masm.cmpPtrSet(Assembler::LessThan, Address(output, offsetof(NativeIterator, props_cursor)),
5959 temp, output);
5961 masm.bind(ool->rejoin());
5962 return true;
5963 }
5965 typedef bool (*CloseIteratorFn)(JSContext *, HandleObject);
5966 static const VMFunction CloseIteratorInfo = FunctionInfo<CloseIteratorFn>(CloseIterator);
5968 bool
5969 CodeGenerator::visitIteratorEnd(LIteratorEnd *lir)
5970 {
5971 const Register obj = ToRegister(lir->object());
5972 const Register temp1 = ToRegister(lir->temp1());
5973 const Register temp2 = ToRegister(lir->temp2());
5974 const Register temp3 = ToRegister(lir->temp3());
5976 OutOfLineCode *ool = oolCallVM(CloseIteratorInfo, lir, (ArgList(), obj), StoreNothing());
5977 if (!ool)
5978 return false;
5980 LoadNativeIterator(masm, obj, temp1, ool->entry());
5982 masm.branchTest32(Assembler::Zero, Address(temp1, offsetof(NativeIterator, flags)),
5983 Imm32(JSITER_ENUMERATE), ool->entry());
5985 // Clear active bit.
5986 masm.and32(Imm32(~JSITER_ACTIVE), Address(temp1, offsetof(NativeIterator, flags)));
5988 // Reset property cursor.
5989 masm.loadPtr(Address(temp1, offsetof(NativeIterator, props_array)), temp2);
5990 masm.storePtr(temp2, Address(temp1, offsetof(NativeIterator, props_cursor)));
5992 // Unlink from the iterator list.
5993 const Register next = temp2;
5994 const Register prev = temp3;
5995 masm.loadPtr(Address(temp1, NativeIterator::offsetOfNext()), next);
5996 masm.loadPtr(Address(temp1, NativeIterator::offsetOfPrev()), prev);
5997 masm.storePtr(prev, Address(next, NativeIterator::offsetOfPrev()));
5998 masm.storePtr(next, Address(prev, NativeIterator::offsetOfNext()));
5999 #ifdef DEBUG
6000 masm.storePtr(ImmPtr(nullptr), Address(temp1, NativeIterator::offsetOfNext()));
6001 masm.storePtr(ImmPtr(nullptr), Address(temp1, NativeIterator::offsetOfPrev()));
6002 #endif
6004 masm.bind(ool->rejoin());
6005 return true;
6006 }
6008 bool
6009 CodeGenerator::visitArgumentsLength(LArgumentsLength *lir)
6010 {
6011 // read number of actual arguments from the JS frame.
6012 Register argc = ToRegister(lir->output());
6013 Address ptr(StackPointer, frameSize() + IonJSFrameLayout::offsetOfNumActualArgs());
6015 masm.loadPtr(ptr, argc);
6016 return true;
6017 }
6019 bool
6020 CodeGenerator::visitGetFrameArgument(LGetFrameArgument *lir)
6021 {
6022 ValueOperand result = GetValueOutput(lir);
6023 const LAllocation *index = lir->index();
6024 size_t argvOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs();
6026 if (index->isConstant()) {
6027 int32_t i = index->toConstant()->toInt32();
6028 Address argPtr(StackPointer, sizeof(Value) * i + argvOffset);
6029 masm.loadValue(argPtr, result);
6030 } else {
6031 Register i = ToRegister(index);
6032 BaseIndex argPtr(StackPointer, i, ScaleFromElemWidth(sizeof(Value)), argvOffset);
6033 masm.loadValue(argPtr, result);
6034 }
6035 return true;
6036 }
6038 bool
6039 CodeGenerator::visitSetFrameArgumentT(LSetFrameArgumentT *lir)
6040 {
6041 size_t argOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs() +
6042 (sizeof(Value) * lir->mir()->argno());
6044 MIRType type = lir->mir()->value()->type();
6046 if (type == MIRType_Double) {
6047 // Store doubles directly.
6048 FloatRegister input = ToFloatRegister(lir->input());
6049 masm.storeDouble(input, Address(StackPointer, argOffset));
6051 } else {
6052 Register input = ToRegister(lir->input());
6053 masm.storeValue(ValueTypeFromMIRType(type), input, Address(StackPointer, argOffset));
6054 }
6055 return true;
6056 }
6058 bool
6059 CodeGenerator:: visitSetFrameArgumentC(LSetFrameArgumentC *lir)
6060 {
6061 size_t argOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs() +
6062 (sizeof(Value) * lir->mir()->argno());
6063 masm.storeValue(lir->val(), Address(StackPointer, argOffset));
6064 return true;
6065 }
6067 bool
6068 CodeGenerator:: visitSetFrameArgumentV(LSetFrameArgumentV *lir)
6069 {
6070 const ValueOperand val = ToValue(lir, LSetFrameArgumentV::Input);
6071 size_t argOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs() +
6072 (sizeof(Value) * lir->mir()->argno());
6073 masm.storeValue(val, Address(StackPointer, argOffset));
6074 return true;
6075 }
6077 typedef bool (*RunOnceScriptPrologueFn)(JSContext *, HandleScript);
6078 static const VMFunction RunOnceScriptPrologueInfo =
6079 FunctionInfo<RunOnceScriptPrologueFn>(js::RunOnceScriptPrologue);
6081 bool
6082 CodeGenerator::visitRunOncePrologue(LRunOncePrologue *lir)
6083 {
6084 pushArg(ImmGCPtr(lir->mir()->block()->info().script()));
6085 return callVM(RunOnceScriptPrologueInfo, lir);
6086 }
6089 typedef JSObject *(*InitRestParameterFn)(JSContext *, uint32_t, Value *, HandleObject,
6090 HandleObject);
6091 typedef JSObject *(*InitRestParameterParFn)(ForkJoinContext *, uint32_t, Value *,
6092 HandleObject, HandleObject);
6093 static const VMFunctionsModal InitRestParameterInfo = VMFunctionsModal(
6094 FunctionInfo<InitRestParameterFn>(InitRestParameter),
6095 FunctionInfo<InitRestParameterParFn>(InitRestParameterPar));
6097 bool
6098 CodeGenerator::emitRest(LInstruction *lir, Register array, Register numActuals,
6099 Register temp0, Register temp1, unsigned numFormals,
6100 JSObject *templateObject)
6101 {
6102 // Compute actuals() + numFormals.
6103 size_t actualsOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs();
6104 masm.movePtr(StackPointer, temp1);
6105 masm.addPtr(Imm32(sizeof(Value) * numFormals + actualsOffset), temp1);
6107 // Compute numActuals - numFormals.
6108 Label emptyLength, joinLength;
6109 masm.movePtr(numActuals, temp0);
6110 masm.branch32(Assembler::LessThanOrEqual, temp0, Imm32(numFormals), &emptyLength);
6111 masm.sub32(Imm32(numFormals), temp0);
6112 masm.jump(&joinLength);
6113 {
6114 masm.bind(&emptyLength);
6115 masm.move32(Imm32(0), temp0);
6116 }
6117 masm.bind(&joinLength);
6119 pushArg(array);
6120 pushArg(ImmGCPtr(templateObject));
6121 pushArg(temp1);
6122 pushArg(temp0);
6124 return callVM(InitRestParameterInfo, lir);
6125 }
6127 bool
6128 CodeGenerator::visitRest(LRest *lir)
6129 {
6130 Register numActuals = ToRegister(lir->numActuals());
6131 Register temp0 = ToRegister(lir->getTemp(0));
6132 Register temp1 = ToRegister(lir->getTemp(1));
6133 Register temp2 = ToRegister(lir->getTemp(2));
6134 unsigned numFormals = lir->mir()->numFormals();
6135 JSObject *templateObject = lir->mir()->templateObject();
6137 Label joinAlloc, failAlloc;
6138 masm.newGCThing(temp2, temp0, templateObject, &failAlloc, gc::DefaultHeap);
6139 masm.initGCThing(temp2, temp0, templateObject);
6140 masm.jump(&joinAlloc);
6141 {
6142 masm.bind(&failAlloc);
6143 masm.movePtr(ImmPtr(nullptr), temp2);
6144 }
6145 masm.bind(&joinAlloc);
6147 return emitRest(lir, temp2, numActuals, temp0, temp1, numFormals, templateObject);
6148 }
6150 bool
6151 CodeGenerator::visitRestPar(LRestPar *lir)
6152 {
6153 Register numActuals = ToRegister(lir->numActuals());
6154 Register cx = ToRegister(lir->forkJoinContext());
6155 Register temp0 = ToRegister(lir->getTemp(0));
6156 Register temp1 = ToRegister(lir->getTemp(1));
6157 Register temp2 = ToRegister(lir->getTemp(2));
6158 unsigned numFormals = lir->mir()->numFormals();
6159 JSObject *templateObject = lir->mir()->templateObject();
6161 if (!emitAllocateGCThingPar(lir, temp2, cx, temp0, temp1, templateObject))
6162 return false;
6164 return emitRest(lir, temp2, numActuals, temp0, temp1, numFormals, templateObject);
6165 }
6167 bool
6168 CodeGenerator::generateAsmJS(Label *stackOverflowLabel)
6169 {
6170 IonSpew(IonSpew_Codegen, "# Emitting asm.js code");
6172 // AsmJS doesn't do profiler instrumentation.
6173 sps_.disable();
6175 // The caller (either another asm.js function or the external-entry
6176 // trampoline) has placed all arguments in registers and on the stack
6177 // according to the system ABI. The MAsmJSParameters which represent these
6178 // parameters have been useFixed()ed to these ABI-specified positions.
6179 // Thus, there is nothing special to do in the prologue except (possibly)
6180 // bump the stack.
6181 if (!generateAsmJSPrologue(stackOverflowLabel))
6182 return false;
6183 if (!generateBody())
6184 return false;
6185 if (!generateEpilogue())
6186 return false;
6187 #if defined(JS_ION_PERF)
6188 // Note the end of the inline code and start of the OOL code.
6189 gen->perfSpewer().noteEndInlineCode(masm);
6190 #endif
6191 if (!generateOutOfLineCode())
6192 return false;
6194 // The only remaining work needed to compile this function is to patch the
6195 // switch-statement jump tables (the entries of the table need the absolute
6196 // address of the cases). These table entries are accmulated as CodeLabels
6197 // in the MacroAssembler's codeLabels_ list and processed all at once at in
6198 // the "static-link" phase of module compilation. It is critical that there
6199 // is nothing else to do after this point since the LifoAlloc memory
6200 // holding the MIR graph is about to be popped and reused. In particular,
6201 // every step in CodeGenerator::link must be a nop, as asserted here:
6202 JS_ASSERT(snapshots_.listSize() == 0);
6203 JS_ASSERT(snapshots_.RVATableSize() == 0);
6204 JS_ASSERT(recovers_.size() == 0);
6205 JS_ASSERT(bailouts_.empty());
6206 JS_ASSERT(graph.numConstants() == 0);
6207 JS_ASSERT(safepointIndices_.empty());
6208 JS_ASSERT(osiIndices_.empty());
6209 JS_ASSERT(cacheList_.empty());
6210 JS_ASSERT(safepoints_.size() == 0);
6211 return true;
6212 }
6214 bool
6215 CodeGenerator::generate()
6216 {
6217 IonSpew(IonSpew_Codegen, "# Emitting code for script %s:%d",
6218 gen->info().script()->filename(),
6219 gen->info().script()->lineno());
6221 if (!snapshots_.init())
6222 return false;
6224 if (!safepoints_.init(gen->alloc(), graph.totalSlotCount()))
6225 return false;
6227 #ifdef JS_TRACE_LOGGING
6228 if (!gen->compilingAsmJS() && gen->info().executionMode() == SequentialExecution) {
6229 if (!emitTracelogScriptStart())
6230 return false;
6231 if (!emitTracelogStartEvent(TraceLogger::IonMonkey))
6232 return false;
6233 }
6234 #endif
6236 // Before generating any code, we generate type checks for all parameters.
6237 // This comes before deoptTable_, because we can't use deopt tables without
6238 // creating the actual frame.
6239 if (!generateArgumentsChecks())
6240 return false;
6242 if (frameClass_ != FrameSizeClass::None()) {
6243 deoptTable_ = gen->jitRuntime()->getBailoutTable(frameClass_);
6244 if (!deoptTable_)
6245 return false;
6246 }
6248 #ifdef JS_TRACE_LOGGING
6249 Label skip;
6250 masm.jump(&skip);
6251 #endif
6253 // Remember the entry offset to skip the argument check.
6254 masm.flushBuffer();
6255 setSkipArgCheckEntryOffset(masm.size());
6257 #ifdef JS_TRACE_LOGGING
6258 if (!gen->compilingAsmJS() && gen->info().executionMode() == SequentialExecution) {
6259 if (!emitTracelogScriptStart())
6260 return false;
6261 if (!emitTracelogStartEvent(TraceLogger::IonMonkey))
6262 return false;
6263 }
6264 masm.bind(&skip);
6265 #endif
6267 #ifdef DEBUG
6268 // Assert that the argument types are correct.
6269 if (!generateArgumentsChecks(/* bailout = */ false))
6270 return false;
6271 #endif
6273 if (!generatePrologue())
6274 return false;
6275 if (!generateBody())
6276 return false;
6277 if (!generateEpilogue())
6278 return false;
6279 if (!generateInvalidateEpilogue())
6280 return false;
6281 #if defined(JS_ION_PERF)
6282 // Note the end of the inline code and start of the OOL code.
6283 perfSpewer_.noteEndInlineCode(masm);
6284 #endif
6285 if (!generateOutOfLineCode())
6286 return false;
6288 return !masm.oom();
6289 }
6291 bool
6292 CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints)
6293 {
6294 RootedScript script(cx, gen->info().script());
6295 ExecutionMode executionMode = gen->info().executionMode();
6296 OptimizationLevel optimizationLevel = gen->optimizationInfo().level();
6298 JS_ASSERT_IF(HasIonScript(script, executionMode), executionMode == SequentialExecution);
6300 // We finished the new IonScript. Invalidate the current active IonScript,
6301 // so we can replace it with this new (probably higher optimized) version.
6302 if (HasIonScript(script, executionMode)) {
6303 JS_ASSERT(GetIonScript(script, executionMode)->isRecompiling());
6304 // Do a normal invalidate, except don't cancel offThread compilations,
6305 // since that will cancel this compilation too.
6306 if (!Invalidate(cx, script, SequentialExecution,
6307 /* resetUses */ false, /* cancelOffThread*/ false))
6308 {
6309 return false;
6310 }
6311 }
6313 // Check to make sure we didn't have a mid-build invalidation. If so, we
6314 // will trickle to jit::Compile() and return Method_Skipped.
6315 types::RecompileInfo recompileInfo;
6316 if (!types::FinishCompilation(cx, script, executionMode, constraints, &recompileInfo))
6317 return true;
6319 uint32_t scriptFrameSize = frameClass_ == FrameSizeClass::None()
6320 ? frameDepth_
6321 : FrameSizeClass::FromDepth(frameDepth_).frameSize();
6323 // We encode safepoints after the OSI-point offsets have been determined.
6324 encodeSafepoints();
6326 // List of possible scripts that this graph may call. Currently this is
6327 // only tracked when compiling for parallel execution.
6328 CallTargetVector callTargets(alloc());
6329 if (executionMode == ParallelExecution)
6330 AddPossibleCallees(cx, graph.mir(), callTargets);
6332 IonScript *ionScript =
6333 IonScript::New(cx, recompileInfo,
6334 graph.totalSlotCount(), scriptFrameSize,
6335 snapshots_.listSize(), snapshots_.RVATableSize(),
6336 recovers_.size(), bailouts_.length(), graph.numConstants(),
6337 safepointIndices_.length(), osiIndices_.length(),
6338 cacheList_.length(), runtimeData_.length(),
6339 safepoints_.size(), callTargets.length(),
6340 patchableBackedges_.length(), optimizationLevel);
6341 if (!ionScript) {
6342 recompileInfo.compilerOutput(cx->zone()->types)->invalidate();
6343 return false;
6344 }
6346 // Lock the runtime against interrupt callbacks during the link.
6347 // We don't want an interrupt request to protect the code for the script
6348 // before it has been filled in, as we could segv before the runtime's
6349 // patchable backedges have been fully updated.
6350 JSRuntime::AutoLockForInterrupt lock(cx->runtime());
6352 // Make sure we don't segv while filling in the code, to avoid deadlocking
6353 // inside the signal handler.
6354 cx->runtime()->jitRuntime()->ensureIonCodeAccessible(cx->runtime());
6356 // Implicit interrupts are used only for sequential code. In parallel mode
6357 // use the normal executable allocator so that we cannot segv during
6358 // execution off the main thread.
6359 Linker linker(masm);
6360 AutoFlushICache afc("IonLink");
6361 JitCode *code = (executionMode == SequentialExecution)
6362 ? linker.newCodeForIonScript(cx)
6363 : linker.newCode<CanGC>(cx, JSC::ION_CODE);
6364 if (!code) {
6365 // Use js_free instead of IonScript::Destroy: the cache list and
6366 // backedge list are still uninitialized.
6367 js_free(ionScript);
6368 recompileInfo.compilerOutput(cx->zone()->types)->invalidate();
6369 return false;
6370 }
6372 ionScript->setMethod(code);
6373 ionScript->setSkipArgCheckEntryOffset(getSkipArgCheckEntryOffset());
6375 // If SPS is enabled, mark IonScript as having been instrumented with SPS
6376 if (sps_.enabled())
6377 ionScript->setHasSPSInstrumentation();
6379 SetIonScript(script, executionMode, ionScript);
6381 if (cx->runtime()->spsProfiler.enabled()) {
6382 const char *filename = script->filename();
6383 if (filename == nullptr)
6384 filename = "<unknown>";
6385 unsigned len = strlen(filename) + 50;
6386 char *buf = js_pod_malloc<char>(len);
6387 if (!buf)
6388 return false;
6389 JS_snprintf(buf, len, "Ion compiled %s:%d", filename, (int) script->lineno());
6390 cx->runtime()->spsProfiler.markEvent(buf);
6391 js_free(buf);
6392 }
6394 // In parallel execution mode, when we first compile a script, we
6395 // don't know that its potential callees are compiled, so set a
6396 // flag warning that the callees may not be fully compiled.
6397 if (!callTargets.empty())
6398 ionScript->setHasUncompiledCallTarget();
6400 invalidateEpilogueData_.fixup(&masm);
6401 Assembler::patchDataWithValueCheck(CodeLocationLabel(code, invalidateEpilogueData_),
6402 ImmPtr(ionScript),
6403 ImmPtr((void*)-1));
6405 IonSpew(IonSpew_Codegen, "Created IonScript %p (raw %p)",
6406 (void *) ionScript, (void *) code->raw());
6408 ionScript->setInvalidationEpilogueDataOffset(invalidateEpilogueData_.offset());
6409 ionScript->setOsrPc(gen->info().osrPc());
6410 ionScript->setOsrEntryOffset(getOsrEntryOffset());
6411 ptrdiff_t real_invalidate = masm.actualOffset(invalidate_.offset());
6412 ionScript->setInvalidationEpilogueOffset(real_invalidate);
6414 ionScript->setDeoptTable(deoptTable_);
6416 #if defined(JS_ION_PERF)
6417 if (PerfEnabled())
6418 perfSpewer_.writeProfile(script, code, masm);
6419 #endif
6421 for (size_t i = 0; i < ionScriptLabels_.length(); i++) {
6422 ionScriptLabels_[i].fixup(&masm);
6423 Assembler::patchDataWithValueCheck(CodeLocationLabel(code, ionScriptLabels_[i]),
6424 ImmPtr(ionScript),
6425 ImmPtr((void*)-1));
6426 }
6428 // for generating inline caches during the execution.
6429 if (runtimeData_.length())
6430 ionScript->copyRuntimeData(&runtimeData_[0]);
6431 if (cacheList_.length())
6432 ionScript->copyCacheEntries(&cacheList_[0], masm);
6434 // for marking during GC.
6435 if (safepointIndices_.length())
6436 ionScript->copySafepointIndices(&safepointIndices_[0], masm);
6437 if (safepoints_.size())
6438 ionScript->copySafepoints(&safepoints_);
6440 // for reconvering from an Ion Frame.
6441 if (bailouts_.length())
6442 ionScript->copyBailoutTable(&bailouts_[0]);
6443 if (osiIndices_.length())
6444 ionScript->copyOsiIndices(&osiIndices_[0], masm);
6445 if (snapshots_.listSize())
6446 ionScript->copySnapshots(&snapshots_);
6447 MOZ_ASSERT_IF(snapshots_.listSize(), recovers_.size());
6448 if (recovers_.size())
6449 ionScript->copyRecovers(&recovers_);
6450 if (graph.numConstants())
6451 ionScript->copyConstants(graph.constantPool());
6452 if (callTargets.length() > 0)
6453 ionScript->copyCallTargetEntries(callTargets.begin());
6454 if (patchableBackedges_.length() > 0)
6455 ionScript->copyPatchableBackedges(cx, code, patchableBackedges_.begin());
6457 #ifdef JS_TRACE_LOGGING
6458 TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
6459 for (uint32_t i = 0; i < patchableTraceLoggers_.length(); i++) {
6460 patchableTraceLoggers_[i].fixup(&masm);
6461 Assembler::patchDataWithValueCheck(CodeLocationLabel(code, patchableTraceLoggers_[i]),
6462 ImmPtr(logger),
6463 ImmPtr(nullptr));
6464 }
6465 uint32_t scriptId = TraceLogCreateTextId(logger, script);
6466 for (uint32_t i = 0; i < patchableTLScripts_.length(); i++) {
6467 patchableTLScripts_[i].fixup(&masm);
6468 Assembler::patchDataWithValueCheck(CodeLocationLabel(code, patchableTLScripts_[i]),
6469 ImmPtr((void *) uintptr_t(scriptId)),
6470 ImmPtr((void *)0));
6471 }
6472 #endif
6474 switch (executionMode) {
6475 case SequentialExecution:
6476 // The correct state for prebarriers is unknown until the end of compilation,
6477 // since a GC can occur during code generation. All barriers are emitted
6478 // off-by-default, and are toggled on here if necessary.
6479 if (cx->zone()->needsBarrier())
6480 ionScript->toggleBarriers(true);
6481 break;
6482 case ParallelExecution:
6483 // We don't run incremental GC during parallel execution; no need to
6484 // turn on barriers.
6485 break;
6486 default:
6487 MOZ_ASSUME_UNREACHABLE("No such execution mode");
6488 }
6490 return true;
6491 }
6493 // An out-of-line path to convert a boxed int32 to either a float or double.
6494 class OutOfLineUnboxFloatingPoint : public OutOfLineCodeBase<CodeGenerator>
6495 {
6496 LUnboxFloatingPoint *unboxFloatingPoint_;
6498 public:
6499 OutOfLineUnboxFloatingPoint(LUnboxFloatingPoint *unboxFloatingPoint)
6500 : unboxFloatingPoint_(unboxFloatingPoint)
6501 { }
6503 bool accept(CodeGenerator *codegen) {
6504 return codegen->visitOutOfLineUnboxFloatingPoint(this);
6505 }
6507 LUnboxFloatingPoint *unboxFloatingPoint() const {
6508 return unboxFloatingPoint_;
6509 }
6510 };
6512 bool
6513 CodeGenerator::visitUnboxFloatingPoint(LUnboxFloatingPoint *lir)
6514 {
6515 const ValueOperand box = ToValue(lir, LUnboxFloatingPoint::Input);
6516 const LDefinition *result = lir->output();
6518 // Out-of-line path to convert int32 to double or bailout
6519 // if this instruction is fallible.
6520 OutOfLineUnboxFloatingPoint *ool = new(alloc()) OutOfLineUnboxFloatingPoint(lir);
6521 if (!addOutOfLineCode(ool))
6522 return false;
6524 FloatRegister resultReg = ToFloatRegister(result);
6525 masm.branchTestDouble(Assembler::NotEqual, box, ool->entry());
6526 masm.unboxDouble(box, resultReg);
6527 if (lir->type() == MIRType_Float32)
6528 masm.convertDoubleToFloat32(resultReg, resultReg);
6529 masm.bind(ool->rejoin());
6530 return true;
6531 }
6533 bool
6534 CodeGenerator::visitOutOfLineUnboxFloatingPoint(OutOfLineUnboxFloatingPoint *ool)
6535 {
6536 LUnboxFloatingPoint *ins = ool->unboxFloatingPoint();
6537 const ValueOperand value = ToValue(ins, LUnboxFloatingPoint::Input);
6539 if (ins->mir()->fallible()) {
6540 Label bail;
6541 masm.branchTestInt32(Assembler::NotEqual, value, &bail);
6542 if (!bailoutFrom(&bail, ins->snapshot()))
6543 return false;
6544 }
6545 masm.int32ValueToFloatingPoint(value, ToFloatRegister(ins->output()), ins->type());
6546 masm.jump(ool->rejoin());
6547 return true;
6548 }
6550 typedef bool (*GetPropertyFn)(JSContext *, HandleValue, HandlePropertyName, MutableHandleValue);
6551 static const VMFunction GetPropertyInfo = FunctionInfo<GetPropertyFn>(GetProperty);
6552 static const VMFunction CallPropertyInfo = FunctionInfo<GetPropertyFn>(CallProperty);
6554 bool
6555 CodeGenerator::visitCallGetProperty(LCallGetProperty *lir)
6556 {
6557 pushArg(ImmGCPtr(lir->mir()->name()));
6558 pushArg(ToValue(lir, LCallGetProperty::Value));
6560 if (lir->mir()->callprop())
6561 return callVM(CallPropertyInfo, lir);
6562 return callVM(GetPropertyInfo, lir);
6563 }
6565 typedef bool (*GetOrCallElementFn)(JSContext *, MutableHandleValue, HandleValue, MutableHandleValue);
6566 static const VMFunction GetElementInfo = FunctionInfo<GetOrCallElementFn>(js::GetElement);
6567 static const VMFunction CallElementInfo = FunctionInfo<GetOrCallElementFn>(js::CallElement);
6569 bool
6570 CodeGenerator::visitCallGetElement(LCallGetElement *lir)
6571 {
6572 pushArg(ToValue(lir, LCallGetElement::RhsInput));
6573 pushArg(ToValue(lir, LCallGetElement::LhsInput));
6575 JSOp op = JSOp(*lir->mir()->resumePoint()->pc());
6577 if (op == JSOP_GETELEM) {
6578 return callVM(GetElementInfo, lir);
6579 } else {
6580 JS_ASSERT(op == JSOP_CALLELEM);
6581 return callVM(CallElementInfo, lir);
6582 }
6583 }
6585 typedef bool (*SetObjectElementFn)(JSContext *, HandleObject, HandleValue, HandleValue,
6586 bool strict);
6587 typedef bool (*SetElementParFn)(ForkJoinContext *, HandleObject, HandleValue, HandleValue, bool);
6588 static const VMFunctionsModal SetObjectElementInfo = VMFunctionsModal(
6589 FunctionInfo<SetObjectElementFn>(SetObjectElement),
6590 FunctionInfo<SetElementParFn>(SetElementPar));
6592 bool
6593 CodeGenerator::visitCallSetElement(LCallSetElement *lir)
6594 {
6595 pushArg(Imm32(current->mir()->strict()));
6596 pushArg(ToValue(lir, LCallSetElement::Value));
6597 pushArg(ToValue(lir, LCallSetElement::Index));
6598 pushArg(ToRegister(lir->getOperand(0)));
6599 return callVM(SetObjectElementInfo, lir);
6600 }
6602 typedef bool (*InitElementArrayFn)(JSContext *, jsbytecode *, HandleObject, uint32_t, HandleValue);
6603 static const VMFunction InitElementArrayInfo = FunctionInfo<InitElementArrayFn>(js::InitElementArray);
6605 bool
6606 CodeGenerator::visitCallInitElementArray(LCallInitElementArray *lir)
6607 {
6608 pushArg(ToValue(lir, LCallInitElementArray::Value));
6609 pushArg(Imm32(lir->mir()->index()));
6610 pushArg(ToRegister(lir->getOperand(0)));
6611 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
6612 return callVM(InitElementArrayInfo, lir);
6613 }
6615 bool
6616 CodeGenerator::visitLoadFixedSlotV(LLoadFixedSlotV *ins)
6617 {
6618 const Register obj = ToRegister(ins->getOperand(0));
6619 size_t slot = ins->mir()->slot();
6620 ValueOperand result = GetValueOutput(ins);
6622 masm.loadValue(Address(obj, JSObject::getFixedSlotOffset(slot)), result);
6623 return true;
6624 }
6626 bool
6627 CodeGenerator::visitLoadFixedSlotT(LLoadFixedSlotT *ins)
6628 {
6629 const Register obj = ToRegister(ins->getOperand(0));
6630 size_t slot = ins->mir()->slot();
6631 AnyRegister result = ToAnyRegister(ins->getDef(0));
6632 MIRType type = ins->mir()->type();
6634 masm.loadUnboxedValue(Address(obj, JSObject::getFixedSlotOffset(slot)), type, result);
6636 return true;
6637 }
6639 bool
6640 CodeGenerator::visitStoreFixedSlotV(LStoreFixedSlotV *ins)
6641 {
6642 const Register obj = ToRegister(ins->getOperand(0));
6643 size_t slot = ins->mir()->slot();
6645 const ValueOperand value = ToValue(ins, LStoreFixedSlotV::Value);
6647 Address address(obj, JSObject::getFixedSlotOffset(slot));
6648 if (ins->mir()->needsBarrier())
6649 emitPreBarrier(address, MIRType_Value);
6651 masm.storeValue(value, address);
6653 return true;
6654 }
6656 bool
6657 CodeGenerator::visitStoreFixedSlotT(LStoreFixedSlotT *ins)
6658 {
6659 const Register obj = ToRegister(ins->getOperand(0));
6660 size_t slot = ins->mir()->slot();
6662 const LAllocation *value = ins->value();
6663 MIRType valueType = ins->mir()->value()->type();
6665 ConstantOrRegister nvalue = value->isConstant()
6666 ? ConstantOrRegister(*value->toConstant())
6667 : TypedOrValueRegister(valueType, ToAnyRegister(value));
6669 Address address(obj, JSObject::getFixedSlotOffset(slot));
6670 if (ins->mir()->needsBarrier())
6671 emitPreBarrier(address, MIRType_Value);
6673 masm.storeConstantOrRegister(nvalue, address);
6675 return true;
6676 }
6678 bool
6679 CodeGenerator::visitCallsiteCloneCache(LCallsiteCloneCache *ins)
6680 {
6681 const MCallsiteCloneCache *mir = ins->mir();
6682 Register callee = ToRegister(ins->callee());
6683 Register output = ToRegister(ins->output());
6685 CallsiteCloneIC cache(callee, mir->block()->info().script(), mir->callPc(), output);
6686 return addCache(ins, allocateCache(cache));
6687 }
6689 typedef JSObject *(*CallsiteCloneICFn)(JSContext *, size_t, HandleObject);
6690 const VMFunction CallsiteCloneIC::UpdateInfo =
6691 FunctionInfo<CallsiteCloneICFn>(CallsiteCloneIC::update);
6693 bool
6694 CodeGenerator::visitCallsiteCloneIC(OutOfLineUpdateCache *ool, DataPtr<CallsiteCloneIC> &ic)
6695 {
6696 LInstruction *lir = ool->lir();
6697 saveLive(lir);
6699 pushArg(ic->calleeReg());
6700 pushArg(Imm32(ool->getCacheIndex()));
6701 if (!callVM(CallsiteCloneIC::UpdateInfo, lir))
6702 return false;
6703 StoreRegisterTo(ic->outputReg()).generate(this);
6704 restoreLiveIgnore(lir, StoreRegisterTo(ic->outputReg()).clobbered());
6706 masm.jump(ool->rejoin());
6707 return true;
6708 }
6710 bool
6711 CodeGenerator::visitGetNameCache(LGetNameCache *ins)
6712 {
6713 RegisterSet liveRegs = ins->safepoint()->liveRegs();
6714 Register scopeChain = ToRegister(ins->scopeObj());
6715 TypedOrValueRegister output(GetValueOutput(ins));
6716 bool isTypeOf = ins->mir()->accessKind() != MGetNameCache::NAME;
6718 NameIC cache(liveRegs, isTypeOf, scopeChain, ins->mir()->name(), output);
6719 return addCache(ins, allocateCache(cache));
6720 }
6722 typedef bool (*NameICFn)(JSContext *, size_t, HandleObject, MutableHandleValue);
6723 const VMFunction NameIC::UpdateInfo = FunctionInfo<NameICFn>(NameIC::update);
6725 bool
6726 CodeGenerator::visitNameIC(OutOfLineUpdateCache *ool, DataPtr<NameIC> &ic)
6727 {
6728 LInstruction *lir = ool->lir();
6729 saveLive(lir);
6731 pushArg(ic->scopeChainReg());
6732 pushArg(Imm32(ool->getCacheIndex()));
6733 if (!callVM(NameIC::UpdateInfo, lir))
6734 return false;
6735 StoreValueTo(ic->outputReg()).generate(this);
6736 restoreLiveIgnore(lir, StoreValueTo(ic->outputReg()).clobbered());
6738 masm.jump(ool->rejoin());
6739 return true;
6740 }
6742 bool
6743 CodeGenerator::addGetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
6744 PropertyName *name, TypedOrValueRegister output,
6745 bool monitoredResult)
6746 {
6747 switch (gen->info().executionMode()) {
6748 case SequentialExecution: {
6749 GetPropertyIC cache(liveRegs, objReg, name, output, monitoredResult);
6750 return addCache(ins, allocateCache(cache));
6751 }
6752 case ParallelExecution: {
6753 GetPropertyParIC cache(objReg, name, output);
6754 return addCache(ins, allocateCache(cache));
6755 }
6756 default:
6757 MOZ_ASSUME_UNREACHABLE("Bad execution mode");
6758 }
6759 }
6761 bool
6762 CodeGenerator::addSetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
6763 PropertyName *name, ConstantOrRegister value, bool strict,
6764 bool needsTypeBarrier)
6765 {
6766 switch (gen->info().executionMode()) {
6767 case SequentialExecution: {
6768 SetPropertyIC cache(liveRegs, objReg, name, value, strict, needsTypeBarrier);
6769 return addCache(ins, allocateCache(cache));
6770 }
6771 case ParallelExecution: {
6772 SetPropertyParIC cache(objReg, name, value, strict, needsTypeBarrier);
6773 return addCache(ins, allocateCache(cache));
6774 }
6775 default:
6776 MOZ_ASSUME_UNREACHABLE("Bad execution mode");
6777 }
6778 }
6780 bool
6781 CodeGenerator::addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex,
6782 Register temp, FloatRegister tempFloat, ValueOperand index,
6783 ConstantOrRegister value, bool strict, bool guardHoles)
6784 {
6785 switch (gen->info().executionMode()) {
6786 case SequentialExecution: {
6787 SetElementIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict,
6788 guardHoles);
6789 return addCache(ins, allocateCache(cache));
6790 }
6791 case ParallelExecution: {
6792 SetElementParIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict,
6793 guardHoles);
6794 return addCache(ins, allocateCache(cache));
6795 }
6796 default:
6797 MOZ_ASSUME_UNREACHABLE("Bad execution mode");
6798 }
6799 }
6801 bool
6802 CodeGenerator::visitGetPropertyCacheV(LGetPropertyCacheV *ins)
6803 {
6804 RegisterSet liveRegs = ins->safepoint()->liveRegs();
6805 Register objReg = ToRegister(ins->getOperand(0));
6806 PropertyName *name = ins->mir()->name();
6807 bool monitoredResult = ins->mir()->monitoredResult();
6808 TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins));
6810 return addGetPropertyCache(ins, liveRegs, objReg, name, output, monitoredResult);
6811 }
6813 bool
6814 CodeGenerator::visitGetPropertyCacheT(LGetPropertyCacheT *ins)
6815 {
6816 RegisterSet liveRegs = ins->safepoint()->liveRegs();
6817 Register objReg = ToRegister(ins->getOperand(0));
6818 PropertyName *name = ins->mir()->name();
6819 bool monitoredResult = ins->mir()->monitoredResult();
6820 TypedOrValueRegister output(ins->mir()->type(), ToAnyRegister(ins->getDef(0)));
6822 return addGetPropertyCache(ins, liveRegs, objReg, name, output, monitoredResult);
6823 }
6825 typedef bool (*GetPropertyICFn)(JSContext *, size_t, HandleObject, MutableHandleValue);
6826 const VMFunction GetPropertyIC::UpdateInfo =
6827 FunctionInfo<GetPropertyICFn>(GetPropertyIC::update);
6829 bool
6830 CodeGenerator::visitGetPropertyIC(OutOfLineUpdateCache *ool, DataPtr<GetPropertyIC> &ic)
6831 {
6832 LInstruction *lir = ool->lir();
6834 if (ic->idempotent()) {
6835 size_t numLocs;
6836 CacheLocationList &cacheLocs = lir->mirRaw()->toGetPropertyCache()->location();
6837 size_t locationBase = addCacheLocations(cacheLocs, &numLocs);
6838 ic->setLocationInfo(locationBase, numLocs);
6839 }
6841 saveLive(lir);
6843 pushArg(ic->object());
6844 pushArg(Imm32(ool->getCacheIndex()));
6845 if (!callVM(GetPropertyIC::UpdateInfo, lir))
6846 return false;
6847 StoreValueTo(ic->output()).generate(this);
6848 restoreLiveIgnore(lir, StoreValueTo(ic->output()).clobbered());
6850 masm.jump(ool->rejoin());
6851 return true;
6852 }
6854 typedef bool (*GetPropertyParICFn)(ForkJoinContext *, size_t, HandleObject, MutableHandleValue);
6855 const VMFunction GetPropertyParIC::UpdateInfo =
6856 FunctionInfo<GetPropertyParICFn>(GetPropertyParIC::update);
6858 bool
6859 CodeGenerator::visitGetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<GetPropertyParIC> &ic)
6860 {
6861 LInstruction *lir = ool->lir();
6862 saveLive(lir);
6864 pushArg(ic->object());
6865 pushArg(Imm32(ool->getCacheIndex()));
6866 if (!callVM(GetPropertyParIC::UpdateInfo, lir))
6867 return false;
6868 StoreValueTo(ic->output()).generate(this);
6869 restoreLiveIgnore(lir, StoreValueTo(ic->output()).clobbered());
6871 masm.jump(ool->rejoin());
6872 return true;
6873 }
6875 bool
6876 CodeGenerator::addGetElementCache(LInstruction *ins, Register obj, ConstantOrRegister index,
6877 TypedOrValueRegister output, bool monitoredResult,
6878 bool allowDoubleResult)
6879 {
6880 switch (gen->info().executionMode()) {
6881 case SequentialExecution: {
6882 RegisterSet liveRegs = ins->safepoint()->liveRegs();
6883 GetElementIC cache(liveRegs, obj, index, output, monitoredResult, allowDoubleResult);
6884 return addCache(ins, allocateCache(cache));
6885 }
6886 case ParallelExecution: {
6887 GetElementParIC cache(obj, index, output, monitoredResult, allowDoubleResult);
6888 return addCache(ins, allocateCache(cache));
6889 }
6890 default:
6891 MOZ_ASSUME_UNREACHABLE("No such execution mode");
6892 }
6893 }
6895 bool
6896 CodeGenerator::visitGetElementCacheV(LGetElementCacheV *ins)
6897 {
6898 Register obj = ToRegister(ins->object());
6899 ConstantOrRegister index = TypedOrValueRegister(ToValue(ins, LGetElementCacheV::Index));
6900 TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins));
6901 const MGetElementCache *mir = ins->mir();
6903 return addGetElementCache(ins, obj, index, output, mir->monitoredResult(), mir->allowDoubleResult());
6904 }
6906 bool
6907 CodeGenerator::visitGetElementCacheT(LGetElementCacheT *ins)
6908 {
6909 Register obj = ToRegister(ins->object());
6910 ConstantOrRegister index = TypedOrValueRegister(MIRType_Int32, ToAnyRegister(ins->index()));
6911 TypedOrValueRegister output(ins->mir()->type(), ToAnyRegister(ins->output()));
6912 const MGetElementCache *mir = ins->mir();
6914 return addGetElementCache(ins, obj, index, output, mir->monitoredResult(), mir->allowDoubleResult());
6915 }
6917 typedef bool (*GetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, MutableHandleValue);
6918 const VMFunction GetElementIC::UpdateInfo =
6919 FunctionInfo<GetElementICFn>(GetElementIC::update);
6921 bool
6922 CodeGenerator::visitGetElementIC(OutOfLineUpdateCache *ool, DataPtr<GetElementIC> &ic)
6923 {
6924 LInstruction *lir = ool->lir();
6925 saveLive(lir);
6927 pushArg(ic->index());
6928 pushArg(ic->object());
6929 pushArg(Imm32(ool->getCacheIndex()));
6930 if (!callVM(GetElementIC::UpdateInfo, lir))
6931 return false;
6932 StoreValueTo(ic->output()).generate(this);
6933 restoreLiveIgnore(lir, StoreValueTo(ic->output()).clobbered());
6935 masm.jump(ool->rejoin());
6936 return true;
6937 }
6939 bool
6940 CodeGenerator::visitSetElementCacheV(LSetElementCacheV *ins)
6941 {
6942 Register obj = ToRegister(ins->object());
6943 Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex());
6944 Register temp = ToRegister(ins->temp());
6945 FloatRegister tempFloat = ToFloatRegister(ins->tempFloat());
6946 ValueOperand index = ToValue(ins, LSetElementCacheV::Index);
6947 ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetElementCacheV::Value));
6949 return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value,
6950 ins->mir()->strict(), ins->mir()->guardHoles());
6951 }
6953 bool
6954 CodeGenerator::visitSetElementCacheT(LSetElementCacheT *ins)
6955 {
6956 Register obj = ToRegister(ins->object());
6957 Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex());
6958 Register temp = ToRegister(ins->temp());
6959 FloatRegister tempFloat = ToFloatRegister(ins->tempFloat());
6960 ValueOperand index = ToValue(ins, LSetElementCacheT::Index);
6961 ConstantOrRegister value;
6962 const LAllocation *tmp = ins->value();
6963 if (tmp->isConstant())
6964 value = *tmp->toConstant();
6965 else
6966 value = TypedOrValueRegister(ins->mir()->value()->type(), ToAnyRegister(tmp));
6968 return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value,
6969 ins->mir()->strict(), ins->mir()->guardHoles());
6970 }
6972 typedef bool (*SetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, HandleValue);
6973 const VMFunction SetElementIC::UpdateInfo =
6974 FunctionInfo<SetElementICFn>(SetElementIC::update);
6976 bool
6977 CodeGenerator::visitSetElementIC(OutOfLineUpdateCache *ool, DataPtr<SetElementIC> &ic)
6978 {
6979 LInstruction *lir = ool->lir();
6980 saveLive(lir);
6982 pushArg(ic->value());
6983 pushArg(ic->index());
6984 pushArg(ic->object());
6985 pushArg(Imm32(ool->getCacheIndex()));
6986 if (!callVM(SetElementIC::UpdateInfo, lir))
6987 return false;
6988 restoreLive(lir);
6990 masm.jump(ool->rejoin());
6991 return true;
6992 }
6994 typedef bool (*SetElementParICFn)(ForkJoinContext *, size_t, HandleObject, HandleValue, HandleValue);
6995 const VMFunction SetElementParIC::UpdateInfo =
6996 FunctionInfo<SetElementParICFn>(SetElementParIC::update);
6998 bool
6999 CodeGenerator::visitSetElementParIC(OutOfLineUpdateCache *ool, DataPtr<SetElementParIC> &ic)
7000 {
7001 LInstruction *lir = ool->lir();
7002 saveLive(lir);
7004 pushArg(ic->value());
7005 pushArg(ic->index());
7006 pushArg(ic->object());
7007 pushArg(Imm32(ool->getCacheIndex()));
7008 if (!callVM(SetElementParIC::UpdateInfo, lir))
7009 return false;
7010 restoreLive(lir);
7012 masm.jump(ool->rejoin());
7013 return true;
7014 }
7016 typedef bool (*GetElementParICFn)(ForkJoinContext *, size_t, HandleObject, HandleValue,
7017 MutableHandleValue);
7018 const VMFunction GetElementParIC::UpdateInfo =
7019 FunctionInfo<GetElementParICFn>(GetElementParIC::update);
7021 bool
7022 CodeGenerator::visitGetElementParIC(OutOfLineUpdateCache *ool, DataPtr<GetElementParIC> &ic)
7023 {
7024 LInstruction *lir = ool->lir();
7025 saveLive(lir);
7027 pushArg(ic->index());
7028 pushArg(ic->object());
7029 pushArg(Imm32(ool->getCacheIndex()));
7030 if (!callVM(GetElementParIC::UpdateInfo, lir))
7031 return false;
7032 StoreValueTo(ic->output()).generate(this);
7033 restoreLiveIgnore(lir, StoreValueTo(ic->output()).clobbered());
7035 masm.jump(ool->rejoin());
7036 return true;
7037 }
7039 bool
7040 CodeGenerator::visitBindNameCache(LBindNameCache *ins)
7041 {
7042 Register scopeChain = ToRegister(ins->scopeChain());
7043 Register output = ToRegister(ins->output());
7044 BindNameIC cache(scopeChain, ins->mir()->name(), output);
7046 return addCache(ins, allocateCache(cache));
7047 }
7049 typedef JSObject *(*BindNameICFn)(JSContext *, size_t, HandleObject);
7050 const VMFunction BindNameIC::UpdateInfo =
7051 FunctionInfo<BindNameICFn>(BindNameIC::update);
7053 bool
7054 CodeGenerator::visitBindNameIC(OutOfLineUpdateCache *ool, DataPtr<BindNameIC> &ic)
7055 {
7056 LInstruction *lir = ool->lir();
7057 saveLive(lir);
7059 pushArg(ic->scopeChainReg());
7060 pushArg(Imm32(ool->getCacheIndex()));
7061 if (!callVM(BindNameIC::UpdateInfo, lir))
7062 return false;
7063 StoreRegisterTo(ic->outputReg()).generate(this);
7064 restoreLiveIgnore(lir, StoreRegisterTo(ic->outputReg()).clobbered());
7066 masm.jump(ool->rejoin());
7067 return true;
7068 }
7070 typedef bool (*SetPropertyFn)(JSContext *, HandleObject,
7071 HandlePropertyName, const HandleValue, bool, jsbytecode *);
7072 typedef bool (*SetPropertyParFn)(ForkJoinContext *, HandleObject,
7073 HandlePropertyName, const HandleValue, bool, jsbytecode *);
7074 static const VMFunctionsModal SetPropertyInfo = VMFunctionsModal(
7075 FunctionInfo<SetPropertyFn>(SetProperty),
7076 FunctionInfo<SetPropertyParFn>(SetPropertyPar));
7078 bool
7079 CodeGenerator::visitCallSetProperty(LCallSetProperty *ins)
7080 {
7081 ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LCallSetProperty::Value));
7083 const Register objReg = ToRegister(ins->getOperand(0));
7085 pushArg(ImmPtr(ins->mir()->resumePoint()->pc()));
7086 pushArg(Imm32(ins->mir()->strict()));
7088 pushArg(value);
7089 pushArg(ImmGCPtr(ins->mir()->name()));
7090 pushArg(objReg);
7092 return callVM(SetPropertyInfo, ins);
7093 }
7095 typedef bool (*DeletePropertyFn)(JSContext *, HandleValue, HandlePropertyName, bool *);
7096 static const VMFunction DeletePropertyStrictInfo =
7097 FunctionInfo<DeletePropertyFn>(DeleteProperty<true>);
7098 static const VMFunction DeletePropertyNonStrictInfo =
7099 FunctionInfo<DeletePropertyFn>(DeleteProperty<false>);
7101 bool
7102 CodeGenerator::visitCallDeleteProperty(LCallDeleteProperty *lir)
7103 {
7104 pushArg(ImmGCPtr(lir->mir()->name()));
7105 pushArg(ToValue(lir, LCallDeleteProperty::Value));
7107 if (lir->mir()->block()->info().script()->strict())
7108 return callVM(DeletePropertyStrictInfo, lir);
7110 return callVM(DeletePropertyNonStrictInfo, lir);
7111 }
7113 typedef bool (*DeleteElementFn)(JSContext *, HandleValue, HandleValue, bool *);
7114 static const VMFunction DeleteElementStrictInfo =
7115 FunctionInfo<DeleteElementFn>(DeleteElement<true>);
7116 static const VMFunction DeleteElementNonStrictInfo =
7117 FunctionInfo<DeleteElementFn>(DeleteElement<false>);
7119 bool
7120 CodeGenerator::visitCallDeleteElement(LCallDeleteElement *lir)
7121 {
7122 pushArg(ToValue(lir, LCallDeleteElement::Index));
7123 pushArg(ToValue(lir, LCallDeleteElement::Value));
7125 if (lir->mir()->block()->info().script()->strict())
7126 return callVM(DeleteElementStrictInfo, lir);
7128 return callVM(DeleteElementNonStrictInfo, lir);
7129 }
7131 bool
7132 CodeGenerator::visitSetPropertyCacheV(LSetPropertyCacheV *ins)
7133 {
7134 RegisterSet liveRegs = ins->safepoint()->liveRegs();
7135 Register objReg = ToRegister(ins->getOperand(0));
7136 ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetPropertyCacheV::Value));
7138 return addSetPropertyCache(ins, liveRegs, objReg, ins->mir()->name(), value,
7139 ins->mir()->strict(), ins->mir()->needsTypeBarrier());
7140 }
7142 bool
7143 CodeGenerator::visitSetPropertyCacheT(LSetPropertyCacheT *ins)
7144 {
7145 RegisterSet liveRegs = ins->safepoint()->liveRegs();
7146 Register objReg = ToRegister(ins->getOperand(0));
7147 ConstantOrRegister value;
7149 if (ins->getOperand(1)->isConstant())
7150 value = ConstantOrRegister(*ins->getOperand(1)->toConstant());
7151 else
7152 value = TypedOrValueRegister(ins->valueType(), ToAnyRegister(ins->getOperand(1)));
7154 return addSetPropertyCache(ins, liveRegs, objReg, ins->mir()->name(), value,
7155 ins->mir()->strict(), ins->mir()->needsTypeBarrier());
7156 }
7158 typedef bool (*SetPropertyICFn)(JSContext *, size_t, HandleObject, HandleValue);
7159 const VMFunction SetPropertyIC::UpdateInfo =
7160 FunctionInfo<SetPropertyICFn>(SetPropertyIC::update);
7162 bool
7163 CodeGenerator::visitSetPropertyIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyIC> &ic)
7164 {
7165 LInstruction *lir = ool->lir();
7166 saveLive(lir);
7168 pushArg(ic->value());
7169 pushArg(ic->object());
7170 pushArg(Imm32(ool->getCacheIndex()));
7171 if (!callVM(SetPropertyIC::UpdateInfo, lir))
7172 return false;
7173 restoreLive(lir);
7175 masm.jump(ool->rejoin());
7176 return true;
7177 }
7179 typedef bool (*SetPropertyParICFn)(ForkJoinContext *, size_t, HandleObject, HandleValue);
7180 const VMFunction SetPropertyParIC::UpdateInfo =
7181 FunctionInfo<SetPropertyParICFn>(SetPropertyParIC::update);
7183 bool
7184 CodeGenerator::visitSetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyParIC> &ic)
7185 {
7186 LInstruction *lir = ool->lir();
7187 saveLive(lir);
7189 pushArg(ic->value());
7190 pushArg(ic->object());
7191 pushArg(Imm32(ool->getCacheIndex()));
7192 if (!callVM(SetPropertyParIC::UpdateInfo, lir))
7193 return false;
7194 restoreLive(lir);
7196 masm.jump(ool->rejoin());
7197 return true;
7198 }
7200 typedef bool (*ThrowFn)(JSContext *, HandleValue);
7201 static const VMFunction ThrowInfoCodeGen = FunctionInfo<ThrowFn>(js::Throw);
7203 bool
7204 CodeGenerator::visitThrow(LThrow *lir)
7205 {
7206 pushArg(ToValue(lir, LThrow::Value));
7207 return callVM(ThrowInfoCodeGen, lir);
7208 }
7210 typedef bool (*BitNotFn)(JSContext *, HandleValue, int *p);
7211 typedef bool (*BitNotParFn)(ForkJoinContext *, HandleValue, int32_t *);
7212 static const VMFunctionsModal BitNotInfo = VMFunctionsModal(
7213 FunctionInfo<BitNotFn>(BitNot),
7214 FunctionInfo<BitNotParFn>(BitNotPar));
7216 bool
7217 CodeGenerator::visitBitNotV(LBitNotV *lir)
7218 {
7219 pushArg(ToValue(lir, LBitNotV::Input));
7220 return callVM(BitNotInfo, lir);
7221 }
7223 typedef bool (*BitopFn)(JSContext *, HandleValue, HandleValue, int *p);
7224 typedef bool (*BitopParFn)(ForkJoinContext *, HandleValue, HandleValue, int32_t *);
7225 static const VMFunctionsModal BitAndInfo = VMFunctionsModal(
7226 FunctionInfo<BitopFn>(BitAnd),
7227 FunctionInfo<BitopParFn>(BitAndPar));
7228 static const VMFunctionsModal BitOrInfo = VMFunctionsModal(
7229 FunctionInfo<BitopFn>(BitOr),
7230 FunctionInfo<BitopParFn>(BitOrPar));
7231 static const VMFunctionsModal BitXorInfo = VMFunctionsModal(
7232 FunctionInfo<BitopFn>(BitXor),
7233 FunctionInfo<BitopParFn>(BitXorPar));
7234 static const VMFunctionsModal BitLhsInfo = VMFunctionsModal(
7235 FunctionInfo<BitopFn>(BitLsh),
7236 FunctionInfo<BitopParFn>(BitLshPar));
7237 static const VMFunctionsModal BitRhsInfo = VMFunctionsModal(
7238 FunctionInfo<BitopFn>(BitRsh),
7239 FunctionInfo<BitopParFn>(BitRshPar));
7241 bool
7242 CodeGenerator::visitBitOpV(LBitOpV *lir)
7243 {
7244 pushArg(ToValue(lir, LBitOpV::RhsInput));
7245 pushArg(ToValue(lir, LBitOpV::LhsInput));
7247 switch (lir->jsop()) {
7248 case JSOP_BITAND:
7249 return callVM(BitAndInfo, lir);
7250 case JSOP_BITOR:
7251 return callVM(BitOrInfo, lir);
7252 case JSOP_BITXOR:
7253 return callVM(BitXorInfo, lir);
7254 case JSOP_LSH:
7255 return callVM(BitLhsInfo, lir);
7256 case JSOP_RSH:
7257 return callVM(BitRhsInfo, lir);
7258 default:
7259 break;
7260 }
7261 MOZ_ASSUME_UNREACHABLE("unexpected bitop");
7262 }
7264 class OutOfLineTypeOfV : public OutOfLineCodeBase<CodeGenerator>
7265 {
7266 LTypeOfV *ins_;
7268 public:
7269 OutOfLineTypeOfV(LTypeOfV *ins)
7270 : ins_(ins)
7271 { }
7273 bool accept(CodeGenerator *codegen) {
7274 return codegen->visitOutOfLineTypeOfV(this);
7275 }
7276 LTypeOfV *ins() const {
7277 return ins_;
7278 }
7279 };
7281 bool
7282 CodeGenerator::visitTypeOfV(LTypeOfV *lir)
7283 {
7284 const ValueOperand value = ToValue(lir, LTypeOfV::Input);
7285 Register output = ToRegister(lir->output());
7286 Register tag = masm.splitTagForTest(value);
7288 const JSAtomState &names = GetIonContext()->runtime->names();
7289 Label done;
7291 OutOfLineTypeOfV *ool = nullptr;
7292 if (lir->mir()->inputMaybeCallableOrEmulatesUndefined()) {
7293 // The input may be a callable object (result is "function") or may
7294 // emulate undefined (result is "undefined"). Use an OOL path.
7295 ool = new(alloc()) OutOfLineTypeOfV(lir);
7296 if (!addOutOfLineCode(ool))
7297 return false;
7299 masm.branchTestObject(Assembler::Equal, tag, ool->entry());
7300 } else {
7301 // Input is not callable and does not emulate undefined, so if
7302 // it's an object the result is always "object".
7303 Label notObject;
7304 masm.branchTestObject(Assembler::NotEqual, tag, ¬Object);
7305 masm.movePtr(ImmGCPtr(names.object), output);
7306 masm.jump(&done);
7307 masm.bind(¬Object);
7308 }
7310 Label notNumber;
7311 masm.branchTestNumber(Assembler::NotEqual, tag, ¬Number);
7312 masm.movePtr(ImmGCPtr(names.number), output);
7313 masm.jump(&done);
7314 masm.bind(¬Number);
7316 Label notUndefined;
7317 masm.branchTestUndefined(Assembler::NotEqual, tag, ¬Undefined);
7318 masm.movePtr(ImmGCPtr(names.undefined), output);
7319 masm.jump(&done);
7320 masm.bind(¬Undefined);
7322 Label notNull;
7323 masm.branchTestNull(Assembler::NotEqual, tag, ¬Null);
7324 masm.movePtr(ImmGCPtr(names.object), output);
7325 masm.jump(&done);
7326 masm.bind(¬Null);
7328 Label notBoolean;
7329 masm.branchTestBoolean(Assembler::NotEqual, tag, ¬Boolean);
7330 masm.movePtr(ImmGCPtr(names.boolean), output);
7331 masm.jump(&done);
7332 masm.bind(¬Boolean);
7334 masm.movePtr(ImmGCPtr(names.string), output);
7336 masm.bind(&done);
7337 if (ool)
7338 masm.bind(ool->rejoin());
7339 return true;
7340 }
7342 bool
7343 CodeGenerator::visitOutOfLineTypeOfV(OutOfLineTypeOfV *ool)
7344 {
7345 LTypeOfV *ins = ool->ins();
7347 ValueOperand input = ToValue(ins, LTypeOfV::Input);
7348 Register temp = ToTempUnboxRegister(ins->tempToUnbox());
7349 Register output = ToRegister(ins->output());
7351 Register obj = masm.extractObject(input, temp);
7353 saveVolatile(output);
7354 masm.setupUnalignedABICall(2, output);
7355 masm.passABIArg(obj);
7356 masm.movePtr(ImmPtr(GetIonContext()->runtime), output);
7357 masm.passABIArg(output);
7358 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::TypeOfObjectOperation));
7359 masm.storeCallResult(output);
7360 restoreVolatile(output);
7362 masm.jump(ool->rejoin());
7363 return true;
7364 }
7366 typedef bool (*ToIdFn)(JSContext *, HandleScript, jsbytecode *, HandleValue, HandleValue,
7367 MutableHandleValue);
7368 static const VMFunction ToIdInfo = FunctionInfo<ToIdFn>(ToIdOperation);
7370 bool
7371 CodeGenerator::visitToIdV(LToIdV *lir)
7372 {
7373 Label notInt32;
7374 FloatRegister temp = ToFloatRegister(lir->tempFloat());
7375 const ValueOperand out = ToOutValue(lir);
7376 ValueOperand index = ToValue(lir, LToIdV::Index);
7378 OutOfLineCode *ool = oolCallVM(ToIdInfo, lir,
7379 (ArgList(),
7380 ImmGCPtr(current->mir()->info().script()),
7381 ImmPtr(lir->mir()->resumePoint()->pc()),
7382 ToValue(lir, LToIdV::Object),
7383 ToValue(lir, LToIdV::Index)),
7384 StoreValueTo(out));
7386 Register tag = masm.splitTagForTest(index);
7388 masm.branchTestInt32(Assembler::NotEqual, tag, ¬Int32);
7389 masm.moveValue(index, out);
7390 masm.jump(ool->rejoin());
7392 masm.bind(¬Int32);
7393 masm.branchTestDouble(Assembler::NotEqual, tag, ool->entry());
7394 masm.unboxDouble(index, temp);
7395 masm.convertDoubleToInt32(temp, out.scratchReg(), ool->entry(), true);
7396 masm.tagValue(JSVAL_TYPE_INT32, out.scratchReg(), out);
7398 masm.bind(ool->rejoin());
7399 return true;
7400 }
7402 bool
7403 CodeGenerator::visitLoadElementV(LLoadElementV *load)
7404 {
7405 Register elements = ToRegister(load->elements());
7406 const ValueOperand out = ToOutValue(load);
7408 if (load->index()->isConstant())
7409 masm.loadValue(Address(elements, ToInt32(load->index()) * sizeof(Value)), out);
7410 else
7411 masm.loadValue(BaseIndex(elements, ToRegister(load->index()), TimesEight), out);
7413 if (load->mir()->needsHoleCheck()) {
7414 Label testMagic;
7415 masm.branchTestMagic(Assembler::Equal, out, &testMagic);
7416 if (!bailoutFrom(&testMagic, load->snapshot()))
7417 return false;
7418 }
7420 return true;
7421 }
7423 bool
7424 CodeGenerator::visitLoadElementHole(LLoadElementHole *lir)
7425 {
7426 Register elements = ToRegister(lir->elements());
7427 Register initLength = ToRegister(lir->initLength());
7428 const ValueOperand out = ToOutValue(lir);
7430 const MLoadElementHole *mir = lir->mir();
7432 // If the index is out of bounds, load |undefined|. Otherwise, load the
7433 // value.
7434 Label undefined, done;
7435 if (lir->index()->isConstant()) {
7436 masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(ToInt32(lir->index())), &undefined);
7437 masm.loadValue(Address(elements, ToInt32(lir->index()) * sizeof(Value)), out);
7438 } else {
7439 masm.branch32(Assembler::BelowOrEqual, initLength, ToRegister(lir->index()), &undefined);
7440 masm.loadValue(BaseIndex(elements, ToRegister(lir->index()), TimesEight), out);
7441 }
7443 // If a hole check is needed, and the value wasn't a hole, we're done.
7444 // Otherwise, we'll load undefined.
7445 if (lir->mir()->needsHoleCheck())
7446 masm.branchTestMagic(Assembler::NotEqual, out, &done);
7447 else
7448 masm.jump(&done);
7450 masm.bind(&undefined);
7452 if (mir->needsNegativeIntCheck()) {
7453 if (lir->index()->isConstant()) {
7454 if (ToInt32(lir->index()) < 0 && !bailout(lir->snapshot()))
7455 return false;
7456 } else {
7457 Label negative;
7458 masm.branch32(Assembler::LessThan, ToRegister(lir->index()), Imm32(0), &negative);
7459 if (!bailoutFrom(&negative, lir->snapshot()))
7460 return false;
7461 }
7462 }
7464 masm.moveValue(UndefinedValue(), out);
7465 masm.bind(&done);
7466 return true;
7467 }
7469 bool
7470 CodeGenerator::visitLoadTypedArrayElement(LLoadTypedArrayElement *lir)
7471 {
7472 Register elements = ToRegister(lir->elements());
7473 Register temp = lir->temp()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp());
7474 AnyRegister out = ToAnyRegister(lir->output());
7476 int arrayType = lir->mir()->arrayType();
7477 int width = TypedArrayObject::slotWidth(arrayType);
7479 Label fail;
7480 if (lir->index()->isConstant()) {
7481 Address source(elements, ToInt32(lir->index()) * width);
7482 masm.loadFromTypedArray(arrayType, source, out, temp, &fail);
7483 } else {
7484 BaseIndex source(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
7485 masm.loadFromTypedArray(arrayType, source, out, temp, &fail);
7486 }
7488 if (fail.used() && !bailoutFrom(&fail, lir->snapshot()))
7489 return false;
7491 return true;
7492 }
7494 bool
7495 CodeGenerator::visitLoadTypedArrayElementHole(LLoadTypedArrayElementHole *lir)
7496 {
7497 Register object = ToRegister(lir->object());
7498 const ValueOperand out = ToOutValue(lir);
7500 // Load the length.
7501 Register scratch = out.scratchReg();
7502 Int32Key key = ToInt32Key(lir->index());
7503 masm.unboxInt32(Address(object, TypedArrayObject::lengthOffset()), scratch);
7505 // Load undefined unless length > key.
7506 Label inbounds, done;
7507 masm.branchKey(Assembler::Above, scratch, key, &inbounds);
7508 masm.moveValue(UndefinedValue(), out);
7509 masm.jump(&done);
7511 // Load the elements vector.
7512 masm.bind(&inbounds);
7513 masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), scratch);
7515 int arrayType = lir->mir()->arrayType();
7516 int width = TypedArrayObject::slotWidth(arrayType);
7518 Label fail;
7519 if (key.isConstant()) {
7520 Address source(scratch, key.constant() * width);
7521 masm.loadFromTypedArray(arrayType, source, out, lir->mir()->allowDouble(),
7522 out.scratchReg(), &fail);
7523 } else {
7524 BaseIndex source(scratch, key.reg(), ScaleFromElemWidth(width));
7525 masm.loadFromTypedArray(arrayType, source, out, lir->mir()->allowDouble(),
7526 out.scratchReg(), &fail);
7527 }
7529 if (fail.used() && !bailoutFrom(&fail, lir->snapshot()))
7530 return false;
7532 masm.bind(&done);
7533 return true;
7534 }
7536 template <typename T>
7537 static inline void
7538 StoreToTypedArray(MacroAssembler &masm, int arrayType, const LAllocation *value, const T &dest)
7539 {
7540 if (arrayType == ScalarTypeDescr::TYPE_FLOAT32 ||
7541 arrayType == ScalarTypeDescr::TYPE_FLOAT64)
7542 {
7543 masm.storeToTypedFloatArray(arrayType, ToFloatRegister(value), dest);
7544 } else {
7545 if (value->isConstant())
7546 masm.storeToTypedIntArray(arrayType, Imm32(ToInt32(value)), dest);
7547 else
7548 masm.storeToTypedIntArray(arrayType, ToRegister(value), dest);
7549 }
7550 }
7552 bool
7553 CodeGenerator::visitStoreTypedArrayElement(LStoreTypedArrayElement *lir)
7554 {
7555 Register elements = ToRegister(lir->elements());
7556 const LAllocation *value = lir->value();
7558 int arrayType = lir->mir()->arrayType();
7559 int width = TypedArrayObject::slotWidth(arrayType);
7561 if (lir->index()->isConstant()) {
7562 Address dest(elements, ToInt32(lir->index()) * width);
7563 StoreToTypedArray(masm, arrayType, value, dest);
7564 } else {
7565 BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
7566 StoreToTypedArray(masm, arrayType, value, dest);
7567 }
7569 return true;
7570 }
7572 bool
7573 CodeGenerator::visitStoreTypedArrayElementHole(LStoreTypedArrayElementHole *lir)
7574 {
7575 Register elements = ToRegister(lir->elements());
7576 const LAllocation *value = lir->value();
7578 int arrayType = lir->mir()->arrayType();
7579 int width = TypedArrayObject::slotWidth(arrayType);
7581 bool guardLength = true;
7582 if (lir->index()->isConstant() && lir->length()->isConstant()) {
7583 uint32_t idx = ToInt32(lir->index());
7584 uint32_t len = ToInt32(lir->length());
7585 if (idx >= len)
7586 return true;
7587 guardLength = false;
7588 }
7589 Label skip;
7590 if (lir->index()->isConstant()) {
7591 uint32_t idx = ToInt32(lir->index());
7592 if (guardLength)
7593 masm.branch32(Assembler::BelowOrEqual, ToOperand(lir->length()), Imm32(idx), &skip);
7594 Address dest(elements, idx * width);
7595 StoreToTypedArray(masm, arrayType, value, dest);
7596 } else {
7597 Register idxReg = ToRegister(lir->index());
7598 JS_ASSERT(guardLength);
7599 if (lir->length()->isConstant())
7600 masm.branch32(Assembler::AboveOrEqual, idxReg, Imm32(ToInt32(lir->length())), &skip);
7601 else
7602 masm.branch32(Assembler::BelowOrEqual, ToOperand(lir->length()), idxReg, &skip);
7603 BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
7604 StoreToTypedArray(masm, arrayType, value, dest);
7605 }
7606 if (guardLength)
7607 masm.bind(&skip);
7609 return true;
7610 }
7612 bool
7613 CodeGenerator::visitClampIToUint8(LClampIToUint8 *lir)
7614 {
7615 Register output = ToRegister(lir->output());
7616 JS_ASSERT(output == ToRegister(lir->input()));
7617 masm.clampIntToUint8(output);
7618 return true;
7619 }
7621 bool
7622 CodeGenerator::visitClampDToUint8(LClampDToUint8 *lir)
7623 {
7624 FloatRegister input = ToFloatRegister(lir->input());
7625 Register output = ToRegister(lir->output());
7626 masm.clampDoubleToUint8(input, output);
7627 return true;
7628 }
7630 bool
7631 CodeGenerator::visitClampVToUint8(LClampVToUint8 *lir)
7632 {
7633 ValueOperand operand = ToValue(lir, LClampVToUint8::Input);
7634 FloatRegister tempFloat = ToFloatRegister(lir->tempFloat());
7635 Register output = ToRegister(lir->output());
7636 MDefinition *input = lir->mir()->input();
7638 Label *stringEntry, *stringRejoin;
7639 if (input->mightBeType(MIRType_String)) {
7640 OutOfLineCode *oolString = oolCallVM(StringToNumberInfo, lir, (ArgList(), output),
7641 StoreFloatRegisterTo(tempFloat));
7642 if (!oolString)
7643 return false;
7644 stringEntry = oolString->entry();
7645 stringRejoin = oolString->rejoin();
7646 } else {
7647 stringEntry = nullptr;
7648 stringRejoin = nullptr;
7649 }
7651 Label fails;
7652 masm.clampValueToUint8(operand, input,
7653 stringEntry, stringRejoin,
7654 output, tempFloat, output, &fails);
7656 if (!bailoutFrom(&fails, lir->snapshot()))
7657 return false;
7659 return true;
7660 }
7662 typedef bool (*OperatorInFn)(JSContext *, HandleValue, HandleObject, bool *);
7663 static const VMFunction OperatorInInfo = FunctionInfo<OperatorInFn>(OperatorIn);
7665 bool
7666 CodeGenerator::visitIn(LIn *ins)
7667 {
7668 pushArg(ToRegister(ins->rhs()));
7669 pushArg(ToValue(ins, LIn::LHS));
7671 return callVM(OperatorInInfo, ins);
7672 }
7674 typedef bool (*OperatorInIFn)(JSContext *, uint32_t, HandleObject, bool *);
7675 static const VMFunction OperatorInIInfo = FunctionInfo<OperatorInIFn>(OperatorInI);
7677 bool
7678 CodeGenerator::visitInArray(LInArray *lir)
7679 {
7680 const MInArray *mir = lir->mir();
7681 Register elements = ToRegister(lir->elements());
7682 Register initLength = ToRegister(lir->initLength());
7683 Register output = ToRegister(lir->output());
7685 // When the array is not packed we need to do a hole check in addition to the bounds check.
7686 Label falseBranch, done, trueBranch;
7688 OutOfLineCode *ool = nullptr;
7689 Label* failedInitLength = &falseBranch;
7691 if (lir->index()->isConstant()) {
7692 int32_t index = ToInt32(lir->index());
7694 JS_ASSERT_IF(index < 0, mir->needsNegativeIntCheck());
7695 if (mir->needsNegativeIntCheck()) {
7696 ool = oolCallVM(OperatorInIInfo, lir,
7697 (ArgList(), Imm32(index), ToRegister(lir->object())),
7698 StoreRegisterTo(output));
7699 failedInitLength = ool->entry();
7700 }
7702 masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index), failedInitLength);
7703 if (mir->needsHoleCheck()) {
7704 Address address = Address(elements, index * sizeof(Value));
7705 masm.branchTestMagic(Assembler::Equal, address, &falseBranch);
7706 }
7707 } else {
7708 Label negativeIntCheck;
7709 Register index = ToRegister(lir->index());
7711 if (mir->needsNegativeIntCheck())
7712 failedInitLength = &negativeIntCheck;
7714 masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength);
7715 if (mir->needsHoleCheck()) {
7716 BaseIndex address = BaseIndex(elements, ToRegister(lir->index()), TimesEight);
7717 masm.branchTestMagic(Assembler::Equal, address, &falseBranch);
7718 }
7719 masm.jump(&trueBranch);
7721 if (mir->needsNegativeIntCheck()) {
7722 masm.bind(&negativeIntCheck);
7723 ool = oolCallVM(OperatorInIInfo, lir,
7724 (ArgList(), index, ToRegister(lir->object())),
7725 StoreRegisterTo(output));
7727 masm.branch32(Assembler::LessThan, index, Imm32(0), ool->entry());
7728 masm.jump(&falseBranch);
7729 }
7730 }
7732 masm.bind(&trueBranch);
7733 masm.move32(Imm32(1), output);
7734 masm.jump(&done);
7736 masm.bind(&falseBranch);
7737 masm.move32(Imm32(0), output);
7738 masm.bind(&done);
7740 if (ool)
7741 masm.bind(ool->rejoin());
7743 return true;
7744 }
7746 bool
7747 CodeGenerator::visitInstanceOfO(LInstanceOfO *ins)
7748 {
7749 return emitInstanceOf(ins, ins->mir()->prototypeObject());
7750 }
7752 bool
7753 CodeGenerator::visitInstanceOfV(LInstanceOfV *ins)
7754 {
7755 return emitInstanceOf(ins, ins->mir()->prototypeObject());
7756 }
7758 // Wrap IsDelegateOfObject, which takes a JSObject*, not a HandleObject
7759 static bool
7760 IsDelegateObject(JSContext *cx, HandleObject protoObj, HandleObject obj, bool *res)
7761 {
7762 return IsDelegateOfObject(cx, protoObj, obj, res);
7763 }
7765 typedef bool (*IsDelegateObjectFn)(JSContext *, HandleObject, HandleObject, bool *);
7766 static const VMFunction IsDelegateObjectInfo = FunctionInfo<IsDelegateObjectFn>(IsDelegateObject);
7768 bool
7769 CodeGenerator::emitInstanceOf(LInstruction *ins, JSObject *prototypeObject)
7770 {
7771 // This path implements fun_hasInstance when the function's prototype is
7772 // known to be prototypeObject.
7774 Label done;
7775 Register output = ToRegister(ins->getDef(0));
7777 // If the lhs is a primitive, the result is false.
7778 Register objReg;
7779 if (ins->isInstanceOfV()) {
7780 Label isObject;
7781 ValueOperand lhsValue = ToValue(ins, LInstanceOfV::LHS);
7782 masm.branchTestObject(Assembler::Equal, lhsValue, &isObject);
7783 masm.mov(ImmWord(0), output);
7784 masm.jump(&done);
7785 masm.bind(&isObject);
7786 objReg = masm.extractObject(lhsValue, output);
7787 } else {
7788 objReg = ToRegister(ins->toInstanceOfO()->lhs());
7789 }
7791 // Crawl the lhs's prototype chain in a loop to search for prototypeObject.
7792 // This follows the main loop of js::IsDelegate, though additionally breaks
7793 // out of the loop on Proxy::LazyProto.
7795 // Load the lhs's prototype.
7796 masm.loadObjProto(objReg, output);
7798 Label testLazy;
7799 {
7800 Label loopPrototypeChain;
7801 masm.bind(&loopPrototypeChain);
7803 // Test for the target prototype object.
7804 Label notPrototypeObject;
7805 masm.branchPtr(Assembler::NotEqual, output, ImmGCPtr(prototypeObject), ¬PrototypeObject);
7806 masm.mov(ImmWord(1), output);
7807 masm.jump(&done);
7808 masm.bind(¬PrototypeObject);
7810 JS_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1);
7812 // Test for nullptr or Proxy::LazyProto
7813 masm.branchPtr(Assembler::BelowOrEqual, output, ImmWord(1), &testLazy);
7815 // Load the current object's prototype.
7816 masm.loadObjProto(output, output);
7818 masm.jump(&loopPrototypeChain);
7819 }
7821 // Make a VM call if an object with a lazy proto was found on the prototype
7822 // chain. This currently occurs only for cross compartment wrappers, which
7823 // we do not expect to be compared with non-wrapper functions from this
7824 // compartment. Otherwise, we stopped on a nullptr prototype and the output
7825 // register is already correct.
7827 OutOfLineCode *ool = oolCallVM(IsDelegateObjectInfo, ins,
7828 (ArgList(), ImmGCPtr(prototypeObject), objReg),
7829 StoreRegisterTo(output));
7831 // Regenerate the original lhs object for the VM call.
7832 Label regenerate, *lazyEntry;
7833 if (objReg != output) {
7834 lazyEntry = ool->entry();
7835 } else {
7836 masm.bind(®enerate);
7837 lazyEntry = ®enerate;
7838 if (ins->isInstanceOfV()) {
7839 ValueOperand lhsValue = ToValue(ins, LInstanceOfV::LHS);
7840 objReg = masm.extractObject(lhsValue, output);
7841 } else {
7842 objReg = ToRegister(ins->toInstanceOfO()->lhs());
7843 }
7844 JS_ASSERT(objReg == output);
7845 masm.jump(ool->entry());
7846 }
7848 masm.bind(&testLazy);
7849 masm.branchPtr(Assembler::Equal, output, ImmWord(1), lazyEntry);
7851 masm.bind(&done);
7852 masm.bind(ool->rejoin());
7853 return true;
7854 }
7856 typedef bool (*HasInstanceFn)(JSContext *, HandleObject, HandleValue, bool *);
7857 static const VMFunction HasInstanceInfo = FunctionInfo<HasInstanceFn>(js::HasInstance);
7859 bool
7860 CodeGenerator::visitCallInstanceOf(LCallInstanceOf *ins)
7861 {
7862 ValueOperand lhs = ToValue(ins, LCallInstanceOf::LHS);
7863 Register rhs = ToRegister(ins->rhs());
7864 JS_ASSERT(ToRegister(ins->output()) == ReturnReg);
7866 pushArg(lhs);
7867 pushArg(rhs);
7868 return callVM(HasInstanceInfo, ins);
7869 }
7871 bool
7872 CodeGenerator::visitGetDOMProperty(LGetDOMProperty *ins)
7873 {
7874 const Register JSContextReg = ToRegister(ins->getJSContextReg());
7875 const Register ObjectReg = ToRegister(ins->getObjectReg());
7876 const Register PrivateReg = ToRegister(ins->getPrivReg());
7877 const Register ValueReg = ToRegister(ins->getValueReg());
7879 DebugOnly<uint32_t> initialStack = masm.framePushed();
7881 masm.checkStackAlignment();
7883 // Make space for the outparam. Pre-initialize it to UndefinedValue so we
7884 // can trace it at GC time.
7885 masm.Push(UndefinedValue());
7886 // We pass the pointer to our out param as an instance of
7887 // JSJitGetterCallArgs, since on the binary level it's the same thing.
7888 JS_STATIC_ASSERT(sizeof(JSJitGetterCallArgs) == sizeof(Value*));
7889 masm.movePtr(StackPointer, ValueReg);
7891 masm.Push(ObjectReg);
7893 // GetReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate()
7894 masm.loadPrivate(Address(ObjectReg, JSObject::getFixedSlotOffset(0)), PrivateReg);
7896 // Rooting will happen at GC time.
7897 masm.movePtr(StackPointer, ObjectReg);
7899 uint32_t safepointOffset;
7900 if (!masm.buildFakeExitFrame(JSContextReg, &safepointOffset))
7901 return false;
7902 masm.enterFakeExitFrame(ION_FRAME_DOMGETTER);
7904 if (!markSafepointAt(safepointOffset, ins))
7905 return false;
7907 masm.setupUnalignedABICall(4, JSContextReg);
7909 masm.loadJSContext(JSContextReg);
7911 masm.passABIArg(JSContextReg);
7912 masm.passABIArg(ObjectReg);
7913 masm.passABIArg(PrivateReg);
7914 masm.passABIArg(ValueReg);
7915 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ins->mir()->fun()));
7917 if (ins->mir()->isInfallible()) {
7918 masm.loadValue(Address(StackPointer, IonDOMExitFrameLayout::offsetOfResult()),
7919 JSReturnOperand);
7920 } else {
7921 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
7923 masm.loadValue(Address(StackPointer, IonDOMExitFrameLayout::offsetOfResult()),
7924 JSReturnOperand);
7925 }
7926 masm.adjustStack(IonDOMExitFrameLayout::Size());
7928 JS_ASSERT(masm.framePushed() == initialStack);
7929 return true;
7930 }
7932 bool
7933 CodeGenerator::visitGetDOMMember(LGetDOMMember *ins)
7934 {
7935 // It's simple to duplicate visitLoadFixedSlotV here than it is to try to
7936 // use an LLoadFixedSlotV or some subclass of it for this case: that would
7937 // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then
7938 // we'd have to duplicate a bunch of stuff we now get for free from
7939 // MGetDOMProperty.
7940 Register object = ToRegister(ins->object());
7941 size_t slot = ins->mir()->domMemberSlotIndex();
7942 ValueOperand result = GetValueOutput(ins);
7944 masm.loadValue(Address(object, JSObject::getFixedSlotOffset(slot)), result);
7945 return true;
7946 }
7948 bool
7949 CodeGenerator::visitSetDOMProperty(LSetDOMProperty *ins)
7950 {
7951 const Register JSContextReg = ToRegister(ins->getJSContextReg());
7952 const Register ObjectReg = ToRegister(ins->getObjectReg());
7953 const Register PrivateReg = ToRegister(ins->getPrivReg());
7954 const Register ValueReg = ToRegister(ins->getValueReg());
7956 DebugOnly<uint32_t> initialStack = masm.framePushed();
7958 masm.checkStackAlignment();
7960 // Push the argument. Rooting will happen at GC time.
7961 ValueOperand argVal = ToValue(ins, LSetDOMProperty::Value);
7962 masm.Push(argVal);
7963 // We pass the pointer to our out param as an instance of
7964 // JSJitGetterCallArgs, since on the binary level it's the same thing.
7965 JS_STATIC_ASSERT(sizeof(JSJitSetterCallArgs) == sizeof(Value*));
7966 masm.movePtr(StackPointer, ValueReg);
7968 masm.Push(ObjectReg);
7970 // GetReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate()
7971 masm.loadPrivate(Address(ObjectReg, JSObject::getFixedSlotOffset(0)), PrivateReg);
7973 // Rooting will happen at GC time.
7974 masm.movePtr(StackPointer, ObjectReg);
7976 uint32_t safepointOffset;
7977 if (!masm.buildFakeExitFrame(JSContextReg, &safepointOffset))
7978 return false;
7979 masm.enterFakeExitFrame(ION_FRAME_DOMSETTER);
7981 if (!markSafepointAt(safepointOffset, ins))
7982 return false;
7984 masm.setupUnalignedABICall(4, JSContextReg);
7986 masm.loadJSContext(JSContextReg);
7988 masm.passABIArg(JSContextReg);
7989 masm.passABIArg(ObjectReg);
7990 masm.passABIArg(PrivateReg);
7991 masm.passABIArg(ValueReg);
7992 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ins->mir()->fun()));
7994 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
7996 masm.adjustStack(IonDOMExitFrameLayout::Size());
7998 JS_ASSERT(masm.framePushed() == initialStack);
7999 return true;
8000 }
8002 typedef bool(*SPSFn)(JSContext *, HandleScript);
8003 static const VMFunction SPSEnterInfo = FunctionInfo<SPSFn>(SPSEnter);
8004 static const VMFunction SPSExitInfo = FunctionInfo<SPSFn>(SPSExit);
8006 bool
8007 CodeGenerator::visitProfilerStackOp(LProfilerStackOp *lir)
8008 {
8009 Register temp = ToRegister(lir->temp()->output());
8010 bool inlinedFunction = lir->inlineLevel() > 0;
8012 switch (lir->type()) {
8013 case MProfilerStackOp::InlineEnter:
8014 // Multiple scripts can be inlined at one depth, but there is only
8015 // one InlineExit node to signify this. To deal with this, if we
8016 // reach the entry of another inline script on the same level, then
8017 // just reset the sps metadata about the frame. We must balance
8018 // calls to leave()/reenter(), so perform the balance without
8019 // emitting any instrumentation. Technically the previous inline
8020 // call at this same depth has reentered, but the instrumentation
8021 // will be emitted at the common join point for all inlines at the
8022 // same depth.
8023 if (sps_.inliningDepth() == lir->inlineLevel()) {
8024 sps_.leaveInlineFrame();
8025 sps_.skipNextReenter();
8026 sps_.reenter(masm, temp);
8027 }
8029 sps_.leave(masm, temp, /* inlinedFunction = */ true);
8030 if (!sps_.enterInlineFrame())
8031 return false;
8032 // fallthrough
8034 case MProfilerStackOp::Enter:
8035 if (gen->options.spsSlowAssertionsEnabled()) {
8036 if (!inlinedFunction || js_JitOptions.profileInlineFrames) {
8037 saveLive(lir);
8038 pushArg(ImmGCPtr(lir->script()));
8039 if (!callVM(SPSEnterInfo, lir))
8040 return false;
8041 restoreLive(lir);
8042 }
8043 sps_.pushManual(lir->script(), masm, temp, /* inlinedFunction = */ inlinedFunction);
8044 return true;
8045 }
8047 return sps_.push(lir->script(), masm, temp, /* inlinedFunction = */ inlinedFunction);
8049 case MProfilerStackOp::InlineExit:
8050 // all inline returns were covered with ::Exit, so we just need to
8051 // maintain the state of inline frames currently active and then
8052 // reenter the caller
8053 sps_.leaveInlineFrame();
8054 sps_.reenter(masm, temp, /* inlinedFunction = */ true);
8055 return true;
8057 case MProfilerStackOp::Exit:
8058 if (gen->options.spsSlowAssertionsEnabled()) {
8059 if (!inlinedFunction || js_JitOptions.profileInlineFrames) {
8060 saveLive(lir);
8061 pushArg(ImmGCPtr(lir->script()));
8062 // Once we've exited, then we shouldn't emit instrumentation for
8063 // the corresponding reenter() because we no longer have a
8064 // frame.
8065 sps_.skipNextReenter();
8066 if (!callVM(SPSExitInfo, lir))
8067 return false;
8068 restoreLive(lir);
8069 }
8070 return true;
8071 }
8073 sps_.pop(masm, temp, /* inlinedFunction = */ inlinedFunction);
8074 return true;
8076 default:
8077 MOZ_ASSUME_UNREACHABLE("invalid LProfilerStackOp type");
8078 }
8079 }
8081 bool
8082 CodeGenerator::visitOutOfLineAbortPar(OutOfLineAbortPar *ool)
8083 {
8084 ParallelBailoutCause cause = ool->cause();
8085 jsbytecode *bytecode = ool->bytecode();
8087 masm.move32(Imm32(cause), CallTempReg0);
8088 loadOutermostJSScript(CallTempReg1);
8089 loadJSScriptForBlock(ool->basicBlock(), CallTempReg2);
8090 masm.movePtr(ImmPtr(bytecode), CallTempReg3);
8092 masm.setupUnalignedABICall(4, CallTempReg4);
8093 masm.passABIArg(CallTempReg0);
8094 masm.passABIArg(CallTempReg1);
8095 masm.passABIArg(CallTempReg2);
8096 masm.passABIArg(CallTempReg3);
8097 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, AbortPar));
8099 masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
8100 masm.jump(&returnLabel_);
8101 return true;
8102 }
8104 bool
8105 CodeGenerator::visitIsCallable(LIsCallable *ins)
8106 {
8107 Register object = ToRegister(ins->object());
8108 Register output = ToRegister(ins->output());
8110 masm.loadObjClass(object, output);
8112 // An object is callable iff (is<JSFunction>() || getClass()->call).
8113 Label notFunction, done, notCall;
8114 masm.branchPtr(Assembler::NotEqual, output, ImmPtr(&JSFunction::class_), ¬Function);
8115 masm.move32(Imm32(1), output);
8116 masm.jump(&done);
8118 masm.bind(¬Function);
8119 masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::Class, call)), ImmPtr(nullptr), output);
8120 masm.bind(&done);
8122 return true;
8123 }
8125 void
8126 CodeGenerator::loadOutermostJSScript(Register reg)
8127 {
8128 // The "outermost" JSScript means the script that we are compiling
8129 // basically; this is not always the script associated with the
8130 // current basic block, which might be an inlined script.
8132 MIRGraph &graph = current->mir()->graph();
8133 MBasicBlock *entryBlock = graph.entryBlock();
8134 masm.movePtr(ImmGCPtr(entryBlock->info().script()), reg);
8135 }
8137 void
8138 CodeGenerator::loadJSScriptForBlock(MBasicBlock *block, Register reg)
8139 {
8140 // The current JSScript means the script for the current
8141 // basic block. This may be an inlined script.
8143 JSScript *script = block->info().script();
8144 masm.movePtr(ImmGCPtr(script), reg);
8145 }
8147 bool
8148 CodeGenerator::visitOutOfLinePropagateAbortPar(OutOfLinePropagateAbortPar *ool)
8149 {
8150 loadOutermostJSScript(CallTempReg0);
8151 loadJSScriptForBlock(ool->lir()->mirRaw()->block(), CallTempReg1);
8153 masm.setupUnalignedABICall(2, CallTempReg2);
8154 masm.passABIArg(CallTempReg0);
8155 masm.passABIArg(CallTempReg1);
8156 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, PropagateAbortPar));
8158 masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
8159 masm.jump(&returnLabel_);
8160 return true;
8161 }
8163 bool
8164 CodeGenerator::visitHaveSameClass(LHaveSameClass *ins)
8165 {
8166 Register lhs = ToRegister(ins->lhs());
8167 Register rhs = ToRegister(ins->rhs());
8168 Register temp = ToRegister(ins->getTemp(0));
8169 Register output = ToRegister(ins->output());
8171 masm.loadObjClass(lhs, temp);
8172 masm.loadObjClass(rhs, output);
8173 masm.cmpPtrSet(Assembler::Equal, temp, output, output);
8175 return true;
8176 }
8178 bool
8179 CodeGenerator::visitHasClass(LHasClass *ins)
8180 {
8181 Register lhs = ToRegister(ins->lhs());
8182 Register output = ToRegister(ins->output());
8184 masm.loadObjClass(lhs, output);
8185 masm.cmpPtrSet(Assembler::Equal, output, ImmPtr(ins->mir()->getClass()), output);
8187 return true;
8188 }
8190 bool
8191 CodeGenerator::visitAsmJSCall(LAsmJSCall *ins)
8192 {
8193 MAsmJSCall *mir = ins->mir();
8195 #if defined(JS_CODEGEN_ARM)
8196 if (!useHardFpABI() && mir->callee().which() == MAsmJSCall::Callee::Builtin) {
8197 for (unsigned i = 0, e = ins->numOperands(); i < e; i++) {
8198 LAllocation *a = ins->getOperand(i);
8199 if (a->isFloatReg()) {
8200 FloatRegister fr = ToFloatRegister(a);
8201 int srcId = fr.code() * 2;
8202 masm.ma_vxfer(fr, Register::FromCode(srcId), Register::FromCode(srcId+1));
8203 }
8204 }
8205 }
8206 #endif
8208 if (mir->spIncrement())
8209 masm.freeStack(mir->spIncrement());
8211 JS_ASSERT((AlignmentAtPrologue + masm.framePushed()) % StackAlignment == 0);
8213 #ifdef DEBUG
8214 Label ok;
8215 JS_ASSERT(IsPowerOfTwo(StackAlignment));
8216 masm.branchTestPtr(Assembler::Zero, StackPointer, Imm32(StackAlignment - 1), &ok);
8217 masm.assumeUnreachable("Stack should be aligned.");
8218 masm.bind(&ok);
8219 #endif
8221 MAsmJSCall::Callee callee = mir->callee();
8222 switch (callee.which()) {
8223 case MAsmJSCall::Callee::Internal:
8224 masm.call(mir->desc(), callee.internal());
8225 break;
8226 case MAsmJSCall::Callee::Dynamic:
8227 masm.call(mir->desc(), ToRegister(ins->getOperand(mir->dynamicCalleeOperandIndex())));
8228 break;
8229 case MAsmJSCall::Callee::Builtin:
8230 masm.call(mir->desc(), callee.builtin());
8231 break;
8232 }
8234 if (mir->spIncrement())
8235 masm.reserveStack(mir->spIncrement());
8237 postAsmJSCall(ins);
8238 return true;
8239 }
8241 bool
8242 CodeGenerator::visitAsmJSParameter(LAsmJSParameter *lir)
8243 {
8244 return true;
8245 }
8247 bool
8248 CodeGenerator::visitAsmJSReturn(LAsmJSReturn *lir)
8249 {
8250 // Don't emit a jump to the return label if this is the last block.
8251 if (current->mir() != *gen->graph().poBegin())
8252 masm.jump(&returnLabel_);
8253 return true;
8254 }
8256 bool
8257 CodeGenerator::visitAsmJSVoidReturn(LAsmJSVoidReturn *lir)
8258 {
8259 // Don't emit a jump to the return label if this is the last block.
8260 if (current->mir() != *gen->graph().poBegin())
8261 masm.jump(&returnLabel_);
8262 return true;
8263 }
8265 bool
8266 CodeGenerator::emitAssertRangeI(const Range *r, Register input)
8267 {
8268 // Check the lower bound.
8269 if (r->hasInt32LowerBound() && r->lower() > INT32_MIN) {
8270 Label success;
8271 masm.branch32(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()), &success);
8272 masm.assumeUnreachable("Integer input should be equal or higher than Lowerbound.");
8273 masm.bind(&success);
8274 }
8276 // Check the upper bound.
8277 if (r->hasInt32UpperBound() && r->upper() < INT32_MAX) {
8278 Label success;
8279 masm.branch32(Assembler::LessThanOrEqual, input, Imm32(r->upper()), &success);
8280 masm.assumeUnreachable("Integer input should be lower or equal than Upperbound.");
8281 masm.bind(&success);
8282 }
8284 // For r->canHaveFractionalPart() and r->exponent(), there's nothing to check, because
8285 // if we ended up in the integer range checking code, the value is already
8286 // in an integer register in the integer range.
8288 return true;
8289 }
8291 bool
8292 CodeGenerator::emitAssertRangeD(const Range *r, FloatRegister input, FloatRegister temp)
8293 {
8294 // Check the lower bound.
8295 if (r->hasInt32LowerBound()) {
8296 Label success;
8297 masm.loadConstantDouble(r->lower(), temp);
8298 if (r->canBeNaN())
8299 masm.branchDouble(Assembler::DoubleUnordered, input, input, &success);
8300 masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp, &success);
8301 masm.assumeUnreachable("Double input should be equal or higher than Lowerbound.");
8302 masm.bind(&success);
8303 }
8304 // Check the upper bound.
8305 if (r->hasInt32UpperBound()) {
8306 Label success;
8307 masm.loadConstantDouble(r->upper(), temp);
8308 if (r->canBeNaN())
8309 masm.branchDouble(Assembler::DoubleUnordered, input, input, &success);
8310 masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &success);
8311 masm.assumeUnreachable("Double input should be lower or equal than Upperbound.");
8312 masm.bind(&success);
8313 }
8315 // This code does not yet check r->canHaveFractionalPart(). This would require new
8316 // assembler interfaces to make rounding instructions available.
8318 if (!r->hasInt32Bounds() && !r->canBeInfiniteOrNaN() &&
8319 r->exponent() < FloatingPoint<double>::ExponentBias)
8320 {
8321 // Check the bounds implied by the maximum exponent.
8322 Label exponentLoOk;
8323 masm.loadConstantDouble(pow(2.0, r->exponent() + 1), temp);
8324 masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentLoOk);
8325 masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &exponentLoOk);
8326 masm.assumeUnreachable("Check for exponent failed.");
8327 masm.bind(&exponentLoOk);
8329 Label exponentHiOk;
8330 masm.loadConstantDouble(-pow(2.0, r->exponent() + 1), temp);
8331 masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentHiOk);
8332 masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp, &exponentHiOk);
8333 masm.assumeUnreachable("Check for exponent failed.");
8334 masm.bind(&exponentHiOk);
8335 } else if (!r->hasInt32Bounds() && !r->canBeNaN()) {
8336 // If we think the value can't be NaN, check that it isn't.
8337 Label notnan;
8338 masm.branchDouble(Assembler::DoubleOrdered, input, input, ¬nan);
8339 masm.assumeUnreachable("Input shouldn't be NaN.");
8340 masm.bind(¬nan);
8342 // If we think the value also can't be an infinity, check that it isn't.
8343 if (!r->canBeInfiniteOrNaN()) {
8344 Label notposinf;
8345 masm.loadConstantDouble(PositiveInfinity<double>(), temp);
8346 masm.branchDouble(Assembler::DoubleLessThan, input, temp, ¬posinf);
8347 masm.assumeUnreachable("Input shouldn't be +Inf.");
8348 masm.bind(¬posinf);
8350 Label notneginf;
8351 masm.loadConstantDouble(NegativeInfinity<double>(), temp);
8352 masm.branchDouble(Assembler::DoubleGreaterThan, input, temp, ¬neginf);
8353 masm.assumeUnreachable("Input shouldn't be -Inf.");
8354 masm.bind(¬neginf);
8355 }
8356 }
8358 return true;
8359 }
8361 bool
8362 CodeGenerator::visitAssertRangeI(LAssertRangeI *ins)
8363 {
8364 Register input = ToRegister(ins->input());
8365 const Range *r = ins->range();
8367 return emitAssertRangeI(r, input);
8368 }
8370 bool
8371 CodeGenerator::visitAssertRangeD(LAssertRangeD *ins)
8372 {
8373 FloatRegister input = ToFloatRegister(ins->input());
8374 FloatRegister temp = ToFloatRegister(ins->temp());
8375 const Range *r = ins->range();
8377 return emitAssertRangeD(r, input, temp);
8378 }
8380 bool
8381 CodeGenerator::visitAssertRangeF(LAssertRangeF *ins)
8382 {
8383 FloatRegister input = ToFloatRegister(ins->input());
8384 FloatRegister temp = ToFloatRegister(ins->temp());
8385 const Range *r = ins->range();
8387 masm.convertFloat32ToDouble(input, input);
8388 bool success = emitAssertRangeD(r, input, temp);
8389 masm.convertDoubleToFloat32(input, input);
8390 return success;
8391 }
8393 bool
8394 CodeGenerator::visitAssertRangeV(LAssertRangeV *ins)
8395 {
8396 const Range *r = ins->range();
8397 const ValueOperand value = ToValue(ins, LAssertRangeV::Input);
8398 Register tag = masm.splitTagForTest(value);
8399 Label done;
8401 {
8402 Label isNotInt32;
8403 masm.branchTestInt32(Assembler::NotEqual, tag, &isNotInt32);
8404 Register unboxInt32 = ToTempUnboxRegister(ins->temp());
8405 Register input = masm.extractInt32(value, unboxInt32);
8406 emitAssertRangeI(r, input);
8407 masm.jump(&done);
8408 masm.bind(&isNotInt32);
8409 }
8411 {
8412 Label isNotDouble;
8413 masm.branchTestDouble(Assembler::NotEqual, tag, &isNotDouble);
8414 FloatRegister input = ToFloatRegister(ins->floatTemp1());
8415 FloatRegister temp = ToFloatRegister(ins->floatTemp2());
8416 masm.unboxDouble(value, input);
8417 emitAssertRangeD(r, input, temp);
8418 masm.jump(&done);
8419 masm.bind(&isNotDouble);
8420 }
8422 masm.assumeUnreachable("Incorrect range for Value.");
8423 masm.bind(&done);
8424 return true;
8425 }
8427 typedef bool (*RecompileFn)(JSContext *);
8428 static const VMFunction RecompileFnInfo = FunctionInfo<RecompileFn>(Recompile);
8430 bool
8431 CodeGenerator::visitRecompileCheck(LRecompileCheck *ins)
8432 {
8433 Label done;
8434 Register tmp = ToRegister(ins->scratch());
8435 OutOfLineCode *ool = oolCallVM(RecompileFnInfo, ins, (ArgList()), StoreRegisterTo(tmp));
8437 // Check if usecount is high enough.
8438 masm.movePtr(ImmPtr(ins->mir()->script()->addressOfUseCount()), tmp);
8439 Address ptr(tmp, 0);
8440 masm.add32(Imm32(1), tmp);
8441 masm.branch32(Assembler::BelowOrEqual, ptr, Imm32(ins->mir()->useCount()), &done);
8443 // Check if not yet recompiling.
8444 CodeOffsetLabel label = masm.movWithPatch(ImmWord(uintptr_t(-1)), tmp);
8445 if (!ionScriptLabels_.append(label))
8446 return false;
8447 masm.branch32(Assembler::Equal,
8448 Address(tmp, IonScript::offsetOfRecompiling()),
8449 Imm32(0),
8450 ool->entry());
8451 masm.bind(ool->rejoin());
8452 masm.bind(&done);
8454 return true;
8455 }
8457 } // namespace jit
8458 } // namespace js