|
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/BaselineCompiler.h" |
|
8 |
|
9 #include "jit/BaselineHelpers.h" |
|
10 #include "jit/BaselineIC.h" |
|
11 #include "jit/BaselineJIT.h" |
|
12 #include "jit/FixedList.h" |
|
13 #include "jit/IonAnalysis.h" |
|
14 #include "jit/IonLinker.h" |
|
15 #include "jit/IonSpewer.h" |
|
16 #ifdef JS_ION_PERF |
|
17 # include "jit/PerfSpewer.h" |
|
18 #endif |
|
19 #include "jit/VMFunctions.h" |
|
20 #include "vm/TraceLogging.h" |
|
21 |
|
22 #include "jsscriptinlines.h" |
|
23 |
|
24 #include "vm/Interpreter-inl.h" |
|
25 |
|
26 using namespace js; |
|
27 using namespace js::jit; |
|
28 |
|
29 BaselineCompiler::BaselineCompiler(JSContext *cx, TempAllocator &alloc, JSScript *script) |
|
30 : BaselineCompilerSpecific(cx, alloc, script), |
|
31 modifiesArguments_(false) |
|
32 { |
|
33 } |
|
34 |
|
35 bool |
|
36 BaselineCompiler::init() |
|
37 { |
|
38 if (!analysis_.init(alloc_, cx->runtime()->gsnCache)) |
|
39 return false; |
|
40 |
|
41 if (!labels_.init(alloc_, script->length())) |
|
42 return false; |
|
43 |
|
44 for (size_t i = 0; i < script->length(); i++) |
|
45 new (&labels_[i]) Label(); |
|
46 |
|
47 if (!frame.init(alloc_)) |
|
48 return false; |
|
49 |
|
50 return true; |
|
51 } |
|
52 |
|
53 bool |
|
54 BaselineCompiler::addPCMappingEntry(bool addIndexEntry) |
|
55 { |
|
56 // Don't add multiple entries for a single pc. |
|
57 size_t nentries = pcMappingEntries_.length(); |
|
58 if (nentries > 0 && pcMappingEntries_[nentries - 1].pcOffset == script->pcToOffset(pc)) |
|
59 return true; |
|
60 |
|
61 PCMappingEntry entry; |
|
62 entry.pcOffset = script->pcToOffset(pc); |
|
63 entry.nativeOffset = masm.currentOffset(); |
|
64 entry.slotInfo = getStackTopSlotInfo(); |
|
65 entry.addIndexEntry = addIndexEntry; |
|
66 |
|
67 return pcMappingEntries_.append(entry); |
|
68 } |
|
69 |
|
70 MethodStatus |
|
71 BaselineCompiler::compile() |
|
72 { |
|
73 IonSpew(IonSpew_BaselineScripts, "Baseline compiling script %s:%d (%p)", |
|
74 script->filename(), script->lineno(), script); |
|
75 |
|
76 IonSpew(IonSpew_Codegen, "# Emitting baseline code for script %s:%d", |
|
77 script->filename(), script->lineno()); |
|
78 |
|
79 TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); |
|
80 AutoTraceLog logScript(logger, TraceLogCreateTextId(logger, script)); |
|
81 AutoTraceLog logCompile(logger, TraceLogger::BaselineCompilation); |
|
82 |
|
83 if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx)) |
|
84 return Method_Error; |
|
85 |
|
86 // Pin analysis info during compilation. |
|
87 types::AutoEnterAnalysis autoEnterAnalysis(cx); |
|
88 |
|
89 JS_ASSERT(!script->hasBaselineScript()); |
|
90 |
|
91 if (!emitPrologue()) |
|
92 return Method_Error; |
|
93 |
|
94 MethodStatus status = emitBody(); |
|
95 if (status != Method_Compiled) |
|
96 return status; |
|
97 |
|
98 if (!emitEpilogue()) |
|
99 return Method_Error; |
|
100 |
|
101 #ifdef JSGC_GENERATIONAL |
|
102 if (!emitOutOfLinePostBarrierSlot()) |
|
103 return Method_Error; |
|
104 #endif |
|
105 |
|
106 if (masm.oom()) |
|
107 return Method_Error; |
|
108 |
|
109 Linker linker(masm); |
|
110 AutoFlushICache afc("Baseline"); |
|
111 JitCode *code = linker.newCode<CanGC>(cx, JSC::BASELINE_CODE); |
|
112 if (!code) |
|
113 return Method_Error; |
|
114 |
|
115 JSObject *templateScope = nullptr; |
|
116 if (script->functionNonDelazifying()) { |
|
117 RootedFunction fun(cx, script->functionNonDelazifying()); |
|
118 if (fun->isHeavyweight()) { |
|
119 RootedScript scriptRoot(cx, script); |
|
120 templateScope = CallObject::createTemplateObject(cx, scriptRoot, gc::TenuredHeap); |
|
121 if (!templateScope) |
|
122 return Method_Error; |
|
123 |
|
124 if (fun->isNamedLambda()) { |
|
125 RootedObject declEnvObject(cx, DeclEnvObject::createTemplateObject(cx, fun, gc::TenuredHeap)); |
|
126 if (!declEnvObject) |
|
127 return Method_Error; |
|
128 templateScope->as<ScopeObject>().setEnclosingScope(declEnvObject); |
|
129 } |
|
130 } |
|
131 } |
|
132 |
|
133 // Encode the pc mapping table. See PCMappingIndexEntry for |
|
134 // more information. |
|
135 Vector<PCMappingIndexEntry> pcMappingIndexEntries(cx); |
|
136 CompactBufferWriter pcEntries; |
|
137 uint32_t previousOffset = 0; |
|
138 |
|
139 for (size_t i = 0; i < pcMappingEntries_.length(); i++) { |
|
140 PCMappingEntry &entry = pcMappingEntries_[i]; |
|
141 entry.fixupNativeOffset(masm); |
|
142 |
|
143 if (entry.addIndexEntry) { |
|
144 PCMappingIndexEntry indexEntry; |
|
145 indexEntry.pcOffset = entry.pcOffset; |
|
146 indexEntry.nativeOffset = entry.nativeOffset; |
|
147 indexEntry.bufferOffset = pcEntries.length(); |
|
148 if (!pcMappingIndexEntries.append(indexEntry)) |
|
149 return Method_Error; |
|
150 previousOffset = entry.nativeOffset; |
|
151 } |
|
152 |
|
153 // Use the high bit of the SlotInfo byte to indicate the |
|
154 // native code offset (relative to the previous op) > 0 and |
|
155 // comes next in the buffer. |
|
156 JS_ASSERT((entry.slotInfo.toByte() & 0x80) == 0); |
|
157 |
|
158 if (entry.nativeOffset == previousOffset) { |
|
159 pcEntries.writeByte(entry.slotInfo.toByte()); |
|
160 } else { |
|
161 JS_ASSERT(entry.nativeOffset > previousOffset); |
|
162 pcEntries.writeByte(0x80 | entry.slotInfo.toByte()); |
|
163 pcEntries.writeUnsigned(entry.nativeOffset - previousOffset); |
|
164 } |
|
165 |
|
166 previousOffset = entry.nativeOffset; |
|
167 } |
|
168 |
|
169 if (pcEntries.oom()) |
|
170 return Method_Error; |
|
171 |
|
172 prologueOffset_.fixup(&masm); |
|
173 epilogueOffset_.fixup(&masm); |
|
174 spsPushToggleOffset_.fixup(&masm); |
|
175 postDebugPrologueOffset_.fixup(&masm); |
|
176 |
|
177 // Note: There is an extra entry in the bytecode type map for the search hint, see below. |
|
178 size_t bytecodeTypeMapEntries = script->nTypeSets() + 1; |
|
179 |
|
180 BaselineScript *baselineScript = BaselineScript::New(cx, prologueOffset_.offset(), |
|
181 epilogueOffset_.offset(), |
|
182 spsPushToggleOffset_.offset(), |
|
183 postDebugPrologueOffset_.offset(), |
|
184 icEntries_.length(), |
|
185 pcMappingIndexEntries.length(), |
|
186 pcEntries.length(), |
|
187 bytecodeTypeMapEntries); |
|
188 if (!baselineScript) |
|
189 return Method_Error; |
|
190 |
|
191 baselineScript->setMethod(code); |
|
192 baselineScript->setTemplateScope(templateScope); |
|
193 |
|
194 IonSpew(IonSpew_BaselineScripts, "Created BaselineScript %p (raw %p) for %s:%d", |
|
195 (void *) baselineScript, (void *) code->raw(), |
|
196 script->filename(), script->lineno()); |
|
197 |
|
198 #ifdef JS_ION_PERF |
|
199 writePerfSpewerBaselineProfile(script, code); |
|
200 #endif |
|
201 |
|
202 JS_ASSERT(pcMappingIndexEntries.length() > 0); |
|
203 baselineScript->copyPCMappingIndexEntries(&pcMappingIndexEntries[0]); |
|
204 |
|
205 JS_ASSERT(pcEntries.length() > 0); |
|
206 baselineScript->copyPCMappingEntries(pcEntries); |
|
207 |
|
208 // Copy IC entries |
|
209 if (icEntries_.length()) |
|
210 baselineScript->copyICEntries(script, &icEntries_[0], masm); |
|
211 |
|
212 // Adopt fallback stubs from the compiler into the baseline script. |
|
213 baselineScript->adoptFallbackStubs(&stubSpace_); |
|
214 |
|
215 // Patch IC loads using IC entries |
|
216 for (size_t i = 0; i < icLoadLabels_.length(); i++) { |
|
217 CodeOffsetLabel label = icLoadLabels_[i].label; |
|
218 label.fixup(&masm); |
|
219 size_t icEntry = icLoadLabels_[i].icEntry; |
|
220 ICEntry *entryAddr = &(baselineScript->icEntry(icEntry)); |
|
221 Assembler::patchDataWithValueCheck(CodeLocationLabel(code, label), |
|
222 ImmPtr(entryAddr), |
|
223 ImmPtr((void*)-1)); |
|
224 } |
|
225 |
|
226 if (modifiesArguments_) |
|
227 baselineScript->setModifiesArguments(); |
|
228 |
|
229 // All barriers are emitted off-by-default, toggle them on if needed. |
|
230 if (cx->zone()->needsBarrier()) |
|
231 baselineScript->toggleBarriers(true); |
|
232 |
|
233 // All SPS instrumentation is emitted toggled off. Toggle them on if needed. |
|
234 if (cx->runtime()->spsProfiler.enabled()) |
|
235 baselineScript->toggleSPS(true); |
|
236 |
|
237 uint32_t *bytecodeMap = baselineScript->bytecodeTypeMap(); |
|
238 types::FillBytecodeTypeMap(script, bytecodeMap); |
|
239 |
|
240 // The last entry in the last index found, and is used to avoid binary |
|
241 // searches for the sought entry when queries are in linear order. |
|
242 bytecodeMap[script->nTypeSets()] = 0; |
|
243 |
|
244 if (script->compartment()->debugMode()) |
|
245 baselineScript->setDebugMode(); |
|
246 |
|
247 script->setBaselineScript(cx, baselineScript); |
|
248 |
|
249 return Method_Compiled; |
|
250 } |
|
251 |
|
252 bool |
|
253 BaselineCompiler::emitPrologue() |
|
254 { |
|
255 masm.push(BaselineFrameReg); |
|
256 masm.mov(BaselineStackReg, BaselineFrameReg); |
|
257 |
|
258 masm.subPtr(Imm32(BaselineFrame::Size()), BaselineStackReg); |
|
259 masm.checkStackAlignment(); |
|
260 |
|
261 // Initialize BaselineFrame. For eval scripts, the scope chain |
|
262 // is passed in R1, so we have to be careful not to clobber |
|
263 // it. |
|
264 |
|
265 // Initialize BaselineFrame::flags. |
|
266 uint32_t flags = 0; |
|
267 if (script->isForEval()) |
|
268 flags |= BaselineFrame::EVAL; |
|
269 masm.store32(Imm32(flags), frame.addressOfFlags()); |
|
270 |
|
271 if (script->isForEval()) |
|
272 masm.storePtr(ImmGCPtr(script), frame.addressOfEvalScript()); |
|
273 |
|
274 // Handle scope chain pre-initialization (in case GC gets run |
|
275 // during stack check). For global and eval scripts, the scope |
|
276 // chain is in R1. For function scripts, the scope chain is in |
|
277 // the callee, nullptr is stored for now so that GC doesn't choke |
|
278 // on a bogus ScopeChain value in the frame. |
|
279 if (function()) |
|
280 masm.storePtr(ImmPtr(nullptr), frame.addressOfScopeChain()); |
|
281 else |
|
282 masm.storePtr(R1.scratchReg(), frame.addressOfScopeChain()); |
|
283 |
|
284 // Functions with a large number of locals require two stack checks. |
|
285 // The VMCall for a fallible stack check can only occur after the |
|
286 // scope chain has been initialized, as that is required for proper |
|
287 // exception handling if the VMCall returns false. The scope chain |
|
288 // initialization can only happen after the UndefinedValues for the |
|
289 // local slots have been pushed. |
|
290 // However by that time, the stack might have grown too much. |
|
291 // In these cases, we emit an extra, early, infallible check |
|
292 // before pushing the locals. The early check sets a flag on the |
|
293 // frame if the stack check fails (but otherwise doesn't throw an |
|
294 // exception). If the flag is set, then the jitcode skips past |
|
295 // the pushing of the locals, and directly to scope chain initialization |
|
296 // followed by the actual stack check, which will throw the correct |
|
297 // exception. |
|
298 Label earlyStackCheckFailed; |
|
299 if (needsEarlyStackCheck()) { |
|
300 if (!emitStackCheck(/* earlyCheck = */ true)) |
|
301 return false; |
|
302 masm.branchTest32(Assembler::NonZero, |
|
303 frame.addressOfFlags(), |
|
304 Imm32(BaselineFrame::OVER_RECURSED), |
|
305 &earlyStackCheckFailed); |
|
306 } |
|
307 |
|
308 // Initialize locals to |undefined|. Use R0 to minimize code size. |
|
309 // If the number of locals to push is < LOOP_UNROLL_FACTOR, then the |
|
310 // initialization pushes are emitted directly and inline. Otherwise, |
|
311 // they're emitted in a partially unrolled loop. |
|
312 if (frame.nlocals() > 0) { |
|
313 size_t LOOP_UNROLL_FACTOR = 4; |
|
314 size_t toPushExtra = frame.nlocals() % LOOP_UNROLL_FACTOR; |
|
315 |
|
316 masm.moveValue(UndefinedValue(), R0); |
|
317 |
|
318 // Handle any extra pushes left over by the optional unrolled loop below. |
|
319 for (size_t i = 0; i < toPushExtra; i++) |
|
320 masm.pushValue(R0); |
|
321 |
|
322 // Partially unrolled loop of pushes. |
|
323 if (frame.nlocals() >= LOOP_UNROLL_FACTOR) { |
|
324 size_t toPush = frame.nlocals() - toPushExtra; |
|
325 JS_ASSERT(toPush % LOOP_UNROLL_FACTOR == 0); |
|
326 JS_ASSERT(toPush >= LOOP_UNROLL_FACTOR); |
|
327 masm.move32(Imm32(toPush), R1.scratchReg()); |
|
328 // Emit unrolled loop with 4 pushes per iteration. |
|
329 Label pushLoop; |
|
330 masm.bind(&pushLoop); |
|
331 for (size_t i = 0; i < LOOP_UNROLL_FACTOR; i++) |
|
332 masm.pushValue(R0); |
|
333 masm.branchSub32(Assembler::NonZero, |
|
334 Imm32(LOOP_UNROLL_FACTOR), R1.scratchReg(), &pushLoop); |
|
335 } |
|
336 } |
|
337 |
|
338 if (needsEarlyStackCheck()) |
|
339 masm.bind(&earlyStackCheckFailed); |
|
340 |
|
341 #ifdef JS_TRACE_LOGGING |
|
342 TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); |
|
343 Register loggerReg = RegisterSet::Volatile().takeGeneral(); |
|
344 masm.Push(loggerReg); |
|
345 masm.movePtr(ImmPtr(logger), loggerReg); |
|
346 masm.tracelogStart(loggerReg, TraceLogCreateTextId(logger, script.get())); |
|
347 masm.tracelogStart(loggerReg, TraceLogger::Baseline); |
|
348 masm.Pop(loggerReg); |
|
349 #endif |
|
350 |
|
351 // Record the offset of the prologue, because Ion can bailout before |
|
352 // the scope chain is initialized. |
|
353 prologueOffset_ = masm.currentOffset(); |
|
354 |
|
355 // Initialize the scope chain before any operation that may |
|
356 // call into the VM and trigger a GC. |
|
357 if (!initScopeChain()) |
|
358 return false; |
|
359 |
|
360 if (!emitStackCheck()) |
|
361 return false; |
|
362 |
|
363 if (!emitDebugPrologue()) |
|
364 return false; |
|
365 |
|
366 if (!emitUseCountIncrement()) |
|
367 return false; |
|
368 |
|
369 if (!emitArgumentTypeChecks()) |
|
370 return false; |
|
371 |
|
372 if (!emitSPSPush()) |
|
373 return false; |
|
374 |
|
375 return true; |
|
376 } |
|
377 |
|
378 bool |
|
379 BaselineCompiler::emitEpilogue() |
|
380 { |
|
381 // Record the offset of the epilogue, so we can do early return from |
|
382 // Debugger handlers during on-stack recompile. |
|
383 epilogueOffset_ = masm.currentOffset(); |
|
384 |
|
385 masm.bind(&return_); |
|
386 |
|
387 #ifdef JS_TRACE_LOGGING |
|
388 TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); |
|
389 Register loggerReg = RegisterSet::Volatile().takeGeneral(); |
|
390 masm.Push(loggerReg); |
|
391 masm.movePtr(ImmPtr(logger), loggerReg); |
|
392 masm.tracelogStop(loggerReg, TraceLogger::Baseline); |
|
393 // Stop the script. Using a stop without checking the textId, since we |
|
394 // we didn't save the textId for the script. |
|
395 masm.tracelogStop(loggerReg); |
|
396 masm.Pop(loggerReg); |
|
397 #endif |
|
398 |
|
399 // Pop SPS frame if necessary |
|
400 emitSPSPop(); |
|
401 |
|
402 masm.mov(BaselineFrameReg, BaselineStackReg); |
|
403 masm.pop(BaselineFrameReg); |
|
404 |
|
405 masm.ret(); |
|
406 return true; |
|
407 } |
|
408 |
|
409 #ifdef JSGC_GENERATIONAL |
|
410 // On input: |
|
411 // R2.scratchReg() contains object being written to. |
|
412 // Otherwise, baseline stack will be synced, so all other registers are usable as scratch. |
|
413 // This calls: |
|
414 // void PostWriteBarrier(JSRuntime *rt, JSObject *obj); |
|
415 bool |
|
416 BaselineCompiler::emitOutOfLinePostBarrierSlot() |
|
417 { |
|
418 masm.bind(&postBarrierSlot_); |
|
419 |
|
420 Register objReg = R2.scratchReg(); |
|
421 GeneralRegisterSet regs(GeneralRegisterSet::All()); |
|
422 regs.take(objReg); |
|
423 regs.take(BaselineFrameReg); |
|
424 Register scratch = regs.takeAny(); |
|
425 #if defined(JS_CODEGEN_ARM) |
|
426 // On ARM, save the link register before calling. It contains the return |
|
427 // address. The |masm.ret()| later will pop this into |pc| to return. |
|
428 masm.push(lr); |
|
429 #elif defined(JS_CODEGEN_MIPS) |
|
430 masm.push(ra); |
|
431 #endif |
|
432 |
|
433 masm.setupUnalignedABICall(2, scratch); |
|
434 masm.movePtr(ImmPtr(cx->runtime()), scratch); |
|
435 masm.passABIArg(scratch); |
|
436 masm.passABIArg(objReg); |
|
437 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, PostWriteBarrier)); |
|
438 |
|
439 masm.ret(); |
|
440 return true; |
|
441 } |
|
442 #endif // JSGC_GENERATIONAL |
|
443 |
|
444 bool |
|
445 BaselineCompiler::emitIC(ICStub *stub, ICEntry::Kind kind) |
|
446 { |
|
447 ICEntry *entry = allocateICEntry(stub, kind); |
|
448 if (!entry) |
|
449 return false; |
|
450 |
|
451 CodeOffsetLabel patchOffset; |
|
452 EmitCallIC(&patchOffset, masm); |
|
453 entry->setReturnOffset(masm.currentOffset()); |
|
454 if (!addICLoadLabel(patchOffset)) |
|
455 return false; |
|
456 |
|
457 return true; |
|
458 } |
|
459 |
|
460 typedef bool (*CheckOverRecursedWithExtraFn)(JSContext *, BaselineFrame *, uint32_t, uint32_t); |
|
461 static const VMFunction CheckOverRecursedWithExtraInfo = |
|
462 FunctionInfo<CheckOverRecursedWithExtraFn>(CheckOverRecursedWithExtra); |
|
463 |
|
464 bool |
|
465 BaselineCompiler::emitStackCheck(bool earlyCheck) |
|
466 { |
|
467 Label skipCall; |
|
468 uintptr_t *limitAddr = &cx->runtime()->mainThread.jitStackLimit; |
|
469 uint32_t slotsSize = script->nslots() * sizeof(Value); |
|
470 uint32_t tolerance = earlyCheck ? slotsSize : 0; |
|
471 |
|
472 masm.movePtr(BaselineStackReg, R1.scratchReg()); |
|
473 |
|
474 // If this is the early stack check, locals haven't been pushed yet. Adjust the |
|
475 // stack pointer to account for the locals that would be pushed before performing |
|
476 // the guard around the vmcall to the stack check. |
|
477 if (earlyCheck) |
|
478 masm.subPtr(Imm32(tolerance), R1.scratchReg()); |
|
479 |
|
480 // If this is the late stack check for a frame which contains an early stack check, |
|
481 // then the early stack check might have failed and skipped past the pushing of locals |
|
482 // on the stack. |
|
483 // |
|
484 // If this is a possibility, then the OVER_RECURSED flag should be checked, and the |
|
485 // VMCall to CheckOverRecursed done unconditionally if it's set. |
|
486 Label forceCall; |
|
487 if (!earlyCheck && needsEarlyStackCheck()) { |
|
488 masm.branchTest32(Assembler::NonZero, |
|
489 frame.addressOfFlags(), |
|
490 Imm32(BaselineFrame::OVER_RECURSED), |
|
491 &forceCall); |
|
492 } |
|
493 |
|
494 masm.branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(limitAddr), R1.scratchReg(), |
|
495 &skipCall); |
|
496 |
|
497 if (!earlyCheck && needsEarlyStackCheck()) |
|
498 masm.bind(&forceCall); |
|
499 |
|
500 prepareVMCall(); |
|
501 pushArg(Imm32(earlyCheck)); |
|
502 pushArg(Imm32(tolerance)); |
|
503 masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg()); |
|
504 pushArg(R1.scratchReg()); |
|
505 |
|
506 CallVMPhase phase = POST_INITIALIZE; |
|
507 if (earlyCheck) |
|
508 phase = PRE_INITIALIZE; |
|
509 else if (needsEarlyStackCheck()) |
|
510 phase = CHECK_OVER_RECURSED; |
|
511 |
|
512 if (!callVM(CheckOverRecursedWithExtraInfo, phase)) |
|
513 return false; |
|
514 |
|
515 masm.bind(&skipCall); |
|
516 return true; |
|
517 } |
|
518 |
|
519 typedef bool (*DebugPrologueFn)(JSContext *, BaselineFrame *, jsbytecode *, bool *); |
|
520 static const VMFunction DebugPrologueInfo = FunctionInfo<DebugPrologueFn>(jit::DebugPrologue); |
|
521 |
|
522 bool |
|
523 BaselineCompiler::emitDebugPrologue() |
|
524 { |
|
525 if (debugMode_) { |
|
526 // Load pointer to BaselineFrame in R0. |
|
527 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); |
|
528 |
|
529 prepareVMCall(); |
|
530 pushArg(ImmPtr(pc)); |
|
531 pushArg(R0.scratchReg()); |
|
532 if (!callVM(DebugPrologueInfo)) |
|
533 return false; |
|
534 |
|
535 // Fix up the fake ICEntry appended by callVM for on-stack recompilation. |
|
536 icEntries_.back().setForDebugPrologue(); |
|
537 |
|
538 // If the stub returns |true|, we have to return the value stored in the |
|
539 // frame's return value slot. |
|
540 Label done; |
|
541 masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done); |
|
542 { |
|
543 masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand); |
|
544 masm.jump(&return_); |
|
545 } |
|
546 masm.bind(&done); |
|
547 } |
|
548 |
|
549 postDebugPrologueOffset_ = masm.currentOffset(); |
|
550 |
|
551 return true; |
|
552 } |
|
553 |
|
554 typedef bool (*StrictEvalPrologueFn)(JSContext *, BaselineFrame *); |
|
555 static const VMFunction StrictEvalPrologueInfo = |
|
556 FunctionInfo<StrictEvalPrologueFn>(jit::StrictEvalPrologue); |
|
557 |
|
558 typedef bool (*HeavyweightFunPrologueFn)(JSContext *, BaselineFrame *); |
|
559 static const VMFunction HeavyweightFunPrologueInfo = |
|
560 FunctionInfo<HeavyweightFunPrologueFn>(jit::HeavyweightFunPrologue); |
|
561 |
|
562 bool |
|
563 BaselineCompiler::initScopeChain() |
|
564 { |
|
565 CallVMPhase phase = POST_INITIALIZE; |
|
566 if (needsEarlyStackCheck()) |
|
567 phase = CHECK_OVER_RECURSED; |
|
568 |
|
569 RootedFunction fun(cx, function()); |
|
570 if (fun) { |
|
571 // Use callee->environment as scope chain. Note that we do |
|
572 // this also for heavy-weight functions, so that the scope |
|
573 // chain slot is properly initialized if the call triggers GC. |
|
574 Register callee = R0.scratchReg(); |
|
575 Register scope = R1.scratchReg(); |
|
576 masm.loadPtr(frame.addressOfCallee(), callee); |
|
577 masm.loadPtr(Address(callee, JSFunction::offsetOfEnvironment()), scope); |
|
578 masm.storePtr(scope, frame.addressOfScopeChain()); |
|
579 |
|
580 if (fun->isHeavyweight()) { |
|
581 // Call into the VM to create a new call object. |
|
582 prepareVMCall(); |
|
583 |
|
584 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); |
|
585 pushArg(R0.scratchReg()); |
|
586 |
|
587 if (!callVM(HeavyweightFunPrologueInfo, phase)) |
|
588 return false; |
|
589 } |
|
590 } else { |
|
591 // ScopeChain pointer in BaselineFrame has already been initialized |
|
592 // in prologue. |
|
593 |
|
594 if (script->isForEval() && script->strict()) { |
|
595 // Strict eval needs its own call object. |
|
596 prepareVMCall(); |
|
597 |
|
598 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); |
|
599 pushArg(R0.scratchReg()); |
|
600 |
|
601 if (!callVM(StrictEvalPrologueInfo, phase)) |
|
602 return false; |
|
603 } |
|
604 } |
|
605 |
|
606 return true; |
|
607 } |
|
608 |
|
609 typedef bool (*InterruptCheckFn)(JSContext *); |
|
610 static const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(InterruptCheck); |
|
611 |
|
612 bool |
|
613 BaselineCompiler::emitInterruptCheck() |
|
614 { |
|
615 frame.syncStack(0); |
|
616 |
|
617 Label done; |
|
618 void *interrupt = (void *)&cx->runtime()->interrupt; |
|
619 masm.branch32(Assembler::Equal, AbsoluteAddress(interrupt), Imm32(0), &done); |
|
620 |
|
621 prepareVMCall(); |
|
622 if (!callVM(InterruptCheckInfo)) |
|
623 return false; |
|
624 |
|
625 masm.bind(&done); |
|
626 return true; |
|
627 } |
|
628 |
|
629 bool |
|
630 BaselineCompiler::emitUseCountIncrement(bool allowOsr) |
|
631 { |
|
632 // Emit no use count increments or bailouts if Ion is not |
|
633 // enabled, or if the script will never be Ion-compileable |
|
634 |
|
635 if (!ionCompileable_ && !ionOSRCompileable_) |
|
636 return true; |
|
637 |
|
638 Register scriptReg = R2.scratchReg(); |
|
639 Register countReg = R0.scratchReg(); |
|
640 Address useCountAddr(scriptReg, JSScript::offsetOfUseCount()); |
|
641 |
|
642 masm.movePtr(ImmGCPtr(script), scriptReg); |
|
643 masm.load32(useCountAddr, countReg); |
|
644 masm.add32(Imm32(1), countReg); |
|
645 masm.store32(countReg, useCountAddr); |
|
646 |
|
647 // If this is a loop inside a catch or finally block, increment the use |
|
648 // count but don't attempt OSR (Ion only compiles the try block). |
|
649 if (analysis_.info(pc).loopEntryInCatchOrFinally) { |
|
650 JS_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY); |
|
651 return true; |
|
652 } |
|
653 |
|
654 // OSR not possible at this loop entry. |
|
655 if (!allowOsr) { |
|
656 JS_ASSERT(JSOp(*pc) == JSOP_LOOPENTRY); |
|
657 return true; |
|
658 } |
|
659 |
|
660 Label skipCall; |
|
661 |
|
662 const OptimizationInfo *info = js_IonOptimizations.get(js_IonOptimizations.firstLevel()); |
|
663 uint32_t minUses = info->usesBeforeCompile(script, pc); |
|
664 masm.branch32(Assembler::LessThan, countReg, Imm32(minUses), &skipCall); |
|
665 |
|
666 masm.branchPtr(Assembler::Equal, |
|
667 Address(scriptReg, JSScript::offsetOfIonScript()), |
|
668 ImmPtr(ION_COMPILING_SCRIPT), &skipCall); |
|
669 |
|
670 // Call IC. |
|
671 ICUseCount_Fallback::Compiler stubCompiler(cx); |
|
672 if (!emitNonOpIC(stubCompiler.getStub(&stubSpace_))) |
|
673 return false; |
|
674 |
|
675 masm.bind(&skipCall); |
|
676 |
|
677 return true; |
|
678 } |
|
679 |
|
680 bool |
|
681 BaselineCompiler::emitArgumentTypeChecks() |
|
682 { |
|
683 if (!function()) |
|
684 return true; |
|
685 |
|
686 frame.pushThis(); |
|
687 frame.popRegsAndSync(1); |
|
688 |
|
689 ICTypeMonitor_Fallback::Compiler compiler(cx, (uint32_t) 0); |
|
690 if (!emitNonOpIC(compiler.getStub(&stubSpace_))) |
|
691 return false; |
|
692 |
|
693 for (size_t i = 0; i < function()->nargs(); i++) { |
|
694 frame.pushArg(i); |
|
695 frame.popRegsAndSync(1); |
|
696 |
|
697 ICTypeMonitor_Fallback::Compiler compiler(cx, i + 1); |
|
698 if (!emitNonOpIC(compiler.getStub(&stubSpace_))) |
|
699 return false; |
|
700 } |
|
701 |
|
702 return true; |
|
703 } |
|
704 |
|
705 bool |
|
706 BaselineCompiler::emitDebugTrap() |
|
707 { |
|
708 JS_ASSERT(debugMode_); |
|
709 JS_ASSERT(frame.numUnsyncedSlots() == 0); |
|
710 |
|
711 bool enabled = script->stepModeEnabled() || script->hasBreakpointsAt(pc); |
|
712 |
|
713 // Emit patchable call to debug trap handler. |
|
714 JitCode *handler = cx->runtime()->jitRuntime()->debugTrapHandler(cx); |
|
715 mozilla::DebugOnly<CodeOffsetLabel> offset = masm.toggledCall(handler, enabled); |
|
716 |
|
717 #ifdef DEBUG |
|
718 // Patchable call offset has to match the pc mapping offset. |
|
719 PCMappingEntry &entry = pcMappingEntries_.back(); |
|
720 JS_ASSERT((&offset)->offset() == entry.nativeOffset); |
|
721 #endif |
|
722 |
|
723 // Add an IC entry for the return offset -> pc mapping. |
|
724 ICEntry icEntry(script->pcToOffset(pc), ICEntry::Kind_DebugTrap); |
|
725 icEntry.setReturnOffset(masm.currentOffset()); |
|
726 if (!icEntries_.append(icEntry)) |
|
727 return false; |
|
728 |
|
729 return true; |
|
730 } |
|
731 |
|
732 bool |
|
733 BaselineCompiler::emitSPSPush() |
|
734 { |
|
735 // Enter the IC, guarded by a toggled jump (initially disabled). |
|
736 Label noPush; |
|
737 CodeOffsetLabel toggleOffset = masm.toggledJump(&noPush); |
|
738 JS_ASSERT(frame.numUnsyncedSlots() == 0); |
|
739 ICProfiler_Fallback::Compiler compiler(cx); |
|
740 if (!emitNonOpIC(compiler.getStub(&stubSpace_))) |
|
741 return false; |
|
742 masm.bind(&noPush); |
|
743 |
|
744 // Store the start offset in the appropriate location. |
|
745 JS_ASSERT(spsPushToggleOffset_.offset() == 0); |
|
746 spsPushToggleOffset_ = toggleOffset; |
|
747 return true; |
|
748 } |
|
749 |
|
750 void |
|
751 BaselineCompiler::emitSPSPop() |
|
752 { |
|
753 // If profiler entry was pushed on this frame, pop it. |
|
754 Label noPop; |
|
755 masm.branchTest32(Assembler::Zero, frame.addressOfFlags(), |
|
756 Imm32(BaselineFrame::HAS_PUSHED_SPS_FRAME), &noPop); |
|
757 masm.spsPopFrameSafe(&cx->runtime()->spsProfiler, R1.scratchReg()); |
|
758 masm.bind(&noPop); |
|
759 } |
|
760 |
|
761 MethodStatus |
|
762 BaselineCompiler::emitBody() |
|
763 { |
|
764 JS_ASSERT(pc == script->code()); |
|
765 |
|
766 bool lastOpUnreachable = false; |
|
767 uint32_t emittedOps = 0; |
|
768 mozilla::DebugOnly<jsbytecode *> prevpc = pc; |
|
769 |
|
770 while (true) { |
|
771 JSOp op = JSOp(*pc); |
|
772 IonSpew(IonSpew_BaselineOp, "Compiling op @ %d: %s", |
|
773 int(script->pcToOffset(pc)), js_CodeName[op]); |
|
774 |
|
775 BytecodeInfo *info = analysis_.maybeInfo(pc); |
|
776 |
|
777 // Skip unreachable ops. |
|
778 if (!info) { |
|
779 // Test if last instructions and stop emitting in that case. |
|
780 pc += GetBytecodeLength(pc); |
|
781 if (pc >= script->codeEnd()) |
|
782 break; |
|
783 |
|
784 lastOpUnreachable = true; |
|
785 prevpc = pc; |
|
786 continue; |
|
787 } |
|
788 |
|
789 // Fully sync the stack if there are incoming jumps. |
|
790 if (info->jumpTarget) { |
|
791 frame.syncStack(0); |
|
792 frame.setStackDepth(info->stackDepth); |
|
793 } |
|
794 |
|
795 // Always sync in debug mode. |
|
796 if (debugMode_) |
|
797 frame.syncStack(0); |
|
798 |
|
799 // At the beginning of any op, at most the top 2 stack-values are unsynced. |
|
800 if (frame.stackDepth() > 2) |
|
801 frame.syncStack(2); |
|
802 |
|
803 frame.assertValidState(*info); |
|
804 |
|
805 masm.bind(labelOf(pc)); |
|
806 |
|
807 // Add a PC -> native mapping entry for the current op. These entries are |
|
808 // used when we need the native code address for a given pc, for instance |
|
809 // for bailouts from Ion, the debugger and exception handling. See |
|
810 // PCMappingIndexEntry for more information. |
|
811 bool addIndexEntry = (pc == script->code() || lastOpUnreachable || emittedOps > 100); |
|
812 if (addIndexEntry) |
|
813 emittedOps = 0; |
|
814 if (!addPCMappingEntry(addIndexEntry)) |
|
815 return Method_Error; |
|
816 |
|
817 // Emit traps for breakpoints and step mode. |
|
818 if (debugMode_ && !emitDebugTrap()) |
|
819 return Method_Error; |
|
820 |
|
821 switch (op) { |
|
822 default: |
|
823 IonSpew(IonSpew_BaselineAbort, "Unhandled op: %s", js_CodeName[op]); |
|
824 return Method_CantCompile; |
|
825 |
|
826 #define EMIT_OP(OP) \ |
|
827 case OP: \ |
|
828 if (!this->emit_##OP()) \ |
|
829 return Method_Error; \ |
|
830 break; |
|
831 OPCODE_LIST(EMIT_OP) |
|
832 #undef EMIT_OP |
|
833 } |
|
834 |
|
835 // Test if last instructions and stop emitting in that case. |
|
836 pc += GetBytecodeLength(pc); |
|
837 if (pc >= script->codeEnd()) |
|
838 break; |
|
839 |
|
840 emittedOps++; |
|
841 lastOpUnreachable = false; |
|
842 #ifdef DEBUG |
|
843 prevpc = pc; |
|
844 #endif |
|
845 } |
|
846 |
|
847 JS_ASSERT(JSOp(*prevpc) == JSOP_RETRVAL); |
|
848 return Method_Compiled; |
|
849 } |
|
850 |
|
851 bool |
|
852 BaselineCompiler::emit_JSOP_NOP() |
|
853 { |
|
854 return true; |
|
855 } |
|
856 |
|
857 bool |
|
858 BaselineCompiler::emit_JSOP_LABEL() |
|
859 { |
|
860 return true; |
|
861 } |
|
862 |
|
863 bool |
|
864 BaselineCompiler::emit_JSOP_POP() |
|
865 { |
|
866 frame.pop(); |
|
867 return true; |
|
868 } |
|
869 |
|
870 bool |
|
871 BaselineCompiler::emit_JSOP_POPN() |
|
872 { |
|
873 frame.popn(GET_UINT16(pc)); |
|
874 return true; |
|
875 } |
|
876 |
|
877 bool |
|
878 BaselineCompiler::emit_JSOP_DUPAT() |
|
879 { |
|
880 frame.syncStack(0); |
|
881 |
|
882 // DUPAT takes a value on the stack and re-pushes it on top. It's like |
|
883 // GETLOCAL but it addresses from the top of the stack instead of from the |
|
884 // stack frame. |
|
885 |
|
886 int depth = -(GET_UINT24(pc) + 1); |
|
887 masm.loadValue(frame.addressOfStackValue(frame.peek(depth)), R0); |
|
888 frame.push(R0); |
|
889 return true; |
|
890 } |
|
891 |
|
892 bool |
|
893 BaselineCompiler::emit_JSOP_DUP() |
|
894 { |
|
895 // Keep top stack value in R0, sync the rest so that we can use R1. We use |
|
896 // separate registers because every register can be used by at most one |
|
897 // StackValue. |
|
898 frame.popRegsAndSync(1); |
|
899 masm.moveValue(R0, R1); |
|
900 |
|
901 // inc/dec ops use DUP followed by ONE, ADD. Push R0 last to avoid a move. |
|
902 frame.push(R1); |
|
903 frame.push(R0); |
|
904 return true; |
|
905 } |
|
906 |
|
907 bool |
|
908 BaselineCompiler::emit_JSOP_DUP2() |
|
909 { |
|
910 frame.syncStack(0); |
|
911 |
|
912 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0); |
|
913 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1); |
|
914 |
|
915 frame.push(R0); |
|
916 frame.push(R1); |
|
917 return true; |
|
918 } |
|
919 |
|
920 bool |
|
921 BaselineCompiler::emit_JSOP_SWAP() |
|
922 { |
|
923 // Keep top stack values in R0 and R1. |
|
924 frame.popRegsAndSync(2); |
|
925 |
|
926 frame.push(R1); |
|
927 frame.push(R0); |
|
928 return true; |
|
929 } |
|
930 |
|
931 bool |
|
932 BaselineCompiler::emit_JSOP_PICK() |
|
933 { |
|
934 frame.syncStack(0); |
|
935 |
|
936 // Pick takes a value on the stack and moves it to the top. |
|
937 // For instance, pick 2: |
|
938 // before: A B C D E |
|
939 // after : A B D E C |
|
940 |
|
941 // First, move value at -(amount + 1) into R0. |
|
942 int depth = -(GET_INT8(pc) + 1); |
|
943 masm.loadValue(frame.addressOfStackValue(frame.peek(depth)), R0); |
|
944 |
|
945 // Move the other values down. |
|
946 depth++; |
|
947 for (; depth < 0; depth++) { |
|
948 Address source = frame.addressOfStackValue(frame.peek(depth)); |
|
949 Address dest = frame.addressOfStackValue(frame.peek(depth - 1)); |
|
950 masm.loadValue(source, R1); |
|
951 masm.storeValue(R1, dest); |
|
952 } |
|
953 |
|
954 // Push R0. |
|
955 frame.pop(); |
|
956 frame.push(R0); |
|
957 return true; |
|
958 } |
|
959 |
|
960 bool |
|
961 BaselineCompiler::emit_JSOP_GOTO() |
|
962 { |
|
963 frame.syncStack(0); |
|
964 |
|
965 jsbytecode *target = pc + GET_JUMP_OFFSET(pc); |
|
966 masm.jump(labelOf(target)); |
|
967 return true; |
|
968 } |
|
969 |
|
970 bool |
|
971 BaselineCompiler::emitToBoolean() |
|
972 { |
|
973 Label skipIC; |
|
974 masm.branchTestBoolean(Assembler::Equal, R0, &skipIC); |
|
975 |
|
976 // Call IC |
|
977 ICToBool_Fallback::Compiler stubCompiler(cx); |
|
978 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
979 return false; |
|
980 |
|
981 masm.bind(&skipIC); |
|
982 return true; |
|
983 } |
|
984 |
|
985 bool |
|
986 BaselineCompiler::emitTest(bool branchIfTrue) |
|
987 { |
|
988 bool knownBoolean = frame.peek(-1)->isKnownBoolean(); |
|
989 |
|
990 // Keep top stack value in R0. |
|
991 frame.popRegsAndSync(1); |
|
992 |
|
993 if (!knownBoolean && !emitToBoolean()) |
|
994 return false; |
|
995 |
|
996 // IC will leave a BooleanValue in R0, just need to branch on it. |
|
997 masm.branchTestBooleanTruthy(branchIfTrue, R0, labelOf(pc + GET_JUMP_OFFSET(pc))); |
|
998 return true; |
|
999 } |
|
1000 |
|
1001 bool |
|
1002 BaselineCompiler::emit_JSOP_IFEQ() |
|
1003 { |
|
1004 return emitTest(false); |
|
1005 } |
|
1006 |
|
1007 bool |
|
1008 BaselineCompiler::emit_JSOP_IFNE() |
|
1009 { |
|
1010 return emitTest(true); |
|
1011 } |
|
1012 |
|
1013 bool |
|
1014 BaselineCompiler::emitAndOr(bool branchIfTrue) |
|
1015 { |
|
1016 bool knownBoolean = frame.peek(-1)->isKnownBoolean(); |
|
1017 |
|
1018 // AND and OR leave the original value on the stack. |
|
1019 frame.syncStack(0); |
|
1020 |
|
1021 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0); |
|
1022 if (!knownBoolean && !emitToBoolean()) |
|
1023 return false; |
|
1024 |
|
1025 masm.branchTestBooleanTruthy(branchIfTrue, R0, labelOf(pc + GET_JUMP_OFFSET(pc))); |
|
1026 return true; |
|
1027 } |
|
1028 |
|
1029 bool |
|
1030 BaselineCompiler::emit_JSOP_AND() |
|
1031 { |
|
1032 return emitAndOr(false); |
|
1033 } |
|
1034 |
|
1035 bool |
|
1036 BaselineCompiler::emit_JSOP_OR() |
|
1037 { |
|
1038 return emitAndOr(true); |
|
1039 } |
|
1040 |
|
1041 bool |
|
1042 BaselineCompiler::emit_JSOP_NOT() |
|
1043 { |
|
1044 bool knownBoolean = frame.peek(-1)->isKnownBoolean(); |
|
1045 |
|
1046 // Keep top stack value in R0. |
|
1047 frame.popRegsAndSync(1); |
|
1048 |
|
1049 if (!knownBoolean && !emitToBoolean()) |
|
1050 return false; |
|
1051 |
|
1052 masm.notBoolean(R0); |
|
1053 |
|
1054 frame.push(R0, JSVAL_TYPE_BOOLEAN); |
|
1055 return true; |
|
1056 } |
|
1057 |
|
1058 bool |
|
1059 BaselineCompiler::emit_JSOP_POS() |
|
1060 { |
|
1061 // Keep top stack value in R0. |
|
1062 frame.popRegsAndSync(1); |
|
1063 |
|
1064 // Inline path for int32 and double. |
|
1065 Label done; |
|
1066 masm.branchTestNumber(Assembler::Equal, R0, &done); |
|
1067 |
|
1068 // Call IC. |
|
1069 ICToNumber_Fallback::Compiler stubCompiler(cx); |
|
1070 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
1071 return false; |
|
1072 |
|
1073 masm.bind(&done); |
|
1074 frame.push(R0); |
|
1075 return true; |
|
1076 } |
|
1077 |
|
1078 bool |
|
1079 BaselineCompiler::emit_JSOP_LOOPHEAD() |
|
1080 { |
|
1081 return emitInterruptCheck(); |
|
1082 } |
|
1083 |
|
1084 bool |
|
1085 BaselineCompiler::emit_JSOP_LOOPENTRY() |
|
1086 { |
|
1087 frame.syncStack(0); |
|
1088 return emitUseCountIncrement(LoopEntryCanIonOsr(pc)); |
|
1089 } |
|
1090 |
|
1091 bool |
|
1092 BaselineCompiler::emit_JSOP_VOID() |
|
1093 { |
|
1094 frame.pop(); |
|
1095 frame.push(UndefinedValue()); |
|
1096 return true; |
|
1097 } |
|
1098 |
|
1099 bool |
|
1100 BaselineCompiler::emit_JSOP_UNDEFINED() |
|
1101 { |
|
1102 frame.push(UndefinedValue()); |
|
1103 return true; |
|
1104 } |
|
1105 |
|
1106 bool |
|
1107 BaselineCompiler::emit_JSOP_HOLE() |
|
1108 { |
|
1109 frame.push(MagicValue(JS_ELEMENTS_HOLE)); |
|
1110 return true; |
|
1111 } |
|
1112 |
|
1113 bool |
|
1114 BaselineCompiler::emit_JSOP_NULL() |
|
1115 { |
|
1116 frame.push(NullValue()); |
|
1117 return true; |
|
1118 } |
|
1119 |
|
1120 bool |
|
1121 BaselineCompiler::emit_JSOP_THIS() |
|
1122 { |
|
1123 if (function() && function()->isArrow()) { |
|
1124 // Arrow functions store their (lexical) |this| value in an |
|
1125 // extended slot. |
|
1126 frame.syncStack(0); |
|
1127 Register scratch = R0.scratchReg(); |
|
1128 masm.loadPtr(frame.addressOfCallee(), scratch); |
|
1129 masm.loadValue(Address(scratch, FunctionExtended::offsetOfArrowThisSlot()), R0); |
|
1130 frame.push(R0); |
|
1131 return true; |
|
1132 } |
|
1133 |
|
1134 // Keep this value in R0 |
|
1135 frame.pushThis(); |
|
1136 |
|
1137 // In strict mode code or self-hosted functions, |this| is left alone. |
|
1138 if (script->strict() || (function() && function()->isSelfHostedBuiltin())) |
|
1139 return true; |
|
1140 |
|
1141 Label skipIC; |
|
1142 // Keep |thisv| in R0 |
|
1143 frame.popRegsAndSync(1); |
|
1144 // If |this| is already an object, skip the IC. |
|
1145 masm.branchTestObject(Assembler::Equal, R0, &skipIC); |
|
1146 |
|
1147 // Call IC |
|
1148 ICThis_Fallback::Compiler stubCompiler(cx); |
|
1149 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
1150 return false; |
|
1151 |
|
1152 masm.storeValue(R0, frame.addressOfThis()); |
|
1153 |
|
1154 // R0 is new pushed |this| value. |
|
1155 masm.bind(&skipIC); |
|
1156 frame.push(R0); |
|
1157 |
|
1158 return true; |
|
1159 } |
|
1160 |
|
1161 bool |
|
1162 BaselineCompiler::emit_JSOP_TRUE() |
|
1163 { |
|
1164 frame.push(BooleanValue(true)); |
|
1165 return true; |
|
1166 } |
|
1167 |
|
1168 bool |
|
1169 BaselineCompiler::emit_JSOP_FALSE() |
|
1170 { |
|
1171 frame.push(BooleanValue(false)); |
|
1172 return true; |
|
1173 } |
|
1174 |
|
1175 bool |
|
1176 BaselineCompiler::emit_JSOP_ZERO() |
|
1177 { |
|
1178 frame.push(Int32Value(0)); |
|
1179 return true; |
|
1180 } |
|
1181 |
|
1182 bool |
|
1183 BaselineCompiler::emit_JSOP_ONE() |
|
1184 { |
|
1185 frame.push(Int32Value(1)); |
|
1186 return true; |
|
1187 } |
|
1188 |
|
1189 bool |
|
1190 BaselineCompiler::emit_JSOP_INT8() |
|
1191 { |
|
1192 frame.push(Int32Value(GET_INT8(pc))); |
|
1193 return true; |
|
1194 } |
|
1195 |
|
1196 bool |
|
1197 BaselineCompiler::emit_JSOP_INT32() |
|
1198 { |
|
1199 frame.push(Int32Value(GET_INT32(pc))); |
|
1200 return true; |
|
1201 } |
|
1202 |
|
1203 bool |
|
1204 BaselineCompiler::emit_JSOP_UINT16() |
|
1205 { |
|
1206 frame.push(Int32Value(GET_UINT16(pc))); |
|
1207 return true; |
|
1208 } |
|
1209 |
|
1210 bool |
|
1211 BaselineCompiler::emit_JSOP_UINT24() |
|
1212 { |
|
1213 frame.push(Int32Value(GET_UINT24(pc))); |
|
1214 return true; |
|
1215 } |
|
1216 |
|
1217 bool |
|
1218 BaselineCompiler::emit_JSOP_DOUBLE() |
|
1219 { |
|
1220 frame.push(script->getConst(GET_UINT32_INDEX(pc))); |
|
1221 return true; |
|
1222 } |
|
1223 |
|
1224 bool |
|
1225 BaselineCompiler::emit_JSOP_STRING() |
|
1226 { |
|
1227 frame.push(StringValue(script->getAtom(pc))); |
|
1228 return true; |
|
1229 } |
|
1230 |
|
1231 typedef JSObject *(*DeepCloneObjectLiteralFn)(JSContext *, HandleObject, NewObjectKind); |
|
1232 static const VMFunction DeepCloneObjectLiteralInfo = |
|
1233 FunctionInfo<DeepCloneObjectLiteralFn>(DeepCloneObjectLiteral); |
|
1234 |
|
1235 bool |
|
1236 BaselineCompiler::emit_JSOP_OBJECT() |
|
1237 { |
|
1238 if (JS::CompartmentOptionsRef(cx).cloneSingletons(cx)) { |
|
1239 RootedObject obj(cx, script->getObject(GET_UINT32_INDEX(pc))); |
|
1240 if (!obj) |
|
1241 return false; |
|
1242 |
|
1243 prepareVMCall(); |
|
1244 |
|
1245 pushArg(ImmWord(js::MaybeSingletonObject)); |
|
1246 pushArg(ImmGCPtr(obj)); |
|
1247 |
|
1248 if (!callVM(DeepCloneObjectLiteralInfo)) |
|
1249 return false; |
|
1250 |
|
1251 // Box and push return value. |
|
1252 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); |
|
1253 frame.push(R0); |
|
1254 return true; |
|
1255 } |
|
1256 |
|
1257 JS::CompartmentOptionsRef(cx).setSingletonsAsValues(); |
|
1258 frame.push(ObjectValue(*script->getObject(pc))); |
|
1259 return true; |
|
1260 } |
|
1261 |
|
1262 typedef JSObject *(*CloneRegExpObjectFn)(JSContext *, JSObject *); |
|
1263 static const VMFunction CloneRegExpObjectInfo = |
|
1264 FunctionInfo<CloneRegExpObjectFn>(CloneRegExpObject); |
|
1265 |
|
1266 bool |
|
1267 BaselineCompiler::emit_JSOP_REGEXP() |
|
1268 { |
|
1269 RootedObject reObj(cx, script->getRegExp(pc)); |
|
1270 |
|
1271 prepareVMCall(); |
|
1272 pushArg(ImmGCPtr(reObj)); |
|
1273 if (!callVM(CloneRegExpObjectInfo)) |
|
1274 return false; |
|
1275 |
|
1276 // Box and push return value. |
|
1277 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); |
|
1278 frame.push(R0); |
|
1279 return true; |
|
1280 } |
|
1281 |
|
1282 typedef JSObject *(*LambdaFn)(JSContext *, HandleFunction, HandleObject); |
|
1283 static const VMFunction LambdaInfo = FunctionInfo<LambdaFn>(js::Lambda); |
|
1284 |
|
1285 bool |
|
1286 BaselineCompiler::emit_JSOP_LAMBDA() |
|
1287 { |
|
1288 RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc))); |
|
1289 |
|
1290 prepareVMCall(); |
|
1291 masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); |
|
1292 |
|
1293 pushArg(R0.scratchReg()); |
|
1294 pushArg(ImmGCPtr(fun)); |
|
1295 |
|
1296 if (!callVM(LambdaInfo)) |
|
1297 return false; |
|
1298 |
|
1299 // Box and push return value. |
|
1300 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); |
|
1301 frame.push(R0); |
|
1302 return true; |
|
1303 } |
|
1304 |
|
1305 typedef JSObject *(*LambdaArrowFn)(JSContext *, HandleFunction, HandleObject, HandleValue); |
|
1306 static const VMFunction LambdaArrowInfo = FunctionInfo<LambdaArrowFn>(js::LambdaArrow); |
|
1307 |
|
1308 bool |
|
1309 BaselineCompiler::emit_JSOP_LAMBDA_ARROW() |
|
1310 { |
|
1311 // Keep pushed |this| in R0. |
|
1312 frame.popRegsAndSync(1); |
|
1313 |
|
1314 RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc))); |
|
1315 |
|
1316 prepareVMCall(); |
|
1317 masm.loadPtr(frame.addressOfScopeChain(), R1.scratchReg()); |
|
1318 |
|
1319 pushArg(R0); |
|
1320 pushArg(R1.scratchReg()); |
|
1321 pushArg(ImmGCPtr(fun)); |
|
1322 |
|
1323 if (!callVM(LambdaArrowInfo)) |
|
1324 return false; |
|
1325 |
|
1326 // Box and push return value. |
|
1327 masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); |
|
1328 frame.push(R0); |
|
1329 return true; |
|
1330 } |
|
1331 |
|
1332 void |
|
1333 BaselineCompiler::storeValue(const StackValue *source, const Address &dest, |
|
1334 const ValueOperand &scratch) |
|
1335 { |
|
1336 switch (source->kind()) { |
|
1337 case StackValue::Constant: |
|
1338 masm.storeValue(source->constant(), dest); |
|
1339 break; |
|
1340 case StackValue::Register: |
|
1341 masm.storeValue(source->reg(), dest); |
|
1342 break; |
|
1343 case StackValue::LocalSlot: |
|
1344 masm.loadValue(frame.addressOfLocal(source->localSlot()), scratch); |
|
1345 masm.storeValue(scratch, dest); |
|
1346 break; |
|
1347 case StackValue::ArgSlot: |
|
1348 masm.loadValue(frame.addressOfArg(source->argSlot()), scratch); |
|
1349 masm.storeValue(scratch, dest); |
|
1350 break; |
|
1351 case StackValue::ThisSlot: |
|
1352 masm.loadValue(frame.addressOfThis(), scratch); |
|
1353 masm.storeValue(scratch, dest); |
|
1354 break; |
|
1355 case StackValue::Stack: |
|
1356 masm.loadValue(frame.addressOfStackValue(source), scratch); |
|
1357 masm.storeValue(scratch, dest); |
|
1358 break; |
|
1359 default: |
|
1360 MOZ_ASSUME_UNREACHABLE("Invalid kind"); |
|
1361 } |
|
1362 } |
|
1363 |
|
1364 bool |
|
1365 BaselineCompiler::emit_JSOP_BITOR() |
|
1366 { |
|
1367 return emitBinaryArith(); |
|
1368 } |
|
1369 |
|
1370 bool |
|
1371 BaselineCompiler::emit_JSOP_BITXOR() |
|
1372 { |
|
1373 return emitBinaryArith(); |
|
1374 } |
|
1375 |
|
1376 bool |
|
1377 BaselineCompiler::emit_JSOP_BITAND() |
|
1378 { |
|
1379 return emitBinaryArith(); |
|
1380 } |
|
1381 |
|
1382 bool |
|
1383 BaselineCompiler::emit_JSOP_LSH() |
|
1384 { |
|
1385 return emitBinaryArith(); |
|
1386 } |
|
1387 |
|
1388 bool |
|
1389 BaselineCompiler::emit_JSOP_RSH() |
|
1390 { |
|
1391 return emitBinaryArith(); |
|
1392 } |
|
1393 |
|
1394 bool |
|
1395 BaselineCompiler::emit_JSOP_URSH() |
|
1396 { |
|
1397 return emitBinaryArith(); |
|
1398 } |
|
1399 |
|
1400 bool |
|
1401 BaselineCompiler::emit_JSOP_ADD() |
|
1402 { |
|
1403 return emitBinaryArith(); |
|
1404 } |
|
1405 |
|
1406 bool |
|
1407 BaselineCompiler::emit_JSOP_SUB() |
|
1408 { |
|
1409 return emitBinaryArith(); |
|
1410 } |
|
1411 |
|
1412 bool |
|
1413 BaselineCompiler::emit_JSOP_MUL() |
|
1414 { |
|
1415 return emitBinaryArith(); |
|
1416 } |
|
1417 |
|
1418 bool |
|
1419 BaselineCompiler::emit_JSOP_DIV() |
|
1420 { |
|
1421 return emitBinaryArith(); |
|
1422 } |
|
1423 |
|
1424 bool |
|
1425 BaselineCompiler::emit_JSOP_MOD() |
|
1426 { |
|
1427 return emitBinaryArith(); |
|
1428 } |
|
1429 |
|
1430 bool |
|
1431 BaselineCompiler::emitBinaryArith() |
|
1432 { |
|
1433 // Keep top JSStack value in R0 and R2 |
|
1434 frame.popRegsAndSync(2); |
|
1435 |
|
1436 // Call IC |
|
1437 ICBinaryArith_Fallback::Compiler stubCompiler(cx); |
|
1438 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
1439 return false; |
|
1440 |
|
1441 // Mark R0 as pushed stack value. |
|
1442 frame.push(R0); |
|
1443 return true; |
|
1444 } |
|
1445 |
|
1446 bool |
|
1447 BaselineCompiler::emitUnaryArith() |
|
1448 { |
|
1449 // Keep top stack value in R0. |
|
1450 frame.popRegsAndSync(1); |
|
1451 |
|
1452 // Call IC |
|
1453 ICUnaryArith_Fallback::Compiler stubCompiler(cx); |
|
1454 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
1455 return false; |
|
1456 |
|
1457 // Mark R0 as pushed stack value. |
|
1458 frame.push(R0); |
|
1459 return true; |
|
1460 } |
|
1461 |
|
1462 bool |
|
1463 BaselineCompiler::emit_JSOP_BITNOT() |
|
1464 { |
|
1465 return emitUnaryArith(); |
|
1466 } |
|
1467 |
|
1468 bool |
|
1469 BaselineCompiler::emit_JSOP_NEG() |
|
1470 { |
|
1471 return emitUnaryArith(); |
|
1472 } |
|
1473 |
|
1474 bool |
|
1475 BaselineCompiler::emit_JSOP_LT() |
|
1476 { |
|
1477 return emitCompare(); |
|
1478 } |
|
1479 |
|
1480 bool |
|
1481 BaselineCompiler::emit_JSOP_LE() |
|
1482 { |
|
1483 return emitCompare(); |
|
1484 } |
|
1485 |
|
1486 bool |
|
1487 BaselineCompiler::emit_JSOP_GT() |
|
1488 { |
|
1489 return emitCompare(); |
|
1490 } |
|
1491 |
|
1492 bool |
|
1493 BaselineCompiler::emit_JSOP_GE() |
|
1494 { |
|
1495 return emitCompare(); |
|
1496 } |
|
1497 |
|
1498 bool |
|
1499 BaselineCompiler::emit_JSOP_EQ() |
|
1500 { |
|
1501 return emitCompare(); |
|
1502 } |
|
1503 |
|
1504 bool |
|
1505 BaselineCompiler::emit_JSOP_NE() |
|
1506 { |
|
1507 return emitCompare(); |
|
1508 } |
|
1509 |
|
1510 bool |
|
1511 BaselineCompiler::emitCompare() |
|
1512 { |
|
1513 // CODEGEN |
|
1514 |
|
1515 // Keep top JSStack value in R0 and R1. |
|
1516 frame.popRegsAndSync(2); |
|
1517 |
|
1518 // Call IC. |
|
1519 ICCompare_Fallback::Compiler stubCompiler(cx); |
|
1520 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
1521 return false; |
|
1522 |
|
1523 // Mark R0 as pushed stack value. |
|
1524 frame.push(R0, JSVAL_TYPE_BOOLEAN); |
|
1525 return true; |
|
1526 } |
|
1527 |
|
1528 bool |
|
1529 BaselineCompiler::emit_JSOP_STRICTEQ() |
|
1530 { |
|
1531 return emitCompare(); |
|
1532 } |
|
1533 |
|
1534 bool |
|
1535 BaselineCompiler::emit_JSOP_STRICTNE() |
|
1536 { |
|
1537 return emitCompare(); |
|
1538 } |
|
1539 |
|
1540 bool |
|
1541 BaselineCompiler::emit_JSOP_CONDSWITCH() |
|
1542 { |
|
1543 return true; |
|
1544 } |
|
1545 |
|
1546 bool |
|
1547 BaselineCompiler::emit_JSOP_CASE() |
|
1548 { |
|
1549 frame.popRegsAndSync(2); |
|
1550 frame.push(R0); |
|
1551 frame.syncStack(0); |
|
1552 |
|
1553 // Call IC. |
|
1554 ICCompare_Fallback::Compiler stubCompiler(cx); |
|
1555 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
1556 return false; |
|
1557 |
|
1558 Register payload = masm.extractInt32(R0, R0.scratchReg()); |
|
1559 jsbytecode *target = pc + GET_JUMP_OFFSET(pc); |
|
1560 |
|
1561 Label done; |
|
1562 masm.branch32(Assembler::Equal, payload, Imm32(0), &done); |
|
1563 { |
|
1564 // Pop the switch value if the case matches. |
|
1565 masm.addPtr(Imm32(sizeof(Value)), StackPointer); |
|
1566 masm.jump(labelOf(target)); |
|
1567 } |
|
1568 masm.bind(&done); |
|
1569 return true; |
|
1570 } |
|
1571 |
|
1572 bool |
|
1573 BaselineCompiler::emit_JSOP_DEFAULT() |
|
1574 { |
|
1575 frame.pop(); |
|
1576 return emit_JSOP_GOTO(); |
|
1577 } |
|
1578 |
|
1579 bool |
|
1580 BaselineCompiler::emit_JSOP_LINENO() |
|
1581 { |
|
1582 return true; |
|
1583 } |
|
1584 |
|
1585 bool |
|
1586 BaselineCompiler::emit_JSOP_NEWARRAY() |
|
1587 { |
|
1588 frame.syncStack(0); |
|
1589 |
|
1590 uint32_t length = GET_UINT24(pc); |
|
1591 RootedTypeObject type(cx); |
|
1592 if (!types::UseNewTypeForInitializer(script, pc, JSProto_Array)) { |
|
1593 type = types::TypeScript::InitObject(cx, script, pc, JSProto_Array); |
|
1594 if (!type) |
|
1595 return false; |
|
1596 } |
|
1597 |
|
1598 // Pass length in R0, type in R1. |
|
1599 masm.move32(Imm32(length), R0.scratchReg()); |
|
1600 masm.movePtr(ImmGCPtr(type), R1.scratchReg()); |
|
1601 |
|
1602 JSObject *templateObject = NewDenseUnallocatedArray(cx, length, nullptr, TenuredObject); |
|
1603 if (!templateObject) |
|
1604 return false; |
|
1605 templateObject->setType(type); |
|
1606 |
|
1607 ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject); |
|
1608 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
1609 return false; |
|
1610 |
|
1611 frame.push(R0); |
|
1612 return true; |
|
1613 } |
|
1614 |
|
1615 bool |
|
1616 BaselineCompiler::emit_JSOP_INITELEM_ARRAY() |
|
1617 { |
|
1618 // Keep the object and rhs on the stack. |
|
1619 frame.syncStack(0); |
|
1620 |
|
1621 // Load object in R0, index in R1. |
|
1622 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0); |
|
1623 masm.moveValue(Int32Value(GET_UINT24(pc)), R1); |
|
1624 |
|
1625 // Call IC. |
|
1626 ICSetElem_Fallback::Compiler stubCompiler(cx); |
|
1627 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
1628 return false; |
|
1629 |
|
1630 // Pop the rhs, so that the object is on the top of the stack. |
|
1631 frame.pop(); |
|
1632 return true; |
|
1633 } |
|
1634 |
|
1635 bool |
|
1636 BaselineCompiler::emit_JSOP_NEWOBJECT() |
|
1637 { |
|
1638 frame.syncStack(0); |
|
1639 |
|
1640 RootedTypeObject type(cx); |
|
1641 if (!types::UseNewTypeForInitializer(script, pc, JSProto_Object)) { |
|
1642 type = types::TypeScript::InitObject(cx, script, pc, JSProto_Object); |
|
1643 if (!type) |
|
1644 return false; |
|
1645 } |
|
1646 |
|
1647 RootedObject baseObject(cx, script->getObject(pc)); |
|
1648 RootedObject templateObject(cx, CopyInitializerObject(cx, baseObject, TenuredObject)); |
|
1649 if (!templateObject) |
|
1650 return false; |
|
1651 |
|
1652 if (type) { |
|
1653 templateObject->setType(type); |
|
1654 } else { |
|
1655 if (!JSObject::setSingletonType(cx, templateObject)) |
|
1656 return false; |
|
1657 } |
|
1658 |
|
1659 ICNewObject_Fallback::Compiler stubCompiler(cx, templateObject); |
|
1660 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
1661 return false; |
|
1662 |
|
1663 frame.push(R0); |
|
1664 return true; |
|
1665 } |
|
1666 |
|
1667 bool |
|
1668 BaselineCompiler::emit_JSOP_NEWINIT() |
|
1669 { |
|
1670 frame.syncStack(0); |
|
1671 JSProtoKey key = JSProtoKey(GET_UINT8(pc)); |
|
1672 |
|
1673 RootedTypeObject type(cx); |
|
1674 if (!types::UseNewTypeForInitializer(script, pc, key)) { |
|
1675 type = types::TypeScript::InitObject(cx, script, pc, key); |
|
1676 if (!type) |
|
1677 return false; |
|
1678 } |
|
1679 |
|
1680 if (key == JSProto_Array) { |
|
1681 // Pass length in R0, type in R1. |
|
1682 masm.move32(Imm32(0), R0.scratchReg()); |
|
1683 masm.movePtr(ImmGCPtr(type), R1.scratchReg()); |
|
1684 |
|
1685 JSObject *templateObject = NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject); |
|
1686 if (!templateObject) |
|
1687 return false; |
|
1688 templateObject->setType(type); |
|
1689 |
|
1690 ICNewArray_Fallback::Compiler stubCompiler(cx, templateObject); |
|
1691 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
1692 return false; |
|
1693 } else { |
|
1694 JS_ASSERT(key == JSProto_Object); |
|
1695 |
|
1696 RootedObject templateObject(cx); |
|
1697 templateObject = NewBuiltinClassInstance(cx, &JSObject::class_, TenuredObject); |
|
1698 if (!templateObject) |
|
1699 return false; |
|
1700 |
|
1701 if (type) { |
|
1702 templateObject->setType(type); |
|
1703 } else { |
|
1704 if (!JSObject::setSingletonType(cx, templateObject)) |
|
1705 return false; |
|
1706 } |
|
1707 |
|
1708 ICNewObject_Fallback::Compiler stubCompiler(cx, templateObject); |
|
1709 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
1710 return false; |
|
1711 } |
|
1712 |
|
1713 frame.push(R0); |
|
1714 return true; |
|
1715 } |
|
1716 |
|
1717 bool |
|
1718 BaselineCompiler::emit_JSOP_INITELEM() |
|
1719 { |
|
1720 // Store RHS in the scratch slot. |
|
1721 storeValue(frame.peek(-1), frame.addressOfScratchValue(), R2); |
|
1722 frame.pop(); |
|
1723 |
|
1724 // Keep object and index in R0 and R1. |
|
1725 frame.popRegsAndSync(2); |
|
1726 |
|
1727 // Push the object to store the result of the IC. |
|
1728 frame.push(R0); |
|
1729 frame.syncStack(0); |
|
1730 |
|
1731 // Keep RHS on the stack. |
|
1732 frame.pushScratchValue(); |
|
1733 |
|
1734 // Call IC. |
|
1735 ICSetElem_Fallback::Compiler stubCompiler(cx); |
|
1736 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
1737 return false; |
|
1738 |
|
1739 // Pop the rhs, so that the object is on the top of the stack. |
|
1740 frame.pop(); |
|
1741 return true; |
|
1742 } |
|
1743 |
|
1744 typedef bool (*MutateProtoFn)(JSContext *cx, HandleObject obj, HandleValue newProto); |
|
1745 static const VMFunction MutateProtoInfo = FunctionInfo<MutateProtoFn>(MutatePrototype); |
|
1746 |
|
1747 bool |
|
1748 BaselineCompiler::emit_JSOP_MUTATEPROTO() |
|
1749 { |
|
1750 // Keep values on the stack for the decompiler. |
|
1751 frame.syncStack(0); |
|
1752 |
|
1753 masm.extractObject(frame.addressOfStackValue(frame.peek(-2)), R0.scratchReg()); |
|
1754 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1); |
|
1755 |
|
1756 prepareVMCall(); |
|
1757 |
|
1758 pushArg(R1); |
|
1759 pushArg(R0.scratchReg()); |
|
1760 |
|
1761 if (!callVM(MutateProtoInfo)) |
|
1762 return false; |
|
1763 |
|
1764 frame.pop(); |
|
1765 return true; |
|
1766 } |
|
1767 |
|
1768 bool |
|
1769 BaselineCompiler::emit_JSOP_INITPROP() |
|
1770 { |
|
1771 // Keep lhs in R0, rhs in R1. |
|
1772 frame.popRegsAndSync(2); |
|
1773 |
|
1774 // Push the object to store the result of the IC. |
|
1775 frame.push(R0); |
|
1776 frame.syncStack(0); |
|
1777 |
|
1778 // Call IC. |
|
1779 ICSetProp_Fallback::Compiler compiler(cx); |
|
1780 return emitOpIC(compiler.getStub(&stubSpace_)); |
|
1781 } |
|
1782 |
|
1783 bool |
|
1784 BaselineCompiler::emit_JSOP_ENDINIT() |
|
1785 { |
|
1786 return true; |
|
1787 } |
|
1788 |
|
1789 typedef bool (*NewbornArrayPushFn)(JSContext *, HandleObject, const Value &); |
|
1790 static const VMFunction NewbornArrayPushInfo = FunctionInfo<NewbornArrayPushFn>(NewbornArrayPush); |
|
1791 |
|
1792 bool |
|
1793 BaselineCompiler::emit_JSOP_ARRAYPUSH() |
|
1794 { |
|
1795 // Keep value in R0, object in R1. |
|
1796 frame.popRegsAndSync(2); |
|
1797 masm.unboxObject(R1, R1.scratchReg()); |
|
1798 |
|
1799 prepareVMCall(); |
|
1800 |
|
1801 pushArg(R0); |
|
1802 pushArg(R1.scratchReg()); |
|
1803 |
|
1804 return callVM(NewbornArrayPushInfo); |
|
1805 } |
|
1806 |
|
1807 bool |
|
1808 BaselineCompiler::emit_JSOP_GETELEM() |
|
1809 { |
|
1810 // Keep top two stack values in R0 and R1. |
|
1811 frame.popRegsAndSync(2); |
|
1812 |
|
1813 // Call IC. |
|
1814 ICGetElem_Fallback::Compiler stubCompiler(cx); |
|
1815 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
1816 return false; |
|
1817 |
|
1818 // Mark R0 as pushed stack value. |
|
1819 frame.push(R0); |
|
1820 return true; |
|
1821 } |
|
1822 |
|
1823 bool |
|
1824 BaselineCompiler::emit_JSOP_CALLELEM() |
|
1825 { |
|
1826 return emit_JSOP_GETELEM(); |
|
1827 } |
|
1828 |
|
1829 bool |
|
1830 BaselineCompiler::emit_JSOP_SETELEM() |
|
1831 { |
|
1832 // Store RHS in the scratch slot. |
|
1833 storeValue(frame.peek(-1), frame.addressOfScratchValue(), R2); |
|
1834 frame.pop(); |
|
1835 |
|
1836 // Keep object and index in R0 and R1. |
|
1837 frame.popRegsAndSync(2); |
|
1838 |
|
1839 // Keep RHS on the stack. |
|
1840 frame.pushScratchValue(); |
|
1841 |
|
1842 // Call IC. |
|
1843 ICSetElem_Fallback::Compiler stubCompiler(cx); |
|
1844 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
1845 return false; |
|
1846 |
|
1847 return true; |
|
1848 } |
|
1849 |
|
1850 typedef bool (*DeleteElementFn)(JSContext *, HandleValue, HandleValue, bool *); |
|
1851 static const VMFunction DeleteElementStrictInfo = FunctionInfo<DeleteElementFn>(DeleteElement<true>); |
|
1852 static const VMFunction DeleteElementNonStrictInfo = FunctionInfo<DeleteElementFn>(DeleteElement<false>); |
|
1853 |
|
1854 bool |
|
1855 BaselineCompiler::emit_JSOP_DELELEM() |
|
1856 { |
|
1857 // Keep values on the stack for the decompiler. |
|
1858 frame.syncStack(0); |
|
1859 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0); |
|
1860 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R1); |
|
1861 |
|
1862 prepareVMCall(); |
|
1863 |
|
1864 pushArg(R1); |
|
1865 pushArg(R0); |
|
1866 |
|
1867 if (!callVM(script->strict() ? DeleteElementStrictInfo : DeleteElementNonStrictInfo)) |
|
1868 return false; |
|
1869 |
|
1870 masm.boxNonDouble(JSVAL_TYPE_BOOLEAN, ReturnReg, R1); |
|
1871 frame.popn(2); |
|
1872 frame.push(R1); |
|
1873 return true; |
|
1874 } |
|
1875 |
|
1876 bool |
|
1877 BaselineCompiler::emit_JSOP_IN() |
|
1878 { |
|
1879 frame.popRegsAndSync(2); |
|
1880 |
|
1881 ICIn_Fallback::Compiler stubCompiler(cx); |
|
1882 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
1883 return false; |
|
1884 |
|
1885 frame.push(R0); |
|
1886 return true; |
|
1887 } |
|
1888 |
|
1889 bool |
|
1890 BaselineCompiler::emit_JSOP_GETGNAME() |
|
1891 { |
|
1892 RootedPropertyName name(cx, script->getName(pc)); |
|
1893 |
|
1894 if (name == cx->names().undefined) { |
|
1895 frame.push(UndefinedValue()); |
|
1896 return true; |
|
1897 } |
|
1898 if (name == cx->names().NaN) { |
|
1899 frame.push(cx->runtime()->NaNValue); |
|
1900 return true; |
|
1901 } |
|
1902 if (name == cx->names().Infinity) { |
|
1903 frame.push(cx->runtime()->positiveInfinityValue); |
|
1904 return true; |
|
1905 } |
|
1906 |
|
1907 frame.syncStack(0); |
|
1908 |
|
1909 masm.movePtr(ImmGCPtr(&script->global()), R0.scratchReg()); |
|
1910 |
|
1911 // Call IC. |
|
1912 ICGetName_Fallback::Compiler stubCompiler(cx); |
|
1913 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
1914 return false; |
|
1915 |
|
1916 // Mark R0 as pushed stack value. |
|
1917 frame.push(R0); |
|
1918 return true; |
|
1919 } |
|
1920 |
|
1921 bool |
|
1922 BaselineCompiler::emit_JSOP_BINDGNAME() |
|
1923 { |
|
1924 frame.push(ObjectValue(script->global())); |
|
1925 return true; |
|
1926 } |
|
1927 |
|
1928 bool |
|
1929 BaselineCompiler::emit_JSOP_SETPROP() |
|
1930 { |
|
1931 // Keep lhs in R0, rhs in R1. |
|
1932 frame.popRegsAndSync(2); |
|
1933 |
|
1934 // Call IC. |
|
1935 ICSetProp_Fallback::Compiler compiler(cx); |
|
1936 if (!emitOpIC(compiler.getStub(&stubSpace_))) |
|
1937 return false; |
|
1938 |
|
1939 // The IC will return the RHS value in R0, mark it as pushed value. |
|
1940 frame.push(R0); |
|
1941 return true; |
|
1942 } |
|
1943 |
|
1944 bool |
|
1945 BaselineCompiler::emit_JSOP_SETNAME() |
|
1946 { |
|
1947 return emit_JSOP_SETPROP(); |
|
1948 } |
|
1949 |
|
1950 bool |
|
1951 BaselineCompiler::emit_JSOP_SETGNAME() |
|
1952 { |
|
1953 return emit_JSOP_SETPROP(); |
|
1954 } |
|
1955 |
|
1956 bool |
|
1957 BaselineCompiler::emit_JSOP_GETPROP() |
|
1958 { |
|
1959 // Keep object in R0. |
|
1960 frame.popRegsAndSync(1); |
|
1961 |
|
1962 // Call IC. |
|
1963 ICGetProp_Fallback::Compiler compiler(cx); |
|
1964 if (!emitOpIC(compiler.getStub(&stubSpace_))) |
|
1965 return false; |
|
1966 |
|
1967 // Mark R0 as pushed stack value. |
|
1968 frame.push(R0); |
|
1969 return true; |
|
1970 } |
|
1971 |
|
1972 bool |
|
1973 BaselineCompiler::emit_JSOP_CALLPROP() |
|
1974 { |
|
1975 return emit_JSOP_GETPROP(); |
|
1976 } |
|
1977 |
|
1978 bool |
|
1979 BaselineCompiler::emit_JSOP_LENGTH() |
|
1980 { |
|
1981 return emit_JSOP_GETPROP(); |
|
1982 } |
|
1983 |
|
1984 bool |
|
1985 BaselineCompiler::emit_JSOP_GETXPROP() |
|
1986 { |
|
1987 return emit_JSOP_GETPROP(); |
|
1988 } |
|
1989 |
|
1990 typedef bool (*DeletePropertyFn)(JSContext *, HandleValue, HandlePropertyName, bool *); |
|
1991 static const VMFunction DeletePropertyStrictInfo = FunctionInfo<DeletePropertyFn>(DeleteProperty<true>); |
|
1992 static const VMFunction DeletePropertyNonStrictInfo = FunctionInfo<DeletePropertyFn>(DeleteProperty<false>); |
|
1993 |
|
1994 bool |
|
1995 BaselineCompiler::emit_JSOP_DELPROP() |
|
1996 { |
|
1997 // Keep value on the stack for the decompiler. |
|
1998 frame.syncStack(0); |
|
1999 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0); |
|
2000 |
|
2001 prepareVMCall(); |
|
2002 |
|
2003 pushArg(ImmGCPtr(script->getName(pc))); |
|
2004 pushArg(R0); |
|
2005 |
|
2006 if (!callVM(script->strict() ? DeletePropertyStrictInfo : DeletePropertyNonStrictInfo)) |
|
2007 return false; |
|
2008 |
|
2009 masm.boxNonDouble(JSVAL_TYPE_BOOLEAN, ReturnReg, R1); |
|
2010 frame.pop(); |
|
2011 frame.push(R1); |
|
2012 return true; |
|
2013 } |
|
2014 |
|
2015 void |
|
2016 BaselineCompiler::getScopeCoordinateObject(Register reg) |
|
2017 { |
|
2018 ScopeCoordinate sc(pc); |
|
2019 |
|
2020 masm.loadPtr(frame.addressOfScopeChain(), reg); |
|
2021 for (unsigned i = sc.hops(); i; i--) |
|
2022 masm.extractObject(Address(reg, ScopeObject::offsetOfEnclosingScope()), reg); |
|
2023 } |
|
2024 |
|
2025 Address |
|
2026 BaselineCompiler::getScopeCoordinateAddressFromObject(Register objReg, Register reg) |
|
2027 { |
|
2028 ScopeCoordinate sc(pc); |
|
2029 Shape *shape = ScopeCoordinateToStaticScopeShape(script, pc); |
|
2030 |
|
2031 Address addr; |
|
2032 if (shape->numFixedSlots() <= sc.slot()) { |
|
2033 masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), reg); |
|
2034 return Address(reg, (sc.slot() - shape->numFixedSlots()) * sizeof(Value)); |
|
2035 } |
|
2036 |
|
2037 return Address(objReg, JSObject::getFixedSlotOffset(sc.slot())); |
|
2038 } |
|
2039 |
|
2040 Address |
|
2041 BaselineCompiler::getScopeCoordinateAddress(Register reg) |
|
2042 { |
|
2043 getScopeCoordinateObject(reg); |
|
2044 return getScopeCoordinateAddressFromObject(reg, reg); |
|
2045 } |
|
2046 |
|
2047 bool |
|
2048 BaselineCompiler::emit_JSOP_GETALIASEDVAR() |
|
2049 { |
|
2050 frame.syncStack(0); |
|
2051 |
|
2052 Address address = getScopeCoordinateAddress(R0.scratchReg()); |
|
2053 masm.loadValue(address, R0); |
|
2054 |
|
2055 ICTypeMonitor_Fallback::Compiler compiler(cx, (ICMonitoredFallbackStub *) nullptr); |
|
2056 if (!emitOpIC(compiler.getStub(&stubSpace_))) |
|
2057 return false; |
|
2058 |
|
2059 frame.push(R0); |
|
2060 return true; |
|
2061 } |
|
2062 |
|
2063 bool |
|
2064 BaselineCompiler::emit_JSOP_SETALIASEDVAR() |
|
2065 { |
|
2066 JSScript *outerScript = ScopeCoordinateFunctionScript(script, pc); |
|
2067 if (outerScript && outerScript->treatAsRunOnce()) { |
|
2068 // Type updates for this operation might need to be tracked, so treat |
|
2069 // this as a SETPROP. |
|
2070 |
|
2071 // Load rhs into R1. |
|
2072 frame.syncStack(1); |
|
2073 frame.popValue(R1); |
|
2074 |
|
2075 // Load and box lhs into R0. |
|
2076 getScopeCoordinateObject(R2.scratchReg()); |
|
2077 masm.tagValue(JSVAL_TYPE_OBJECT, R2.scratchReg(), R0); |
|
2078 |
|
2079 // Call SETPROP IC. |
|
2080 ICSetProp_Fallback::Compiler compiler(cx); |
|
2081 if (!emitOpIC(compiler.getStub(&stubSpace_))) |
|
2082 return false; |
|
2083 |
|
2084 // The IC will return the RHS value in R0, mark it as pushed value. |
|
2085 frame.push(R0); |
|
2086 return true; |
|
2087 } |
|
2088 |
|
2089 // Keep rvalue in R0. |
|
2090 frame.popRegsAndSync(1); |
|
2091 Register objReg = R2.scratchReg(); |
|
2092 |
|
2093 getScopeCoordinateObject(objReg); |
|
2094 Address address = getScopeCoordinateAddressFromObject(objReg, R1.scratchReg()); |
|
2095 masm.patchableCallPreBarrier(address, MIRType_Value); |
|
2096 masm.storeValue(R0, address); |
|
2097 frame.push(R0); |
|
2098 |
|
2099 #ifdef JSGC_GENERATIONAL |
|
2100 // Fully sync the stack if post-barrier is needed. |
|
2101 // Scope coordinate object is already in R2.scratchReg(). |
|
2102 frame.syncStack(0); |
|
2103 Register temp = R1.scratchReg(); |
|
2104 |
|
2105 Label skipBarrier; |
|
2106 masm.branchTestObject(Assembler::NotEqual, R0, &skipBarrier); |
|
2107 masm.branchPtrInNurseryRange(objReg, temp, &skipBarrier); |
|
2108 |
|
2109 masm.call(&postBarrierSlot_); |
|
2110 |
|
2111 masm.bind(&skipBarrier); |
|
2112 #endif |
|
2113 |
|
2114 return true; |
|
2115 } |
|
2116 |
|
2117 bool |
|
2118 BaselineCompiler::emit_JSOP_NAME() |
|
2119 { |
|
2120 frame.syncStack(0); |
|
2121 |
|
2122 masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); |
|
2123 |
|
2124 // Call IC. |
|
2125 ICGetName_Fallback::Compiler stubCompiler(cx); |
|
2126 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
2127 return false; |
|
2128 |
|
2129 // Mark R0 as pushed stack value. |
|
2130 frame.push(R0); |
|
2131 return true; |
|
2132 } |
|
2133 |
|
2134 bool |
|
2135 BaselineCompiler::emit_JSOP_BINDNAME() |
|
2136 { |
|
2137 frame.syncStack(0); |
|
2138 |
|
2139 masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); |
|
2140 |
|
2141 // Call IC. |
|
2142 ICBindName_Fallback::Compiler stubCompiler(cx); |
|
2143 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
2144 return false; |
|
2145 |
|
2146 // Mark R0 as pushed stack value. |
|
2147 frame.push(R0); |
|
2148 return true; |
|
2149 } |
|
2150 |
|
2151 typedef bool (*DeleteNameFn)(JSContext *, HandlePropertyName, HandleObject, |
|
2152 MutableHandleValue); |
|
2153 static const VMFunction DeleteNameInfo = FunctionInfo<DeleteNameFn>(DeleteNameOperation); |
|
2154 |
|
2155 bool |
|
2156 BaselineCompiler::emit_JSOP_DELNAME() |
|
2157 { |
|
2158 frame.syncStack(0); |
|
2159 masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); |
|
2160 |
|
2161 prepareVMCall(); |
|
2162 |
|
2163 pushArg(R0.scratchReg()); |
|
2164 pushArg(ImmGCPtr(script->getName(pc))); |
|
2165 |
|
2166 if (!callVM(DeleteNameInfo)) |
|
2167 return false; |
|
2168 |
|
2169 frame.push(R0); |
|
2170 return true; |
|
2171 } |
|
2172 |
|
2173 bool |
|
2174 BaselineCompiler::emit_JSOP_GETINTRINSIC() |
|
2175 { |
|
2176 frame.syncStack(0); |
|
2177 |
|
2178 ICGetIntrinsic_Fallback::Compiler stubCompiler(cx); |
|
2179 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
2180 return false; |
|
2181 |
|
2182 frame.push(R0); |
|
2183 return true; |
|
2184 } |
|
2185 |
|
2186 typedef bool (*DefVarOrConstFn)(JSContext *, HandlePropertyName, unsigned, HandleObject); |
|
2187 static const VMFunction DefVarOrConstInfo = FunctionInfo<DefVarOrConstFn>(DefVarOrConst); |
|
2188 |
|
2189 bool |
|
2190 BaselineCompiler::emit_JSOP_DEFVAR() |
|
2191 { |
|
2192 frame.syncStack(0); |
|
2193 |
|
2194 unsigned attrs = JSPROP_ENUMERATE; |
|
2195 if (!script->isForEval()) |
|
2196 attrs |= JSPROP_PERMANENT; |
|
2197 if (JSOp(*pc) == JSOP_DEFCONST) |
|
2198 attrs |= JSPROP_READONLY; |
|
2199 JS_ASSERT(attrs <= UINT32_MAX); |
|
2200 |
|
2201 masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); |
|
2202 |
|
2203 prepareVMCall(); |
|
2204 |
|
2205 pushArg(R0.scratchReg()); |
|
2206 pushArg(Imm32(attrs)); |
|
2207 pushArg(ImmGCPtr(script->getName(pc))); |
|
2208 |
|
2209 return callVM(DefVarOrConstInfo); |
|
2210 } |
|
2211 |
|
2212 bool |
|
2213 BaselineCompiler::emit_JSOP_DEFCONST() |
|
2214 { |
|
2215 return emit_JSOP_DEFVAR(); |
|
2216 } |
|
2217 |
|
2218 typedef bool (*SetConstFn)(JSContext *, HandlePropertyName, HandleObject, HandleValue); |
|
2219 static const VMFunction SetConstInfo = FunctionInfo<SetConstFn>(SetConst); |
|
2220 |
|
2221 bool |
|
2222 BaselineCompiler::emit_JSOP_SETCONST() |
|
2223 { |
|
2224 frame.popRegsAndSync(1); |
|
2225 frame.push(R0); |
|
2226 frame.syncStack(0); |
|
2227 |
|
2228 masm.loadPtr(frame.addressOfScopeChain(), R1.scratchReg()); |
|
2229 |
|
2230 prepareVMCall(); |
|
2231 |
|
2232 pushArg(R0); |
|
2233 pushArg(R1.scratchReg()); |
|
2234 pushArg(ImmGCPtr(script->getName(pc))); |
|
2235 |
|
2236 return callVM(SetConstInfo); |
|
2237 } |
|
2238 |
|
2239 typedef bool (*DefFunOperationFn)(JSContext *, HandleScript, HandleObject, HandleFunction); |
|
2240 static const VMFunction DefFunOperationInfo = FunctionInfo<DefFunOperationFn>(DefFunOperation); |
|
2241 |
|
2242 bool |
|
2243 BaselineCompiler::emit_JSOP_DEFFUN() |
|
2244 { |
|
2245 RootedFunction fun(cx, script->getFunction(GET_UINT32_INDEX(pc))); |
|
2246 |
|
2247 frame.syncStack(0); |
|
2248 masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); |
|
2249 |
|
2250 prepareVMCall(); |
|
2251 |
|
2252 pushArg(ImmGCPtr(fun)); |
|
2253 pushArg(R0.scratchReg()); |
|
2254 pushArg(ImmGCPtr(script)); |
|
2255 |
|
2256 return callVM(DefFunOperationInfo); |
|
2257 } |
|
2258 |
|
2259 typedef bool (*InitPropGetterSetterFn)(JSContext *, jsbytecode *, HandleObject, HandlePropertyName, |
|
2260 HandleObject); |
|
2261 static const VMFunction InitPropGetterSetterInfo = |
|
2262 FunctionInfo<InitPropGetterSetterFn>(InitGetterSetterOperation); |
|
2263 |
|
2264 bool |
|
2265 BaselineCompiler::emitInitPropGetterSetter() |
|
2266 { |
|
2267 JS_ASSERT(JSOp(*pc) == JSOP_INITPROP_GETTER || |
|
2268 JSOp(*pc) == JSOP_INITPROP_SETTER); |
|
2269 |
|
2270 // Keep values on the stack for the decompiler. |
|
2271 frame.syncStack(0); |
|
2272 |
|
2273 prepareVMCall(); |
|
2274 |
|
2275 masm.extractObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg()); |
|
2276 masm.extractObject(frame.addressOfStackValue(frame.peek(-2)), R1.scratchReg()); |
|
2277 |
|
2278 pushArg(R0.scratchReg()); |
|
2279 pushArg(ImmGCPtr(script->getName(pc))); |
|
2280 pushArg(R1.scratchReg()); |
|
2281 pushArg(ImmPtr(pc)); |
|
2282 |
|
2283 if (!callVM(InitPropGetterSetterInfo)) |
|
2284 return false; |
|
2285 |
|
2286 frame.pop(); |
|
2287 return true; |
|
2288 } |
|
2289 |
|
2290 bool |
|
2291 BaselineCompiler::emit_JSOP_INITPROP_GETTER() |
|
2292 { |
|
2293 return emitInitPropGetterSetter(); |
|
2294 } |
|
2295 |
|
2296 bool |
|
2297 BaselineCompiler::emit_JSOP_INITPROP_SETTER() |
|
2298 { |
|
2299 return emitInitPropGetterSetter(); |
|
2300 } |
|
2301 |
|
2302 typedef bool (*InitElemGetterSetterFn)(JSContext *, jsbytecode *, HandleObject, HandleValue, |
|
2303 HandleObject); |
|
2304 static const VMFunction InitElemGetterSetterInfo = |
|
2305 FunctionInfo<InitElemGetterSetterFn>(InitGetterSetterOperation); |
|
2306 |
|
2307 bool |
|
2308 BaselineCompiler::emitInitElemGetterSetter() |
|
2309 { |
|
2310 JS_ASSERT(JSOp(*pc) == JSOP_INITELEM_GETTER || |
|
2311 JSOp(*pc) == JSOP_INITELEM_SETTER); |
|
2312 |
|
2313 // Load index and value in R0 and R1, but keep values on the stack for the |
|
2314 // decompiler. |
|
2315 frame.syncStack(0); |
|
2316 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R0); |
|
2317 masm.extractObject(frame.addressOfStackValue(frame.peek(-1)), R1.scratchReg()); |
|
2318 |
|
2319 prepareVMCall(); |
|
2320 |
|
2321 pushArg(R1.scratchReg()); |
|
2322 pushArg(R0); |
|
2323 masm.extractObject(frame.addressOfStackValue(frame.peek(-3)), R0.scratchReg()); |
|
2324 pushArg(R0.scratchReg()); |
|
2325 pushArg(ImmPtr(pc)); |
|
2326 |
|
2327 if (!callVM(InitElemGetterSetterInfo)) |
|
2328 return false; |
|
2329 |
|
2330 frame.popn(2); |
|
2331 return true; |
|
2332 } |
|
2333 |
|
2334 bool |
|
2335 BaselineCompiler::emit_JSOP_INITELEM_GETTER() |
|
2336 { |
|
2337 return emitInitElemGetterSetter(); |
|
2338 } |
|
2339 |
|
2340 bool |
|
2341 BaselineCompiler::emit_JSOP_INITELEM_SETTER() |
|
2342 { |
|
2343 return emitInitElemGetterSetter(); |
|
2344 } |
|
2345 |
|
2346 bool |
|
2347 BaselineCompiler::emit_JSOP_GETLOCAL() |
|
2348 { |
|
2349 frame.pushLocal(GET_LOCALNO(pc)); |
|
2350 return true; |
|
2351 } |
|
2352 |
|
2353 bool |
|
2354 BaselineCompiler::emit_JSOP_SETLOCAL() |
|
2355 { |
|
2356 // Ensure no other StackValue refers to the old value, for instance i + (i = 3). |
|
2357 // This also allows us to use R0 as scratch below. |
|
2358 frame.syncStack(1); |
|
2359 |
|
2360 uint32_t local = GET_LOCALNO(pc); |
|
2361 storeValue(frame.peek(-1), frame.addressOfLocal(local), R0); |
|
2362 return true; |
|
2363 } |
|
2364 |
|
2365 bool |
|
2366 BaselineCompiler::emitFormalArgAccess(uint32_t arg, bool get) |
|
2367 { |
|
2368 // Fast path: the script does not use |arguments|, or is strict. In strict |
|
2369 // mode, formals do not alias the arguments object. |
|
2370 if (!script->argumentsHasVarBinding() || script->strict()) { |
|
2371 if (get) { |
|
2372 frame.pushArg(arg); |
|
2373 } else { |
|
2374 // See the comment in emit_JSOP_SETLOCAL. |
|
2375 frame.syncStack(1); |
|
2376 storeValue(frame.peek(-1), frame.addressOfArg(arg), R0); |
|
2377 } |
|
2378 |
|
2379 return true; |
|
2380 } |
|
2381 |
|
2382 // Sync so that we can use R0. |
|
2383 frame.syncStack(0); |
|
2384 |
|
2385 // If the script is known to have an arguments object, we can just use it. |
|
2386 // Else, we *may* have an arguments object (because we can't invalidate |
|
2387 // when needsArgsObj becomes |true|), so we have to test HAS_ARGS_OBJ. |
|
2388 Label done; |
|
2389 if (!script->needsArgsObj()) { |
|
2390 Label hasArgsObj; |
|
2391 masm.branchTest32(Assembler::NonZero, frame.addressOfFlags(), |
|
2392 Imm32(BaselineFrame::HAS_ARGS_OBJ), &hasArgsObj); |
|
2393 if (get) |
|
2394 masm.loadValue(frame.addressOfArg(arg), R0); |
|
2395 else |
|
2396 storeValue(frame.peek(-1), frame.addressOfArg(arg), R0); |
|
2397 masm.jump(&done); |
|
2398 masm.bind(&hasArgsObj); |
|
2399 } |
|
2400 |
|
2401 // Load the arguments object data vector. |
|
2402 Register reg = R2.scratchReg(); |
|
2403 masm.loadPtr(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfArgsObj()), reg); |
|
2404 masm.loadPrivate(Address(reg, ArgumentsObject::getDataSlotOffset()), reg); |
|
2405 |
|
2406 // Load/store the argument. |
|
2407 Address argAddr(reg, ArgumentsData::offsetOfArgs() + arg * sizeof(Value)); |
|
2408 if (get) { |
|
2409 masm.loadValue(argAddr, R0); |
|
2410 frame.push(R0); |
|
2411 } else { |
|
2412 masm.patchableCallPreBarrier(argAddr, MIRType_Value); |
|
2413 storeValue(frame.peek(-1), argAddr, R0); |
|
2414 |
|
2415 #ifdef JSGC_GENERATIONAL |
|
2416 // Fully sync the stack if post-barrier is needed. |
|
2417 frame.syncStack(0); |
|
2418 Register temp = R1.scratchReg(); |
|
2419 |
|
2420 // Reload the arguments object |
|
2421 Register reg = R2.scratchReg(); |
|
2422 masm.loadPtr(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfArgsObj()), reg); |
|
2423 |
|
2424 Label skipBarrier; |
|
2425 masm.branchPtrInNurseryRange(reg, temp, &skipBarrier); |
|
2426 |
|
2427 masm.call(&postBarrierSlot_); |
|
2428 |
|
2429 masm.bind(&skipBarrier); |
|
2430 #endif |
|
2431 } |
|
2432 |
|
2433 masm.bind(&done); |
|
2434 return true; |
|
2435 } |
|
2436 |
|
2437 bool |
|
2438 BaselineCompiler::emit_JSOP_GETARG() |
|
2439 { |
|
2440 uint32_t arg = GET_ARGNO(pc); |
|
2441 return emitFormalArgAccess(arg, /* get = */ true); |
|
2442 } |
|
2443 |
|
2444 bool |
|
2445 BaselineCompiler::emit_JSOP_SETARG() |
|
2446 { |
|
2447 // Ionmonkey can't inline functions with SETARG with magic arguments. |
|
2448 if (!script->argsObjAliasesFormals() && script->argumentsAliasesFormals()) |
|
2449 script->setUninlineable(); |
|
2450 |
|
2451 modifiesArguments_ = true; |
|
2452 |
|
2453 uint32_t arg = GET_ARGNO(pc); |
|
2454 return emitFormalArgAccess(arg, /* get = */ false); |
|
2455 } |
|
2456 |
|
2457 bool |
|
2458 BaselineCompiler::emitCall() |
|
2459 { |
|
2460 JS_ASSERT(IsCallPC(pc)); |
|
2461 |
|
2462 uint32_t argc = GET_ARGC(pc); |
|
2463 |
|
2464 frame.syncStack(0); |
|
2465 masm.mov(ImmWord(argc), R0.scratchReg()); |
|
2466 |
|
2467 // Call IC |
|
2468 ICCall_Fallback::Compiler stubCompiler(cx, /* isConstructing = */ JSOp(*pc) == JSOP_NEW); |
|
2469 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
2470 return false; |
|
2471 |
|
2472 // Update FrameInfo. |
|
2473 frame.popn(argc + 2); |
|
2474 frame.push(R0); |
|
2475 return true; |
|
2476 } |
|
2477 |
|
2478 bool |
|
2479 BaselineCompiler::emit_JSOP_CALL() |
|
2480 { |
|
2481 return emitCall(); |
|
2482 } |
|
2483 |
|
2484 bool |
|
2485 BaselineCompiler::emit_JSOP_NEW() |
|
2486 { |
|
2487 return emitCall(); |
|
2488 } |
|
2489 |
|
2490 bool |
|
2491 BaselineCompiler::emit_JSOP_FUNCALL() |
|
2492 { |
|
2493 return emitCall(); |
|
2494 } |
|
2495 |
|
2496 bool |
|
2497 BaselineCompiler::emit_JSOP_FUNAPPLY() |
|
2498 { |
|
2499 return emitCall(); |
|
2500 } |
|
2501 |
|
2502 bool |
|
2503 BaselineCompiler::emit_JSOP_EVAL() |
|
2504 { |
|
2505 return emitCall(); |
|
2506 } |
|
2507 |
|
2508 typedef bool (*ImplicitThisFn)(JSContext *, HandleObject, HandlePropertyName, |
|
2509 MutableHandleValue); |
|
2510 static const VMFunction ImplicitThisInfo = FunctionInfo<ImplicitThisFn>(ImplicitThisOperation); |
|
2511 |
|
2512 bool |
|
2513 BaselineCompiler::emit_JSOP_IMPLICITTHIS() |
|
2514 { |
|
2515 frame.syncStack(0); |
|
2516 masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); |
|
2517 |
|
2518 prepareVMCall(); |
|
2519 |
|
2520 pushArg(ImmGCPtr(script->getName(pc))); |
|
2521 pushArg(R0.scratchReg()); |
|
2522 |
|
2523 if (!callVM(ImplicitThisInfo)) |
|
2524 return false; |
|
2525 |
|
2526 frame.push(R0); |
|
2527 return true; |
|
2528 } |
|
2529 |
|
2530 bool |
|
2531 BaselineCompiler::emit_JSOP_INSTANCEOF() |
|
2532 { |
|
2533 frame.popRegsAndSync(2); |
|
2534 |
|
2535 ICInstanceOf_Fallback::Compiler stubCompiler(cx); |
|
2536 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
2537 return false; |
|
2538 |
|
2539 frame.push(R0); |
|
2540 return true; |
|
2541 } |
|
2542 |
|
2543 bool |
|
2544 BaselineCompiler::emit_JSOP_TYPEOF() |
|
2545 { |
|
2546 frame.popRegsAndSync(1); |
|
2547 |
|
2548 ICTypeOf_Fallback::Compiler stubCompiler(cx); |
|
2549 if (!emitOpIC(stubCompiler.getStub(&stubSpace_))) |
|
2550 return false; |
|
2551 |
|
2552 frame.push(R0); |
|
2553 return true; |
|
2554 } |
|
2555 |
|
2556 bool |
|
2557 BaselineCompiler::emit_JSOP_TYPEOFEXPR() |
|
2558 { |
|
2559 return emit_JSOP_TYPEOF(); |
|
2560 } |
|
2561 |
|
2562 typedef bool (*SetCallFn)(JSContext *); |
|
2563 static const VMFunction SetCallInfo = FunctionInfo<SetCallFn>(js::SetCallOperation); |
|
2564 |
|
2565 bool |
|
2566 BaselineCompiler::emit_JSOP_SETCALL() |
|
2567 { |
|
2568 prepareVMCall(); |
|
2569 return callVM(SetCallInfo); |
|
2570 } |
|
2571 |
|
2572 typedef bool (*ThrowFn)(JSContext *, HandleValue); |
|
2573 static const VMFunction ThrowInfo = FunctionInfo<ThrowFn>(js::Throw); |
|
2574 |
|
2575 bool |
|
2576 BaselineCompiler::emit_JSOP_THROW() |
|
2577 { |
|
2578 // Keep value to throw in R0. |
|
2579 frame.popRegsAndSync(1); |
|
2580 |
|
2581 prepareVMCall(); |
|
2582 pushArg(R0); |
|
2583 |
|
2584 return callVM(ThrowInfo); |
|
2585 } |
|
2586 |
|
2587 bool |
|
2588 BaselineCompiler::emit_JSOP_TRY() |
|
2589 { |
|
2590 // Ionmonkey can't inline function with JSOP_TRY. |
|
2591 script->setUninlineable(); |
|
2592 return true; |
|
2593 } |
|
2594 |
|
2595 bool |
|
2596 BaselineCompiler::emit_JSOP_FINALLY() |
|
2597 { |
|
2598 // JSOP_FINALLY has a def count of 2, but these values are already on the |
|
2599 // stack (they're pushed by JSOP_GOSUB). Update the compiler's stack state. |
|
2600 frame.setStackDepth(frame.stackDepth() + 2); |
|
2601 |
|
2602 // To match the interpreter, emit an interrupt check at the start of the |
|
2603 // finally block. |
|
2604 return emitInterruptCheck(); |
|
2605 } |
|
2606 |
|
2607 bool |
|
2608 BaselineCompiler::emit_JSOP_GOSUB() |
|
2609 { |
|
2610 // Push |false| so that RETSUB knows the value on top of the |
|
2611 // stack is not an exception but the offset to the op following |
|
2612 // this GOSUB. |
|
2613 frame.push(BooleanValue(false)); |
|
2614 |
|
2615 int32_t nextOffset = script->pcToOffset(GetNextPc(pc)); |
|
2616 frame.push(Int32Value(nextOffset)); |
|
2617 |
|
2618 // Jump to the finally block. |
|
2619 frame.syncStack(0); |
|
2620 jsbytecode *target = pc + GET_JUMP_OFFSET(pc); |
|
2621 masm.jump(labelOf(target)); |
|
2622 return true; |
|
2623 } |
|
2624 |
|
2625 bool |
|
2626 BaselineCompiler::emit_JSOP_RETSUB() |
|
2627 { |
|
2628 frame.popRegsAndSync(2); |
|
2629 |
|
2630 ICRetSub_Fallback::Compiler stubCompiler(cx); |
|
2631 return emitOpIC(stubCompiler.getStub(&stubSpace_)); |
|
2632 } |
|
2633 |
|
2634 typedef bool (*PushBlockScopeFn)(JSContext *, BaselineFrame *, Handle<StaticBlockObject *>); |
|
2635 static const VMFunction PushBlockScopeInfo = FunctionInfo<PushBlockScopeFn>(jit::PushBlockScope); |
|
2636 |
|
2637 bool |
|
2638 BaselineCompiler::emit_JSOP_PUSHBLOCKSCOPE() |
|
2639 { |
|
2640 StaticBlockObject &blockObj = script->getObject(pc)->as<StaticBlockObject>(); |
|
2641 |
|
2642 // Call a stub to push the block on the block chain. |
|
2643 prepareVMCall(); |
|
2644 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); |
|
2645 |
|
2646 pushArg(ImmGCPtr(&blockObj)); |
|
2647 pushArg(R0.scratchReg()); |
|
2648 |
|
2649 return callVM(PushBlockScopeInfo); |
|
2650 } |
|
2651 |
|
2652 typedef bool (*PopBlockScopeFn)(JSContext *, BaselineFrame *); |
|
2653 static const VMFunction PopBlockScopeInfo = FunctionInfo<PopBlockScopeFn>(jit::PopBlockScope); |
|
2654 |
|
2655 bool |
|
2656 BaselineCompiler::emit_JSOP_POPBLOCKSCOPE() |
|
2657 { |
|
2658 // Call a stub to pop the block from the block chain. |
|
2659 prepareVMCall(); |
|
2660 |
|
2661 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); |
|
2662 pushArg(R0.scratchReg()); |
|
2663 |
|
2664 return callVM(PopBlockScopeInfo); |
|
2665 } |
|
2666 |
|
2667 typedef bool (*DebugLeaveBlockFn)(JSContext *, BaselineFrame *, jsbytecode *); |
|
2668 static const VMFunction DebugLeaveBlockInfo = FunctionInfo<DebugLeaveBlockFn>(jit::DebugLeaveBlock); |
|
2669 |
|
2670 bool |
|
2671 BaselineCompiler::emit_JSOP_DEBUGLEAVEBLOCK() |
|
2672 { |
|
2673 if (!debugMode_) |
|
2674 return true; |
|
2675 |
|
2676 prepareVMCall(); |
|
2677 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); |
|
2678 pushArg(ImmPtr(pc)); |
|
2679 pushArg(R0.scratchReg()); |
|
2680 |
|
2681 return callVM(DebugLeaveBlockInfo); |
|
2682 } |
|
2683 |
|
2684 typedef bool (*EnterWithFn)(JSContext *, BaselineFrame *, HandleValue, Handle<StaticWithObject *>); |
|
2685 static const VMFunction EnterWithInfo = FunctionInfo<EnterWithFn>(jit::EnterWith); |
|
2686 |
|
2687 bool |
|
2688 BaselineCompiler::emit_JSOP_ENTERWITH() |
|
2689 { |
|
2690 StaticWithObject &withObj = script->getObject(pc)->as<StaticWithObject>(); |
|
2691 |
|
2692 // Pop "with" object to R0. |
|
2693 frame.popRegsAndSync(1); |
|
2694 |
|
2695 // Call a stub to push the object onto the scope chain. |
|
2696 prepareVMCall(); |
|
2697 masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg()); |
|
2698 |
|
2699 pushArg(ImmGCPtr(&withObj)); |
|
2700 pushArg(R0); |
|
2701 pushArg(R1.scratchReg()); |
|
2702 |
|
2703 return callVM(EnterWithInfo); |
|
2704 } |
|
2705 |
|
2706 typedef bool (*LeaveWithFn)(JSContext *, BaselineFrame *); |
|
2707 static const VMFunction LeaveWithInfo = FunctionInfo<LeaveWithFn>(jit::LeaveWith); |
|
2708 |
|
2709 bool |
|
2710 BaselineCompiler::emit_JSOP_LEAVEWITH() |
|
2711 { |
|
2712 // Call a stub to pop the with object from the scope chain. |
|
2713 prepareVMCall(); |
|
2714 |
|
2715 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); |
|
2716 pushArg(R0.scratchReg()); |
|
2717 |
|
2718 return callVM(LeaveWithInfo); |
|
2719 } |
|
2720 |
|
2721 typedef bool (*GetAndClearExceptionFn)(JSContext *, MutableHandleValue); |
|
2722 static const VMFunction GetAndClearExceptionInfo = |
|
2723 FunctionInfo<GetAndClearExceptionFn>(GetAndClearException); |
|
2724 |
|
2725 bool |
|
2726 BaselineCompiler::emit_JSOP_EXCEPTION() |
|
2727 { |
|
2728 prepareVMCall(); |
|
2729 |
|
2730 if (!callVM(GetAndClearExceptionInfo)) |
|
2731 return false; |
|
2732 |
|
2733 frame.push(R0); |
|
2734 return true; |
|
2735 } |
|
2736 |
|
2737 typedef bool (*OnDebuggerStatementFn)(JSContext *, BaselineFrame *, jsbytecode *pc, bool *); |
|
2738 static const VMFunction OnDebuggerStatementInfo = |
|
2739 FunctionInfo<OnDebuggerStatementFn>(jit::OnDebuggerStatement); |
|
2740 |
|
2741 bool |
|
2742 BaselineCompiler::emit_JSOP_DEBUGGER() |
|
2743 { |
|
2744 if (!debugMode_) |
|
2745 return true; |
|
2746 |
|
2747 prepareVMCall(); |
|
2748 pushArg(ImmPtr(pc)); |
|
2749 |
|
2750 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); |
|
2751 pushArg(R0.scratchReg()); |
|
2752 |
|
2753 if (!callVM(OnDebuggerStatementInfo)) |
|
2754 return false; |
|
2755 |
|
2756 // If the stub returns |true|, return the frame's return value. |
|
2757 Label done; |
|
2758 masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done); |
|
2759 { |
|
2760 masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand); |
|
2761 masm.jump(&return_); |
|
2762 } |
|
2763 masm.bind(&done); |
|
2764 return true; |
|
2765 } |
|
2766 |
|
2767 typedef bool (*DebugEpilogueFn)(JSContext *, BaselineFrame *, jsbytecode *, bool); |
|
2768 static const VMFunction DebugEpilogueInfo = FunctionInfo<DebugEpilogueFn>(jit::DebugEpilogue); |
|
2769 |
|
2770 bool |
|
2771 BaselineCompiler::emitReturn() |
|
2772 { |
|
2773 if (debugMode_) { |
|
2774 // Move return value into the frame's rval slot. |
|
2775 masm.storeValue(JSReturnOperand, frame.addressOfReturnValue()); |
|
2776 masm.or32(Imm32(BaselineFrame::HAS_RVAL), frame.addressOfFlags()); |
|
2777 |
|
2778 // Load BaselineFrame pointer in R0. |
|
2779 frame.syncStack(0); |
|
2780 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); |
|
2781 |
|
2782 prepareVMCall(); |
|
2783 pushArg(Imm32(1)); |
|
2784 pushArg(ImmPtr(pc)); |
|
2785 pushArg(R0.scratchReg()); |
|
2786 if (!callVM(DebugEpilogueInfo)) |
|
2787 return false; |
|
2788 |
|
2789 // Fix up the fake ICEntry appended by callVM for on-stack recompilation. |
|
2790 icEntries_.back().setForDebugEpilogue(); |
|
2791 |
|
2792 masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand); |
|
2793 } |
|
2794 |
|
2795 // Only emit the jump if this JSOP_RETRVAL is not the last instruction. |
|
2796 // Not needed for last instruction, because last instruction flows |
|
2797 // into return label. |
|
2798 if (pc + GetBytecodeLength(pc) < script->codeEnd()) |
|
2799 masm.jump(&return_); |
|
2800 |
|
2801 return true; |
|
2802 } |
|
2803 |
|
2804 bool |
|
2805 BaselineCompiler::emit_JSOP_RETURN() |
|
2806 { |
|
2807 JS_ASSERT(frame.stackDepth() == 1); |
|
2808 |
|
2809 frame.popValue(JSReturnOperand); |
|
2810 return emitReturn(); |
|
2811 } |
|
2812 |
|
2813 bool |
|
2814 BaselineCompiler::emit_JSOP_RETRVAL() |
|
2815 { |
|
2816 JS_ASSERT(frame.stackDepth() == 0); |
|
2817 |
|
2818 masm.moveValue(UndefinedValue(), JSReturnOperand); |
|
2819 |
|
2820 if (!script->noScriptRval()) { |
|
2821 // Return the value in the return value slot, if any. |
|
2822 Label done; |
|
2823 Address flags = frame.addressOfFlags(); |
|
2824 masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL), &done); |
|
2825 masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand); |
|
2826 masm.bind(&done); |
|
2827 } |
|
2828 |
|
2829 return emitReturn(); |
|
2830 } |
|
2831 |
|
2832 typedef bool (*ToIdFn)(JSContext *, HandleScript, jsbytecode *, HandleValue, HandleValue, |
|
2833 MutableHandleValue); |
|
2834 static const VMFunction ToIdInfo = FunctionInfo<ToIdFn>(js::ToIdOperation); |
|
2835 |
|
2836 bool |
|
2837 BaselineCompiler::emit_JSOP_TOID() |
|
2838 { |
|
2839 // Load index in R0, but keep values on the stack for the decompiler. |
|
2840 frame.syncStack(0); |
|
2841 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0); |
|
2842 |
|
2843 // No-op if index is int32. |
|
2844 Label done; |
|
2845 masm.branchTestInt32(Assembler::Equal, R0, &done); |
|
2846 |
|
2847 prepareVMCall(); |
|
2848 |
|
2849 masm.loadValue(frame.addressOfStackValue(frame.peek(-2)), R1); |
|
2850 |
|
2851 pushArg(R0); |
|
2852 pushArg(R1); |
|
2853 pushArg(ImmPtr(pc)); |
|
2854 pushArg(ImmGCPtr(script)); |
|
2855 |
|
2856 if (!callVM(ToIdInfo)) |
|
2857 return false; |
|
2858 |
|
2859 masm.bind(&done); |
|
2860 frame.pop(); // Pop index. |
|
2861 frame.push(R0); |
|
2862 return true; |
|
2863 } |
|
2864 |
|
2865 bool |
|
2866 BaselineCompiler::emit_JSOP_TABLESWITCH() |
|
2867 { |
|
2868 frame.popRegsAndSync(1); |
|
2869 |
|
2870 // Call IC. |
|
2871 ICTableSwitch::Compiler compiler(cx, pc); |
|
2872 return emitOpIC(compiler.getStub(&stubSpace_)); |
|
2873 } |
|
2874 |
|
2875 bool |
|
2876 BaselineCompiler::emit_JSOP_ITER() |
|
2877 { |
|
2878 frame.popRegsAndSync(1); |
|
2879 |
|
2880 ICIteratorNew_Fallback::Compiler compiler(cx); |
|
2881 if (!emitOpIC(compiler.getStub(&stubSpace_))) |
|
2882 return false; |
|
2883 |
|
2884 frame.push(R0); |
|
2885 return true; |
|
2886 } |
|
2887 |
|
2888 bool |
|
2889 BaselineCompiler::emit_JSOP_MOREITER() |
|
2890 { |
|
2891 frame.syncStack(0); |
|
2892 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0); |
|
2893 |
|
2894 ICIteratorMore_Fallback::Compiler compiler(cx); |
|
2895 if (!emitOpIC(compiler.getStub(&stubSpace_))) |
|
2896 return false; |
|
2897 |
|
2898 frame.push(R0); |
|
2899 return true; |
|
2900 } |
|
2901 |
|
2902 bool |
|
2903 BaselineCompiler::emit_JSOP_ITERNEXT() |
|
2904 { |
|
2905 frame.syncStack(0); |
|
2906 masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0); |
|
2907 |
|
2908 ICIteratorNext_Fallback::Compiler compiler(cx); |
|
2909 if (!emitOpIC(compiler.getStub(&stubSpace_))) |
|
2910 return false; |
|
2911 |
|
2912 frame.push(R0); |
|
2913 return true; |
|
2914 } |
|
2915 |
|
2916 bool |
|
2917 BaselineCompiler::emit_JSOP_ENDITER() |
|
2918 { |
|
2919 frame.popRegsAndSync(1); |
|
2920 |
|
2921 ICIteratorClose_Fallback::Compiler compiler(cx); |
|
2922 return emitOpIC(compiler.getStub(&stubSpace_)); |
|
2923 } |
|
2924 |
|
2925 bool |
|
2926 BaselineCompiler::emit_JSOP_SETRVAL() |
|
2927 { |
|
2928 // Store to the frame's return value slot. |
|
2929 storeValue(frame.peek(-1), frame.addressOfReturnValue(), R2); |
|
2930 masm.or32(Imm32(BaselineFrame::HAS_RVAL), frame.addressOfFlags()); |
|
2931 frame.pop(); |
|
2932 return true; |
|
2933 } |
|
2934 |
|
2935 bool |
|
2936 BaselineCompiler::emit_JSOP_CALLEE() |
|
2937 { |
|
2938 JS_ASSERT(function()); |
|
2939 frame.syncStack(0); |
|
2940 masm.loadPtr(frame.addressOfCallee(), R0.scratchReg()); |
|
2941 masm.tagValue(JSVAL_TYPE_OBJECT, R0.scratchReg(), R0); |
|
2942 frame.push(R0); |
|
2943 return true; |
|
2944 } |
|
2945 |
|
2946 typedef bool (*NewArgumentsObjectFn)(JSContext *, BaselineFrame *, MutableHandleValue); |
|
2947 static const VMFunction NewArgumentsObjectInfo = |
|
2948 FunctionInfo<NewArgumentsObjectFn>(jit::NewArgumentsObject); |
|
2949 |
|
2950 bool |
|
2951 BaselineCompiler::emit_JSOP_ARGUMENTS() |
|
2952 { |
|
2953 frame.syncStack(0); |
|
2954 |
|
2955 Label done; |
|
2956 if (!script->argumentsHasVarBinding() || !script->needsArgsObj()) { |
|
2957 // We assume the script does not need an arguments object. However, this |
|
2958 // assumption can be invalidated later, see argumentsOptimizationFailed |
|
2959 // in JSScript. Because we can't invalidate baseline JIT code, we set a |
|
2960 // flag on BaselineScript when that happens and guard on it here. |
|
2961 masm.moveValue(MagicValue(JS_OPTIMIZED_ARGUMENTS), R0); |
|
2962 |
|
2963 // Load script->baseline. |
|
2964 Register scratch = R1.scratchReg(); |
|
2965 masm.movePtr(ImmGCPtr(script), scratch); |
|
2966 masm.loadPtr(Address(scratch, JSScript::offsetOfBaselineScript()), scratch); |
|
2967 |
|
2968 // If we don't need an arguments object, skip the VM call. |
|
2969 masm.branchTest32(Assembler::Zero, Address(scratch, BaselineScript::offsetOfFlags()), |
|
2970 Imm32(BaselineScript::NEEDS_ARGS_OBJ), &done); |
|
2971 } |
|
2972 |
|
2973 prepareVMCall(); |
|
2974 |
|
2975 masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); |
|
2976 pushArg(R0.scratchReg()); |
|
2977 |
|
2978 if (!callVM(NewArgumentsObjectInfo)) |
|
2979 return false; |
|
2980 |
|
2981 masm.bind(&done); |
|
2982 frame.push(R0); |
|
2983 return true; |
|
2984 } |
|
2985 |
|
2986 typedef bool (*RunOnceScriptPrologueFn)(JSContext *, HandleScript); |
|
2987 static const VMFunction RunOnceScriptPrologueInfo = |
|
2988 FunctionInfo<RunOnceScriptPrologueFn>(js::RunOnceScriptPrologue); |
|
2989 |
|
2990 bool |
|
2991 BaselineCompiler::emit_JSOP_RUNONCE() |
|
2992 { |
|
2993 frame.syncStack(0); |
|
2994 |
|
2995 prepareVMCall(); |
|
2996 |
|
2997 masm.movePtr(ImmGCPtr(script), R0.scratchReg()); |
|
2998 pushArg(R0.scratchReg()); |
|
2999 |
|
3000 return callVM(RunOnceScriptPrologueInfo); |
|
3001 } |
|
3002 |
|
3003 bool |
|
3004 BaselineCompiler::emit_JSOP_REST() |
|
3005 { |
|
3006 frame.syncStack(0); |
|
3007 |
|
3008 JSObject *templateObject = NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject); |
|
3009 if (!templateObject) |
|
3010 return false; |
|
3011 types::FixRestArgumentsType(cx, templateObject); |
|
3012 |
|
3013 // Call IC. |
|
3014 ICRest_Fallback::Compiler compiler(cx, templateObject); |
|
3015 if (!emitOpIC(compiler.getStub(&stubSpace_))) |
|
3016 return false; |
|
3017 |
|
3018 // Mark R0 as pushed stack value. |
|
3019 frame.push(R0); |
|
3020 return true; |
|
3021 } |