|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "jit/CodeGenerator.h" |
|
8 |
|
9 #include "mozilla/Assertions.h" |
|
10 #include "mozilla/Attributes.h" |
|
11 #include "mozilla/DebugOnly.h" |
|
12 |
|
13 #include "jslibmath.h" |
|
14 #include "jsmath.h" |
|
15 #include "jsnum.h" |
|
16 #include "jsprf.h" |
|
17 |
|
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" |
|
34 |
|
35 #include "jsboolinlines.h" |
|
36 |
|
37 #include "jit/ExecutionMode-inl.h" |
|
38 #include "jit/shared/CodeGenerator-shared-inl.h" |
|
39 #include "vm/Interpreter-inl.h" |
|
40 |
|
41 using namespace js; |
|
42 using namespace js::jit; |
|
43 |
|
44 using mozilla::DebugOnly; |
|
45 using mozilla::FloatingPoint; |
|
46 using mozilla::Maybe; |
|
47 using mozilla::NegativeInfinity; |
|
48 using mozilla::PositiveInfinity; |
|
49 using JS::GenericNaN; |
|
50 |
|
51 namespace js { |
|
52 namespace jit { |
|
53 |
|
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_; |
|
64 |
|
65 public: |
|
66 OutOfLineUpdateCache(LInstruction *lir, size_t cacheIndex) |
|
67 : lir_(lir), |
|
68 cacheIndex_(cacheIndex) |
|
69 { } |
|
70 |
|
71 void bind(MacroAssembler *masm) { |
|
72 // The binding of the initial jump is done in |
|
73 // CodeGenerator::visitOutOfLineCache. |
|
74 } |
|
75 |
|
76 size_t getCacheIndex() const { |
|
77 return cacheIndex_; |
|
78 } |
|
79 LInstruction *lir() const { |
|
80 return lir_; |
|
81 } |
|
82 AddCacheState &state() { |
|
83 return state_; |
|
84 } |
|
85 |
|
86 bool accept(CodeGenerator *codegen) { |
|
87 return codegen->visitOutOfLineCache(this); |
|
88 } |
|
89 |
|
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 } |
|
96 |
|
97 IONCACHE_KIND_LIST(VISIT_CACHE_FUNCTION) |
|
98 #undef VISIT_CACHE_FUNCTION |
|
99 }; |
|
100 |
|
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; |
|
110 |
|
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(); |
|
118 |
|
119 OutOfLineUpdateCache *ool = new(alloc()) OutOfLineUpdateCache(lir, cacheIndex); |
|
120 if (!addOutOfLineCode(ool)) |
|
121 return false; |
|
122 |
|
123 // OOL-specific state depends on the type of cache. |
|
124 cache->initializeAddCacheState(lir, &ool->state()); |
|
125 |
|
126 cache->emitInitialJump(masm, ool->state()); |
|
127 masm.bind(ool->rejoin()); |
|
128 |
|
129 return true; |
|
130 } |
|
131 |
|
132 bool |
|
133 CodeGenerator::visitOutOfLineCache(OutOfLineUpdateCache *ool) |
|
134 { |
|
135 DataPtr<IonCache> cache(this, ool->getCacheIndex()); |
|
136 |
|
137 // Register the location of the OOL path in the IC. |
|
138 cache->setFallbackLabel(masm.labelForPatch()); |
|
139 cache->bindInitialJump(masm, ool->state()); |
|
140 |
|
141 // Dispatch to ICs' accept functions. |
|
142 return cache->accept(this, ool); |
|
143 } |
|
144 |
|
145 StringObject * |
|
146 MNewStringObject::templateObj() const { |
|
147 return &templateObj_->as<StringObject>(); |
|
148 } |
|
149 |
|
150 CodeGenerator::CodeGenerator(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm) |
|
151 : CodeGeneratorSpecific(gen, graph, masm) |
|
152 , ionScriptLabels_(gen->alloc()) |
|
153 { |
|
154 } |
|
155 |
|
156 CodeGenerator::~CodeGenerator() |
|
157 { |
|
158 JS_ASSERT_IF(!gen->compilingAsmJS(), masm.numAsmJSAbsoluteLinks() == 0); |
|
159 } |
|
160 |
|
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)); |
|
166 |
|
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()); |
|
173 |
|
174 MDefinition *input; |
|
175 if (lir->mode() == LValueToInt32::NORMAL) |
|
176 input = lir->mirNormal()->input(); |
|
177 else |
|
178 input = lir->mirTruncate()->input(); |
|
179 |
|
180 Label fails; |
|
181 if (lir->mode() == LValueToInt32::TRUNCATE) { |
|
182 OutOfLineCode *oolDouble = oolTruncateDouble(temp, output); |
|
183 if (!oolDouble) |
|
184 return false; |
|
185 |
|
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 } |
|
203 |
|
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 } |
|
212 |
|
213 return bailoutFrom(&fails, lir->snapshot()); |
|
214 } |
|
215 |
|
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()); |
|
222 |
|
223 Register tag = masm.splitTagForTest(operand); |
|
224 |
|
225 Label isDouble, isInt32, isBool, isNull, isUndefined, done; |
|
226 bool hasBoolean = false, hasNull = false, hasUndefined = false; |
|
227 |
|
228 masm.branchTestDouble(Assembler::Equal, tag, &isDouble); |
|
229 masm.branchTestInt32(Assembler::Equal, tag, &isInt32); |
|
230 |
|
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 } |
|
241 |
|
242 if (!bailout(lir->snapshot())) |
|
243 return false; |
|
244 |
|
245 if (hasNull) { |
|
246 masm.bind(&isNull); |
|
247 masm.loadConstantDouble(0.0, output); |
|
248 masm.jump(&done); |
|
249 } |
|
250 |
|
251 if (hasUndefined) { |
|
252 masm.bind(&isUndefined); |
|
253 masm.loadConstantDouble(GenericNaN(), output); |
|
254 masm.jump(&done); |
|
255 } |
|
256 |
|
257 if (hasBoolean) { |
|
258 masm.bind(&isBool); |
|
259 masm.boolValueToDouble(operand, output); |
|
260 masm.jump(&done); |
|
261 } |
|
262 |
|
263 masm.bind(&isInt32); |
|
264 masm.int32ValueToDouble(operand, output); |
|
265 masm.jump(&done); |
|
266 |
|
267 masm.bind(&isDouble); |
|
268 masm.unboxDouble(operand, output); |
|
269 masm.bind(&done); |
|
270 |
|
271 return true; |
|
272 } |
|
273 |
|
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()); |
|
280 |
|
281 Register tag = masm.splitTagForTest(operand); |
|
282 |
|
283 Label isDouble, isInt32, isBool, isNull, isUndefined, done; |
|
284 bool hasBoolean = false, hasNull = false, hasUndefined = false; |
|
285 |
|
286 masm.branchTestDouble(Assembler::Equal, tag, &isDouble); |
|
287 masm.branchTestInt32(Assembler::Equal, tag, &isInt32); |
|
288 |
|
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 } |
|
299 |
|
300 if (!bailout(lir->snapshot())) |
|
301 return false; |
|
302 |
|
303 if (hasNull) { |
|
304 masm.bind(&isNull); |
|
305 masm.loadConstantFloat32(0.0f, output); |
|
306 masm.jump(&done); |
|
307 } |
|
308 |
|
309 if (hasUndefined) { |
|
310 masm.bind(&isUndefined); |
|
311 masm.loadConstantFloat32(float(GenericNaN()), output); |
|
312 masm.jump(&done); |
|
313 } |
|
314 |
|
315 if (hasBoolean) { |
|
316 masm.bind(&isBool); |
|
317 masm.boolValueToFloat32(operand, output); |
|
318 masm.jump(&done); |
|
319 } |
|
320 |
|
321 masm.bind(&isInt32); |
|
322 masm.int32ValueToFloat32(operand, output); |
|
323 masm.jump(&done); |
|
324 |
|
325 masm.bind(&isDouble); |
|
326 masm.unboxDouble(operand, output); |
|
327 masm.convertDoubleToFloat32(output, output); |
|
328 masm.bind(&done); |
|
329 |
|
330 return true; |
|
331 } |
|
332 |
|
333 bool |
|
334 CodeGenerator::visitInt32ToDouble(LInt32ToDouble *lir) |
|
335 { |
|
336 masm.convertInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output())); |
|
337 return true; |
|
338 } |
|
339 |
|
340 bool |
|
341 CodeGenerator::visitFloat32ToDouble(LFloat32ToDouble *lir) |
|
342 { |
|
343 masm.convertFloat32ToDouble(ToFloatRegister(lir->input()), ToFloatRegister(lir->output())); |
|
344 return true; |
|
345 } |
|
346 |
|
347 bool |
|
348 CodeGenerator::visitDoubleToFloat32(LDoubleToFloat32 *lir) |
|
349 { |
|
350 masm.convertDoubleToFloat32(ToFloatRegister(lir->input()), ToFloatRegister(lir->output())); |
|
351 return true; |
|
352 } |
|
353 |
|
354 bool |
|
355 CodeGenerator::visitInt32ToFloat32(LInt32ToFloat32 *lir) |
|
356 { |
|
357 masm.convertInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output())); |
|
358 return true; |
|
359 } |
|
360 |
|
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 } |
|
372 |
|
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 } |
|
384 |
|
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); |
|
397 |
|
398 masm.branchIfTrueBool(scratch, ifEmulatesUndefined); |
|
399 masm.jump(ifDoesntEmulateUndefined); |
|
400 } |
|
401 |
|
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_; |
|
414 |
|
415 Label *ifEmulatesUndefined_; |
|
416 Label *ifDoesntEmulateUndefined_; |
|
417 |
|
418 #ifdef DEBUG |
|
419 bool initialized() { return ifEmulatesUndefined_ != nullptr; } |
|
420 #endif |
|
421 |
|
422 public: |
|
423 OutOfLineTestObject() |
|
424 #ifdef DEBUG |
|
425 : ifEmulatesUndefined_(nullptr), ifDoesntEmulateUndefined_(nullptr) |
|
426 #endif |
|
427 { } |
|
428 |
|
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 } |
|
435 |
|
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 }; |
|
450 |
|
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_; |
|
459 |
|
460 public: |
|
461 OutOfLineTestObjectWithLabels() { } |
|
462 |
|
463 Label *label1() { return &label1_; } |
|
464 Label *label2() { return &label2_; } |
|
465 }; |
|
466 |
|
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); |
|
474 |
|
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 } |
|
480 |
|
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"); |
|
489 |
|
490 testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined, |
|
491 scratch, ool); |
|
492 masm.bind(ifDoesntEmulateUndefined); |
|
493 } |
|
494 |
|
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 } |
|
505 |
|
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); |
|
514 |
|
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); |
|
521 |
|
522 Label notBoolean; |
|
523 masm.branchTestBoolean(Assembler::NotEqual, tag, ¬Boolean); |
|
524 masm.branchTestBooleanTruthy(false, value, ifFalsy); |
|
525 masm.jump(ifTruthy); |
|
526 masm.bind(¬Boolean); |
|
527 |
|
528 Label notInt32; |
|
529 masm.branchTestInt32(Assembler::NotEqual, tag, ¬Int32); |
|
530 masm.branchTestInt32Truthy(false, value, ifFalsy); |
|
531 masm.jump(ifTruthy); |
|
532 masm.bind(¬Int32); |
|
533 |
|
534 if (ool) { |
|
535 Label notObject; |
|
536 |
|
537 masm.branchTestObject(Assembler::NotEqual, tag, ¬Object); |
|
538 |
|
539 Register objreg = masm.extractObject(value, ToRegister(scratch1)); |
|
540 testObjectEmulatesUndefined(objreg, ifFalsy, ifTruthy, ToRegister(scratch2), ool); |
|
541 |
|
542 masm.bind(¬Object); |
|
543 } else { |
|
544 masm.branchTestObject(Assembler::Equal, tag, ifTruthy); |
|
545 } |
|
546 |
|
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); |
|
553 |
|
554 // If we reach here the value is a double. |
|
555 masm.unboxDouble(value, fr); |
|
556 masm.branchTestDoubleTruthy(false, fr, ifFalsy); |
|
557 |
|
558 // Fall through for truthy. |
|
559 } |
|
560 |
|
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 } |
|
571 |
|
572 Label * |
|
573 CodeGenerator::getJumpLabelForBranch(MBasicBlock *block) |
|
574 { |
|
575 if (!labelForBackedgeWithImplicitCheck(block)) |
|
576 return block->lir()->label(); |
|
577 |
|
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 } |
|
591 |
|
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"); |
|
597 |
|
598 OutOfLineTestObject *ool = new(alloc()) OutOfLineTestObject(); |
|
599 if (!addOutOfLineCode(ool)) |
|
600 return false; |
|
601 |
|
602 Label *truthy = getJumpLabelForBranch(lir->ifTruthy()); |
|
603 Label *falsy = getJumpLabelForBranch(lir->ifFalsy()); |
|
604 |
|
605 testObjectEmulatesUndefined(ToRegister(lir->input()), falsy, truthy, |
|
606 ToRegister(lir->temp()), ool); |
|
607 return true; |
|
608 |
|
609 } |
|
610 |
|
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 } |
|
620 |
|
621 Label *truthy = getJumpLabelForBranch(lir->ifTruthy()); |
|
622 Label *falsy = getJumpLabelForBranch(lir->ifFalsy()); |
|
623 |
|
624 testValueTruthy(ToValue(lir, LTestVAndBranch::Input), |
|
625 lir->temp1(), lir->temp2(), |
|
626 ToFloatRegister(lir->tempFloat()), |
|
627 truthy, falsy, ool); |
|
628 return true; |
|
629 } |
|
630 |
|
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; |
|
638 |
|
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 } |
|
648 |
|
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 } |
|
656 |
|
657 // Jump to the last case. |
|
658 masm.jump(lastLabel); |
|
659 |
|
660 return true; |
|
661 } |
|
662 |
|
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()); |
|
669 |
|
670 // Hold the incoming TypeObject. |
|
671 masm.loadPtr(Address(input, JSObject::offsetOfType()), temp); |
|
672 |
|
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(); |
|
678 |
|
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 } |
|
689 |
|
690 // Unknown function: jump to fallback block. |
|
691 LBlock *fallback = mir->getFallback()->lir(); |
|
692 masm.jump(fallback->label()); |
|
693 return true; |
|
694 } |
|
695 |
|
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; |
|
703 |
|
704 masm.branchTest32(Assembler::NonZero, input, input, &true_); |
|
705 masm.movePtr(ImmGCPtr(names.false_), output); |
|
706 masm.jump(&done); |
|
707 |
|
708 masm.bind(&true_); |
|
709 masm.movePtr(ImmGCPtr(names.true_), output); |
|
710 |
|
711 masm.bind(&done); |
|
712 |
|
713 return true; |
|
714 } |
|
715 |
|
716 void |
|
717 CodeGenerator::emitIntToString(Register input, Register output, Label *ool) |
|
718 { |
|
719 masm.branch32(Assembler::AboveOrEqual, input, Imm32(StaticStrings::INT_STATIC_LIMIT), ool); |
|
720 |
|
721 // Fast path for small integers. |
|
722 masm.movePtr(ImmPtr(&GetIonContext()->runtime->staticStrings().intStaticTable), output); |
|
723 masm.loadPtr(BaseIndex(output, input, ScalePointer), output); |
|
724 } |
|
725 |
|
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)); |
|
731 |
|
732 bool |
|
733 CodeGenerator::visitIntToString(LIntToString *lir) |
|
734 { |
|
735 Register input = ToRegister(lir->input()); |
|
736 Register output = ToRegister(lir->output()); |
|
737 |
|
738 OutOfLineCode *ool = oolCallVM(IntToStringInfo, lir, (ArgList(), input), |
|
739 StoreRegisterTo(output)); |
|
740 if (!ool) |
|
741 return false; |
|
742 |
|
743 emitIntToString(input, output, ool->entry()); |
|
744 |
|
745 masm.bind(ool->rejoin()); |
|
746 return true; |
|
747 } |
|
748 |
|
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)); |
|
754 |
|
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()); |
|
761 |
|
762 OutOfLineCode *ool = oolCallVM(DoubleToStringInfo, lir, (ArgList(), input), |
|
763 StoreRegisterTo(output)); |
|
764 if (!ool) |
|
765 return false; |
|
766 |
|
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()); |
|
770 |
|
771 masm.bind(ool->rejoin()); |
|
772 return true; |
|
773 } |
|
774 |
|
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)); |
|
780 |
|
781 bool |
|
782 CodeGenerator::visitPrimitiveToString(LPrimitiveToString *lir) |
|
783 { |
|
784 ValueOperand input = ToValue(lir, LPrimitiveToString::Input); |
|
785 Register output = ToRegister(lir->output()); |
|
786 |
|
787 OutOfLineCode *ool = oolCallVM(PrimitiveToStringInfo, lir, (ArgList(), input), |
|
788 StoreRegisterTo(output)); |
|
789 if (!ool) |
|
790 return false; |
|
791 |
|
792 Label done; |
|
793 Register tag = masm.splitTagForTest(input); |
|
794 const JSAtomState &names = GetIonContext()->runtime->names(); |
|
795 |
|
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 } |
|
804 |
|
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 } |
|
815 |
|
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 } |
|
822 |
|
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 } |
|
831 |
|
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 } |
|
840 |
|
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 } |
|
853 |
|
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 |
|
858 |
|
859 masm.bind(&done); |
|
860 masm.bind(ool->rejoin()); |
|
861 return true; |
|
862 } |
|
863 |
|
864 typedef JSObject *(*CloneRegExpObjectFn)(JSContext *, JSObject *); |
|
865 static const VMFunction CloneRegExpObjectInfo = |
|
866 FunctionInfo<CloneRegExpObjectFn>(CloneRegExpObject); |
|
867 |
|
868 bool |
|
869 CodeGenerator::visitRegExp(LRegExp *lir) |
|
870 { |
|
871 pushArg(ImmGCPtr(lir->mir()->source())); |
|
872 return callVM(CloneRegExpObjectInfo, lir); |
|
873 } |
|
874 |
|
875 typedef bool (*RegExpExecRawFn)(JSContext *cx, HandleObject regexp, |
|
876 HandleString input, MutableHandleValue output); |
|
877 static const VMFunction RegExpExecRawInfo = FunctionInfo<RegExpExecRawFn>(regexp_exec_raw); |
|
878 |
|
879 bool |
|
880 CodeGenerator::visitRegExpExec(LRegExpExec *lir) |
|
881 { |
|
882 pushArg(ToRegister(lir->string())); |
|
883 pushArg(ToRegister(lir->regexp())); |
|
884 return callVM(RegExpExecRawInfo, lir); |
|
885 } |
|
886 |
|
887 typedef bool (*RegExpTestRawFn)(JSContext *cx, HandleObject regexp, |
|
888 HandleString input, bool *result); |
|
889 static const VMFunction RegExpTestRawInfo = FunctionInfo<RegExpTestRawFn>(regexp_test_raw); |
|
890 |
|
891 bool |
|
892 CodeGenerator::visitRegExpTest(LRegExpTest *lir) |
|
893 { |
|
894 pushArg(ToRegister(lir->string())); |
|
895 pushArg(ToRegister(lir->regexp())); |
|
896 return callVM(RegExpTestRawInfo, lir); |
|
897 } |
|
898 |
|
899 typedef JSString *(*RegExpReplaceFn)(JSContext *, HandleString, HandleObject, HandleString); |
|
900 static const VMFunction RegExpReplaceInfo = FunctionInfo<RegExpReplaceFn>(RegExpReplace); |
|
901 |
|
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())); |
|
909 |
|
910 pushArg(ToRegister(lir->pattern())); |
|
911 |
|
912 if (lir->string()->isConstant()) |
|
913 pushArg(ImmGCPtr(lir->string()->toConstant()->toString())); |
|
914 else |
|
915 pushArg(ToRegister(lir->string())); |
|
916 |
|
917 return callVM(RegExpReplaceInfo, lir); |
|
918 } |
|
919 |
|
920 typedef JSString *(*StringReplaceFn)(JSContext *, HandleString, HandleString, HandleString); |
|
921 static const VMFunction StringReplaceInfo = FunctionInfo<StringReplaceFn>(StringReplace); |
|
922 |
|
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())); |
|
930 |
|
931 if (lir->pattern()->isConstant()) |
|
932 pushArg(ImmGCPtr(lir->pattern()->toConstant()->toString())); |
|
933 else |
|
934 pushArg(ToRegister(lir->pattern())); |
|
935 |
|
936 if (lir->string()->isConstant()) |
|
937 pushArg(ImmGCPtr(lir->string()->toConstant()->toString())); |
|
938 else |
|
939 pushArg(ToRegister(lir->string())); |
|
940 |
|
941 return callVM(StringReplaceInfo, lir); |
|
942 } |
|
943 |
|
944 |
|
945 typedef JSObject *(*LambdaFn)(JSContext *, HandleFunction, HandleObject); |
|
946 static const VMFunction LambdaInfo = FunctionInfo<LambdaFn>(js::Lambda); |
|
947 |
|
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 } |
|
955 |
|
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(); |
|
963 |
|
964 OutOfLineCode *ool = oolCallVM(LambdaInfo, lir, (ArgList(), ImmGCPtr(info.fun), scopeChain), |
|
965 StoreRegisterTo(output)); |
|
966 if (!ool) |
|
967 return false; |
|
968 |
|
969 JS_ASSERT(!info.singletonType); |
|
970 |
|
971 masm.newGCThing(output, tempReg, info.fun, ool->entry(), gc::DefaultHeap); |
|
972 masm.initGCThing(output, tempReg, info.fun); |
|
973 |
|
974 emitLambdaInit(output, scopeChain, info); |
|
975 |
|
976 masm.bind(ool->rejoin()); |
|
977 return true; |
|
978 } |
|
979 |
|
980 typedef JSObject *(*LambdaArrowFn)(JSContext *, HandleFunction, HandleObject, HandleValue); |
|
981 static const VMFunction LambdaArrowInfo = FunctionInfo<LambdaArrowFn>(js::LambdaArrow); |
|
982 |
|
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(); |
|
991 |
|
992 OutOfLineCode *ool = oolCallVM(LambdaArrowInfo, lir, |
|
993 (ArgList(), ImmGCPtr(info.fun), scopeChain, thisv), |
|
994 StoreRegisterTo(output)); |
|
995 if (!ool) |
|
996 return false; |
|
997 |
|
998 MOZ_ASSERT(!info.useNewTypeForClone); |
|
999 |
|
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 } |
|
1007 |
|
1008 masm.newGCThing(output, tempReg, info.fun, ool->entry(), gc::DefaultHeap); |
|
1009 masm.initGCThing(output, tempReg, info.fun); |
|
1010 |
|
1011 emitLambdaInit(output, scopeChain, info); |
|
1012 |
|
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))); |
|
1019 |
|
1020 masm.bind(ool->rejoin()); |
|
1021 return true; |
|
1022 } |
|
1023 |
|
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)); |
|
1029 |
|
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; |
|
1041 |
|
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 } |
|
1049 |
|
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(); |
|
1059 |
|
1060 JS_ASSERT(scopeChainReg != resultReg); |
|
1061 |
|
1062 emitAllocateGCThingPar(lir, resultReg, cxReg, tempReg1, tempReg2, info.fun); |
|
1063 emitLambdaInit(resultReg, scopeChainReg, info); |
|
1064 return true; |
|
1065 } |
|
1066 |
|
1067 bool |
|
1068 CodeGenerator::visitLabel(LLabel *lir) |
|
1069 { |
|
1070 return true; |
|
1071 } |
|
1072 |
|
1073 bool |
|
1074 CodeGenerator::visitNop(LNop *lir) |
|
1075 { |
|
1076 return true; |
|
1077 } |
|
1078 |
|
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. |
|
1084 |
|
1085 JS_ASSERT(masm.framePushed() == frameSize()); |
|
1086 |
|
1087 uint32_t osiCallPointOffset; |
|
1088 if (!markOsiPoint(lir, &osiCallPointOffset)) |
|
1089 return false; |
|
1090 |
|
1091 LSafepoint *safepoint = lir->associatedSafepoint(); |
|
1092 JS_ASSERT(!safepoint->osiCallPointOffset()); |
|
1093 safepoint->setOsiCallPointOffset(osiCallPointOffset); |
|
1094 |
|
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 |
|
1107 |
|
1108 #ifdef CHECK_OSIPOINT_REGISTERS |
|
1109 if (shouldVerifyOsiPointRegs(safepoint)) |
|
1110 verifyOsiPointRegs(safepoint); |
|
1111 #endif |
|
1112 |
|
1113 return true; |
|
1114 } |
|
1115 |
|
1116 bool |
|
1117 CodeGenerator::visitGoto(LGoto *lir) |
|
1118 { |
|
1119 jumpToBlock(lir->target()); |
|
1120 return true; |
|
1121 } |
|
1122 |
|
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; |
|
1130 |
|
1131 OutOfLineInterruptCheckImplicit(LBlock *block, LInterruptCheckImplicit *lir) |
|
1132 : block(block), lir(lir) |
|
1133 { } |
|
1134 |
|
1135 bool accept(CodeGenerator *codegen) { |
|
1136 return codegen->visitOutOfLineInterruptCheckImplicit(this); |
|
1137 } |
|
1138 }; |
|
1139 |
|
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 |
|
1150 |
|
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); |
|
1165 |
|
1166 saveLive(ool->lir); |
|
1167 if (!callVM(InterruptCheckInfo, ool->lir)) |
|
1168 return false; |
|
1169 restoreLive(ool->lir); |
|
1170 masm.jump(ool->rejoin()); |
|
1171 |
|
1172 return true; |
|
1173 } |
|
1174 |
|
1175 bool |
|
1176 CodeGenerator::visitInterruptCheckImplicit(LInterruptCheckImplicit *lir) |
|
1177 { |
|
1178 OutOfLineInterruptCheckImplicit *ool = new(alloc()) OutOfLineInterruptCheckImplicit(current, lir); |
|
1179 if (!addOutOfLineCode(ool)) |
|
1180 return false; |
|
1181 |
|
1182 lir->setOolEntry(ool->entry()); |
|
1183 masm.bind(ool->rejoin()); |
|
1184 return true; |
|
1185 } |
|
1186 |
|
1187 bool |
|
1188 CodeGenerator::visitTableSwitch(LTableSwitch *ins) |
|
1189 { |
|
1190 MTableSwitch *mir = ins->mir(); |
|
1191 Label *defaultcase = mir->getDefault()->lir()->label(); |
|
1192 const LAllocation *temp; |
|
1193 |
|
1194 if (mir->getOperand(0)->type() != MIRType_Int32) { |
|
1195 temp = ins->tempInt()->output(); |
|
1196 |
|
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 } |
|
1203 |
|
1204 return emitTableSwitchDispatch(mir, ToRegister(temp), ToRegisterOrInvalid(ins->tempPointer())); |
|
1205 } |
|
1206 |
|
1207 bool |
|
1208 CodeGenerator::visitTableSwitchV(LTableSwitchV *ins) |
|
1209 { |
|
1210 MTableSwitch *mir = ins->mir(); |
|
1211 Label *defaultcase = mir->getDefault()->lir()->label(); |
|
1212 |
|
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); |
|
1217 |
|
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 } |
|
1226 |
|
1227 masm.bind(&unboxInt); |
|
1228 masm.unboxInt32(value, index); |
|
1229 |
|
1230 masm.bind(&isInt); |
|
1231 |
|
1232 return emitTableSwitchDispatch(mir, index, ToRegisterOrInvalid(ins->tempPointer())); |
|
1233 } |
|
1234 |
|
1235 typedef JSObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleObject, NewObjectKind); |
|
1236 static const VMFunction DeepCloneObjectLiteralInfo = |
|
1237 FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral); |
|
1238 |
|
1239 bool |
|
1240 CodeGenerator::visitCloneLiteral(LCloneLiteral *lir) |
|
1241 { |
|
1242 pushArg(ImmWord(js::MaybeSingletonObject)); |
|
1243 pushArg(ToRegister(lir->output())); |
|
1244 return callVM(DeepCloneObjectLiteralInfo, lir); |
|
1245 } |
|
1246 |
|
1247 bool |
|
1248 CodeGenerator::visitParameter(LParameter *lir) |
|
1249 { |
|
1250 return true; |
|
1251 } |
|
1252 |
|
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()); |
|
1259 |
|
1260 masm.loadPtr(ptr, callee); |
|
1261 return true; |
|
1262 } |
|
1263 |
|
1264 bool |
|
1265 CodeGenerator::visitStart(LStart *lir) |
|
1266 { |
|
1267 return true; |
|
1268 } |
|
1269 |
|
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 } |
|
1287 |
|
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()); |
|
1294 |
|
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 |
|
1303 |
|
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 } |
|
1310 |
|
1311 bool |
|
1312 CodeGenerator::visitOsrScopeChain(LOsrScopeChain *lir) |
|
1313 { |
|
1314 const LAllocation *frame = lir->getOperand(0); |
|
1315 const LDefinition *object = lir->getDef(0); |
|
1316 |
|
1317 const ptrdiff_t frameOffset = BaselineFrame::reverseOffsetOfScopeChain(); |
|
1318 |
|
1319 masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object)); |
|
1320 return true; |
|
1321 } |
|
1322 |
|
1323 bool |
|
1324 CodeGenerator::visitOsrArgumentsObject(LOsrArgumentsObject *lir) |
|
1325 { |
|
1326 const LAllocation *frame = lir->getOperand(0); |
|
1327 const LDefinition *object = lir->getDef(0); |
|
1328 |
|
1329 const ptrdiff_t frameOffset = BaselineFrame::reverseOffsetOfArgsObj(); |
|
1330 |
|
1331 masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object)); |
|
1332 return true; |
|
1333 } |
|
1334 |
|
1335 bool |
|
1336 CodeGenerator::visitOsrValue(LOsrValue *value) |
|
1337 { |
|
1338 const LAllocation *frame = value->getOperand(0); |
|
1339 const ValueOperand out = ToOutValue(value); |
|
1340 |
|
1341 const ptrdiff_t frameOffset = value->mir()->frameOffset(); |
|
1342 |
|
1343 masm.loadValue(Address(ToRegister(frame), frameOffset), out); |
|
1344 return true; |
|
1345 } |
|
1346 |
|
1347 bool |
|
1348 CodeGenerator::visitOsrReturnValue(LOsrReturnValue *lir) |
|
1349 { |
|
1350 const LAllocation *frame = lir->getOperand(0); |
|
1351 const ValueOperand out = ToOutValue(lir); |
|
1352 |
|
1353 Address flags = Address(ToRegister(frame), BaselineFrame::reverseOffsetOfFlags()); |
|
1354 Address retval = Address(ToRegister(frame), BaselineFrame::reverseOffsetOfReturnValue()); |
|
1355 |
|
1356 masm.moveValue(UndefinedValue(), out); |
|
1357 |
|
1358 Label done; |
|
1359 masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL), &done); |
|
1360 masm.loadValue(retval, out); |
|
1361 masm.bind(&done); |
|
1362 |
|
1363 return true; |
|
1364 } |
|
1365 |
|
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()); |
|
1373 |
|
1374 int32_t stack_offset = StackOffsetOfPassedArg(argslot); |
|
1375 Address dest(StackPointer, stack_offset); |
|
1376 |
|
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); |
|
1383 |
|
1384 uint32_t slot = StackOffsetToSlot(stack_offset); |
|
1385 JS_ASSERT(slot - 1u < graph.totalSlotCount()); |
|
1386 return pushedArgumentSlots_.append(slot); |
|
1387 } |
|
1388 |
|
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()); |
|
1395 |
|
1396 int32_t stack_offset = StackOffsetOfPassedArg(argslot); |
|
1397 |
|
1398 masm.storeValue(val, Address(StackPointer, stack_offset)); |
|
1399 |
|
1400 uint32_t slot = StackOffsetToSlot(stack_offset); |
|
1401 JS_ASSERT(slot - 1u < graph.totalSlotCount()); |
|
1402 return pushedArgumentSlots_.append(slot); |
|
1403 } |
|
1404 |
|
1405 bool |
|
1406 CodeGenerator::visitMoveGroup(LMoveGroup *group) |
|
1407 { |
|
1408 if (!group->numMoves()) |
|
1409 return true; |
|
1410 |
|
1411 MoveResolver &resolver = masm.moveResolver(); |
|
1412 |
|
1413 for (size_t i = 0; i < group->numMoves(); i++) { |
|
1414 const LMove &move = group->getMove(i); |
|
1415 |
|
1416 const LAllocation *from = move.from(); |
|
1417 const LAllocation *to = move.to(); |
|
1418 LDefinition::Type type = move.type(); |
|
1419 |
|
1420 // No bogus moves. |
|
1421 JS_ASSERT(*from != *to); |
|
1422 JS_ASSERT(!from->isConstant()); |
|
1423 |
|
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 } |
|
1440 |
|
1441 if (!resolver.addMove(toMoveOperand(from), toMoveOperand(to), moveType)) |
|
1442 return false; |
|
1443 } |
|
1444 |
|
1445 if (!resolver.resolve()) |
|
1446 return false; |
|
1447 |
|
1448 MoveEmitter emitter(masm); |
|
1449 emitter.emit(resolver); |
|
1450 emitter.finish(); |
|
1451 |
|
1452 return true; |
|
1453 } |
|
1454 |
|
1455 bool |
|
1456 CodeGenerator::visitInteger(LInteger *lir) |
|
1457 { |
|
1458 masm.move32(Imm32(lir->getValue()), ToRegister(lir->output())); |
|
1459 return true; |
|
1460 } |
|
1461 |
|
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 } |
|
1471 |
|
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 } |
|
1479 |
|
1480 bool |
|
1481 CodeGenerator::visitStoreSlotV(LStoreSlotV *store) |
|
1482 { |
|
1483 Register base = ToRegister(store->slots()); |
|
1484 int32_t offset = store->mir()->slot() * sizeof(Value); |
|
1485 |
|
1486 const ValueOperand value = ToValue(store, LStoreSlotV::Value); |
|
1487 |
|
1488 if (store->mir()->needsBarrier()) |
|
1489 emitPreBarrier(Address(base, offset), MIRType_Value); |
|
1490 |
|
1491 masm.storeValue(value, Address(base, offset)); |
|
1492 return true; |
|
1493 } |
|
1494 |
|
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); |
|
1501 |
|
1502 masm.loadObjShape(obj, scratch); |
|
1503 |
|
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); |
|
1508 |
|
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 } |
|
1520 |
|
1521 masm.jump(&done); |
|
1522 masm.bind(&next); |
|
1523 } |
|
1524 |
|
1525 // Bailout if no shape matches. |
|
1526 if (!bailout(ins->snapshot())) |
|
1527 return false; |
|
1528 |
|
1529 masm.bind(&done); |
|
1530 return true; |
|
1531 } |
|
1532 |
|
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 } |
|
1540 |
|
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 } |
|
1551 |
|
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); |
|
1558 |
|
1559 masm.loadObjShape(obj, scratch); |
|
1560 |
|
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); |
|
1565 |
|
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 } |
|
1581 |
|
1582 masm.jump(&done); |
|
1583 masm.bind(&next); |
|
1584 } |
|
1585 |
|
1586 // Bailout if no shape matches. |
|
1587 if (!bailout(ins->snapshot())) |
|
1588 return false; |
|
1589 |
|
1590 masm.bind(&done); |
|
1591 return true; |
|
1592 } |
|
1593 |
|
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 } |
|
1602 |
|
1603 bool |
|
1604 CodeGenerator::visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT *ins) |
|
1605 { |
|
1606 Register obj = ToRegister(ins->obj()); |
|
1607 Register temp = ToRegister(ins->temp()); |
|
1608 |
|
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())); |
|
1614 |
|
1615 return emitSetPropertyPolymorphic(ins, obj, temp, value); |
|
1616 } |
|
1617 |
|
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 } |
|
1625 |
|
1626 typedef bool (*ConvertElementsToDoublesFn)(JSContext *, uintptr_t); |
|
1627 static const VMFunction ConvertElementsToDoublesInfo = |
|
1628 FunctionInfo<ConvertElementsToDoublesFn>(ObjectElements::ConvertElementsToDoubles); |
|
1629 |
|
1630 bool |
|
1631 CodeGenerator::visitConvertElementsToDoubles(LConvertElementsToDoubles *lir) |
|
1632 { |
|
1633 Register elements = ToRegister(lir->elements()); |
|
1634 |
|
1635 OutOfLineCode *ool = oolCallVM(ConvertElementsToDoublesInfo, lir, |
|
1636 (ArgList(), elements), StoreNothing()); |
|
1637 if (!ool) |
|
1638 return false; |
|
1639 |
|
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 } |
|
1646 |
|
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); |
|
1653 |
|
1654 FloatRegister temp = ToFloatRegister(lir->tempFloat()); |
|
1655 Label convert, done; |
|
1656 |
|
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); |
|
1663 |
|
1664 masm.tagValue(JSVAL_TYPE_INT32, value, out); |
|
1665 masm.jump(&done); |
|
1666 |
|
1667 masm.bind(&convert); |
|
1668 masm.convertInt32ToDouble(value, temp); |
|
1669 masm.boxDouble(temp, out); |
|
1670 |
|
1671 masm.bind(&done); |
|
1672 return true; |
|
1673 } |
|
1674 |
|
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 } |
|
1682 |
|
1683 bool |
|
1684 CodeGenerator::visitForkJoinContext(LForkJoinContext *lir) |
|
1685 { |
|
1686 const Register tempReg = ToRegister(lir->getTempReg()); |
|
1687 |
|
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 } |
|
1693 |
|
1694 bool |
|
1695 CodeGenerator::visitGuardThreadExclusive(LGuardThreadExclusive *lir) |
|
1696 { |
|
1697 JS_ASSERT(gen->info().executionMode() == ParallelExecution); |
|
1698 |
|
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)); |
|
1704 |
|
1705 OutOfLineAbortPar *bail = oolAbortPar(ParallelBailoutIllegalWrite, lir); |
|
1706 if (!bail) |
|
1707 return false; |
|
1708 |
|
1709 // branch to the OOL failure code if false is returned |
|
1710 masm.branchIfFalseBool(ReturnReg, bail->entry()); |
|
1711 return true; |
|
1712 } |
|
1713 |
|
1714 bool |
|
1715 CodeGenerator::visitGuardObjectIdentity(LGuardObjectIdentity *guard) |
|
1716 { |
|
1717 Register obj = ToRegister(guard->input()); |
|
1718 |
|
1719 Assembler::Condition cond = |
|
1720 guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual; |
|
1721 return bailoutCmpPtr(cond, obj, ImmGCPtr(guard->mir()->singleObject()), guard->snapshot()); |
|
1722 } |
|
1723 |
|
1724 bool |
|
1725 CodeGenerator::visitTypeBarrierV(LTypeBarrierV *lir) |
|
1726 { |
|
1727 ValueOperand operand = ToValue(lir, LTypeBarrierV::Input); |
|
1728 Register scratch = ToTempRegisterOrInvalid(lir->temp()); |
|
1729 |
|
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 } |
|
1736 |
|
1737 bool |
|
1738 CodeGenerator::visitTypeBarrierO(LTypeBarrierO *lir) |
|
1739 { |
|
1740 Register obj = ToRegister(lir->object()); |
|
1741 Register scratch = ToTempRegisterOrInvalid(lir->temp()); |
|
1742 |
|
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 } |
|
1749 |
|
1750 bool |
|
1751 CodeGenerator::visitMonitorTypes(LMonitorTypes *lir) |
|
1752 { |
|
1753 ValueOperand operand = ToValue(lir, LMonitorTypes::Input); |
|
1754 Register scratch = ToTempUnboxRegister(lir->temp()); |
|
1755 |
|
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 } |
|
1762 |
|
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_; |
|
1769 |
|
1770 public: |
|
1771 OutOfLineCallPostWriteBarrier(LInstruction *lir, const LAllocation *object) |
|
1772 : lir_(lir), object_(object) |
|
1773 { } |
|
1774 |
|
1775 bool accept(CodeGenerator *codegen) { |
|
1776 return codegen->visitOutOfLineCallPostWriteBarrier(this); |
|
1777 } |
|
1778 |
|
1779 LInstruction *lir() const { |
|
1780 return lir_; |
|
1781 } |
|
1782 const LAllocation *object() const { |
|
1783 return object_; |
|
1784 } |
|
1785 }; |
|
1786 |
|
1787 bool |
|
1788 CodeGenerator::visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier *ool) |
|
1789 { |
|
1790 saveLiveVolatile(ool->lir()); |
|
1791 |
|
1792 const LAllocation *obj = ool->object(); |
|
1793 |
|
1794 GeneralRegisterSet regs = GeneralRegisterSet::Volatile(); |
|
1795 |
|
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 } |
|
1807 |
|
1808 Register runtimereg = regs.takeAny(); |
|
1809 masm.mov(ImmPtr(GetIonContext()->runtime), runtimereg); |
|
1810 |
|
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)); |
|
1816 |
|
1817 restoreLiveVolatile(ool->lir()); |
|
1818 |
|
1819 masm.jump(ool->rejoin()); |
|
1820 return true; |
|
1821 } |
|
1822 #endif |
|
1823 |
|
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; |
|
1831 |
|
1832 Register temp = ToTempRegisterOrInvalid(lir->temp()); |
|
1833 |
|
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 } |
|
1842 |
|
1843 masm.branchPtrInNurseryRange(ToRegister(lir->value()), temp, ool->entry()); |
|
1844 |
|
1845 masm.bind(ool->rejoin()); |
|
1846 #endif |
|
1847 return true; |
|
1848 } |
|
1849 |
|
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; |
|
1857 |
|
1858 Register temp = ToTempRegisterOrInvalid(lir->temp()); |
|
1859 |
|
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 } |
|
1868 |
|
1869 ValueOperand value = ToValue(lir, LPostWriteBarrierV::Input); |
|
1870 masm.branchValueIsNurseryObject(value, temp, ool->entry()); |
|
1871 |
|
1872 masm.bind(ool->rejoin()); |
|
1873 #endif |
|
1874 return true; |
|
1875 } |
|
1876 |
|
1877 bool |
|
1878 CodeGenerator::visitCallNative(LCallNative *call) |
|
1879 { |
|
1880 JSFunction *target = call->getSingleTarget(); |
|
1881 JS_ASSERT(target); |
|
1882 JS_ASSERT(target->isNative()); |
|
1883 |
|
1884 int callargslot = call->argslot(); |
|
1885 int unusedStack = StackOffsetOfPassedArg(callargslot); |
|
1886 |
|
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()); |
|
1891 |
|
1892 // Misc. temporary registers. |
|
1893 const Register tempReg = ToRegister(call->getTempReg()); |
|
1894 |
|
1895 DebugOnly<uint32_t> initialStack = masm.framePushed(); |
|
1896 |
|
1897 masm.checkStackAlignment(); |
|
1898 |
|
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. |
|
1905 |
|
1906 // Allocate space for the outparam, moving the StackPointer to what will be &vp[1]. |
|
1907 masm.adjustStack(unusedStack); |
|
1908 |
|
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)); |
|
1912 |
|
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); |
|
1922 |
|
1923 masm.Push(argUintNReg); |
|
1924 |
|
1925 // Construct native exit frame. |
|
1926 uint32_t safepointOffset; |
|
1927 if (!masm.buildFakeExitFrame(tempReg, &safepointOffset)) |
|
1928 return false; |
|
1929 masm.enterFakeExitFrame(argContextReg, tempReg, executionMode); |
|
1930 |
|
1931 if (!markSafepointAt(safepointOffset, call)) |
|
1932 return false; |
|
1933 |
|
1934 // Construct and execute call. |
|
1935 masm.setupUnalignedABICall(3, tempReg); |
|
1936 masm.passABIArg(argContextReg); |
|
1937 masm.passABIArg(argUintNReg); |
|
1938 masm.passABIArg(argVpReg); |
|
1939 |
|
1940 switch (executionMode) { |
|
1941 case SequentialExecution: |
|
1942 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->native())); |
|
1943 break; |
|
1944 |
|
1945 case ParallelExecution: |
|
1946 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->parallelNative())); |
|
1947 break; |
|
1948 |
|
1949 default: |
|
1950 MOZ_ASSUME_UNREACHABLE("No such execution mode"); |
|
1951 } |
|
1952 |
|
1953 // Test for failure. |
|
1954 masm.branchIfFalseBool(ReturnReg, masm.failureLabel(executionMode)); |
|
1955 |
|
1956 // Load the outparam vp[0] into output register(s). |
|
1957 masm.loadValue(Address(StackPointer, IonNativeExitFrameLayout::offsetOfResult()), JSReturnOperand); |
|
1958 |
|
1959 // The next instruction is removing the footer of the exit frame, so there |
|
1960 // is no need for leaveFakeExitFrame. |
|
1961 |
|
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); |
|
1965 |
|
1966 dropArguments(call->numStackArgs() + 1); |
|
1967 return true; |
|
1968 } |
|
1969 |
|
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()); |
|
1978 |
|
1979 int callargslot = call->argslot(); |
|
1980 int unusedStack = StackOffsetOfPassedArg(callargslot); |
|
1981 |
|
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()); |
|
1987 |
|
1988 DebugOnly<uint32_t> initialStack = masm.framePushed(); |
|
1989 |
|
1990 masm.checkStackAlignment(); |
|
1991 |
|
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. |
|
1998 |
|
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); |
|
2005 |
|
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)); |
|
2009 |
|
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); |
|
2017 |
|
2018 // GetReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate() |
|
2019 masm.loadPrivate(Address(obj, JSObject::getFixedSlotOffset(0)), argPrivate); |
|
2020 |
|
2021 // Push argc from the call instruction into what will become the IonExitFrame |
|
2022 masm.Push(Imm32(call->numStackArgs())); |
|
2023 |
|
2024 // Push our argv onto the stack |
|
2025 masm.Push(argArgs); |
|
2026 // And store our JSJitMethodCallArgs* in argArgs. |
|
2027 masm.movePtr(StackPointer, argArgs); |
|
2028 |
|
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); |
|
2034 |
|
2035 // Construct native exit frame. |
|
2036 uint32_t safepointOffset; |
|
2037 if (!masm.buildFakeExitFrame(argJSContext, &safepointOffset)) |
|
2038 return false; |
|
2039 masm.enterFakeExitFrame(ION_FRAME_DOMMETHOD); |
|
2040 |
|
2041 if (!markSafepointAt(safepointOffset, call)) |
|
2042 return false; |
|
2043 |
|
2044 // Construct and execute call. |
|
2045 masm.setupUnalignedABICall(4, argJSContext); |
|
2046 |
|
2047 masm.loadJSContext(argJSContext); |
|
2048 |
|
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)); |
|
2054 |
|
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()); |
|
2061 |
|
2062 // Load the outparam vp[0] into output register(s). |
|
2063 masm.loadValue(Address(StackPointer, IonDOMMethodExitFrameLayout::offsetOfResult()), |
|
2064 JSReturnOperand); |
|
2065 } |
|
2066 |
|
2067 // The next instruction is removing the footer of the exit frame, so there |
|
2068 // is no need for leaveFakeExitFrame. |
|
2069 |
|
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); |
|
2073 |
|
2074 dropArguments(call->numStackArgs() + 1); |
|
2075 return true; |
|
2076 } |
|
2077 |
|
2078 typedef bool (*GetIntrinsicValueFn)(JSContext *cx, HandlePropertyName, MutableHandleValue); |
|
2079 static const VMFunction GetIntrinsicValueInfo = |
|
2080 FunctionInfo<GetIntrinsicValueFn>(GetIntrinsicValue); |
|
2081 |
|
2082 bool |
|
2083 CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue *lir) |
|
2084 { |
|
2085 pushArg(ImmGCPtr(lir->mir()->name())); |
|
2086 return callVM(GetIntrinsicValueInfo, lir); |
|
2087 } |
|
2088 |
|
2089 typedef bool (*InvokeFunctionFn)(JSContext *, HandleObject, uint32_t, Value *, Value *); |
|
2090 static const VMFunction InvokeFunctionInfo = FunctionInfo<InvokeFunctionFn>(InvokeFunction); |
|
2091 |
|
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); |
|
2099 |
|
2100 pushArg(StackPointer); // argv. |
|
2101 pushArg(Imm32(argc)); // argc. |
|
2102 pushArg(calleereg); // JSFunction *. |
|
2103 |
|
2104 if (!callVM(InvokeFunctionInfo, call)) |
|
2105 return false; |
|
2106 |
|
2107 // Un-nestle %esp from the argument vector. No prefix was pushed. |
|
2108 masm.reserveStack(unusedStack); |
|
2109 return true; |
|
2110 } |
|
2111 |
|
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; |
|
2121 |
|
2122 // Known-target case is handled by LCallKnown. |
|
2123 JS_ASSERT(!call->hasSingleTarget()); |
|
2124 |
|
2125 // Generate an ArgumentsRectifier. |
|
2126 JitCode *argumentsRectifier = gen->jitRuntime()->getArgumentsRectifier(executionMode); |
|
2127 |
|
2128 masm.checkStackAlignment(); |
|
2129 |
|
2130 // Guard that calleereg is actually a function object. |
|
2131 masm.loadObjClass(calleereg, nargsreg); |
|
2132 masm.branchPtr(Assembler::NotEqual, nargsreg, ImmPtr(&JSFunction::class_), &invoke); |
|
2133 |
|
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); |
|
2140 |
|
2141 // Knowing that calleereg is a non-native function, load the JSScript. |
|
2142 masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg); |
|
2143 |
|
2144 // Load script jitcode. |
|
2145 masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &invoke); |
|
2146 |
|
2147 // Nestle the StackPointer up to the argument vector. |
|
2148 masm.freeStack(unusedStack); |
|
2149 |
|
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)); |
|
2155 |
|
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); |
|
2160 |
|
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 } |
|
2169 |
|
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; |
|
2175 |
|
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); |
|
2181 |
|
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; |
|
2189 |
|
2190 case ParallelExecution: |
|
2191 if (!emitCallToUncompiledScriptPar(call, calleereg)) |
|
2192 return false; |
|
2193 break; |
|
2194 |
|
2195 default: |
|
2196 MOZ_ASSUME_UNREACHABLE("No such execution mode"); |
|
2197 } |
|
2198 |
|
2199 masm.bind(&end); |
|
2200 |
|
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 } |
|
2209 |
|
2210 if (!checkForAbortPar(call)) |
|
2211 return false; |
|
2212 |
|
2213 dropArguments(call->numStackArgs() + 1); |
|
2214 return true; |
|
2215 } |
|
2216 |
|
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; |
|
2225 |
|
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 } |
|
2233 |
|
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; |
|
2243 |
|
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()); |
|
2248 |
|
2249 JS_ASSERT_IF(call->mir()->isConstructing(), target->isInterpretedConstructor()); |
|
2250 |
|
2251 masm.checkStackAlignment(); |
|
2252 |
|
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); |
|
2256 |
|
2257 // Knowing that calleereg is a non-native function, load the JSScript. |
|
2258 masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg); |
|
2259 |
|
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); |
|
2265 |
|
2266 // Nestle the StackPointer up to the argument vector. |
|
2267 masm.freeStack(unusedStack); |
|
2268 |
|
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)); |
|
2274 |
|
2275 // Finally call the function in objreg. |
|
2276 uint32_t callOffset = masm.callIon(objreg); |
|
2277 if (!markSafepointAt(callOffset, call)) |
|
2278 return false; |
|
2279 |
|
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); |
|
2285 |
|
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; |
|
2293 |
|
2294 case ParallelExecution: |
|
2295 if (!emitCallToUncompiledScriptPar(call, calleereg)) |
|
2296 return false; |
|
2297 break; |
|
2298 |
|
2299 default: |
|
2300 MOZ_ASSUME_UNREACHABLE("No such execution mode"); |
|
2301 } |
|
2302 |
|
2303 masm.bind(&end); |
|
2304 |
|
2305 if (!checkForAbortPar(call)) |
|
2306 return false; |
|
2307 |
|
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 } |
|
2316 |
|
2317 dropArguments(call->numStackArgs() + 1); |
|
2318 return true; |
|
2319 } |
|
2320 |
|
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 } |
|
2336 |
|
2337 bool |
|
2338 CodeGenerator::emitCallInvokeFunction(LApplyArgsGeneric *apply, Register extraStackSize) |
|
2339 { |
|
2340 Register objreg = ToRegister(apply->getTempObject()); |
|
2341 JS_ASSERT(objreg != extraStackSize); |
|
2342 |
|
2343 // Push the space used by the arguments. |
|
2344 masm.movePtr(StackPointer, objreg); |
|
2345 masm.Push(extraStackSize); |
|
2346 |
|
2347 pushArg(objreg); // argv. |
|
2348 pushArg(ToRegister(apply->getArgc())); // argc. |
|
2349 pushArg(ToRegister(apply->getFunction())); // JSFunction *. |
|
2350 |
|
2351 // This specialization og callVM restore the extraStackSize after the call. |
|
2352 if (!callVM(InvokeFunctionInfo, apply, &extraStackSize)) |
|
2353 return false; |
|
2354 |
|
2355 masm.Pop(extraStackSize); |
|
2356 return true; |
|
2357 } |
|
2358 |
|
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()); |
|
2366 |
|
2367 Register copyreg = ToRegister(apply->getTempObject()); |
|
2368 size_t argvOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs(); |
|
2369 Label end; |
|
2370 |
|
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); |
|
2374 |
|
2375 // Copy arguments. |
|
2376 { |
|
2377 Register count = extraStackSpace; // <- argcreg |
|
2378 Label loop; |
|
2379 masm.bind(&loop); |
|
2380 |
|
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*)); |
|
2384 |
|
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); |
|
2389 |
|
2390 // Handle 32 bits architectures. |
|
2391 if (sizeof(Value) == 2 * sizeof(void*)) { |
|
2392 masm.loadPtr(disp, copyreg); |
|
2393 masm.push(copyreg); |
|
2394 } |
|
2395 |
|
2396 masm.decBranchPtr(Assembler::NonZero, count, Imm32(1), &loop); |
|
2397 } |
|
2398 |
|
2399 // Compute the stack usage. |
|
2400 masm.movePtr(argcreg, extraStackSpace); |
|
2401 masm.lshiftPtr(Imm32::ShiftOf(ScaleFromElemWidth(sizeof(Value))), extraStackSpace); |
|
2402 |
|
2403 // Join with all arguments copied and the extra stack usage computed. |
|
2404 masm.bind(&end); |
|
2405 |
|
2406 // Push |this|. |
|
2407 masm.addPtr(Imm32(sizeof(Value)), extraStackSpace); |
|
2408 masm.pushValue(ToValue(apply, LApplyArgsGeneric::ThisIndex)); |
|
2409 } |
|
2410 |
|
2411 void |
|
2412 CodeGenerator::emitPopArguments(LApplyArgsGeneric *apply, Register extraStackSpace) |
|
2413 { |
|
2414 // Pop |this| and Arguments. |
|
2415 masm.freeStack(extraStackSpace); |
|
2416 } |
|
2417 |
|
2418 bool |
|
2419 CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric *apply) |
|
2420 { |
|
2421 // Holds the function object. |
|
2422 Register calleereg = ToRegister(apply->getFunction()); |
|
2423 |
|
2424 // Temporary register for modifying the function object. |
|
2425 Register objreg = ToRegister(apply->getTempObject()); |
|
2426 Register copyreg = ToRegister(apply->getTempCopy()); |
|
2427 |
|
2428 // Holds the function nargs. Initially undefined. |
|
2429 Register argcreg = ToRegister(apply->getArgc()); |
|
2430 |
|
2431 // Unless already known, guard that calleereg is actually a function object. |
|
2432 if (!apply->hasSingleTarget()) { |
|
2433 masm.loadObjClass(calleereg, objreg); |
|
2434 |
|
2435 ImmPtr ptr = ImmPtr(&JSFunction::class_); |
|
2436 if (!bailoutCmpPtr(Assembler::NotEqual, objreg, ptr, apply->snapshot())) |
|
2437 return false; |
|
2438 } |
|
2439 |
|
2440 // Copy the arguments of the current function. |
|
2441 emitPushArguments(apply, copyreg); |
|
2442 |
|
2443 masm.checkStackAlignment(); |
|
2444 |
|
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 } |
|
2456 |
|
2457 Label end, invoke; |
|
2458 |
|
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 } |
|
2466 |
|
2467 // Knowing that calleereg is a non-native function, load the JSScript. |
|
2468 masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg); |
|
2469 |
|
2470 // Load script jitcode. |
|
2471 masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &invoke); |
|
2472 |
|
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); |
|
2479 |
|
2480 masm.Push(argcreg); |
|
2481 masm.Push(calleereg); |
|
2482 masm.Push(copyreg); // descriptor |
|
2483 |
|
2484 Label underflow, rejoin; |
|
2485 |
|
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 } |
|
2494 |
|
2495 // Skip the construction of the rectifier frame because we have no |
|
2496 // underflow. |
|
2497 masm.jump(&rejoin); |
|
2498 |
|
2499 // Argument fixup needed. Get ready to call the argumentsRectifier. |
|
2500 { |
|
2501 masm.bind(&underflow); |
|
2502 |
|
2503 // Hardcode the address of the argumentsRectifier code. |
|
2504 JitCode *argumentsRectifier = gen->jitRuntime()->getArgumentsRectifier(executionMode); |
|
2505 |
|
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 } |
|
2511 |
|
2512 masm.bind(&rejoin); |
|
2513 |
|
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; |
|
2518 |
|
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); |
|
2523 |
|
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 } |
|
2530 |
|
2531 // Handle uncompiled or native functions. |
|
2532 { |
|
2533 masm.bind(&invoke); |
|
2534 if (!emitCallInvokeFunction(apply, copyreg)) |
|
2535 return false; |
|
2536 } |
|
2537 |
|
2538 // Pop arguments and continue. |
|
2539 masm.bind(&end); |
|
2540 emitPopArguments(apply, copyreg); |
|
2541 |
|
2542 return true; |
|
2543 } |
|
2544 |
|
2545 typedef bool (*ArraySpliceDenseFn)(JSContext *, HandleObject, uint32_t, uint32_t); |
|
2546 static const VMFunction ArraySpliceDenseInfo = FunctionInfo<ArraySpliceDenseFn>(ArraySpliceDense); |
|
2547 |
|
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 } |
|
2556 |
|
2557 |
|
2558 bool |
|
2559 CodeGenerator::visitBail(LBail *lir) |
|
2560 { |
|
2561 return bailout(lir->snapshot()); |
|
2562 } |
|
2563 |
|
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()); |
|
2572 |
|
2573 masm.loadJSContext(temp3); |
|
2574 |
|
2575 /* Make space for the outparam. */ |
|
2576 masm.adjustStack(-int32_t(sizeof(Value))); |
|
2577 masm.movePtr(StackPointer, temp2); |
|
2578 |
|
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)); |
|
2585 |
|
2586 const ValueOperand out = ToOutValue(lir); |
|
2587 |
|
2588 masm.loadValue(Address(StackPointer, 0), out); |
|
2589 masm.adjustStack(sizeof(Value)); |
|
2590 |
|
2591 Label undefined; |
|
2592 masm.branchTestUndefined(Assembler::Equal, out, &undefined); |
|
2593 return bailoutFrom(&undefined, lir->snapshot()); |
|
2594 } |
|
2595 |
|
2596 bool |
|
2597 CodeGenerator::emitFilterArgumentsOrEval(LInstruction *lir, Register string, |
|
2598 Register temp1, Register temp2) |
|
2599 { |
|
2600 masm.loadJSContext(temp2); |
|
2601 |
|
2602 masm.setupUnalignedABICall(2, temp1); |
|
2603 masm.passABIArg(temp2); |
|
2604 masm.passABIArg(string); |
|
2605 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, FilterArgumentsOrEval)); |
|
2606 |
|
2607 Label bail; |
|
2608 masm.branchIfFalseBool(ReturnReg, &bail); |
|
2609 return bailoutFrom(&bail, lir->snapshot()); |
|
2610 } |
|
2611 |
|
2612 bool |
|
2613 CodeGenerator::visitFilterArgumentsOrEvalS(LFilterArgumentsOrEvalS *lir) |
|
2614 { |
|
2615 return emitFilterArgumentsOrEval(lir, ToRegister(lir->getString()), |
|
2616 ToRegister(lir->temp1()), |
|
2617 ToRegister(lir->temp2())); |
|
2618 } |
|
2619 |
|
2620 bool |
|
2621 CodeGenerator::visitFilterArgumentsOrEvalV(LFilterArgumentsOrEvalV *lir) |
|
2622 { |
|
2623 ValueOperand input = ToValue(lir, LFilterArgumentsOrEvalV::Input); |
|
2624 |
|
2625 // Act as nop on non-strings. |
|
2626 Label done; |
|
2627 masm.branchTestString(Assembler::NotEqual, input, &done); |
|
2628 |
|
2629 if (!emitFilterArgumentsOrEval(lir, masm.extractString(input, ToRegister(lir->temp3())), |
|
2630 ToRegister(lir->temp1()), ToRegister(lir->temp2()))) |
|
2631 { |
|
2632 return false; |
|
2633 } |
|
2634 |
|
2635 masm.bind(&done); |
|
2636 return true; |
|
2637 } |
|
2638 |
|
2639 typedef bool (*DirectEvalSFn)(JSContext *, HandleObject, HandleScript, HandleValue, HandleString, |
|
2640 jsbytecode *, MutableHandleValue); |
|
2641 static const VMFunction DirectEvalStringInfo = FunctionInfo<DirectEvalSFn>(DirectEvalStringFromIon); |
|
2642 |
|
2643 bool |
|
2644 CodeGenerator::visitCallDirectEvalS(LCallDirectEvalS *lir) |
|
2645 { |
|
2646 Register scopeChain = ToRegister(lir->getScopeChain()); |
|
2647 Register string = ToRegister(lir->getString()); |
|
2648 |
|
2649 pushArg(ImmPtr(lir->mir()->pc())); |
|
2650 pushArg(string); |
|
2651 pushArg(ToValue(lir, LCallDirectEvalS::ThisValue)); |
|
2652 pushArg(ImmGCPtr(gen->info().script())); |
|
2653 pushArg(scopeChain); |
|
2654 |
|
2655 return callVM(DirectEvalStringInfo, lir); |
|
2656 } |
|
2657 |
|
2658 typedef bool (*DirectEvalVFn)(JSContext *, HandleObject, HandleScript, HandleValue, HandleValue, |
|
2659 jsbytecode *, MutableHandleValue); |
|
2660 static const VMFunction DirectEvalValueInfo = FunctionInfo<DirectEvalVFn>(DirectEvalValueFromIon); |
|
2661 |
|
2662 bool |
|
2663 CodeGenerator::visitCallDirectEvalV(LCallDirectEvalV *lir) |
|
2664 { |
|
2665 Register scopeChain = ToRegister(lir->getScopeChain()); |
|
2666 |
|
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); |
|
2672 |
|
2673 return callVM(DirectEvalValueInfo, lir); |
|
2674 } |
|
2675 |
|
2676 // Registers safe for use before generatePrologue(). |
|
2677 static const uint32_t EntryTempMask = Registers::TempMask & ~(1 << OsrFrameReg.code()); |
|
2678 |
|
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. |
|
2686 |
|
2687 MIRGraph &mir = gen->graph(); |
|
2688 MResumePoint *rp = mir.entryResumePoint(); |
|
2689 |
|
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()); |
|
2694 |
|
2695 // No registers are allocated yet, so it's safe to grab anything. |
|
2696 Register temp = GeneralRegisterSet(EntryTempMask).getAny(); |
|
2697 |
|
2698 CompileInfo &info = gen->info(); |
|
2699 |
|
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; |
|
2707 |
|
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 } |
|
2715 |
|
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 } |
|
2728 |
|
2729 masm.freeStack(frameSize()); |
|
2730 |
|
2731 return true; |
|
2732 } |
|
2733 |
|
2734 // Out-of-line path to report over-recursed error and fail. |
|
2735 class CheckOverRecursedFailure : public OutOfLineCodeBase<CodeGenerator> |
|
2736 { |
|
2737 LCheckOverRecursed *lir_; |
|
2738 |
|
2739 public: |
|
2740 CheckOverRecursedFailure(LCheckOverRecursed *lir) |
|
2741 : lir_(lir) |
|
2742 { } |
|
2743 |
|
2744 bool accept(CodeGenerator *codegen) { |
|
2745 return codegen->visitCheckOverRecursedFailure(this); |
|
2746 } |
|
2747 |
|
2748 LCheckOverRecursed *lir() const { |
|
2749 return lir_; |
|
2750 } |
|
2751 }; |
|
2752 |
|
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; |
|
2759 |
|
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. |
|
2768 |
|
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(); |
|
2772 |
|
2773 CheckOverRecursedFailure *ool = new(alloc()) CheckOverRecursedFailure(lir); |
|
2774 if (!addOutOfLineCode(ool)) |
|
2775 return false; |
|
2776 |
|
2777 // Conditional forward (unlikely) branch to failure. |
|
2778 masm.branchPtr(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr), StackPointer, ool->entry()); |
|
2779 masm.bind(ool->rejoin()); |
|
2780 |
|
2781 return true; |
|
2782 } |
|
2783 |
|
2784 typedef bool (*DefVarOrConstFn)(JSContext *, HandlePropertyName, unsigned, HandleObject); |
|
2785 static const VMFunction DefVarOrConstInfo = |
|
2786 FunctionInfo<DefVarOrConstFn>(DefVarOrConst); |
|
2787 |
|
2788 bool |
|
2789 CodeGenerator::visitDefVar(LDefVar *lir) |
|
2790 { |
|
2791 Register scopeChain = ToRegister(lir->scopeChain()); |
|
2792 |
|
2793 pushArg(scopeChain); // JSObject * |
|
2794 pushArg(Imm32(lir->mir()->attrs())); // unsigned |
|
2795 pushArg(ImmGCPtr(lir->mir()->name())); // PropertyName * |
|
2796 |
|
2797 if (!callVM(DefVarOrConstInfo, lir)) |
|
2798 return false; |
|
2799 |
|
2800 return true; |
|
2801 } |
|
2802 |
|
2803 typedef bool (*DefFunOperationFn)(JSContext *, HandleScript, HandleObject, HandleFunction); |
|
2804 static const VMFunction DefFunOperationInfo = FunctionInfo<DefFunOperationFn>(DefFunOperation); |
|
2805 |
|
2806 bool |
|
2807 CodeGenerator::visitDefFun(LDefFun *lir) |
|
2808 { |
|
2809 Register scopeChain = ToRegister(lir->scopeChain()); |
|
2810 |
|
2811 pushArg(ImmGCPtr(lir->mir()->fun())); |
|
2812 pushArg(scopeChain); |
|
2813 pushArg(ImmGCPtr(current->mir()->info().script())); |
|
2814 |
|
2815 return callVM(DefFunOperationInfo, lir); |
|
2816 } |
|
2817 |
|
2818 typedef bool (*ReportOverRecursedFn)(JSContext *); |
|
2819 static const VMFunction CheckOverRecursedInfo = |
|
2820 FunctionInfo<ReportOverRecursedFn>(CheckOverRecursed); |
|
2821 |
|
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. |
|
2827 |
|
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()); |
|
2832 |
|
2833 if (!callVM(CheckOverRecursedInfo, ool->lir())) |
|
2834 return false; |
|
2835 |
|
2836 restoreLive(ool->lir()); |
|
2837 masm.jump(ool->rejoin()); |
|
2838 return true; |
|
2839 } |
|
2840 |
|
2841 // Out-of-line path to report over-recursed error and fail. |
|
2842 class CheckOverRecursedFailurePar : public OutOfLineCodeBase<CodeGenerator> |
|
2843 { |
|
2844 LCheckOverRecursedPar *lir_; |
|
2845 |
|
2846 public: |
|
2847 CheckOverRecursedFailurePar(LCheckOverRecursedPar *lir) |
|
2848 : lir_(lir) |
|
2849 { } |
|
2850 |
|
2851 bool accept(CodeGenerator *codegen) { |
|
2852 return codegen->visitCheckOverRecursedFailurePar(this); |
|
2853 } |
|
2854 |
|
2855 LCheckOverRecursedPar *lir() const { |
|
2856 return lir_; |
|
2857 } |
|
2858 }; |
|
2859 |
|
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. |
|
2869 |
|
2870 Register cxReg = ToRegister(lir->forkJoinContext()); |
|
2871 Register tempReg = ToRegister(lir->getTempReg()); |
|
2872 |
|
2873 masm.loadPtr(Address(cxReg, offsetof(ForkJoinContext, perThreadData)), tempReg); |
|
2874 masm.loadPtr(Address(tempReg, offsetof(PerThreadData, jitStackLimit)), tempReg); |
|
2875 |
|
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()); |
|
2883 |
|
2884 return true; |
|
2885 } |
|
2886 |
|
2887 bool |
|
2888 CodeGenerator::visitCheckOverRecursedFailurePar(CheckOverRecursedFailurePar *ool) |
|
2889 { |
|
2890 OutOfLinePropagateAbortPar *bail = oolPropagateAbortPar(ool->lir()); |
|
2891 if (!bail) |
|
2892 return false; |
|
2893 |
|
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); |
|
2901 |
|
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()); |
|
2911 |
|
2912 return true; |
|
2913 } |
|
2914 |
|
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; |
|
2920 |
|
2921 OutOfLineInterruptCheckPar(LInterruptCheckPar *lir) |
|
2922 : lir(lir) |
|
2923 { } |
|
2924 |
|
2925 bool accept(CodeGenerator *codegen) { |
|
2926 return codegen->visitOutOfLineInterruptCheckPar(this); |
|
2927 } |
|
2928 }; |
|
2929 |
|
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; |
|
2937 |
|
2938 Register tempReg = ToRegister(lir->getTempReg()); |
|
2939 masm.checkInterruptFlagPar(tempReg, ool->entry()); |
|
2940 masm.bind(ool->rejoin()); |
|
2941 return true; |
|
2942 } |
|
2943 |
|
2944 bool |
|
2945 CodeGenerator::visitOutOfLineInterruptCheckPar(OutOfLineInterruptCheckPar *ool) |
|
2946 { |
|
2947 OutOfLinePropagateAbortPar *bail = oolPropagateAbortPar(ool->lir); |
|
2948 if (!bail) |
|
2949 return false; |
|
2950 |
|
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); |
|
2958 |
|
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()); |
|
2968 |
|
2969 return true; |
|
2970 } |
|
2971 |
|
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; |
|
2980 |
|
2981 CompileInfo *outerInfo = &gen->info(); |
|
2982 JSScript *script = outerInfo->script(); |
|
2983 if (!script) |
|
2984 return nullptr; |
|
2985 |
|
2986 if (!script->hasScriptCounts() && !script->initScriptCounts(cx)) |
|
2987 return nullptr; |
|
2988 |
|
2989 IonScriptCounts *counts = js_new<IonScriptCounts>(); |
|
2990 if (!counts || !counts->init(graph.numBlocks())) { |
|
2991 js_delete(counts); |
|
2992 return nullptr; |
|
2993 } |
|
2994 |
|
2995 for (size_t i = 0; i < graph.numBlocks(); i++) { |
|
2996 MBasicBlock *block = graph.getBlock(i)->mir(); |
|
2997 |
|
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(); |
|
3004 |
|
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 } |
|
3010 |
|
3011 for (size_t j = 0; j < block->numSuccessors(); j++) |
|
3012 counts->block(i).setSuccessor(j, block->getSuccessor(j)->id()); |
|
3013 } |
|
3014 |
|
3015 script->addIonCounts(counts); |
|
3016 return counts; |
|
3017 } |
|
3018 |
|
3019 // Structure for managing the state tracked for a block by script counters. |
|
3020 struct ScriptCountBlockState |
|
3021 { |
|
3022 IonBlockCounts █ |
|
3023 MacroAssembler &masm; |
|
3024 |
|
3025 Sprinter printer; |
|
3026 |
|
3027 public: |
|
3028 ScriptCountBlockState(IonBlockCounts *block, MacroAssembler *masm) |
|
3029 : block(*block), masm(*masm), printer(GetIonContext()->cx) |
|
3030 { |
|
3031 } |
|
3032 |
|
3033 bool init() |
|
3034 { |
|
3035 if (!printer.init()) |
|
3036 return false; |
|
3037 |
|
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())); |
|
3042 |
|
3043 // Collect human readable assembly for the code generated in the block. |
|
3044 masm.setPrinter(&printer); |
|
3045 |
|
3046 return true; |
|
3047 } |
|
3048 |
|
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 } |
|
3058 |
|
3059 ~ScriptCountBlockState() |
|
3060 { |
|
3061 masm.setPrinter(nullptr); |
|
3062 |
|
3063 block.setCode(printer.string()); |
|
3064 } |
|
3065 }; |
|
3066 |
|
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; |
|
3074 |
|
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 } |
|
3082 |
|
3083 bool |
|
3084 CodeGenerator::emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mir) |
|
3085 { |
|
3086 if (lir->numDefs() == 0) |
|
3087 return true; |
|
3088 |
|
3089 JS_ASSERT(lir->numDefs() == 1); |
|
3090 Register output = ToRegister(lir->getDef(0)); |
|
3091 |
|
3092 GeneralRegisterSet regs(GeneralRegisterSet::All()); |
|
3093 regs.take(output); |
|
3094 |
|
3095 Register temp = regs.takeAny(); |
|
3096 masm.push(temp); |
|
3097 |
|
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; |
|
3103 |
|
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); |
|
3115 |
|
3116 masm.bind(&miss); |
|
3117 masm.assumeUnreachable("MIR instruction returned object with unexpected type"); |
|
3118 |
|
3119 masm.bind(&ok); |
|
3120 } |
|
3121 |
|
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 } |
|
3134 |
|
3135 masm.bind(&done); |
|
3136 masm.pop(temp); |
|
3137 return true; |
|
3138 } |
|
3139 |
|
3140 bool |
|
3141 CodeGenerator::emitValueResultChecks(LInstruction *lir, MDefinition *mir) |
|
3142 { |
|
3143 if (lir->numDefs() == 0) |
|
3144 return true; |
|
3145 |
|
3146 JS_ASSERT(lir->numDefs() == BOX_PIECES); |
|
3147 if (!lir->getDef(0)->output()->isRegister()) |
|
3148 return true; |
|
3149 |
|
3150 ValueOperand output = ToOutValue(lir); |
|
3151 |
|
3152 GeneralRegisterSet regs(GeneralRegisterSet::All()); |
|
3153 regs.take(output); |
|
3154 |
|
3155 Register temp1 = regs.takeAny(); |
|
3156 Register temp2 = regs.takeAny(); |
|
3157 masm.push(temp1); |
|
3158 masm.push(temp2); |
|
3159 |
|
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; |
|
3165 |
|
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); |
|
3171 |
|
3172 masm.bind(&miss); |
|
3173 masm.assumeUnreachable("MIR instruction returned value with unexpected type"); |
|
3174 |
|
3175 masm.bind(&ok); |
|
3176 } |
|
3177 |
|
3178 // Check that we have a valid GC pointer. |
|
3179 if (gen->info().executionMode() != ParallelExecution) { |
|
3180 saveVolatile(); |
|
3181 |
|
3182 masm.pushValue(output); |
|
3183 masm.movePtr(StackPointer, temp1); |
|
3184 |
|
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 } |
|
3193 |
|
3194 masm.bind(&done); |
|
3195 masm.pop(temp2); |
|
3196 masm.pop(temp1); |
|
3197 return true; |
|
3198 } |
|
3199 |
|
3200 bool |
|
3201 CodeGenerator::emitDebugResultChecks(LInstruction *ins) |
|
3202 { |
|
3203 // In debug builds, check that LIR instructions return valid values. |
|
3204 |
|
3205 MDefinition *mir = ins->mirRaw(); |
|
3206 if (!mir) |
|
3207 return true; |
|
3208 |
|
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 |
|
3220 |
|
3221 bool |
|
3222 CodeGenerator::generateBody() |
|
3223 { |
|
3224 IonScriptCounts *counts = maybeCreateScriptCounts(); |
|
3225 |
|
3226 #if defined(JS_ION_PERF) |
|
3227 PerfSpewer *perfSpewer = &perfSpewer_; |
|
3228 if (gen->compilingAsmJS()) |
|
3229 perfSpewer = &gen->perfSpewer(); |
|
3230 #endif |
|
3231 |
|
3232 for (size_t i = 0; i < graph.numBlocks(); i++) { |
|
3233 current = graph.getBlock(i); |
|
3234 masm.bind(current->label()); |
|
3235 |
|
3236 mozilla::Maybe<ScriptCountBlockState> blockCounts; |
|
3237 if (counts) { |
|
3238 blockCounts.construct(&counts->block(i), &masm); |
|
3239 if (!blockCounts.ref().init()) |
|
3240 return false; |
|
3241 } |
|
3242 |
|
3243 #if defined(JS_ION_PERF) |
|
3244 perfSpewer->startBasicBlock(current->mir(), masm); |
|
3245 #endif |
|
3246 |
|
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); |
|
3254 |
|
3255 if (counts) |
|
3256 blockCounts.ref().visitInstruction(*iter); |
|
3257 |
|
3258 if (iter->safepoint() && pushedArgumentSlots_.length()) { |
|
3259 if (!markArgumentSlots(iter->safepoint())) |
|
3260 return false; |
|
3261 } |
|
3262 |
|
3263 #ifdef CHECK_OSIPOINT_REGISTERS |
|
3264 if (iter->safepoint()) |
|
3265 resetOsiPointRegs(iter->safepoint()); |
|
3266 #endif |
|
3267 |
|
3268 if (!callTraceLIR(i, *iter)) |
|
3269 return false; |
|
3270 |
|
3271 if (!iter->accept(this)) |
|
3272 return false; |
|
3273 |
|
3274 #ifdef DEBUG |
|
3275 if (!emitDebugResultChecks(*iter)) |
|
3276 return false; |
|
3277 #endif |
|
3278 } |
|
3279 if (masm.oom()) |
|
3280 return false; |
|
3281 |
|
3282 #if defined(JS_ION_PERF) |
|
3283 perfSpewer->endBasicBlock(masm); |
|
3284 #endif |
|
3285 } |
|
3286 |
|
3287 JS_ASSERT(pushedArgumentSlots_.empty()); |
|
3288 return true; |
|
3289 } |
|
3290 |
|
3291 // Out-of-line object allocation for LNewArray. |
|
3292 class OutOfLineNewArray : public OutOfLineCodeBase<CodeGenerator> |
|
3293 { |
|
3294 LNewArray *lir_; |
|
3295 |
|
3296 public: |
|
3297 OutOfLineNewArray(LNewArray *lir) |
|
3298 : lir_(lir) |
|
3299 { } |
|
3300 |
|
3301 bool accept(CodeGenerator *codegen) { |
|
3302 return codegen->visitOutOfLineNewArray(this); |
|
3303 } |
|
3304 |
|
3305 LNewArray *lir() const { |
|
3306 return lir_; |
|
3307 } |
|
3308 }; |
|
3309 |
|
3310 typedef JSObject *(*NewInitArrayFn)(JSContext *, uint32_t, types::TypeObject *); |
|
3311 static const VMFunction NewInitArrayInfo = |
|
3312 FunctionInfo<NewInitArrayFn>(NewInitArray); |
|
3313 |
|
3314 bool |
|
3315 CodeGenerator::visitNewArrayCallVM(LNewArray *lir) |
|
3316 { |
|
3317 JS_ASSERT(gen->info().executionMode() == SequentialExecution); |
|
3318 |
|
3319 Register objReg = ToRegister(lir->output()); |
|
3320 |
|
3321 JS_ASSERT(!lir->isCall()); |
|
3322 saveLive(lir); |
|
3323 |
|
3324 JSObject *templateObject = lir->mir()->templateObject(); |
|
3325 types::TypeObject *type = |
|
3326 templateObject->hasSingletonType() ? nullptr : templateObject->type(); |
|
3327 |
|
3328 pushArg(ImmGCPtr(type)); |
|
3329 pushArg(Imm32(lir->mir()->count())); |
|
3330 |
|
3331 if (!callVM(NewInitArrayInfo, lir)) |
|
3332 return false; |
|
3333 |
|
3334 if (ReturnReg != objReg) |
|
3335 masm.movePtr(ReturnReg, objReg); |
|
3336 |
|
3337 restoreLive(lir); |
|
3338 |
|
3339 return true; |
|
3340 } |
|
3341 |
|
3342 typedef JSObject *(*NewDerivedTypedObjectFn)(JSContext *, |
|
3343 HandleObject type, |
|
3344 HandleObject owner, |
|
3345 int32_t offset); |
|
3346 static const VMFunction CreateDerivedTypedObjInfo = |
|
3347 FunctionInfo<NewDerivedTypedObjectFn>(CreateDerivedTypedObj); |
|
3348 |
|
3349 bool |
|
3350 CodeGenerator::visitNewDerivedTypedObject(LNewDerivedTypedObject *lir) |
|
3351 { |
|
3352 // Not yet made safe for par exec: |
|
3353 JS_ASSERT(gen->info().executionMode() == SequentialExecution); |
|
3354 |
|
3355 pushArg(ToRegister(lir->offset())); |
|
3356 pushArg(ToRegister(lir->owner())); |
|
3357 pushArg(ToRegister(lir->type())); |
|
3358 return callVM(CreateDerivedTypedObjInfo, lir); |
|
3359 } |
|
3360 |
|
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()); |
|
3368 |
|
3369 masm.mov(ImmPtr(GetIonContext()->runtime), temp1); |
|
3370 masm.mov(ImmWord(lir->mir()->nslots()), temp2); |
|
3371 |
|
3372 masm.setupUnalignedABICall(2, temp3); |
|
3373 masm.passABIArg(temp1); |
|
3374 masm.passABIArg(temp2); |
|
3375 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NewSlots)); |
|
3376 |
|
3377 if (!bailoutTestPtr(Assembler::Zero, output, output, lir->snapshot())) |
|
3378 return false; |
|
3379 |
|
3380 return true; |
|
3381 } |
|
3382 |
|
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()); |
|
3388 |
|
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); |
|
3393 |
|
3394 JS_ASSERT(ToFloatRegister(lir->output()) == ReturnFloatReg); |
|
3395 return true; |
|
3396 } |
|
3397 |
|
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()); |
|
3403 |
|
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); |
|
3408 |
|
3409 JS_ASSERT(ToFloatRegister(lir->output()) == ReturnFloatReg); |
|
3410 return true; |
|
3411 } |
|
3412 |
|
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(); |
|
3421 |
|
3422 JS_ASSERT(count < JSObject::NELEMENTS_LIMIT); |
|
3423 |
|
3424 if (lir->mir()->shouldUseVM()) |
|
3425 return visitNewArrayCallVM(lir); |
|
3426 |
|
3427 OutOfLineNewArray *ool = new(alloc()) OutOfLineNewArray(lir); |
|
3428 if (!addOutOfLineCode(ool)) |
|
3429 return false; |
|
3430 |
|
3431 masm.newGCThing(objReg, tempReg, templateObject, ool->entry(), lir->mir()->initialHeap()); |
|
3432 masm.initGCThing(objReg, tempReg, templateObject); |
|
3433 |
|
3434 masm.bind(ool->rejoin()); |
|
3435 return true; |
|
3436 } |
|
3437 |
|
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 } |
|
3446 |
|
3447 // Out-of-line object allocation for JSOP_NEWOBJECT. |
|
3448 class OutOfLineNewObject : public OutOfLineCodeBase<CodeGenerator> |
|
3449 { |
|
3450 LNewObject *lir_; |
|
3451 |
|
3452 public: |
|
3453 OutOfLineNewObject(LNewObject *lir) |
|
3454 : lir_(lir) |
|
3455 { } |
|
3456 |
|
3457 bool accept(CodeGenerator *codegen) { |
|
3458 return codegen->visitOutOfLineNewObject(this); |
|
3459 } |
|
3460 |
|
3461 LNewObject *lir() const { |
|
3462 return lir_; |
|
3463 } |
|
3464 }; |
|
3465 |
|
3466 typedef JSObject *(*NewInitObjectFn)(JSContext *, HandleObject); |
|
3467 static const VMFunction NewInitObjectInfo = FunctionInfo<NewInitObjectFn>(NewInitObject); |
|
3468 |
|
3469 typedef JSObject *(*NewInitObjectWithClassPrototypeFn)(JSContext *, HandleObject); |
|
3470 static const VMFunction NewInitObjectWithClassPrototypeInfo = |
|
3471 FunctionInfo<NewInitObjectWithClassPrototypeFn>(NewInitObjectWithClassPrototype); |
|
3472 |
|
3473 bool |
|
3474 CodeGenerator::visitNewObjectVMCall(LNewObject *lir) |
|
3475 { |
|
3476 JS_ASSERT(gen->info().executionMode() == SequentialExecution); |
|
3477 |
|
3478 Register objReg = ToRegister(lir->output()); |
|
3479 |
|
3480 JS_ASSERT(!lir->isCall()); |
|
3481 saveLive(lir); |
|
3482 |
|
3483 pushArg(ImmGCPtr(lir->mir()->templateObject())); |
|
3484 |
|
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 } |
|
3495 |
|
3496 if (ReturnReg != objReg) |
|
3497 masm.movePtr(ReturnReg, objReg); |
|
3498 |
|
3499 restoreLive(lir); |
|
3500 return true; |
|
3501 } |
|
3502 |
|
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(); |
|
3510 |
|
3511 if (lir->mir()->shouldUseVM()) |
|
3512 return visitNewObjectVMCall(lir); |
|
3513 |
|
3514 OutOfLineNewObject *ool = new(alloc()) OutOfLineNewObject(lir); |
|
3515 if (!addOutOfLineCode(ool)) |
|
3516 return false; |
|
3517 |
|
3518 masm.newGCThing(objReg, tempReg, templateObject, ool->entry(), lir->mir()->initialHeap()); |
|
3519 masm.initGCThing(objReg, tempReg, templateObject); |
|
3520 |
|
3521 masm.bind(ool->rejoin()); |
|
3522 return true; |
|
3523 } |
|
3524 |
|
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 } |
|
3533 |
|
3534 typedef js::DeclEnvObject *(*NewDeclEnvObjectFn)(JSContext *, HandleFunction, gc::InitialHeap); |
|
3535 static const VMFunction NewDeclEnvObjectInfo = |
|
3536 FunctionInfo<NewDeclEnvObjectFn>(DeclEnvObject::createTemplateObject); |
|
3537 |
|
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(); |
|
3545 |
|
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; |
|
3553 |
|
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 } |
|
3559 |
|
3560 typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleShape, HandleTypeObject, HeapSlot *); |
|
3561 static const VMFunction NewCallObjectInfo = |
|
3562 FunctionInfo<NewCallObjectFn>(NewCallObject); |
|
3563 |
|
3564 bool |
|
3565 CodeGenerator::visitNewCallObject(LNewCallObject *lir) |
|
3566 { |
|
3567 Register objReg = ToRegister(lir->output()); |
|
3568 Register tempReg = ToRegister(lir->temp()); |
|
3569 |
|
3570 JSObject *templateObj = lir->mir()->templateObject(); |
|
3571 |
|
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; |
|
3588 |
|
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 } |
|
3601 |
|
3602 if (lir->slots()->isRegister()) |
|
3603 masm.storePtr(ToRegister(lir->slots()), Address(objReg, JSObject::offsetOfSlots())); |
|
3604 |
|
3605 masm.bind(ool->rejoin()); |
|
3606 return true; |
|
3607 } |
|
3608 |
|
3609 typedef JSObject *(*NewSingletonCallObjectFn)(JSContext *, HandleShape, HeapSlot *); |
|
3610 static const VMFunction NewSingletonCallObjectInfo = |
|
3611 FunctionInfo<NewSingletonCallObjectFn>(NewSingletonCallObject); |
|
3612 |
|
3613 bool |
|
3614 CodeGenerator::visitNewSingletonCallObject(LNewSingletonCallObject *lir) |
|
3615 { |
|
3616 Register objReg = ToRegister(lir->output()); |
|
3617 |
|
3618 JSObject *templateObj = lir->mir()->templateObject(); |
|
3619 |
|
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; |
|
3634 |
|
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()); |
|
3640 |
|
3641 return true; |
|
3642 } |
|
3643 |
|
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(); |
|
3652 |
|
3653 emitAllocateGCThingPar(lir, resultReg, cxReg, tempReg1, tempReg2, templateObj); |
|
3654 |
|
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 |
|
3658 |
|
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 } |
|
3664 |
|
3665 return true; |
|
3666 } |
|
3667 |
|
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(); |
|
3677 |
|
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); |
|
3681 |
|
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)); |
|
3695 |
|
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()); |
|
3702 |
|
3703 return true; |
|
3704 } |
|
3705 |
|
3706 typedef JSObject *(*NewStringObjectFn)(JSContext *, HandleString); |
|
3707 static const VMFunction NewStringObjectInfo = FunctionInfo<NewStringObjectFn>(NewStringObject); |
|
3708 |
|
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()); |
|
3715 |
|
3716 StringObject *templateObj = lir->mir()->templateObj(); |
|
3717 |
|
3718 OutOfLineCode *ool = oolCallVM(NewStringObjectInfo, lir, (ArgList(), input), |
|
3719 StoreRegisterTo(output)); |
|
3720 if (!ool) |
|
3721 return false; |
|
3722 |
|
3723 masm.newGCThing(output, temp, templateObj, ool->entry(), gc::DefaultHeap); |
|
3724 masm.initGCThing(output, temp, templateObj); |
|
3725 |
|
3726 masm.loadStringLength(input, temp); |
|
3727 |
|
3728 masm.storeValue(JSVAL_TYPE_STRING, input, Address(output, StringObject::offsetOfPrimitiveValue())); |
|
3729 masm.storeValue(JSVAL_TYPE_INT32, temp, Address(output, StringObject::offsetOfLength())); |
|
3730 |
|
3731 masm.bind(ool->rejoin()); |
|
3732 return true; |
|
3733 } |
|
3734 |
|
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 } |
|
3746 |
|
3747 class OutOfLineNewGCThingPar : public OutOfLineCodeBase<CodeGenerator> |
|
3748 { |
|
3749 public: |
|
3750 LInstruction *lir; |
|
3751 gc::AllocKind allocKind; |
|
3752 Register objReg; |
|
3753 Register cxReg; |
|
3754 |
|
3755 OutOfLineNewGCThingPar(LInstruction *lir, gc::AllocKind allocKind, Register objReg, |
|
3756 Register cxReg) |
|
3757 : lir(lir), allocKind(allocKind), objReg(objReg), cxReg(cxReg) |
|
3758 {} |
|
3759 |
|
3760 bool accept(CodeGenerator *codegen) { |
|
3761 return codegen->visitOutOfLineNewGCThingPar(this); |
|
3762 } |
|
3763 }; |
|
3764 |
|
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; |
|
3773 |
|
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 } |
|
3779 |
|
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; |
|
3788 |
|
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); |
|
3797 |
|
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 } |
|
3805 |
|
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 } |
|
3815 |
|
3816 typedef bool(*InitElemFn)(JSContext *cx, HandleObject obj, |
|
3817 HandleValue id, HandleValue value); |
|
3818 static const VMFunction InitElemInfo = |
|
3819 FunctionInfo<InitElemFn>(InitElemOperation); |
|
3820 |
|
3821 bool |
|
3822 CodeGenerator::visitInitElem(LInitElem *lir) |
|
3823 { |
|
3824 Register objReg = ToRegister(lir->getObject()); |
|
3825 |
|
3826 pushArg(ToValue(lir, LInitElem::ValueIndex)); |
|
3827 pushArg(ToValue(lir, LInitElem::IdIndex)); |
|
3828 pushArg(objReg); |
|
3829 |
|
3830 return callVM(InitElemInfo, lir); |
|
3831 } |
|
3832 |
|
3833 typedef bool (*InitElemGetterSetterFn)(JSContext *, jsbytecode *, HandleObject, HandleValue, |
|
3834 HandleObject); |
|
3835 static const VMFunction InitElemGetterSetterInfo = |
|
3836 FunctionInfo<InitElemGetterSetterFn>(InitGetterSetterOperation); |
|
3837 |
|
3838 bool |
|
3839 CodeGenerator::visitInitElemGetterSetter(LInitElemGetterSetter *lir) |
|
3840 { |
|
3841 Register obj = ToRegister(lir->object()); |
|
3842 Register value = ToRegister(lir->value()); |
|
3843 |
|
3844 pushArg(value); |
|
3845 pushArg(ToValue(lir, LInitElemGetterSetter::IdIndex)); |
|
3846 pushArg(obj); |
|
3847 pushArg(ImmPtr(lir->mir()->resumePoint()->pc())); |
|
3848 |
|
3849 return callVM(InitElemGetterSetterInfo, lir); |
|
3850 } |
|
3851 |
|
3852 typedef bool(*MutatePrototypeFn)(JSContext *cx, HandleObject obj, HandleValue value); |
|
3853 static const VMFunction MutatePrototypeInfo = |
|
3854 FunctionInfo<MutatePrototypeFn>(MutatePrototype); |
|
3855 |
|
3856 bool |
|
3857 CodeGenerator::visitMutateProto(LMutateProto *lir) |
|
3858 { |
|
3859 Register objReg = ToRegister(lir->getObject()); |
|
3860 |
|
3861 pushArg(ToValue(lir, LMutateProto::ValueIndex)); |
|
3862 pushArg(objReg); |
|
3863 |
|
3864 return callVM(MutatePrototypeInfo, lir); |
|
3865 } |
|
3866 |
|
3867 typedef bool(*InitPropFn)(JSContext *cx, HandleObject obj, |
|
3868 HandlePropertyName name, HandleValue value); |
|
3869 static const VMFunction InitPropInfo = |
|
3870 FunctionInfo<InitPropFn>(InitProp); |
|
3871 |
|
3872 bool |
|
3873 CodeGenerator::visitInitProp(LInitProp *lir) |
|
3874 { |
|
3875 Register objReg = ToRegister(lir->getObject()); |
|
3876 |
|
3877 pushArg(ToValue(lir, LInitProp::ValueIndex)); |
|
3878 pushArg(ImmGCPtr(lir->mir()->propertyName())); |
|
3879 pushArg(objReg); |
|
3880 |
|
3881 return callVM(InitPropInfo, lir); |
|
3882 } |
|
3883 |
|
3884 typedef bool(*InitPropGetterSetterFn)(JSContext *, jsbytecode *, HandleObject, HandlePropertyName, |
|
3885 HandleObject); |
|
3886 static const VMFunction InitPropGetterSetterInfo = |
|
3887 FunctionInfo<InitPropGetterSetterFn>(InitGetterSetterOperation); |
|
3888 |
|
3889 bool |
|
3890 CodeGenerator::visitInitPropGetterSetter(LInitPropGetterSetter *lir) |
|
3891 { |
|
3892 Register obj = ToRegister(lir->object()); |
|
3893 Register value = ToRegister(lir->value()); |
|
3894 |
|
3895 pushArg(value); |
|
3896 pushArg(ImmGCPtr(lir->mir()->name())); |
|
3897 pushArg(obj); |
|
3898 pushArg(ImmPtr(lir->mir()->resumePoint()->pc())); |
|
3899 |
|
3900 return callVM(InitPropGetterSetterInfo, lir); |
|
3901 } |
|
3902 |
|
3903 typedef bool (*CreateThisFn)(JSContext *cx, HandleObject callee, MutableHandleValue rval); |
|
3904 static const VMFunction CreateThisInfoCodeGen = FunctionInfo<CreateThisFn>(CreateThis); |
|
3905 |
|
3906 bool |
|
3907 CodeGenerator::visitCreateThis(LCreateThis *lir) |
|
3908 { |
|
3909 const LAllocation *callee = lir->getCallee(); |
|
3910 |
|
3911 if (callee->isConstant()) |
|
3912 pushArg(ImmGCPtr(&callee->toConstant()->toObject())); |
|
3913 else |
|
3914 pushArg(ToRegister(callee)); |
|
3915 |
|
3916 return callVM(CreateThisInfoCodeGen, lir); |
|
3917 } |
|
3918 |
|
3919 static JSObject * |
|
3920 CreateThisForFunctionWithProtoWrapper(JSContext *cx, js::HandleObject callee, JSObject *proto) |
|
3921 { |
|
3922 return CreateThisForFunctionWithProto(cx, callee, proto); |
|
3923 } |
|
3924 |
|
3925 typedef JSObject *(*CreateThisWithProtoFn)(JSContext *cx, HandleObject callee, JSObject *proto); |
|
3926 static const VMFunction CreateThisWithProtoInfo = |
|
3927 FunctionInfo<CreateThisWithProtoFn>(CreateThisForFunctionWithProtoWrapper); |
|
3928 |
|
3929 bool |
|
3930 CodeGenerator::visitCreateThisWithProto(LCreateThisWithProto *lir) |
|
3931 { |
|
3932 const LAllocation *callee = lir->getCallee(); |
|
3933 const LAllocation *proto = lir->getPrototype(); |
|
3934 |
|
3935 if (proto->isConstant()) |
|
3936 pushArg(ImmGCPtr(&proto->toConstant()->toObject())); |
|
3937 else |
|
3938 pushArg(ToRegister(proto)); |
|
3939 |
|
3940 if (callee->isConstant()) |
|
3941 pushArg(ImmGCPtr(&callee->toConstant()->toObject())); |
|
3942 else |
|
3943 pushArg(ToRegister(callee)); |
|
3944 |
|
3945 return callVM(CreateThisWithProtoInfo, lir); |
|
3946 } |
|
3947 |
|
3948 typedef JSObject *(*NewGCObjectFn)(JSContext *cx, gc::AllocKind allocKind, |
|
3949 gc::InitialHeap initialHeap); |
|
3950 static const VMFunction NewGCObjectInfo = |
|
3951 FunctionInfo<NewGCObjectFn>(js::jit::NewGCObject); |
|
3952 |
|
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()); |
|
3961 |
|
3962 OutOfLineCode *ool = oolCallVM(NewGCObjectInfo, lir, |
|
3963 (ArgList(), Imm32(allocKind), Imm32(initialHeap)), |
|
3964 StoreRegisterTo(objReg)); |
|
3965 if (!ool) |
|
3966 return false; |
|
3967 |
|
3968 // Allocate. If the FreeList is empty, call to VM, which may GC. |
|
3969 masm.newGCThing(objReg, tempReg, templateObject, ool->entry(), lir->mir()->initialHeap()); |
|
3970 |
|
3971 // Initialize based on the templateObject. |
|
3972 masm.bind(ool->rejoin()); |
|
3973 masm.initGCThing(objReg, tempReg, templateObject); |
|
3974 |
|
3975 return true; |
|
3976 } |
|
3977 |
|
3978 typedef JSObject *(*NewIonArgumentsObjectFn)(JSContext *cx, IonJSFrameLayout *frame, HandleObject); |
|
3979 static const VMFunction NewIonArgumentsObjectInfo = |
|
3980 FunctionInfo<NewIonArgumentsObjectFn>((NewIonArgumentsObjectFn) ArgumentsObject::createForIon); |
|
3981 |
|
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); |
|
3987 |
|
3988 const LAllocation *callObj = lir->getCallObject(); |
|
3989 Register temp = ToRegister(lir->getTemp(0)); |
|
3990 |
|
3991 masm.movePtr(StackPointer, temp); |
|
3992 masm.addPtr(Imm32(frameSize()), temp); |
|
3993 |
|
3994 pushArg(ToRegister(callObj)); |
|
3995 pushArg(temp); |
|
3996 return callVM(NewIonArgumentsObjectInfo, lir); |
|
3997 } |
|
3998 |
|
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); |
|
4005 |
|
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 } |
|
4017 |
|
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); |
|
4024 |
|
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 } |
|
4037 |
|
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()); |
|
4044 |
|
4045 Label valueIsObject, end; |
|
4046 |
|
4047 masm.branchTestObject(Assembler::Equal, value, &valueIsObject); |
|
4048 |
|
4049 // Value is not an object. Return that other object. |
|
4050 masm.movePtr(obj, output); |
|
4051 masm.jump(&end); |
|
4052 |
|
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); |
|
4058 |
|
4059 masm.bind(&end); |
|
4060 return true; |
|
4061 } |
|
4062 |
|
4063 typedef JSObject *(*BoxNonStrictThisFn)(JSContext *, HandleValue); |
|
4064 static const VMFunction BoxNonStrictThisInfo = FunctionInfo<BoxNonStrictThisFn>(BoxNonStrictThis); |
|
4065 |
|
4066 bool |
|
4067 CodeGenerator::visitComputeThis(LComputeThis *lir) |
|
4068 { |
|
4069 ValueOperand value = ToValue(lir, LComputeThis::ValueIndex); |
|
4070 Register output = ToRegister(lir->output()); |
|
4071 |
|
4072 OutOfLineCode *ool = oolCallVM(BoxNonStrictThisInfo, lir, (ArgList(), value), |
|
4073 StoreRegisterTo(output)); |
|
4074 if (!ool) |
|
4075 return false; |
|
4076 |
|
4077 masm.branchTestObject(Assembler::NotEqual, value, ool->entry()); |
|
4078 masm.unboxObject(value, output); |
|
4079 |
|
4080 masm.bind(ool->rejoin()); |
|
4081 return true; |
|
4082 } |
|
4083 |
|
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 } |
|
4092 |
|
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 } |
|
4100 |
|
4101 bool |
|
4102 CodeGenerator::visitSetArrayLength(LSetArrayLength *lir) |
|
4103 { |
|
4104 Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength()); |
|
4105 Int32Key newLength = ToInt32Key(lir->index()); |
|
4106 |
|
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 } |
|
4113 |
|
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 } |
|
4122 |
|
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 } |
|
4131 |
|
4132 bool |
|
4133 CodeGenerator::visitNeuterCheck(LNeuterCheck *lir) |
|
4134 { |
|
4135 Register obj = ToRegister(lir->object()); |
|
4136 Register temp = ToRegister(lir->temp()); |
|
4137 |
|
4138 masm.extractObject(Address(obj, TypedObject::offsetOfOwnerSlot()), temp); |
|
4139 masm.unboxInt32(Address(temp, ArrayBufferObject::flagsOffset()), temp); |
|
4140 |
|
4141 Imm32 flag(ArrayBufferObject::neuteredFlag()); |
|
4142 if (!bailoutTest32(Assembler::NonZero, temp, flag, lir->snapshot())) |
|
4143 return false; |
|
4144 |
|
4145 return true; |
|
4146 } |
|
4147 |
|
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 } |
|
4156 |
|
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()); |
|
4163 |
|
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 // } |
|
4189 |
|
4190 // temp0 = typedObj->byteOffset; |
|
4191 masm.unboxInt32(Address(object, TypedObject::offsetOfByteOffsetSlot()), temp0); |
|
4192 |
|
4193 // temp0 -= offset; |
|
4194 masm.subPtr(offset, temp0); |
|
4195 |
|
4196 // obj->pointer -= temp0; |
|
4197 masm.subPtr(temp0, Address(object, TypedObject::offsetOfDataSlot())); |
|
4198 |
|
4199 // obj->byteOffset = offset; |
|
4200 masm.storeValue(JSVAL_TYPE_INT32, offset, |
|
4201 Address(object, TypedObject::offsetOfByteOffsetSlot())); |
|
4202 |
|
4203 return true; |
|
4204 } |
|
4205 |
|
4206 bool |
|
4207 CodeGenerator::visitStringLength(LStringLength *lir) |
|
4208 { |
|
4209 Register input = ToRegister(lir->string()); |
|
4210 Register output = ToRegister(lir->output()); |
|
4211 |
|
4212 masm.loadStringLength(input, output); |
|
4213 return true; |
|
4214 } |
|
4215 |
|
4216 bool |
|
4217 CodeGenerator::visitMinMaxI(LMinMaxI *ins) |
|
4218 { |
|
4219 Register first = ToRegister(ins->first()); |
|
4220 Register output = ToRegister(ins->output()); |
|
4221 |
|
4222 JS_ASSERT(first == output); |
|
4223 |
|
4224 Label done; |
|
4225 Assembler::Condition cond = ins->mir()->isMax() |
|
4226 ? Assembler::GreaterThan |
|
4227 : Assembler::LessThan; |
|
4228 |
|
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 } |
|
4236 |
|
4237 masm.bind(&done); |
|
4238 return true; |
|
4239 } |
|
4240 |
|
4241 bool |
|
4242 CodeGenerator::visitAbsI(LAbsI *ins) |
|
4243 { |
|
4244 Register input = ToRegister(ins->input()); |
|
4245 Label positive; |
|
4246 |
|
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); |
|
4259 |
|
4260 return true; |
|
4261 } |
|
4262 |
|
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()); |
|
4269 |
|
4270 JS_ASSERT(power != temp); |
|
4271 |
|
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); |
|
4278 |
|
4279 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::powi), MoveOp::DOUBLE); |
|
4280 JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg); |
|
4281 |
|
4282 return true; |
|
4283 } |
|
4284 |
|
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()); |
|
4291 |
|
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); |
|
4296 |
|
4297 JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg); |
|
4298 return true; |
|
4299 } |
|
4300 |
|
4301 bool |
|
4302 CodeGenerator::visitRandom(LRandom *ins) |
|
4303 { |
|
4304 Register temp = ToRegister(ins->temp()); |
|
4305 Register temp2 = ToRegister(ins->temp2()); |
|
4306 |
|
4307 masm.loadJSContext(temp); |
|
4308 |
|
4309 masm.setupUnalignedABICall(1, temp2); |
|
4310 masm.passABIArg(temp); |
|
4311 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, math_random_no_outparam), MoveOp::DOUBLE); |
|
4312 |
|
4313 JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg); |
|
4314 return true; |
|
4315 } |
|
4316 |
|
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); |
|
4323 |
|
4324 const MathCache *mathCache = ins->mir()->cache(); |
|
4325 |
|
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); |
|
4332 |
|
4333 # define MAYBE_CACHED(fcn) (mathCache ? (void*)fcn ## _impl : (void*)fcn ## _uncached) |
|
4334 |
|
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 } |
|
4412 |
|
4413 # undef MAYBE_CACHED |
|
4414 |
|
4415 masm.callWithABI(funptr, MoveOp::DOUBLE); |
|
4416 return true; |
|
4417 } |
|
4418 |
|
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); |
|
4425 |
|
4426 masm.setupUnalignedABICall(1, temp); |
|
4427 masm.passABIArg(input, MoveOp::FLOAT32); |
|
4428 |
|
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 } |
|
4437 |
|
4438 masm.callWithABI(funptr, MoveOp::FLOAT32); |
|
4439 return true; |
|
4440 } |
|
4441 |
|
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()); |
|
4448 |
|
4449 JS_ASSERT(ToFloatRegister(ins->output()) == ReturnFloatReg); |
|
4450 |
|
4451 masm.setupUnalignedABICall(2, temp); |
|
4452 masm.passABIArg(lhs, MoveOp::DOUBLE); |
|
4453 masm.passABIArg(rhs, MoveOp::DOUBLE); |
|
4454 |
|
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 } |
|
4461 |
|
4462 typedef bool (*BinaryFn)(JSContext *, MutableHandleValue, MutableHandleValue, MutableHandleValue); |
|
4463 typedef bool (*BinaryParFn)(ForkJoinContext *, HandleValue, HandleValue, MutableHandleValue); |
|
4464 |
|
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)); |
|
4473 |
|
4474 bool |
|
4475 CodeGenerator::visitBinaryV(LBinaryV *lir) |
|
4476 { |
|
4477 pushArg(ToValue(lir, LBinaryV::RhsInput)); |
|
4478 pushArg(ToValue(lir, LBinaryV::LhsInput)); |
|
4479 |
|
4480 switch (lir->jsop()) { |
|
4481 case JSOP_ADD: |
|
4482 return callVM(AddInfo, lir); |
|
4483 |
|
4484 case JSOP_SUB: |
|
4485 return callVM(SubInfo, lir); |
|
4486 |
|
4487 case JSOP_MUL: |
|
4488 return callVM(MulInfo, lir); |
|
4489 |
|
4490 case JSOP_DIV: |
|
4491 return callVM(DivInfo, lir); |
|
4492 |
|
4493 case JSOP_MOD: |
|
4494 return callVM(ModInfo, lir); |
|
4495 |
|
4496 case JSOP_URSH: |
|
4497 return callVM(UrshInfo, lir); |
|
4498 |
|
4499 default: |
|
4500 MOZ_ASSUME_UNREACHABLE("Unexpected binary op"); |
|
4501 } |
|
4502 } |
|
4503 |
|
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)); |
|
4512 |
|
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()); |
|
4518 |
|
4519 OutOfLineCode *ool = nullptr; |
|
4520 |
|
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; |
|
4529 |
|
4530 masm.compareStrings(op, left, right, output, temp, ool->entry()); |
|
4531 |
|
4532 masm.bind(ool->rejoin()); |
|
4533 return true; |
|
4534 } |
|
4535 |
|
4536 bool |
|
4537 CodeGenerator::visitCompareStrictS(LCompareStrictS *lir) |
|
4538 { |
|
4539 JSOp op = lir->mir()->jsop(); |
|
4540 JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE); |
|
4541 |
|
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()); |
|
4547 |
|
4548 Label string, done; |
|
4549 |
|
4550 masm.branchTestString(Assembler::Equal, leftV, &string); |
|
4551 masm.move32(Imm32(op == JSOP_STRICTNE), output); |
|
4552 masm.jump(&done); |
|
4553 |
|
4554 masm.bind(&string); |
|
4555 Register left = masm.extractString(leftV, tempToUnbox); |
|
4556 if (!emitCompareS(lir, op, left, right, output, temp)) |
|
4557 return false; |
|
4558 |
|
4559 masm.bind(&done); |
|
4560 |
|
4561 return true; |
|
4562 } |
|
4563 |
|
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()); |
|
4572 |
|
4573 return emitCompareS(lir, op, left, right, output, temp); |
|
4574 } |
|
4575 |
|
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)); |
|
4602 |
|
4603 bool |
|
4604 CodeGenerator::visitCompareVM(LCompareVM *lir) |
|
4605 { |
|
4606 pushArg(ToValue(lir, LBinaryV::RhsInput)); |
|
4607 pushArg(ToValue(lir, LBinaryV::LhsInput)); |
|
4608 |
|
4609 switch (lir->mir()->jsop()) { |
|
4610 case JSOP_EQ: |
|
4611 return callVM(EqInfo, lir); |
|
4612 |
|
4613 case JSOP_NE: |
|
4614 return callVM(NeInfo, lir); |
|
4615 |
|
4616 case JSOP_STRICTEQ: |
|
4617 return callVM(StrictEqInfo, lir); |
|
4618 |
|
4619 case JSOP_STRICTNE: |
|
4620 return callVM(StrictNeInfo, lir); |
|
4621 |
|
4622 case JSOP_LT: |
|
4623 return callVM(LtInfo, lir); |
|
4624 |
|
4625 case JSOP_LE: |
|
4626 return callVM(LeInfo, lir); |
|
4627 |
|
4628 case JSOP_GT: |
|
4629 return callVM(GtInfo, lir); |
|
4630 |
|
4631 case JSOP_GE: |
|
4632 return callVM(GeInfo, lir); |
|
4633 |
|
4634 default: |
|
4635 MOZ_ASSUME_UNREACHABLE("Unexpected compare op"); |
|
4636 } |
|
4637 } |
|
4638 |
|
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); |
|
4646 |
|
4647 const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefined::Value); |
|
4648 Register output = ToRegister(lir->output()); |
|
4649 |
|
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"); |
|
4654 |
|
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 } |
|
4671 |
|
4672 Register tag = masm.splitTagForTest(value); |
|
4673 |
|
4674 masm.branchTestNull(Assembler::Equal, tag, nullOrLikeUndefined); |
|
4675 masm.branchTestUndefined(Assembler::Equal, tag, nullOrLikeUndefined); |
|
4676 |
|
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); |
|
4681 |
|
4682 Register objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox())); |
|
4683 branchTestObjectEmulatesUndefined(objreg, nullOrLikeUndefined, notNullOrLikeUndefined, |
|
4684 ToRegister(lir->temp()), ool); |
|
4685 // fall through |
|
4686 } |
|
4687 |
|
4688 Label done; |
|
4689 |
|
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); |
|
4694 |
|
4695 masm.bind(nullOrLikeUndefined); |
|
4696 masm.move32(Imm32(op == JSOP_EQ), output); |
|
4697 |
|
4698 // Both branches meet here. |
|
4699 masm.bind(&done); |
|
4700 return true; |
|
4701 } |
|
4702 |
|
4703 JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE); |
|
4704 |
|
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); |
|
4710 |
|
4711 return true; |
|
4712 } |
|
4713 |
|
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); |
|
4721 |
|
4722 const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedAndBranch::Value); |
|
4723 |
|
4724 if (op == JSOP_EQ || op == JSOP_NE) { |
|
4725 MBasicBlock *ifTrue; |
|
4726 MBasicBlock *ifFalse; |
|
4727 |
|
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 } |
|
4737 |
|
4738 MOZ_ASSERT(lir->cmpMir()->lhs()->type() != MIRType_Object || |
|
4739 lir->cmpMir()->operandMightEmulateUndefined(), |
|
4740 "Operands which can't emulate undefined should have been folded"); |
|
4741 |
|
4742 OutOfLineTestObject *ool = nullptr; |
|
4743 if (lir->cmpMir()->operandMightEmulateUndefined()) { |
|
4744 ool = new(alloc()) OutOfLineTestObject(); |
|
4745 if (!addOutOfLineCode(ool)) |
|
4746 return false; |
|
4747 } |
|
4748 |
|
4749 Register tag = masm.splitTagForTest(value); |
|
4750 |
|
4751 Label *ifTrueLabel = getJumpLabelForBranch(ifTrue); |
|
4752 Label *ifFalseLabel = getJumpLabelForBranch(ifFalse); |
|
4753 |
|
4754 masm.branchTestNull(Assembler::Equal, tag, ifTrueLabel); |
|
4755 masm.branchTestUndefined(Assembler::Equal, tag, ifTrueLabel); |
|
4756 |
|
4757 if (ool) { |
|
4758 masm.branchTestObject(Assembler::NotEqual, tag, ifFalseLabel); |
|
4759 |
|
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 } |
|
4769 |
|
4770 JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE); |
|
4771 |
|
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()); |
|
4777 |
|
4778 return true; |
|
4779 } |
|
4780 |
|
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."); |
|
4789 |
|
4790 JSOp op = lir->mir()->jsop(); |
|
4791 MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE, "Strict equality should have been folded"); |
|
4792 |
|
4793 OutOfLineTestObjectWithLabels *ool = new(alloc()) OutOfLineTestObjectWithLabels(); |
|
4794 if (!addOutOfLineCode(ool)) |
|
4795 return false; |
|
4796 |
|
4797 Label *emulatesUndefined = ool->label1(); |
|
4798 Label *doesntEmulateUndefined = ool->label2(); |
|
4799 |
|
4800 Register objreg = ToRegister(lir->input()); |
|
4801 Register output = ToRegister(lir->output()); |
|
4802 branchTestObjectEmulatesUndefined(objreg, emulatesUndefined, doesntEmulateUndefined, |
|
4803 output, ool); |
|
4804 |
|
4805 Label done; |
|
4806 |
|
4807 masm.move32(Imm32(op == JSOP_NE), output); |
|
4808 masm.jump(&done); |
|
4809 |
|
4810 masm.bind(emulatesUndefined); |
|
4811 masm.move32(Imm32(op == JSOP_EQ), output); |
|
4812 masm.bind(&done); |
|
4813 return true; |
|
4814 } |
|
4815 |
|
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"); |
|
4823 |
|
4824 JSOp op = lir->cmpMir()->jsop(); |
|
4825 MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE, "Strict equality should have been folded"); |
|
4826 |
|
4827 OutOfLineTestObject *ool = new(alloc()) OutOfLineTestObject(); |
|
4828 if (!addOutOfLineCode(ool)) |
|
4829 return false; |
|
4830 |
|
4831 Label *equal; |
|
4832 Label *unequal; |
|
4833 |
|
4834 { |
|
4835 MBasicBlock *ifTrue; |
|
4836 MBasicBlock *ifFalse; |
|
4837 |
|
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 } |
|
4847 |
|
4848 equal = getJumpLabelForBranch(ifTrue); |
|
4849 unequal = getJumpLabelForBranch(ifFalse); |
|
4850 } |
|
4851 |
|
4852 Register objreg = ToRegister(lir->input()); |
|
4853 |
|
4854 testObjectEmulatesUndefined(objreg, equal, unequal, ToRegister(lir->temp()), ool); |
|
4855 return true; |
|
4856 } |
|
4857 |
|
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)); |
|
4863 |
|
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; |
|
4871 |
|
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()); |
|
4876 |
|
4877 masm.bind(ool->rejoin()); |
|
4878 return true; |
|
4879 } |
|
4880 |
|
4881 bool |
|
4882 CodeGenerator::visitConcat(LConcat *lir) |
|
4883 { |
|
4884 Register lhs = ToRegister(lir->lhs()); |
|
4885 Register rhs = ToRegister(lir->rhs()); |
|
4886 |
|
4887 Register output = ToRegister(lir->output()); |
|
4888 |
|
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); |
|
4897 |
|
4898 return emitConcat(lir, lhs, rhs, output); |
|
4899 } |
|
4900 |
|
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()); |
|
4908 |
|
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); |
|
4917 |
|
4918 return emitConcat(lir, lhs, rhs, output); |
|
4919 } |
|
4920 |
|
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. |
|
4926 |
|
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 |
|
4933 |
|
4934 JS_STATIC_ASSERT(sizeof(jschar) == 2); |
|
4935 |
|
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 } |
|
4944 |
|
4945 JitCode * |
|
4946 JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode) |
|
4947 { |
|
4948 MacroAssembler masm(cx); |
|
4949 |
|
4950 Register lhs = CallTempReg0; |
|
4951 Register rhs = CallTempReg1; |
|
4952 Register temp1 = CallTempReg2; |
|
4953 Register temp2 = CallTempReg3; |
|
4954 Register temp3 = CallTempReg4; |
|
4955 Register output = CallTempReg5; |
|
4956 |
|
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; |
|
4961 |
|
4962 Label failure, failurePopTemps; |
|
4963 |
|
4964 // If lhs is empty, return rhs. |
|
4965 Label leftEmpty; |
|
4966 masm.loadStringLength(lhs, temp1); |
|
4967 masm.branchTest32(Assembler::Zero, temp1, temp1, &leftEmpty); |
|
4968 |
|
4969 // If rhs is empty, return lhs. |
|
4970 Label rightEmpty; |
|
4971 masm.loadStringLength(rhs, temp2); |
|
4972 masm.branchTest32(Assembler::Zero, temp2, temp2, &rightEmpty); |
|
4973 |
|
4974 masm.add32(temp1, temp2); |
|
4975 |
|
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); |
|
4980 |
|
4981 // Ensure result length <= JSString::MAX_LENGTH. |
|
4982 masm.branch32(Assembler::Above, temp2, Imm32(JSString::MAX_LENGTH), &failure); |
|
4983 |
|
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 } |
|
4999 |
|
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())); |
|
5004 |
|
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(); |
|
5009 |
|
5010 masm.bind(&leftEmpty); |
|
5011 masm.mov(rhs, output); |
|
5012 masm.ret(); |
|
5013 |
|
5014 masm.bind(&rightEmpty); |
|
5015 masm.mov(lhs, output); |
|
5016 masm.ret(); |
|
5017 |
|
5018 masm.bind(&isFatInline); |
|
5019 |
|
5020 // State: lhs length in temp1, result length in temp2. |
|
5021 |
|
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); |
|
5028 |
|
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 } |
|
5044 |
|
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())); |
|
5049 |
|
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())); |
|
5053 |
|
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); |
|
5060 |
|
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); |
|
5066 |
|
5067 // Copy rhs chars. |
|
5068 masm.loadStringLength(rhs, temp1); |
|
5069 masm.loadPtr(Address(rhs, JSString::offsetOfChars()), rhs); |
|
5070 CopyStringChars(masm, temp2, rhs, temp1, temp3); |
|
5071 |
|
5072 if (mode == ParallelExecution) |
|
5073 masm.pop(temp3); |
|
5074 } |
|
5075 |
|
5076 // Null-terminate. |
|
5077 masm.store16(Imm32(0), Address(temp2, 0)); |
|
5078 masm.ret(); |
|
5079 |
|
5080 masm.bind(&failurePopTemps); |
|
5081 masm.pop(temp2); |
|
5082 masm.pop(temp1); |
|
5083 |
|
5084 masm.bind(&failure); |
|
5085 masm.movePtr(ImmPtr(nullptr), output); |
|
5086 masm.ret(); |
|
5087 |
|
5088 Linker linker(masm); |
|
5089 AutoFlushICache afc("StringConcatStub"); |
|
5090 JitCode *code = linker.newCode<CanGC>(cx, JSC::OTHER_CODE); |
|
5091 |
|
5092 #ifdef JS_ION_PERF |
|
5093 writePerfSpewerJitCodeProfile(code, "StringConcatStub"); |
|
5094 #endif |
|
5095 |
|
5096 return code; |
|
5097 } |
|
5098 |
|
5099 typedef bool (*CharCodeAtFn)(JSContext *, HandleString, int32_t, uint32_t *); |
|
5100 static const VMFunction CharCodeAtInfo = FunctionInfo<CharCodeAtFn>(jit::CharCodeAt); |
|
5101 |
|
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()); |
|
5108 |
|
5109 OutOfLineCode *ool = oolCallVM(CharCodeAtInfo, lir, (ArgList(), str, index), StoreRegisterTo(output)); |
|
5110 if (!ool) |
|
5111 return false; |
|
5112 |
|
5113 Address lengthAndFlagsAddr(str, JSString::offsetOfLengthAndFlags()); |
|
5114 masm.branchTest32(Assembler::Zero, lengthAndFlagsAddr, Imm32(JSString::FLAGS_MASK), ool->entry()); |
|
5115 |
|
5116 // getChars |
|
5117 Address charsAddr(str, JSString::offsetOfChars()); |
|
5118 masm.loadPtr(charsAddr, output); |
|
5119 masm.load16ZeroExtend(BaseIndex(output, index, TimesTwo, 0), output); |
|
5120 |
|
5121 masm.bind(ool->rejoin()); |
|
5122 return true; |
|
5123 } |
|
5124 |
|
5125 typedef JSFlatString *(*StringFromCharCodeFn)(JSContext *, int32_t); |
|
5126 static const VMFunction StringFromCharCodeInfo = FunctionInfo<StringFromCharCodeFn>(jit::StringFromCharCode); |
|
5127 |
|
5128 bool |
|
5129 CodeGenerator::visitFromCharCode(LFromCharCode *lir) |
|
5130 { |
|
5131 Register code = ToRegister(lir->code()); |
|
5132 Register output = ToRegister(lir->output()); |
|
5133 |
|
5134 OutOfLineCode *ool = oolCallVM(StringFromCharCodeInfo, lir, (ArgList(), code), StoreRegisterTo(output)); |
|
5135 if (!ool) |
|
5136 return false; |
|
5137 |
|
5138 // OOL path if code >= UNIT_STATIC_LIMIT. |
|
5139 masm.branch32(Assembler::AboveOrEqual, code, Imm32(StaticStrings::UNIT_STATIC_LIMIT), |
|
5140 ool->entry()); |
|
5141 |
|
5142 masm.movePtr(ImmPtr(&GetIonContext()->runtime->staticStrings().unitStaticTable), output); |
|
5143 masm.loadPtr(BaseIndex(output, code, ScalePointer), output); |
|
5144 |
|
5145 masm.bind(ool->rejoin()); |
|
5146 return true; |
|
5147 } |
|
5148 |
|
5149 typedef JSObject *(*StringSplitFn)(JSContext *, HandleTypeObject, HandleString, HandleString); |
|
5150 static const VMFunction StringSplitInfo = FunctionInfo<StringSplitFn>(js::str_split_string); |
|
5151 |
|
5152 bool |
|
5153 CodeGenerator::visitStringSplit(LStringSplit *lir) |
|
5154 { |
|
5155 pushArg(ToRegister(lir->separator())); |
|
5156 pushArg(ToRegister(lir->string())); |
|
5157 pushArg(ImmGCPtr(lir->mir()->typeObject())); |
|
5158 |
|
5159 return callVM(StringSplitInfo, lir); |
|
5160 } |
|
5161 |
|
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 } |
|
5169 |
|
5170 bool |
|
5171 CodeGenerator::visitSetInitializedLength(LSetInitializedLength *lir) |
|
5172 { |
|
5173 Address initLength(ToRegister(lir->elements()), ObjectElements::offsetOfInitializedLength()); |
|
5174 Int32Key index = ToInt32Key(lir->index()); |
|
5175 |
|
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 } |
|
5182 |
|
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."); |
|
5188 |
|
5189 OutOfLineTestObjectWithLabels *ool = new(alloc()) OutOfLineTestObjectWithLabels(); |
|
5190 if (!addOutOfLineCode(ool)) |
|
5191 return false; |
|
5192 |
|
5193 Label *ifEmulatesUndefined = ool->label1(); |
|
5194 Label *ifDoesntEmulateUndefined = ool->label2(); |
|
5195 |
|
5196 Register objreg = ToRegister(lir->input()); |
|
5197 Register output = ToRegister(lir->output()); |
|
5198 branchTestObjectEmulatesUndefined(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined, |
|
5199 output, ool); |
|
5200 // fall through |
|
5201 |
|
5202 Label join; |
|
5203 |
|
5204 masm.move32(Imm32(0), output); |
|
5205 masm.jump(&join); |
|
5206 |
|
5207 masm.bind(ifEmulatesUndefined); |
|
5208 masm.move32(Imm32(1), output); |
|
5209 |
|
5210 masm.bind(&join); |
|
5211 return true; |
|
5212 } |
|
5213 |
|
5214 bool |
|
5215 CodeGenerator::visitNotV(LNotV *lir) |
|
5216 { |
|
5217 Maybe<Label> ifTruthyLabel, ifFalsyLabel; |
|
5218 Label *ifTruthy; |
|
5219 Label *ifFalsy; |
|
5220 |
|
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 } |
|
5234 |
|
5235 testValueTruthyKernel(ToValue(lir, LNotV::Input), lir->temp1(), lir->temp2(), |
|
5236 ToFloatRegister(lir->tempFloat()), |
|
5237 ifTruthy, ifFalsy, ool); |
|
5238 |
|
5239 Label join; |
|
5240 Register output = ToRegister(lir->output()); |
|
5241 |
|
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); |
|
5247 |
|
5248 masm.bind(ifFalsy); |
|
5249 masm.move32(Imm32(1), output); |
|
5250 |
|
5251 // both branches meet here. |
|
5252 masm.bind(&join); |
|
5253 return true; |
|
5254 } |
|
5255 |
|
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 } |
|
5278 |
|
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); |
|
5285 |
|
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 } |
|
5298 |
|
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 } |
|
5309 |
|
5310 if (!bailoutCmp32(Assembler::LessThan, temp, Imm32(0), lir->snapshot())) |
|
5311 return false; |
|
5312 |
|
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 } |
|
5321 |
|
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 } |
|
5337 |
|
5338 return bailoutCmp32(Assembler::BelowOrEqual, ToOperand(lir->length()), temp, lir->snapshot()); |
|
5339 } |
|
5340 |
|
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 } |
|
5348 |
|
5349 class OutOfLineStoreElementHole : public OutOfLineCodeBase<CodeGenerator> |
|
5350 { |
|
5351 LInstruction *ins_; |
|
5352 Label rejoinStore_; |
|
5353 |
|
5354 public: |
|
5355 OutOfLineStoreElementHole(LInstruction *ins) |
|
5356 : ins_(ins) |
|
5357 { |
|
5358 JS_ASSERT(ins->isStoreElementHoleV() || ins->isStoreElementHoleT()); |
|
5359 } |
|
5360 |
|
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 }; |
|
5371 |
|
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 } |
|
5385 |
|
5386 bool |
|
5387 CodeGenerator::visitStoreElementT(LStoreElementT *store) |
|
5388 { |
|
5389 Register elements = ToRegister(store->elements()); |
|
5390 const LAllocation *index = store->index(); |
|
5391 |
|
5392 if (store->mir()->needsBarrier()) |
|
5393 emitPreBarrier(elements, index, store->mir()->elementType()); |
|
5394 |
|
5395 if (store->mir()->needsHoleCheck() && !emitStoreHoleCheck(elements, index, store->snapshot())) |
|
5396 return false; |
|
5397 |
|
5398 storeElementTyped(store->value(), store->mir()->value()->type(), store->mir()->elementType(), |
|
5399 elements, index); |
|
5400 return true; |
|
5401 } |
|
5402 |
|
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(); |
|
5409 |
|
5410 if (lir->mir()->needsBarrier()) |
|
5411 emitPreBarrier(elements, index, MIRType_Value); |
|
5412 |
|
5413 if (lir->mir()->needsHoleCheck() && !emitStoreHoleCheck(elements, index, lir->snapshot())) |
|
5414 return false; |
|
5415 |
|
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 } |
|
5422 |
|
5423 bool |
|
5424 CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT *lir) |
|
5425 { |
|
5426 OutOfLineStoreElementHole *ool = new(alloc()) OutOfLineStoreElementHole(lir); |
|
5427 if (!addOutOfLineCode(ool)) |
|
5428 return false; |
|
5429 |
|
5430 Register elements = ToRegister(lir->elements()); |
|
5431 const LAllocation *index = lir->index(); |
|
5432 |
|
5433 // OOL path if index >= initializedLength. |
|
5434 Address initLength(elements, ObjectElements::offsetOfInitializedLength()); |
|
5435 masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry()); |
|
5436 |
|
5437 if (lir->mir()->needsBarrier()) |
|
5438 emitPreBarrier(elements, index, lir->mir()->elementType()); |
|
5439 |
|
5440 masm.bind(ool->rejoinStore()); |
|
5441 storeElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(), |
|
5442 elements, index); |
|
5443 |
|
5444 masm.bind(ool->rejoin()); |
|
5445 return true; |
|
5446 } |
|
5447 |
|
5448 bool |
|
5449 CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV *lir) |
|
5450 { |
|
5451 OutOfLineStoreElementHole *ool = new(alloc()) OutOfLineStoreElementHole(lir); |
|
5452 if (!addOutOfLineCode(ool)) |
|
5453 return false; |
|
5454 |
|
5455 Register elements = ToRegister(lir->elements()); |
|
5456 const LAllocation *index = lir->index(); |
|
5457 const ValueOperand value = ToValue(lir, LStoreElementHoleV::Value); |
|
5458 |
|
5459 // OOL path if index >= initializedLength. |
|
5460 Address initLength(elements, ObjectElements::offsetOfInitializedLength()); |
|
5461 masm.branchKey(Assembler::BelowOrEqual, initLength, ToInt32Key(index), ool->entry()); |
|
5462 |
|
5463 if (lir->mir()->needsBarrier()) |
|
5464 emitPreBarrier(elements, index, lir->mir()->elementType()); |
|
5465 |
|
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)); |
|
5471 |
|
5472 masm.bind(ool->rejoin()); |
|
5473 return true; |
|
5474 } |
|
5475 |
|
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)); |
|
5482 |
|
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; |
|
5491 |
|
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 } |
|
5510 |
|
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 |
|
5522 |
|
5523 Int32Key key = ToInt32Key(index); |
|
5524 |
|
5525 // Check array capacity. |
|
5526 masm.branchKey(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()), |
|
5527 key, &callStub); |
|
5528 |
|
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())); |
|
5533 |
|
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); |
|
5540 |
|
5541 masm.bumpKey(&key, -1); |
|
5542 |
|
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 } |
|
5554 |
|
5555 masm.bind(&callStub); |
|
5556 saveLive(ins); |
|
5557 |
|
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; |
|
5567 |
|
5568 restoreLive(ins); |
|
5569 masm.jump(ool->rejoin()); |
|
5570 return true; |
|
5571 } |
|
5572 |
|
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); |
|
5576 |
|
5577 bool |
|
5578 CodeGenerator::emitArrayPopShift(LInstruction *lir, const MArrayPopShift *mir, Register obj, |
|
5579 Register elementsTemp, Register lengthTemp, TypedOrValueRegister out) |
|
5580 { |
|
5581 OutOfLineCode *ool; |
|
5582 |
|
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 } |
|
5593 |
|
5594 // VM call if a write barrier is necessary. |
|
5595 masm.branchTestNeedsBarrier(Assembler::NonZero, lengthTemp, ool->entry()); |
|
5596 |
|
5597 // Load elements and length. |
|
5598 masm.loadPtr(Address(obj, JSObject::offsetOfElements()), elementsTemp); |
|
5599 masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp); |
|
5600 |
|
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()); |
|
5605 |
|
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 } |
|
5619 |
|
5620 masm.bumpKey(&key, -1); |
|
5621 |
|
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 } |
|
5630 |
|
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()); |
|
5638 |
|
5639 // Now adjust length and initializedLength. |
|
5640 masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength())); |
|
5641 masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength())); |
|
5642 |
|
5643 if (mir->mode() == MArrayPopShift::Shift) { |
|
5644 // Don't save the temp registers. |
|
5645 RegisterSet temps; |
|
5646 temps.add(elementsTemp); |
|
5647 temps.add(lengthTemp); |
|
5648 |
|
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 } |
|
5655 |
|
5656 masm.bind(&done); |
|
5657 masm.bind(ool->rejoin()); |
|
5658 return true; |
|
5659 } |
|
5660 |
|
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 } |
|
5670 |
|
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 } |
|
5680 |
|
5681 typedef bool (*ArrayPushDenseFn)(JSContext *, HandleObject, HandleValue, uint32_t *); |
|
5682 static const VMFunction ArrayPushDenseInfo = |
|
5683 FunctionInfo<ArrayPushDenseFn>(jit::ArrayPushDense); |
|
5684 |
|
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; |
|
5692 |
|
5693 // Load elements and length. |
|
5694 masm.loadPtr(Address(obj, JSObject::offsetOfElements()), elementsTemp); |
|
5695 masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length); |
|
5696 |
|
5697 Int32Key key = Int32Key(length); |
|
5698 Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength()); |
|
5699 Address capacity(elementsTemp, ObjectElements::offsetOfCapacity()); |
|
5700 |
|
5701 // Guard length == initializedLength. |
|
5702 masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry()); |
|
5703 |
|
5704 // Guard length < capacity. |
|
5705 masm.branchKey(Assembler::BelowOrEqual, capacity, key, ool->entry()); |
|
5706 |
|
5707 masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight)); |
|
5708 |
|
5709 masm.bumpKey(&key, 1); |
|
5710 masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength())); |
|
5711 masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength())); |
|
5712 |
|
5713 masm.bind(ool->rejoin()); |
|
5714 return true; |
|
5715 } |
|
5716 |
|
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 } |
|
5726 |
|
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 } |
|
5740 |
|
5741 typedef JSObject *(*ArrayConcatDenseFn)(JSContext *, HandleObject, HandleObject, HandleObject); |
|
5742 static const VMFunction ArrayConcatDenseInfo = FunctionInfo<ArrayConcatDenseFn>(ArrayConcatDense); |
|
5743 |
|
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()); |
|
5751 |
|
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); |
|
5759 |
|
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); |
|
5763 |
|
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); |
|
5774 |
|
5775 pushArg(temp1); |
|
5776 pushArg(ToRegister(lir->rhs())); |
|
5777 pushArg(ToRegister(lir->lhs())); |
|
5778 return callVM(ArrayConcatDenseInfo, lir); |
|
5779 } |
|
5780 |
|
5781 typedef JSObject *(*GetIteratorObjectFn)(JSContext *, HandleObject, uint32_t); |
|
5782 static const VMFunction GetIteratorObjectInfo = FunctionInfo<GetIteratorObjectFn>(GetIteratorObject); |
|
5783 |
|
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 } |
|
5791 |
|
5792 bool |
|
5793 CodeGenerator::visitIteratorStart(LIteratorStart *lir) |
|
5794 { |
|
5795 const Register obj = ToRegister(lir->object()); |
|
5796 const Register output = ToRegister(lir->output()); |
|
5797 |
|
5798 uint32_t flags = lir->mir()->flags(); |
|
5799 |
|
5800 OutOfLineCode *ool = oolCallVM(GetIteratorObjectInfo, lir, |
|
5801 (ArgList(), obj, Imm32(flags)), StoreRegisterTo(output)); |
|
5802 if (!ool) |
|
5803 return false; |
|
5804 |
|
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. |
|
5808 |
|
5809 // Iterators other than for-in should use LCallIteratorStart. |
|
5810 JS_ASSERT(flags == JSITER_ENUMERATE); |
|
5811 |
|
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()); |
|
5815 |
|
5816 // Load NativeIterator. |
|
5817 masm.loadObjPrivate(output, JSObject::ITER_CLASS_NFIXED_SLOTS, niTemp); |
|
5818 |
|
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()); |
|
5822 |
|
5823 // Load the iterator's shape array. |
|
5824 masm.loadPtr(Address(niTemp, offsetof(NativeIterator, shapes_array)), temp2); |
|
5825 |
|
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()); |
|
5829 |
|
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()); |
|
5834 |
|
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()); |
|
5841 |
|
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()); |
|
5848 |
|
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); |
|
5861 |
|
5862 Address objAddr(niTemp, offsetof(NativeIterator, obj)); |
|
5863 masm.branchPtr(Assembler::NotEqual, objAddr, obj, ool->entry()); |
|
5864 |
|
5865 masm.bind(&noBarrier); |
|
5866 #endif // !JSGC_GENERATIONAL |
|
5867 } |
|
5868 |
|
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))); |
|
5872 |
|
5873 // Chain onto the active iterator stack. |
|
5874 masm.loadPtr(AbsoluteAddress(gen->compartment->addressOfEnumerators()), temp1); |
|
5875 |
|
5876 // ni->next = list |
|
5877 masm.storePtr(temp1, Address(niTemp, NativeIterator::offsetOfNext())); |
|
5878 |
|
5879 // ni->prev = list->prev |
|
5880 masm.loadPtr(Address(temp1, NativeIterator::offsetOfPrev()), temp2); |
|
5881 masm.storePtr(temp2, Address(niTemp, NativeIterator::offsetOfPrev())); |
|
5882 |
|
5883 // list->prev->next = ni |
|
5884 masm.storePtr(niTemp, Address(temp2, NativeIterator::offsetOfNext())); |
|
5885 |
|
5886 // list->prev = ni |
|
5887 masm.storePtr(niTemp, Address(temp1, NativeIterator::offsetOfPrev())); |
|
5888 |
|
5889 masm.bind(ool->rejoin()); |
|
5890 return true; |
|
5891 } |
|
5892 |
|
5893 static void |
|
5894 LoadNativeIterator(MacroAssembler &masm, Register obj, Register dest, Label *failures) |
|
5895 { |
|
5896 JS_ASSERT(obj != dest); |
|
5897 |
|
5898 // Test class. |
|
5899 masm.branchTestObjClass(Assembler::NotEqual, obj, dest, &PropertyIteratorObject::class_, failures); |
|
5900 |
|
5901 // Load NativeIterator object. |
|
5902 masm.loadObjPrivate(obj, JSObject::ITER_CLASS_NFIXED_SLOTS, dest); |
|
5903 } |
|
5904 |
|
5905 typedef bool (*IteratorNextFn)(JSContext *, HandleObject, MutableHandleValue); |
|
5906 static const VMFunction IteratorNextInfo = FunctionInfo<IteratorNextFn>(js_IteratorNext); |
|
5907 |
|
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); |
|
5914 |
|
5915 OutOfLineCode *ool = oolCallVM(IteratorNextInfo, lir, (ArgList(), obj), StoreValueTo(output)); |
|
5916 if (!ool) |
|
5917 return false; |
|
5918 |
|
5919 LoadNativeIterator(masm, obj, temp, ool->entry()); |
|
5920 |
|
5921 masm.branchTest32(Assembler::NonZero, Address(temp, offsetof(NativeIterator, flags)), |
|
5922 Imm32(JSITER_FOREACH), ool->entry()); |
|
5923 |
|
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); |
|
5928 |
|
5929 // Increase the cursor. |
|
5930 masm.addPtr(Imm32(sizeof(JSString *)), Address(temp, offsetof(NativeIterator, props_cursor))); |
|
5931 |
|
5932 masm.bind(ool->rejoin()); |
|
5933 return true; |
|
5934 } |
|
5935 |
|
5936 typedef bool (*IteratorMoreFn)(JSContext *, HandleObject, bool *); |
|
5937 static const VMFunction IteratorMoreInfo = FunctionInfo<IteratorMoreFn>(jit::IteratorMore); |
|
5938 |
|
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()); |
|
5945 |
|
5946 OutOfLineCode *ool = oolCallVM(IteratorMoreInfo, lir, |
|
5947 (ArgList(), obj), StoreRegisterTo(output)); |
|
5948 if (!ool) |
|
5949 return false; |
|
5950 |
|
5951 LoadNativeIterator(masm, obj, output, ool->entry()); |
|
5952 |
|
5953 masm.branchTest32(Assembler::NonZero, Address(output, offsetof(NativeIterator, flags)), |
|
5954 Imm32(JSITER_FOREACH), ool->entry()); |
|
5955 |
|
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); |
|
5960 |
|
5961 masm.bind(ool->rejoin()); |
|
5962 return true; |
|
5963 } |
|
5964 |
|
5965 typedef bool (*CloseIteratorFn)(JSContext *, HandleObject); |
|
5966 static const VMFunction CloseIteratorInfo = FunctionInfo<CloseIteratorFn>(CloseIterator); |
|
5967 |
|
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()); |
|
5975 |
|
5976 OutOfLineCode *ool = oolCallVM(CloseIteratorInfo, lir, (ArgList(), obj), StoreNothing()); |
|
5977 if (!ool) |
|
5978 return false; |
|
5979 |
|
5980 LoadNativeIterator(masm, obj, temp1, ool->entry()); |
|
5981 |
|
5982 masm.branchTest32(Assembler::Zero, Address(temp1, offsetof(NativeIterator, flags)), |
|
5983 Imm32(JSITER_ENUMERATE), ool->entry()); |
|
5984 |
|
5985 // Clear active bit. |
|
5986 masm.and32(Imm32(~JSITER_ACTIVE), Address(temp1, offsetof(NativeIterator, flags))); |
|
5987 |
|
5988 // Reset property cursor. |
|
5989 masm.loadPtr(Address(temp1, offsetof(NativeIterator, props_array)), temp2); |
|
5990 masm.storePtr(temp2, Address(temp1, offsetof(NativeIterator, props_cursor))); |
|
5991 |
|
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 |
|
6003 |
|
6004 masm.bind(ool->rejoin()); |
|
6005 return true; |
|
6006 } |
|
6007 |
|
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()); |
|
6014 |
|
6015 masm.loadPtr(ptr, argc); |
|
6016 return true; |
|
6017 } |
|
6018 |
|
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(); |
|
6025 |
|
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 } |
|
6037 |
|
6038 bool |
|
6039 CodeGenerator::visitSetFrameArgumentT(LSetFrameArgumentT *lir) |
|
6040 { |
|
6041 size_t argOffset = frameSize() + IonJSFrameLayout::offsetOfActualArgs() + |
|
6042 (sizeof(Value) * lir->mir()->argno()); |
|
6043 |
|
6044 MIRType type = lir->mir()->value()->type(); |
|
6045 |
|
6046 if (type == MIRType_Double) { |
|
6047 // Store doubles directly. |
|
6048 FloatRegister input = ToFloatRegister(lir->input()); |
|
6049 masm.storeDouble(input, Address(StackPointer, argOffset)); |
|
6050 |
|
6051 } else { |
|
6052 Register input = ToRegister(lir->input()); |
|
6053 masm.storeValue(ValueTypeFromMIRType(type), input, Address(StackPointer, argOffset)); |
|
6054 } |
|
6055 return true; |
|
6056 } |
|
6057 |
|
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 } |
|
6066 |
|
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 } |
|
6076 |
|
6077 typedef bool (*RunOnceScriptPrologueFn)(JSContext *, HandleScript); |
|
6078 static const VMFunction RunOnceScriptPrologueInfo = |
|
6079 FunctionInfo<RunOnceScriptPrologueFn>(js::RunOnceScriptPrologue); |
|
6080 |
|
6081 bool |
|
6082 CodeGenerator::visitRunOncePrologue(LRunOncePrologue *lir) |
|
6083 { |
|
6084 pushArg(ImmGCPtr(lir->mir()->block()->info().script())); |
|
6085 return callVM(RunOnceScriptPrologueInfo, lir); |
|
6086 } |
|
6087 |
|
6088 |
|
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)); |
|
6096 |
|
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); |
|
6106 |
|
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); |
|
6118 |
|
6119 pushArg(array); |
|
6120 pushArg(ImmGCPtr(templateObject)); |
|
6121 pushArg(temp1); |
|
6122 pushArg(temp0); |
|
6123 |
|
6124 return callVM(InitRestParameterInfo, lir); |
|
6125 } |
|
6126 |
|
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(); |
|
6136 |
|
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); |
|
6146 |
|
6147 return emitRest(lir, temp2, numActuals, temp0, temp1, numFormals, templateObject); |
|
6148 } |
|
6149 |
|
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(); |
|
6160 |
|
6161 if (!emitAllocateGCThingPar(lir, temp2, cx, temp0, temp1, templateObject)) |
|
6162 return false; |
|
6163 |
|
6164 return emitRest(lir, temp2, numActuals, temp0, temp1, numFormals, templateObject); |
|
6165 } |
|
6166 |
|
6167 bool |
|
6168 CodeGenerator::generateAsmJS(Label *stackOverflowLabel) |
|
6169 { |
|
6170 IonSpew(IonSpew_Codegen, "# Emitting asm.js code"); |
|
6171 |
|
6172 // AsmJS doesn't do profiler instrumentation. |
|
6173 sps_.disable(); |
|
6174 |
|
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; |
|
6193 |
|
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 } |
|
6213 |
|
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()); |
|
6220 |
|
6221 if (!snapshots_.init()) |
|
6222 return false; |
|
6223 |
|
6224 if (!safepoints_.init(gen->alloc(), graph.totalSlotCount())) |
|
6225 return false; |
|
6226 |
|
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 |
|
6235 |
|
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; |
|
6241 |
|
6242 if (frameClass_ != FrameSizeClass::None()) { |
|
6243 deoptTable_ = gen->jitRuntime()->getBailoutTable(frameClass_); |
|
6244 if (!deoptTable_) |
|
6245 return false; |
|
6246 } |
|
6247 |
|
6248 #ifdef JS_TRACE_LOGGING |
|
6249 Label skip; |
|
6250 masm.jump(&skip); |
|
6251 #endif |
|
6252 |
|
6253 // Remember the entry offset to skip the argument check. |
|
6254 masm.flushBuffer(); |
|
6255 setSkipArgCheckEntryOffset(masm.size()); |
|
6256 |
|
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 |
|
6266 |
|
6267 #ifdef DEBUG |
|
6268 // Assert that the argument types are correct. |
|
6269 if (!generateArgumentsChecks(/* bailout = */ false)) |
|
6270 return false; |
|
6271 #endif |
|
6272 |
|
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; |
|
6287 |
|
6288 return !masm.oom(); |
|
6289 } |
|
6290 |
|
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(); |
|
6297 |
|
6298 JS_ASSERT_IF(HasIonScript(script, executionMode), executionMode == SequentialExecution); |
|
6299 |
|
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 } |
|
6312 |
|
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; |
|
6318 |
|
6319 uint32_t scriptFrameSize = frameClass_ == FrameSizeClass::None() |
|
6320 ? frameDepth_ |
|
6321 : FrameSizeClass::FromDepth(frameDepth_).frameSize(); |
|
6322 |
|
6323 // We encode safepoints after the OSI-point offsets have been determined. |
|
6324 encodeSafepoints(); |
|
6325 |
|
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); |
|
6331 |
|
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 } |
|
6345 |
|
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()); |
|
6351 |
|
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()); |
|
6355 |
|
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 } |
|
6371 |
|
6372 ionScript->setMethod(code); |
|
6373 ionScript->setSkipArgCheckEntryOffset(getSkipArgCheckEntryOffset()); |
|
6374 |
|
6375 // If SPS is enabled, mark IonScript as having been instrumented with SPS |
|
6376 if (sps_.enabled()) |
|
6377 ionScript->setHasSPSInstrumentation(); |
|
6378 |
|
6379 SetIonScript(script, executionMode, ionScript); |
|
6380 |
|
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 } |
|
6393 |
|
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(); |
|
6399 |
|
6400 invalidateEpilogueData_.fixup(&masm); |
|
6401 Assembler::patchDataWithValueCheck(CodeLocationLabel(code, invalidateEpilogueData_), |
|
6402 ImmPtr(ionScript), |
|
6403 ImmPtr((void*)-1)); |
|
6404 |
|
6405 IonSpew(IonSpew_Codegen, "Created IonScript %p (raw %p)", |
|
6406 (void *) ionScript, (void *) code->raw()); |
|
6407 |
|
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); |
|
6413 |
|
6414 ionScript->setDeoptTable(deoptTable_); |
|
6415 |
|
6416 #if defined(JS_ION_PERF) |
|
6417 if (PerfEnabled()) |
|
6418 perfSpewer_.writeProfile(script, code, masm); |
|
6419 #endif |
|
6420 |
|
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 } |
|
6427 |
|
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); |
|
6433 |
|
6434 // for marking during GC. |
|
6435 if (safepointIndices_.length()) |
|
6436 ionScript->copySafepointIndices(&safepointIndices_[0], masm); |
|
6437 if (safepoints_.size()) |
|
6438 ionScript->copySafepoints(&safepoints_); |
|
6439 |
|
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()); |
|
6456 |
|
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 |
|
6473 |
|
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 } |
|
6489 |
|
6490 return true; |
|
6491 } |
|
6492 |
|
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_; |
|
6497 |
|
6498 public: |
|
6499 OutOfLineUnboxFloatingPoint(LUnboxFloatingPoint *unboxFloatingPoint) |
|
6500 : unboxFloatingPoint_(unboxFloatingPoint) |
|
6501 { } |
|
6502 |
|
6503 bool accept(CodeGenerator *codegen) { |
|
6504 return codegen->visitOutOfLineUnboxFloatingPoint(this); |
|
6505 } |
|
6506 |
|
6507 LUnboxFloatingPoint *unboxFloatingPoint() const { |
|
6508 return unboxFloatingPoint_; |
|
6509 } |
|
6510 }; |
|
6511 |
|
6512 bool |
|
6513 CodeGenerator::visitUnboxFloatingPoint(LUnboxFloatingPoint *lir) |
|
6514 { |
|
6515 const ValueOperand box = ToValue(lir, LUnboxFloatingPoint::Input); |
|
6516 const LDefinition *result = lir->output(); |
|
6517 |
|
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; |
|
6523 |
|
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 } |
|
6532 |
|
6533 bool |
|
6534 CodeGenerator::visitOutOfLineUnboxFloatingPoint(OutOfLineUnboxFloatingPoint *ool) |
|
6535 { |
|
6536 LUnboxFloatingPoint *ins = ool->unboxFloatingPoint(); |
|
6537 const ValueOperand value = ToValue(ins, LUnboxFloatingPoint::Input); |
|
6538 |
|
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 } |
|
6549 |
|
6550 typedef bool (*GetPropertyFn)(JSContext *, HandleValue, HandlePropertyName, MutableHandleValue); |
|
6551 static const VMFunction GetPropertyInfo = FunctionInfo<GetPropertyFn>(GetProperty); |
|
6552 static const VMFunction CallPropertyInfo = FunctionInfo<GetPropertyFn>(CallProperty); |
|
6553 |
|
6554 bool |
|
6555 CodeGenerator::visitCallGetProperty(LCallGetProperty *lir) |
|
6556 { |
|
6557 pushArg(ImmGCPtr(lir->mir()->name())); |
|
6558 pushArg(ToValue(lir, LCallGetProperty::Value)); |
|
6559 |
|
6560 if (lir->mir()->callprop()) |
|
6561 return callVM(CallPropertyInfo, lir); |
|
6562 return callVM(GetPropertyInfo, lir); |
|
6563 } |
|
6564 |
|
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); |
|
6568 |
|
6569 bool |
|
6570 CodeGenerator::visitCallGetElement(LCallGetElement *lir) |
|
6571 { |
|
6572 pushArg(ToValue(lir, LCallGetElement::RhsInput)); |
|
6573 pushArg(ToValue(lir, LCallGetElement::LhsInput)); |
|
6574 |
|
6575 JSOp op = JSOp(*lir->mir()->resumePoint()->pc()); |
|
6576 |
|
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 } |
|
6584 |
|
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)); |
|
6591 |
|
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 } |
|
6601 |
|
6602 typedef bool (*InitElementArrayFn)(JSContext *, jsbytecode *, HandleObject, uint32_t, HandleValue); |
|
6603 static const VMFunction InitElementArrayInfo = FunctionInfo<InitElementArrayFn>(js::InitElementArray); |
|
6604 |
|
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 } |
|
6614 |
|
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); |
|
6621 |
|
6622 masm.loadValue(Address(obj, JSObject::getFixedSlotOffset(slot)), result); |
|
6623 return true; |
|
6624 } |
|
6625 |
|
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(); |
|
6633 |
|
6634 masm.loadUnboxedValue(Address(obj, JSObject::getFixedSlotOffset(slot)), type, result); |
|
6635 |
|
6636 return true; |
|
6637 } |
|
6638 |
|
6639 bool |
|
6640 CodeGenerator::visitStoreFixedSlotV(LStoreFixedSlotV *ins) |
|
6641 { |
|
6642 const Register obj = ToRegister(ins->getOperand(0)); |
|
6643 size_t slot = ins->mir()->slot(); |
|
6644 |
|
6645 const ValueOperand value = ToValue(ins, LStoreFixedSlotV::Value); |
|
6646 |
|
6647 Address address(obj, JSObject::getFixedSlotOffset(slot)); |
|
6648 if (ins->mir()->needsBarrier()) |
|
6649 emitPreBarrier(address, MIRType_Value); |
|
6650 |
|
6651 masm.storeValue(value, address); |
|
6652 |
|
6653 return true; |
|
6654 } |
|
6655 |
|
6656 bool |
|
6657 CodeGenerator::visitStoreFixedSlotT(LStoreFixedSlotT *ins) |
|
6658 { |
|
6659 const Register obj = ToRegister(ins->getOperand(0)); |
|
6660 size_t slot = ins->mir()->slot(); |
|
6661 |
|
6662 const LAllocation *value = ins->value(); |
|
6663 MIRType valueType = ins->mir()->value()->type(); |
|
6664 |
|
6665 ConstantOrRegister nvalue = value->isConstant() |
|
6666 ? ConstantOrRegister(*value->toConstant()) |
|
6667 : TypedOrValueRegister(valueType, ToAnyRegister(value)); |
|
6668 |
|
6669 Address address(obj, JSObject::getFixedSlotOffset(slot)); |
|
6670 if (ins->mir()->needsBarrier()) |
|
6671 emitPreBarrier(address, MIRType_Value); |
|
6672 |
|
6673 masm.storeConstantOrRegister(nvalue, address); |
|
6674 |
|
6675 return true; |
|
6676 } |
|
6677 |
|
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()); |
|
6684 |
|
6685 CallsiteCloneIC cache(callee, mir->block()->info().script(), mir->callPc(), output); |
|
6686 return addCache(ins, allocateCache(cache)); |
|
6687 } |
|
6688 |
|
6689 typedef JSObject *(*CallsiteCloneICFn)(JSContext *, size_t, HandleObject); |
|
6690 const VMFunction CallsiteCloneIC::UpdateInfo = |
|
6691 FunctionInfo<CallsiteCloneICFn>(CallsiteCloneIC::update); |
|
6692 |
|
6693 bool |
|
6694 CodeGenerator::visitCallsiteCloneIC(OutOfLineUpdateCache *ool, DataPtr<CallsiteCloneIC> &ic) |
|
6695 { |
|
6696 LInstruction *lir = ool->lir(); |
|
6697 saveLive(lir); |
|
6698 |
|
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()); |
|
6705 |
|
6706 masm.jump(ool->rejoin()); |
|
6707 return true; |
|
6708 } |
|
6709 |
|
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; |
|
6717 |
|
6718 NameIC cache(liveRegs, isTypeOf, scopeChain, ins->mir()->name(), output); |
|
6719 return addCache(ins, allocateCache(cache)); |
|
6720 } |
|
6721 |
|
6722 typedef bool (*NameICFn)(JSContext *, size_t, HandleObject, MutableHandleValue); |
|
6723 const VMFunction NameIC::UpdateInfo = FunctionInfo<NameICFn>(NameIC::update); |
|
6724 |
|
6725 bool |
|
6726 CodeGenerator::visitNameIC(OutOfLineUpdateCache *ool, DataPtr<NameIC> &ic) |
|
6727 { |
|
6728 LInstruction *lir = ool->lir(); |
|
6729 saveLive(lir); |
|
6730 |
|
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()); |
|
6737 |
|
6738 masm.jump(ool->rejoin()); |
|
6739 return true; |
|
6740 } |
|
6741 |
|
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 } |
|
6760 |
|
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 } |
|
6779 |
|
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 } |
|
6800 |
|
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)); |
|
6809 |
|
6810 return addGetPropertyCache(ins, liveRegs, objReg, name, output, monitoredResult); |
|
6811 } |
|
6812 |
|
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))); |
|
6821 |
|
6822 return addGetPropertyCache(ins, liveRegs, objReg, name, output, monitoredResult); |
|
6823 } |
|
6824 |
|
6825 typedef bool (*GetPropertyICFn)(JSContext *, size_t, HandleObject, MutableHandleValue); |
|
6826 const VMFunction GetPropertyIC::UpdateInfo = |
|
6827 FunctionInfo<GetPropertyICFn>(GetPropertyIC::update); |
|
6828 |
|
6829 bool |
|
6830 CodeGenerator::visitGetPropertyIC(OutOfLineUpdateCache *ool, DataPtr<GetPropertyIC> &ic) |
|
6831 { |
|
6832 LInstruction *lir = ool->lir(); |
|
6833 |
|
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 } |
|
6840 |
|
6841 saveLive(lir); |
|
6842 |
|
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()); |
|
6849 |
|
6850 masm.jump(ool->rejoin()); |
|
6851 return true; |
|
6852 } |
|
6853 |
|
6854 typedef bool (*GetPropertyParICFn)(ForkJoinContext *, size_t, HandleObject, MutableHandleValue); |
|
6855 const VMFunction GetPropertyParIC::UpdateInfo = |
|
6856 FunctionInfo<GetPropertyParICFn>(GetPropertyParIC::update); |
|
6857 |
|
6858 bool |
|
6859 CodeGenerator::visitGetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<GetPropertyParIC> &ic) |
|
6860 { |
|
6861 LInstruction *lir = ool->lir(); |
|
6862 saveLive(lir); |
|
6863 |
|
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()); |
|
6870 |
|
6871 masm.jump(ool->rejoin()); |
|
6872 return true; |
|
6873 } |
|
6874 |
|
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 } |
|
6894 |
|
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(); |
|
6902 |
|
6903 return addGetElementCache(ins, obj, index, output, mir->monitoredResult(), mir->allowDoubleResult()); |
|
6904 } |
|
6905 |
|
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(); |
|
6913 |
|
6914 return addGetElementCache(ins, obj, index, output, mir->monitoredResult(), mir->allowDoubleResult()); |
|
6915 } |
|
6916 |
|
6917 typedef bool (*GetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, MutableHandleValue); |
|
6918 const VMFunction GetElementIC::UpdateInfo = |
|
6919 FunctionInfo<GetElementICFn>(GetElementIC::update); |
|
6920 |
|
6921 bool |
|
6922 CodeGenerator::visitGetElementIC(OutOfLineUpdateCache *ool, DataPtr<GetElementIC> &ic) |
|
6923 { |
|
6924 LInstruction *lir = ool->lir(); |
|
6925 saveLive(lir); |
|
6926 |
|
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()); |
|
6934 |
|
6935 masm.jump(ool->rejoin()); |
|
6936 return true; |
|
6937 } |
|
6938 |
|
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)); |
|
6948 |
|
6949 return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value, |
|
6950 ins->mir()->strict(), ins->mir()->guardHoles()); |
|
6951 } |
|
6952 |
|
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)); |
|
6967 |
|
6968 return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value, |
|
6969 ins->mir()->strict(), ins->mir()->guardHoles()); |
|
6970 } |
|
6971 |
|
6972 typedef bool (*SetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, HandleValue); |
|
6973 const VMFunction SetElementIC::UpdateInfo = |
|
6974 FunctionInfo<SetElementICFn>(SetElementIC::update); |
|
6975 |
|
6976 bool |
|
6977 CodeGenerator::visitSetElementIC(OutOfLineUpdateCache *ool, DataPtr<SetElementIC> &ic) |
|
6978 { |
|
6979 LInstruction *lir = ool->lir(); |
|
6980 saveLive(lir); |
|
6981 |
|
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); |
|
6989 |
|
6990 masm.jump(ool->rejoin()); |
|
6991 return true; |
|
6992 } |
|
6993 |
|
6994 typedef bool (*SetElementParICFn)(ForkJoinContext *, size_t, HandleObject, HandleValue, HandleValue); |
|
6995 const VMFunction SetElementParIC::UpdateInfo = |
|
6996 FunctionInfo<SetElementParICFn>(SetElementParIC::update); |
|
6997 |
|
6998 bool |
|
6999 CodeGenerator::visitSetElementParIC(OutOfLineUpdateCache *ool, DataPtr<SetElementParIC> &ic) |
|
7000 { |
|
7001 LInstruction *lir = ool->lir(); |
|
7002 saveLive(lir); |
|
7003 |
|
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); |
|
7011 |
|
7012 masm.jump(ool->rejoin()); |
|
7013 return true; |
|
7014 } |
|
7015 |
|
7016 typedef bool (*GetElementParICFn)(ForkJoinContext *, size_t, HandleObject, HandleValue, |
|
7017 MutableHandleValue); |
|
7018 const VMFunction GetElementParIC::UpdateInfo = |
|
7019 FunctionInfo<GetElementParICFn>(GetElementParIC::update); |
|
7020 |
|
7021 bool |
|
7022 CodeGenerator::visitGetElementParIC(OutOfLineUpdateCache *ool, DataPtr<GetElementParIC> &ic) |
|
7023 { |
|
7024 LInstruction *lir = ool->lir(); |
|
7025 saveLive(lir); |
|
7026 |
|
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()); |
|
7034 |
|
7035 masm.jump(ool->rejoin()); |
|
7036 return true; |
|
7037 } |
|
7038 |
|
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); |
|
7045 |
|
7046 return addCache(ins, allocateCache(cache)); |
|
7047 } |
|
7048 |
|
7049 typedef JSObject *(*BindNameICFn)(JSContext *, size_t, HandleObject); |
|
7050 const VMFunction BindNameIC::UpdateInfo = |
|
7051 FunctionInfo<BindNameICFn>(BindNameIC::update); |
|
7052 |
|
7053 bool |
|
7054 CodeGenerator::visitBindNameIC(OutOfLineUpdateCache *ool, DataPtr<BindNameIC> &ic) |
|
7055 { |
|
7056 LInstruction *lir = ool->lir(); |
|
7057 saveLive(lir); |
|
7058 |
|
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()); |
|
7065 |
|
7066 masm.jump(ool->rejoin()); |
|
7067 return true; |
|
7068 } |
|
7069 |
|
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)); |
|
7077 |
|
7078 bool |
|
7079 CodeGenerator::visitCallSetProperty(LCallSetProperty *ins) |
|
7080 { |
|
7081 ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LCallSetProperty::Value)); |
|
7082 |
|
7083 const Register objReg = ToRegister(ins->getOperand(0)); |
|
7084 |
|
7085 pushArg(ImmPtr(ins->mir()->resumePoint()->pc())); |
|
7086 pushArg(Imm32(ins->mir()->strict())); |
|
7087 |
|
7088 pushArg(value); |
|
7089 pushArg(ImmGCPtr(ins->mir()->name())); |
|
7090 pushArg(objReg); |
|
7091 |
|
7092 return callVM(SetPropertyInfo, ins); |
|
7093 } |
|
7094 |
|
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>); |
|
7100 |
|
7101 bool |
|
7102 CodeGenerator::visitCallDeleteProperty(LCallDeleteProperty *lir) |
|
7103 { |
|
7104 pushArg(ImmGCPtr(lir->mir()->name())); |
|
7105 pushArg(ToValue(lir, LCallDeleteProperty::Value)); |
|
7106 |
|
7107 if (lir->mir()->block()->info().script()->strict()) |
|
7108 return callVM(DeletePropertyStrictInfo, lir); |
|
7109 |
|
7110 return callVM(DeletePropertyNonStrictInfo, lir); |
|
7111 } |
|
7112 |
|
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>); |
|
7118 |
|
7119 bool |
|
7120 CodeGenerator::visitCallDeleteElement(LCallDeleteElement *lir) |
|
7121 { |
|
7122 pushArg(ToValue(lir, LCallDeleteElement::Index)); |
|
7123 pushArg(ToValue(lir, LCallDeleteElement::Value)); |
|
7124 |
|
7125 if (lir->mir()->block()->info().script()->strict()) |
|
7126 return callVM(DeleteElementStrictInfo, lir); |
|
7127 |
|
7128 return callVM(DeleteElementNonStrictInfo, lir); |
|
7129 } |
|
7130 |
|
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)); |
|
7137 |
|
7138 return addSetPropertyCache(ins, liveRegs, objReg, ins->mir()->name(), value, |
|
7139 ins->mir()->strict(), ins->mir()->needsTypeBarrier()); |
|
7140 } |
|
7141 |
|
7142 bool |
|
7143 CodeGenerator::visitSetPropertyCacheT(LSetPropertyCacheT *ins) |
|
7144 { |
|
7145 RegisterSet liveRegs = ins->safepoint()->liveRegs(); |
|
7146 Register objReg = ToRegister(ins->getOperand(0)); |
|
7147 ConstantOrRegister value; |
|
7148 |
|
7149 if (ins->getOperand(1)->isConstant()) |
|
7150 value = ConstantOrRegister(*ins->getOperand(1)->toConstant()); |
|
7151 else |
|
7152 value = TypedOrValueRegister(ins->valueType(), ToAnyRegister(ins->getOperand(1))); |
|
7153 |
|
7154 return addSetPropertyCache(ins, liveRegs, objReg, ins->mir()->name(), value, |
|
7155 ins->mir()->strict(), ins->mir()->needsTypeBarrier()); |
|
7156 } |
|
7157 |
|
7158 typedef bool (*SetPropertyICFn)(JSContext *, size_t, HandleObject, HandleValue); |
|
7159 const VMFunction SetPropertyIC::UpdateInfo = |
|
7160 FunctionInfo<SetPropertyICFn>(SetPropertyIC::update); |
|
7161 |
|
7162 bool |
|
7163 CodeGenerator::visitSetPropertyIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyIC> &ic) |
|
7164 { |
|
7165 LInstruction *lir = ool->lir(); |
|
7166 saveLive(lir); |
|
7167 |
|
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); |
|
7174 |
|
7175 masm.jump(ool->rejoin()); |
|
7176 return true; |
|
7177 } |
|
7178 |
|
7179 typedef bool (*SetPropertyParICFn)(ForkJoinContext *, size_t, HandleObject, HandleValue); |
|
7180 const VMFunction SetPropertyParIC::UpdateInfo = |
|
7181 FunctionInfo<SetPropertyParICFn>(SetPropertyParIC::update); |
|
7182 |
|
7183 bool |
|
7184 CodeGenerator::visitSetPropertyParIC(OutOfLineUpdateCache *ool, DataPtr<SetPropertyParIC> &ic) |
|
7185 { |
|
7186 LInstruction *lir = ool->lir(); |
|
7187 saveLive(lir); |
|
7188 |
|
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); |
|
7195 |
|
7196 masm.jump(ool->rejoin()); |
|
7197 return true; |
|
7198 } |
|
7199 |
|
7200 typedef bool (*ThrowFn)(JSContext *, HandleValue); |
|
7201 static const VMFunction ThrowInfoCodeGen = FunctionInfo<ThrowFn>(js::Throw); |
|
7202 |
|
7203 bool |
|
7204 CodeGenerator::visitThrow(LThrow *lir) |
|
7205 { |
|
7206 pushArg(ToValue(lir, LThrow::Value)); |
|
7207 return callVM(ThrowInfoCodeGen, lir); |
|
7208 } |
|
7209 |
|
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)); |
|
7215 |
|
7216 bool |
|
7217 CodeGenerator::visitBitNotV(LBitNotV *lir) |
|
7218 { |
|
7219 pushArg(ToValue(lir, LBitNotV::Input)); |
|
7220 return callVM(BitNotInfo, lir); |
|
7221 } |
|
7222 |
|
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)); |
|
7240 |
|
7241 bool |
|
7242 CodeGenerator::visitBitOpV(LBitOpV *lir) |
|
7243 { |
|
7244 pushArg(ToValue(lir, LBitOpV::RhsInput)); |
|
7245 pushArg(ToValue(lir, LBitOpV::LhsInput)); |
|
7246 |
|
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 } |
|
7263 |
|
7264 class OutOfLineTypeOfV : public OutOfLineCodeBase<CodeGenerator> |
|
7265 { |
|
7266 LTypeOfV *ins_; |
|
7267 |
|
7268 public: |
|
7269 OutOfLineTypeOfV(LTypeOfV *ins) |
|
7270 : ins_(ins) |
|
7271 { } |
|
7272 |
|
7273 bool accept(CodeGenerator *codegen) { |
|
7274 return codegen->visitOutOfLineTypeOfV(this); |
|
7275 } |
|
7276 LTypeOfV *ins() const { |
|
7277 return ins_; |
|
7278 } |
|
7279 }; |
|
7280 |
|
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); |
|
7287 |
|
7288 const JSAtomState &names = GetIonContext()->runtime->names(); |
|
7289 Label done; |
|
7290 |
|
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; |
|
7298 |
|
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 } |
|
7309 |
|
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); |
|
7315 |
|
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); |
|
7321 |
|
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); |
|
7327 |
|
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); |
|
7333 |
|
7334 masm.movePtr(ImmGCPtr(names.string), output); |
|
7335 |
|
7336 masm.bind(&done); |
|
7337 if (ool) |
|
7338 masm.bind(ool->rejoin()); |
|
7339 return true; |
|
7340 } |
|
7341 |
|
7342 bool |
|
7343 CodeGenerator::visitOutOfLineTypeOfV(OutOfLineTypeOfV *ool) |
|
7344 { |
|
7345 LTypeOfV *ins = ool->ins(); |
|
7346 |
|
7347 ValueOperand input = ToValue(ins, LTypeOfV::Input); |
|
7348 Register temp = ToTempUnboxRegister(ins->tempToUnbox()); |
|
7349 Register output = ToRegister(ins->output()); |
|
7350 |
|
7351 Register obj = masm.extractObject(input, temp); |
|
7352 |
|
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); |
|
7361 |
|
7362 masm.jump(ool->rejoin()); |
|
7363 return true; |
|
7364 } |
|
7365 |
|
7366 typedef bool (*ToIdFn)(JSContext *, HandleScript, jsbytecode *, HandleValue, HandleValue, |
|
7367 MutableHandleValue); |
|
7368 static const VMFunction ToIdInfo = FunctionInfo<ToIdFn>(ToIdOperation); |
|
7369 |
|
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); |
|
7377 |
|
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)); |
|
7385 |
|
7386 Register tag = masm.splitTagForTest(index); |
|
7387 |
|
7388 masm.branchTestInt32(Assembler::NotEqual, tag, ¬Int32); |
|
7389 masm.moveValue(index, out); |
|
7390 masm.jump(ool->rejoin()); |
|
7391 |
|
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); |
|
7397 |
|
7398 masm.bind(ool->rejoin()); |
|
7399 return true; |
|
7400 } |
|
7401 |
|
7402 bool |
|
7403 CodeGenerator::visitLoadElementV(LLoadElementV *load) |
|
7404 { |
|
7405 Register elements = ToRegister(load->elements()); |
|
7406 const ValueOperand out = ToOutValue(load); |
|
7407 |
|
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); |
|
7412 |
|
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 } |
|
7419 |
|
7420 return true; |
|
7421 } |
|
7422 |
|
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); |
|
7429 |
|
7430 const MLoadElementHole *mir = lir->mir(); |
|
7431 |
|
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 } |
|
7442 |
|
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); |
|
7449 |
|
7450 masm.bind(&undefined); |
|
7451 |
|
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 } |
|
7463 |
|
7464 masm.moveValue(UndefinedValue(), out); |
|
7465 masm.bind(&done); |
|
7466 return true; |
|
7467 } |
|
7468 |
|
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()); |
|
7475 |
|
7476 int arrayType = lir->mir()->arrayType(); |
|
7477 int width = TypedArrayObject::slotWidth(arrayType); |
|
7478 |
|
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 } |
|
7487 |
|
7488 if (fail.used() && !bailoutFrom(&fail, lir->snapshot())) |
|
7489 return false; |
|
7490 |
|
7491 return true; |
|
7492 } |
|
7493 |
|
7494 bool |
|
7495 CodeGenerator::visitLoadTypedArrayElementHole(LLoadTypedArrayElementHole *lir) |
|
7496 { |
|
7497 Register object = ToRegister(lir->object()); |
|
7498 const ValueOperand out = ToOutValue(lir); |
|
7499 |
|
7500 // Load the length. |
|
7501 Register scratch = out.scratchReg(); |
|
7502 Int32Key key = ToInt32Key(lir->index()); |
|
7503 masm.unboxInt32(Address(object, TypedArrayObject::lengthOffset()), scratch); |
|
7504 |
|
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); |
|
7510 |
|
7511 // Load the elements vector. |
|
7512 masm.bind(&inbounds); |
|
7513 masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), scratch); |
|
7514 |
|
7515 int arrayType = lir->mir()->arrayType(); |
|
7516 int width = TypedArrayObject::slotWidth(arrayType); |
|
7517 |
|
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 } |
|
7528 |
|
7529 if (fail.used() && !bailoutFrom(&fail, lir->snapshot())) |
|
7530 return false; |
|
7531 |
|
7532 masm.bind(&done); |
|
7533 return true; |
|
7534 } |
|
7535 |
|
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 } |
|
7551 |
|
7552 bool |
|
7553 CodeGenerator::visitStoreTypedArrayElement(LStoreTypedArrayElement *lir) |
|
7554 { |
|
7555 Register elements = ToRegister(lir->elements()); |
|
7556 const LAllocation *value = lir->value(); |
|
7557 |
|
7558 int arrayType = lir->mir()->arrayType(); |
|
7559 int width = TypedArrayObject::slotWidth(arrayType); |
|
7560 |
|
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 } |
|
7568 |
|
7569 return true; |
|
7570 } |
|
7571 |
|
7572 bool |
|
7573 CodeGenerator::visitStoreTypedArrayElementHole(LStoreTypedArrayElementHole *lir) |
|
7574 { |
|
7575 Register elements = ToRegister(lir->elements()); |
|
7576 const LAllocation *value = lir->value(); |
|
7577 |
|
7578 int arrayType = lir->mir()->arrayType(); |
|
7579 int width = TypedArrayObject::slotWidth(arrayType); |
|
7580 |
|
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); |
|
7608 |
|
7609 return true; |
|
7610 } |
|
7611 |
|
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 } |
|
7620 |
|
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 } |
|
7629 |
|
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(); |
|
7637 |
|
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 } |
|
7650 |
|
7651 Label fails; |
|
7652 masm.clampValueToUint8(operand, input, |
|
7653 stringEntry, stringRejoin, |
|
7654 output, tempFloat, output, &fails); |
|
7655 |
|
7656 if (!bailoutFrom(&fails, lir->snapshot())) |
|
7657 return false; |
|
7658 |
|
7659 return true; |
|
7660 } |
|
7661 |
|
7662 typedef bool (*OperatorInFn)(JSContext *, HandleValue, HandleObject, bool *); |
|
7663 static const VMFunction OperatorInInfo = FunctionInfo<OperatorInFn>(OperatorIn); |
|
7664 |
|
7665 bool |
|
7666 CodeGenerator::visitIn(LIn *ins) |
|
7667 { |
|
7668 pushArg(ToRegister(ins->rhs())); |
|
7669 pushArg(ToValue(ins, LIn::LHS)); |
|
7670 |
|
7671 return callVM(OperatorInInfo, ins); |
|
7672 } |
|
7673 |
|
7674 typedef bool (*OperatorInIFn)(JSContext *, uint32_t, HandleObject, bool *); |
|
7675 static const VMFunction OperatorInIInfo = FunctionInfo<OperatorInIFn>(OperatorInI); |
|
7676 |
|
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()); |
|
7684 |
|
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; |
|
7687 |
|
7688 OutOfLineCode *ool = nullptr; |
|
7689 Label* failedInitLength = &falseBranch; |
|
7690 |
|
7691 if (lir->index()->isConstant()) { |
|
7692 int32_t index = ToInt32(lir->index()); |
|
7693 |
|
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 } |
|
7701 |
|
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()); |
|
7710 |
|
7711 if (mir->needsNegativeIntCheck()) |
|
7712 failedInitLength = &negativeIntCheck; |
|
7713 |
|
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); |
|
7720 |
|
7721 if (mir->needsNegativeIntCheck()) { |
|
7722 masm.bind(&negativeIntCheck); |
|
7723 ool = oolCallVM(OperatorInIInfo, lir, |
|
7724 (ArgList(), index, ToRegister(lir->object())), |
|
7725 StoreRegisterTo(output)); |
|
7726 |
|
7727 masm.branch32(Assembler::LessThan, index, Imm32(0), ool->entry()); |
|
7728 masm.jump(&falseBranch); |
|
7729 } |
|
7730 } |
|
7731 |
|
7732 masm.bind(&trueBranch); |
|
7733 masm.move32(Imm32(1), output); |
|
7734 masm.jump(&done); |
|
7735 |
|
7736 masm.bind(&falseBranch); |
|
7737 masm.move32(Imm32(0), output); |
|
7738 masm.bind(&done); |
|
7739 |
|
7740 if (ool) |
|
7741 masm.bind(ool->rejoin()); |
|
7742 |
|
7743 return true; |
|
7744 } |
|
7745 |
|
7746 bool |
|
7747 CodeGenerator::visitInstanceOfO(LInstanceOfO *ins) |
|
7748 { |
|
7749 return emitInstanceOf(ins, ins->mir()->prototypeObject()); |
|
7750 } |
|
7751 |
|
7752 bool |
|
7753 CodeGenerator::visitInstanceOfV(LInstanceOfV *ins) |
|
7754 { |
|
7755 return emitInstanceOf(ins, ins->mir()->prototypeObject()); |
|
7756 } |
|
7757 |
|
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 } |
|
7764 |
|
7765 typedef bool (*IsDelegateObjectFn)(JSContext *, HandleObject, HandleObject, bool *); |
|
7766 static const VMFunction IsDelegateObjectInfo = FunctionInfo<IsDelegateObjectFn>(IsDelegateObject); |
|
7767 |
|
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. |
|
7773 |
|
7774 Label done; |
|
7775 Register output = ToRegister(ins->getDef(0)); |
|
7776 |
|
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 } |
|
7790 |
|
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. |
|
7794 |
|
7795 // Load the lhs's prototype. |
|
7796 masm.loadObjProto(objReg, output); |
|
7797 |
|
7798 Label testLazy; |
|
7799 { |
|
7800 Label loopPrototypeChain; |
|
7801 masm.bind(&loopPrototypeChain); |
|
7802 |
|
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); |
|
7809 |
|
7810 JS_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1); |
|
7811 |
|
7812 // Test for nullptr or Proxy::LazyProto |
|
7813 masm.branchPtr(Assembler::BelowOrEqual, output, ImmWord(1), &testLazy); |
|
7814 |
|
7815 // Load the current object's prototype. |
|
7816 masm.loadObjProto(output, output); |
|
7817 |
|
7818 masm.jump(&loopPrototypeChain); |
|
7819 } |
|
7820 |
|
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. |
|
7826 |
|
7827 OutOfLineCode *ool = oolCallVM(IsDelegateObjectInfo, ins, |
|
7828 (ArgList(), ImmGCPtr(prototypeObject), objReg), |
|
7829 StoreRegisterTo(output)); |
|
7830 |
|
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 } |
|
7847 |
|
7848 masm.bind(&testLazy); |
|
7849 masm.branchPtr(Assembler::Equal, output, ImmWord(1), lazyEntry); |
|
7850 |
|
7851 masm.bind(&done); |
|
7852 masm.bind(ool->rejoin()); |
|
7853 return true; |
|
7854 } |
|
7855 |
|
7856 typedef bool (*HasInstanceFn)(JSContext *, HandleObject, HandleValue, bool *); |
|
7857 static const VMFunction HasInstanceInfo = FunctionInfo<HasInstanceFn>(js::HasInstance); |
|
7858 |
|
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); |
|
7865 |
|
7866 pushArg(lhs); |
|
7867 pushArg(rhs); |
|
7868 return callVM(HasInstanceInfo, ins); |
|
7869 } |
|
7870 |
|
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()); |
|
7878 |
|
7879 DebugOnly<uint32_t> initialStack = masm.framePushed(); |
|
7880 |
|
7881 masm.checkStackAlignment(); |
|
7882 |
|
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); |
|
7890 |
|
7891 masm.Push(ObjectReg); |
|
7892 |
|
7893 // GetReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate() |
|
7894 masm.loadPrivate(Address(ObjectReg, JSObject::getFixedSlotOffset(0)), PrivateReg); |
|
7895 |
|
7896 // Rooting will happen at GC time. |
|
7897 masm.movePtr(StackPointer, ObjectReg); |
|
7898 |
|
7899 uint32_t safepointOffset; |
|
7900 if (!masm.buildFakeExitFrame(JSContextReg, &safepointOffset)) |
|
7901 return false; |
|
7902 masm.enterFakeExitFrame(ION_FRAME_DOMGETTER); |
|
7903 |
|
7904 if (!markSafepointAt(safepointOffset, ins)) |
|
7905 return false; |
|
7906 |
|
7907 masm.setupUnalignedABICall(4, JSContextReg); |
|
7908 |
|
7909 masm.loadJSContext(JSContextReg); |
|
7910 |
|
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())); |
|
7916 |
|
7917 if (ins->mir()->isInfallible()) { |
|
7918 masm.loadValue(Address(StackPointer, IonDOMExitFrameLayout::offsetOfResult()), |
|
7919 JSReturnOperand); |
|
7920 } else { |
|
7921 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
|
7922 |
|
7923 masm.loadValue(Address(StackPointer, IonDOMExitFrameLayout::offsetOfResult()), |
|
7924 JSReturnOperand); |
|
7925 } |
|
7926 masm.adjustStack(IonDOMExitFrameLayout::Size()); |
|
7927 |
|
7928 JS_ASSERT(masm.framePushed() == initialStack); |
|
7929 return true; |
|
7930 } |
|
7931 |
|
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); |
|
7943 |
|
7944 masm.loadValue(Address(object, JSObject::getFixedSlotOffset(slot)), result); |
|
7945 return true; |
|
7946 } |
|
7947 |
|
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()); |
|
7955 |
|
7956 DebugOnly<uint32_t> initialStack = masm.framePushed(); |
|
7957 |
|
7958 masm.checkStackAlignment(); |
|
7959 |
|
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); |
|
7967 |
|
7968 masm.Push(ObjectReg); |
|
7969 |
|
7970 // GetReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate() |
|
7971 masm.loadPrivate(Address(ObjectReg, JSObject::getFixedSlotOffset(0)), PrivateReg); |
|
7972 |
|
7973 // Rooting will happen at GC time. |
|
7974 masm.movePtr(StackPointer, ObjectReg); |
|
7975 |
|
7976 uint32_t safepointOffset; |
|
7977 if (!masm.buildFakeExitFrame(JSContextReg, &safepointOffset)) |
|
7978 return false; |
|
7979 masm.enterFakeExitFrame(ION_FRAME_DOMSETTER); |
|
7980 |
|
7981 if (!markSafepointAt(safepointOffset, ins)) |
|
7982 return false; |
|
7983 |
|
7984 masm.setupUnalignedABICall(4, JSContextReg); |
|
7985 |
|
7986 masm.loadJSContext(JSContextReg); |
|
7987 |
|
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())); |
|
7993 |
|
7994 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
|
7995 |
|
7996 masm.adjustStack(IonDOMExitFrameLayout::Size()); |
|
7997 |
|
7998 JS_ASSERT(masm.framePushed() == initialStack); |
|
7999 return true; |
|
8000 } |
|
8001 |
|
8002 typedef bool(*SPSFn)(JSContext *, HandleScript); |
|
8003 static const VMFunction SPSEnterInfo = FunctionInfo<SPSFn>(SPSEnter); |
|
8004 static const VMFunction SPSExitInfo = FunctionInfo<SPSFn>(SPSExit); |
|
8005 |
|
8006 bool |
|
8007 CodeGenerator::visitProfilerStackOp(LProfilerStackOp *lir) |
|
8008 { |
|
8009 Register temp = ToRegister(lir->temp()->output()); |
|
8010 bool inlinedFunction = lir->inlineLevel() > 0; |
|
8011 |
|
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 } |
|
8028 |
|
8029 sps_.leave(masm, temp, /* inlinedFunction = */ true); |
|
8030 if (!sps_.enterInlineFrame()) |
|
8031 return false; |
|
8032 // fallthrough |
|
8033 |
|
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 } |
|
8046 |
|
8047 return sps_.push(lir->script(), masm, temp, /* inlinedFunction = */ inlinedFunction); |
|
8048 |
|
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; |
|
8056 |
|
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 } |
|
8072 |
|
8073 sps_.pop(masm, temp, /* inlinedFunction = */ inlinedFunction); |
|
8074 return true; |
|
8075 |
|
8076 default: |
|
8077 MOZ_ASSUME_UNREACHABLE("invalid LProfilerStackOp type"); |
|
8078 } |
|
8079 } |
|
8080 |
|
8081 bool |
|
8082 CodeGenerator::visitOutOfLineAbortPar(OutOfLineAbortPar *ool) |
|
8083 { |
|
8084 ParallelBailoutCause cause = ool->cause(); |
|
8085 jsbytecode *bytecode = ool->bytecode(); |
|
8086 |
|
8087 masm.move32(Imm32(cause), CallTempReg0); |
|
8088 loadOutermostJSScript(CallTempReg1); |
|
8089 loadJSScriptForBlock(ool->basicBlock(), CallTempReg2); |
|
8090 masm.movePtr(ImmPtr(bytecode), CallTempReg3); |
|
8091 |
|
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)); |
|
8098 |
|
8099 masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); |
|
8100 masm.jump(&returnLabel_); |
|
8101 return true; |
|
8102 } |
|
8103 |
|
8104 bool |
|
8105 CodeGenerator::visitIsCallable(LIsCallable *ins) |
|
8106 { |
|
8107 Register object = ToRegister(ins->object()); |
|
8108 Register output = ToRegister(ins->output()); |
|
8109 |
|
8110 masm.loadObjClass(object, output); |
|
8111 |
|
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); |
|
8117 |
|
8118 masm.bind(¬Function); |
|
8119 masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::Class, call)), ImmPtr(nullptr), output); |
|
8120 masm.bind(&done); |
|
8121 |
|
8122 return true; |
|
8123 } |
|
8124 |
|
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. |
|
8131 |
|
8132 MIRGraph &graph = current->mir()->graph(); |
|
8133 MBasicBlock *entryBlock = graph.entryBlock(); |
|
8134 masm.movePtr(ImmGCPtr(entryBlock->info().script()), reg); |
|
8135 } |
|
8136 |
|
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. |
|
8142 |
|
8143 JSScript *script = block->info().script(); |
|
8144 masm.movePtr(ImmGCPtr(script), reg); |
|
8145 } |
|
8146 |
|
8147 bool |
|
8148 CodeGenerator::visitOutOfLinePropagateAbortPar(OutOfLinePropagateAbortPar *ool) |
|
8149 { |
|
8150 loadOutermostJSScript(CallTempReg0); |
|
8151 loadJSScriptForBlock(ool->lir()->mirRaw()->block(), CallTempReg1); |
|
8152 |
|
8153 masm.setupUnalignedABICall(2, CallTempReg2); |
|
8154 masm.passABIArg(CallTempReg0); |
|
8155 masm.passABIArg(CallTempReg1); |
|
8156 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, PropagateAbortPar)); |
|
8157 |
|
8158 masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); |
|
8159 masm.jump(&returnLabel_); |
|
8160 return true; |
|
8161 } |
|
8162 |
|
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()); |
|
8170 |
|
8171 masm.loadObjClass(lhs, temp); |
|
8172 masm.loadObjClass(rhs, output); |
|
8173 masm.cmpPtrSet(Assembler::Equal, temp, output, output); |
|
8174 |
|
8175 return true; |
|
8176 } |
|
8177 |
|
8178 bool |
|
8179 CodeGenerator::visitHasClass(LHasClass *ins) |
|
8180 { |
|
8181 Register lhs = ToRegister(ins->lhs()); |
|
8182 Register output = ToRegister(ins->output()); |
|
8183 |
|
8184 masm.loadObjClass(lhs, output); |
|
8185 masm.cmpPtrSet(Assembler::Equal, output, ImmPtr(ins->mir()->getClass()), output); |
|
8186 |
|
8187 return true; |
|
8188 } |
|
8189 |
|
8190 bool |
|
8191 CodeGenerator::visitAsmJSCall(LAsmJSCall *ins) |
|
8192 { |
|
8193 MAsmJSCall *mir = ins->mir(); |
|
8194 |
|
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 |
|
8207 |
|
8208 if (mir->spIncrement()) |
|
8209 masm.freeStack(mir->spIncrement()); |
|
8210 |
|
8211 JS_ASSERT((AlignmentAtPrologue + masm.framePushed()) % StackAlignment == 0); |
|
8212 |
|
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 |
|
8220 |
|
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 } |
|
8233 |
|
8234 if (mir->spIncrement()) |
|
8235 masm.reserveStack(mir->spIncrement()); |
|
8236 |
|
8237 postAsmJSCall(ins); |
|
8238 return true; |
|
8239 } |
|
8240 |
|
8241 bool |
|
8242 CodeGenerator::visitAsmJSParameter(LAsmJSParameter *lir) |
|
8243 { |
|
8244 return true; |
|
8245 } |
|
8246 |
|
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 } |
|
8255 |
|
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 } |
|
8264 |
|
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 } |
|
8275 |
|
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 } |
|
8283 |
|
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. |
|
8287 |
|
8288 return true; |
|
8289 } |
|
8290 |
|
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 } |
|
8314 |
|
8315 // This code does not yet check r->canHaveFractionalPart(). This would require new |
|
8316 // assembler interfaces to make rounding instructions available. |
|
8317 |
|
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); |
|
8328 |
|
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); |
|
8341 |
|
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); |
|
8349 |
|
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 } |
|
8357 |
|
8358 return true; |
|
8359 } |
|
8360 |
|
8361 bool |
|
8362 CodeGenerator::visitAssertRangeI(LAssertRangeI *ins) |
|
8363 { |
|
8364 Register input = ToRegister(ins->input()); |
|
8365 const Range *r = ins->range(); |
|
8366 |
|
8367 return emitAssertRangeI(r, input); |
|
8368 } |
|
8369 |
|
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(); |
|
8376 |
|
8377 return emitAssertRangeD(r, input, temp); |
|
8378 } |
|
8379 |
|
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(); |
|
8386 |
|
8387 masm.convertFloat32ToDouble(input, input); |
|
8388 bool success = emitAssertRangeD(r, input, temp); |
|
8389 masm.convertDoubleToFloat32(input, input); |
|
8390 return success; |
|
8391 } |
|
8392 |
|
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; |
|
8400 |
|
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 } |
|
8410 |
|
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 } |
|
8421 |
|
8422 masm.assumeUnreachable("Incorrect range for Value."); |
|
8423 masm.bind(&done); |
|
8424 return true; |
|
8425 } |
|
8426 |
|
8427 typedef bool (*RecompileFn)(JSContext *); |
|
8428 static const VMFunction RecompileFnInfo = FunctionInfo<RecompileFn>(Recompile); |
|
8429 |
|
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)); |
|
8436 |
|
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); |
|
8442 |
|
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); |
|
8453 |
|
8454 return true; |
|
8455 } |
|
8456 |
|
8457 } // namespace jit |
|
8458 } // namespace js |