1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=99:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 : * May 28, 2008.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Brendan Eich <brendan@mozilla.org>
22 : *
23 : * Contributor(s):
24 : * David Anderson <danderson@mozilla.com>
25 : * David Mandelin <dmandelin@mozilla.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #if !defined jsjaeger_baseassembler_h__ && defined JS_METHODJIT
42 : #define jsjaeger_baseassembler_h__
43 :
44 : #include "jscntxt.h"
45 : #include "assembler/assembler/MacroAssemblerCodeRef.h"
46 : #include "assembler/assembler/MacroAssembler.h"
47 : #include "assembler/assembler/LinkBuffer.h"
48 : #include "assembler/moco/MocoStubs.h"
49 : #include "methodjit/MethodJIT.h"
50 : #include "methodjit/MachineRegs.h"
51 : #include "CodeGenIncludes.h"
52 : #include "jsobjinlines.h"
53 : #include "jsscopeinlines.h"
54 :
55 : namespace js {
56 : namespace mjit {
57 :
58 : // Represents an int32_t property name in generated code, which must be either
59 : // a RegisterID or a constant value.
60 5703 : struct Int32Key {
61 : typedef JSC::MacroAssembler::RegisterID RegisterID;
62 :
63 : MaybeRegisterID reg_;
64 : int32_t index_;
65 :
66 68986 : Int32Key() : index_(0) { }
67 :
68 41342 : static Int32Key FromRegister(RegisterID reg) {
69 41342 : Int32Key key;
70 41342 : key.reg_ = reg;
71 : return key;
72 : }
73 21941 : static Int32Key FromConstant(int32_t index) {
74 21941 : Int32Key key;
75 21941 : key.index_ = index;
76 : return key;
77 : }
78 :
79 50670 : int32_t index() const {
80 50670 : JS_ASSERT(!reg_.isSet());
81 50670 : return index_;
82 : }
83 :
84 220543 : RegisterID reg() const { return reg_.reg(); }
85 228475 : bool isConstant() const { return !reg_.isSet(); }
86 : };
87 :
88 : struct FrameAddress : JSC::MacroAssembler::Address
89 : {
90 12470690 : FrameAddress(int32_t offset)
91 12470690 : : Address(JSC::MacroAssembler::stackPointerRegister, offset)
92 12470690 : { }
93 : };
94 :
95 : struct ImmIntPtr : public JSC::MacroAssembler::ImmPtr
96 : {
97 223042 : ImmIntPtr(intptr_t val)
98 223042 : : ImmPtr(reinterpret_cast<void*>(val))
99 223042 : { }
100 : };
101 :
102 : struct StackMarker {
103 : uint32_t base;
104 : uint32_t bytes;
105 :
106 216 : StackMarker(uint32_t base, uint32_t bytes)
107 216 : : base(base), bytes(bytes)
108 216 : { }
109 : };
110 :
111 : class Assembler : public ValueAssembler
112 347651 : {
113 4513730 : struct CallPatch {
114 2683842 : CallPatch(Call cl, void *fun)
115 2683842 : : call(cl), fun(fun)
116 2683842 : { }
117 :
118 : Call call;
119 : JSC::FunctionPtr fun;
120 : };
121 :
122 104688 : struct DoublePatch {
123 : double d;
124 : DataLabelPtr label;
125 : };
126 :
127 : /* :TODO: OOM */
128 : Label startLabel;
129 : Vector<CallPatch, 64, SystemAllocPolicy> callPatches;
130 : Vector<DoublePatch, 16, SystemAllocPolicy> doublePatches;
131 :
132 : // Registers that can be clobbered during a call sequence.
133 : Registers availInCall;
134 :
135 : // Extra number of bytes that can be used for storing structs/references
136 : // across calls.
137 : uint32_t extraStackSpace;
138 :
139 : // Calling convention used by the currently in-progress call.
140 : Registers::CallConvention callConvention;
141 :
142 : // Amount of stack space reserved for the currently in-progress call. This
143 : // includes alignment and parameters.
144 : uint32_t stackAdjust;
145 :
146 : // Debug flag to make sure calls do not nest.
147 : #ifdef DEBUG
148 : bool callIsAligned;
149 : #endif
150 :
151 : public:
152 347651 : Assembler()
153 : : callPatches(SystemAllocPolicy()),
154 : availInCall(0),
155 : extraStackSpace(0),
156 : stackAdjust(0)
157 : #ifdef DEBUG
158 347651 : , callIsAligned(false)
159 : #endif
160 : {
161 347651 : startLabel = label();
162 347651 : }
163 :
164 : /* Register pair storing returned type/data for calls. */
165 : #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
166 : static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = JSC::X86Registers::edi;
167 : static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = JSC::X86Registers::esi;
168 : static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::X86Registers::ecx;
169 : #elif defined(JS_CPU_ARM)
170 : static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = JSC::ARMRegisters::r5;
171 : static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = JSC::ARMRegisters::r4;
172 : static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::ARMRegisters::r1;
173 : #elif defined(JS_CPU_SPARC)
174 : static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = JSC::SparcRegisters::l2;
175 : static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = JSC::SparcRegisters::l3;
176 : static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegisters::l4;
177 : #elif defined(JS_CPU_MIPS)
178 : static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = JSC::MIPSRegisters::a0;
179 : static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = JSC::MIPSRegisters::a2;
180 : static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::MIPSRegisters::a1;
181 : #endif
182 :
183 940111 : size_t distanceOf(Label l) {
184 940111 : return differenceBetween(startLabel, l);
185 : }
186 :
187 420297 : void loadPtrFromImm(void *ptr, RegisterID reg) {
188 420297 : loadPtr(ptr, reg);
189 420297 : }
190 :
191 151120 : void loadShape(RegisterID obj, RegisterID shape) {
192 151120 : loadPtr(Address(obj, JSObject::offsetOfShape()), shape);
193 151120 : }
194 :
195 38553 : Jump guardShape(RegisterID objReg, const Shape *shape) {
196 38553 : return branchPtr(NotEqual, Address(objReg, JSObject::offsetOfShape()), ImmPtr(shape));
197 : }
198 :
199 12499 : Jump guardShape(RegisterID objReg, JSObject *obj) {
200 12499 : return guardShape(objReg, obj->lastProperty());
201 : }
202 :
203 : /*
204 : * Finds and returns the address of a known object and slot.
205 : */
206 : Address objSlotRef(JSObject *obj, RegisterID reg, uint32_t slot) {
207 : move(ImmPtr(obj), reg);
208 : if (obj->isFixedSlot(slot)) {
209 : return Address(reg, JSObject::getFixedSlotOffset(slot));
210 : } else {
211 : loadPtr(Address(reg, JSObject::offsetOfSlots()), reg);
212 : return Address(reg, obj->dynamicSlotIndex(slot) * sizeof(Value));
213 : }
214 : }
215 :
216 : #ifdef JS_CPU_X86
217 1008 : void idiv(RegisterID reg) {
218 1008 : m_assembler.cdq();
219 1008 : m_assembler.idivl_r(reg);
220 1008 : }
221 :
222 1006371 : void fastLoadDouble(RegisterID lo, RegisterID hi, FPRegisterID fpReg) {
223 1006371 : JS_ASSERT(fpReg != Registers::FPConversionTemp);
224 1006371 : if (MacroAssemblerX86Common::getSSEState() >= HasSSE4_1) {
225 1006078 : m_assembler.movd_rr(lo, fpReg);
226 1006078 : m_assembler.pinsrd_rr(hi, fpReg);
227 : } else {
228 293 : m_assembler.movd_rr(lo, fpReg);
229 293 : m_assembler.movd_rr(hi, Registers::FPConversionTemp);
230 293 : m_assembler.unpcklps_rr(Registers::FPConversionTemp, fpReg);
231 : }
232 1006371 : }
233 : #endif
234 :
235 : /*
236 : * Move a register pair which may indicate either an int32_t or double into fpreg,
237 : * converting to double in the int32_t case.
238 : */
239 10152 : void moveInt32OrDouble(RegisterID data, RegisterID type, Address address, FPRegisterID fpreg)
240 : {
241 : #ifdef JS_CPU_X86
242 10152 : fastLoadDouble(data, type, fpreg);
243 10152 : Jump notInteger = testInt32(Assembler::NotEqual, type);
244 10152 : convertInt32ToDouble(data, fpreg);
245 10152 : notInteger.linkTo(label(), this);
246 : #else
247 : Jump notInteger = testInt32(Assembler::NotEqual, type);
248 : convertInt32ToDouble(data, fpreg);
249 : Jump fallthrough = jump();
250 : notInteger.linkTo(label(), this);
251 :
252 : /* Store the components, then read it back out as a double. */
253 : storeValueFromComponents(type, data, address);
254 : loadDouble(address, fpreg);
255 :
256 : fallthrough.linkTo(label(), this);
257 : #endif
258 10152 : }
259 :
260 : /*
261 : * Move a memory address which contains either an int32_t or double into fpreg,
262 : * converting to double in the int32_t case.
263 : */
264 : template <typename T>
265 2205 : void moveInt32OrDouble(T address, FPRegisterID fpreg)
266 : {
267 2205 : Jump notInteger = testInt32(Assembler::NotEqual, address);
268 2205 : convertInt32ToDouble(payloadOf(address), fpreg);
269 2205 : Jump fallthrough = jump();
270 2205 : notInteger.linkTo(label(), this);
271 2205 : loadDouble(address, fpreg);
272 2205 : fallthrough.linkTo(label(), this);
273 2205 : }
274 :
275 : /* Ensure that an in-memory address is definitely a double. */
276 20453 : void ensureInMemoryDouble(Address address)
277 : {
278 20453 : Jump notInteger = testInt32(Assembler::NotEqual, address);
279 20453 : convertInt32ToDouble(payloadOf(address), Registers::FPConversionTemp);
280 20453 : storeDouble(Registers::FPConversionTemp, address);
281 20453 : notInteger.linkTo(label(), this);
282 20453 : }
283 :
284 3231 : void negateDouble(FPRegisterID fpreg)
285 : {
286 : #if defined JS_CPU_X86 || defined JS_CPU_X64
287 : static const uint64_t DoubleNegMask = 0x8000000000000000ULL;
288 3231 : loadDouble(&DoubleNegMask, Registers::FPConversionTemp);
289 3231 : xorDouble(Registers::FPConversionTemp, fpreg);
290 : #elif defined JS_CPU_ARM || defined JS_CPU_SPARC || defined JS_CPU_MIPS
291 : negDouble(fpreg, fpreg);
292 : #endif
293 3231 : }
294 :
295 : /* Prepare for a call that might THROW. */
296 2683842 : void *getFallibleCallTarget(void *fun) {
297 : #ifdef JS_CPU_ARM
298 : /*
299 : * Insert a veneer for ARM to allow it to catch exceptions. There is no
300 : * reliable way to determine the location of the return address on the
301 : * stack, so a typical C(++) return address cannot be hijacked.
302 : *
303 : * We put the real target address into IP, as this won't conflict with
304 : * the EABI argument-passing mechanism. JaegerStubVeneer is responsible
305 : * for calling 'fun' (in IP) and catching exceptions.
306 : *
307 : * Note that we must use 'moveWithPatch' here, rather than 'move',
308 : * because 'move' might try to optimize the constant load, and we need a
309 : * consistent code sequence for patching.
310 : */
311 : moveWithPatch(Imm32(intptr_t(fun)), JSC::ARMRegisters::ip);
312 :
313 : return JS_FUNC_TO_DATA_PTR(void *, JaegerStubVeneer);
314 : #elif defined(JS_CPU_SPARC)
315 : /*
316 : * We can simulate the situation in jited code to let call return to a
317 : * target address located on stack without veneer. We record the return
318 : * address and jump to that address after call return to jited code. The
319 : * reason we take veneer back is jited code maybe released when
320 : * exceptions happened. That will make the call have no chance to return
321 : * back to jited code.
322 : */
323 : moveWithPatch(Imm32(intptr_t(fun)), JSC::SparcRegisters::i0);
324 : return JS_FUNC_TO_DATA_PTR(void *, JaegerStubVeneer);
325 : #elif defined(JS_CPU_MIPS)
326 : /*
327 : * For MIPS, we need to call JaegerStubVeneer by passing
328 : * the real target address in v0.
329 : */
330 : moveWithPatch(Imm32(intptr_t(fun)), JSC::MIPSRegisters::v0);
331 : return JS_FUNC_TO_DATA_PTR(void *, JaegerStubVeneer);
332 : #else
333 : /*
334 : * Architectures that push the return address to an easily-determined
335 : * location on the stack can hijack C++'s return mechanism by overwriting
336 : * that address, so a veneer is not required.
337 : */
338 2683842 : return fun;
339 : #endif
340 : }
341 :
342 2684058 : static inline uint32_t align(uint32_t bytes, uint32_t alignment) {
343 2684058 : return (alignment - (bytes % alignment)) % alignment;
344 : }
345 :
346 : // Specifies extra stack space that is available across a call, for storing
347 : // large parameters (structs) or returning values via references. All extra
348 : // stack space must be reserved up-front, and is aligned on an 8-byte
349 : // boundary.
350 : //
351 : // Returns an offset that can be used to index into this stack
352 216 : StackMarker allocStack(uint32_t bytes, uint32_t alignment = 4) {
353 216 : bytes += align(bytes + extraStackSpace, alignment);
354 216 : subPtr(Imm32(bytes), stackPointerRegister);
355 216 : extraStackSpace += bytes;
356 216 : return StackMarker(extraStackSpace, bytes);
357 : }
358 :
359 : // Similar to allocStack(), but combines it with a push().
360 570 : void saveReg(RegisterID reg) {
361 570 : push(reg);
362 570 : extraStackSpace += sizeof(void *);
363 570 : }
364 :
365 : // Similar to freeStack(), but combines it with a pop().
366 570 : void restoreReg(RegisterID reg) {
367 570 : JS_ASSERT(extraStackSpace >= sizeof(void *));
368 570 : extraStackSpace -= sizeof(void *);
369 570 : pop(reg);
370 570 : }
371 :
372 : #if defined JS_CPU_MIPS
373 : static const uint32_t StackAlignment = 8;
374 : #else
375 : static const uint32_t StackAlignment = 16;
376 : #endif
377 :
378 2683842 : static inline uint32_t alignForCall(uint32_t stackBytes) {
379 : #if defined(JS_CPU_X86) || defined(JS_CPU_X64) || defined(JS_CPU_MIPS)
380 : // If StackAlignment is a power of two, % is just two shifts.
381 : // 16 - (x % 16) gives alignment, extra % 16 handles total == 0.
382 2683842 : return align(stackBytes, StackAlignment);
383 : #else
384 : return 0;
385 : #endif
386 : }
387 :
388 : // Some platforms require stack manipulation before making stub calls.
389 : // When using THROW/V, the return address is replaced, meaning the
390 : // stack de-adjustment will not have occured. JaegerThrowpoline accounts
391 : // for this. For stub calls, which are always invoked as if they use
392 : // two parameters, the stack adjustment is constant.
393 : //
394 : // When using callWithABI() manually, for example via an IC, it might
395 : // be necessary to jump directly to JaegerThrowpoline. In this case,
396 : // the constant is provided here in order to appropriately adjust the
397 : // stack.
398 : #ifdef _WIN64
399 : static const uint32_t ReturnStackAdjustment = 32;
400 : #elif defined(JS_CPU_X86) && defined(JS_NO_FASTCALL)
401 : static const uint32_t ReturnStackAdjustment = 16;
402 : #else
403 : static const uint32_t ReturnStackAdjustment = 0;
404 : #endif
405 :
406 17243 : void throwInJIT() {
407 : if (ReturnStackAdjustment)
408 : subPtr(Imm32(ReturnStackAdjustment), stackPointerRegister);
409 17243 : move(ImmPtr(JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline)), Registers::ReturnReg);
410 17243 : jump(Registers::ReturnReg);
411 17243 : }
412 :
413 : // Windows x64 requires extra space in between calls.
414 : #ifdef _WIN64
415 : static const uint32_t ShadowStackSpace = 32;
416 : #elif defined(JS_CPU_SPARC)
417 : static const uint32_t ShadowStackSpace = 92;
418 : #else
419 : static const uint32_t ShadowStackSpace = 0;
420 : #endif
421 :
422 : #if defined(JS_CPU_SPARC)
423 : static const uint32_t BaseStackSpace = 104;
424 : #else
425 : static const uint32_t BaseStackSpace = 0;
426 : #endif
427 :
428 : // Prepare the stack for a call sequence. This must be called AFTER all
429 : // volatile regs have been saved, and BEFORE pushArg() is used. The stack
430 : // is assumed to be aligned to 16-bytes plus any pushes that occured via
431 : // saveRegs().
432 : //
433 : // During a call sequence all registers are "owned" by the Assembler.
434 : // Attempts to perform loads, nested calls, or anything that can clobber
435 : // a register, is asking for breaking on some platform or some situation.
436 : // Be careful to limit to storeArg() during setupABICall.
437 2683842 : void setupABICall(Registers::CallConvention convention, uint32_t generalArgs) {
438 2683842 : JS_ASSERT(!callIsAligned);
439 :
440 2683842 : uint32_t numArgRegs = Registers::numArgRegs(convention);
441 : uint32_t pushCount = (generalArgs > numArgRegs)
442 : ? generalArgs - numArgRegs
443 2683842 : : 0;
444 :
445 : // Assume all temporary regs are available to clobber.
446 2683842 : availInCall = Registers::TempRegs;
447 :
448 : // Find the total number of bytes the stack will have been adjusted by,
449 : // in order to compute alignment.
450 : uint32_t total = (pushCount * sizeof(void *)) +
451 2683842 : extraStackSpace;
452 :
453 : stackAdjust = (pushCount * sizeof(void *)) +
454 2683842 : alignForCall(total);
455 :
456 : #ifdef _WIN64
457 : // Windows x64 ABI requires 32 bytes of "shadow space" for the callee
458 : // to spill its parameters.
459 : stackAdjust += ShadowStackSpace;
460 : #endif
461 :
462 2683842 : if (stackAdjust)
463 17314 : subPtr(Imm32(stackAdjust), stackPointerRegister);
464 :
465 2683842 : callConvention = convention;
466 : #ifdef DEBUG
467 2683842 : callIsAligned = true;
468 : #endif
469 2683842 : }
470 :
471 : // Computes an interior pointer into VMFrame during a call.
472 216 : Address vmFrameOffset(uint32_t offs) {
473 216 : return Address(stackPointerRegister, stackAdjust + extraStackSpace + offs);
474 : }
475 :
476 : // Get an Address to the extra space already allocated before the call.
477 483 : Address addressOfExtra(const StackMarker &marker) {
478 : // Stack looks like this:
479 : // extraStackSpace
480 : // stackAdjust
481 : // To get to the requested offset into extraStackSpace, we can walk
482 : // up to the top of the extra stack space, then subtract |offs|.
483 : //
484 : // Note that it's not required we're in a call - stackAdjust can be 0.
485 483 : JS_ASSERT(marker.base <= extraStackSpace);
486 483 : return Address(stackPointerRegister, BaseStackSpace + stackAdjust + extraStackSpace - marker.base);
487 : }
488 :
489 : // This is an internal function only for use inside a setupABICall(),
490 : // callWithABI() sequence, and only for arguments known to fit in
491 : // registers.
492 51893 : Address addressOfArg(uint32_t i) {
493 51893 : uint32_t numArgRegs = Registers::numArgRegs(callConvention);
494 51893 : JS_ASSERT(i >= numArgRegs);
495 :
496 : // Note that shadow space is for the callee to spill, and thus it must
497 : // be skipped when writing its arguments.
498 51893 : int32_t spOffset = ((i - numArgRegs) * sizeof(void *)) + ShadowStackSpace;
499 51893 : return Address(stackPointerRegister, spOffset);
500 : }
501 :
502 : // Push an argument for a call.
503 5367586 : void storeArg(uint32_t i, RegisterID reg) {
504 5367586 : JS_ASSERT(callIsAligned);
505 : RegisterID to;
506 5367586 : if (Registers::regForArg(callConvention, i, &to)) {
507 5332766 : if (reg != to)
508 0 : move(reg, to);
509 5332766 : availInCall.takeRegUnchecked(to);
510 : } else {
511 34820 : storePtr(reg, addressOfArg(i));
512 : }
513 5367586 : }
514 :
515 : // This variant can clobber temporary registers. However, it will NOT
516 : // clobber any registers that have already been set via storeArg().
517 216 : void storeArg(uint32_t i, Address address) {
518 216 : JS_ASSERT(callIsAligned);
519 : RegisterID to;
520 216 : if (Registers::regForArg(callConvention, i, &to)) {
521 216 : loadPtr(address, to);
522 216 : availInCall.takeRegUnchecked(to);
523 0 : } else if (!availInCall.empty()) {
524 : // Memory-to-memory, and there is a temporary register free.
525 0 : RegisterID reg = availInCall.takeAnyReg().reg();
526 0 : loadPtr(address, reg);
527 0 : storeArg(i, reg);
528 0 : availInCall.putReg(reg);
529 : } else {
530 : // Memory-to-memory, but no temporary registers are free.
531 : // This shouldn't happen on any platforms, because
532 : // (TempRegs) Union (ArgRegs) != 0
533 0 : JS_NOT_REACHED("too much reg pressure");
534 : }
535 216 : }
536 :
537 : // This variant can clobber temporary registers. However, it will NOT
538 : // clobber any registers that have already been set via storeArg().
539 216 : void storeArgAddr(uint32_t i, Address address) {
540 216 : JS_ASSERT(callIsAligned);
541 : RegisterID to;
542 216 : if (Registers::regForArg(callConvention, i, &to)) {
543 216 : lea(address, to);
544 216 : availInCall.takeRegUnchecked(to);
545 0 : } else if (!availInCall.empty()) {
546 : // Memory-to-memory, and there is a temporary register free.
547 0 : RegisterID reg = availInCall.takeAnyReg().reg();
548 0 : lea(address, reg);
549 0 : storeArg(i, reg);
550 0 : availInCall.putReg(reg);
551 : } else {
552 : // Memory-to-memory, but no temporary registers are free.
553 : // This shouldn't happen on any platforms, because
554 : // (TempRegs) Union (ArgRegs) != 0
555 0 : JS_NOT_REACHED("too much reg pressure");
556 : }
557 216 : }
558 :
559 17073 : void storeArg(uint32_t i, ImmPtr imm) {
560 17073 : JS_ASSERT(callIsAligned);
561 : RegisterID to;
562 17073 : if (Registers::regForArg(callConvention, i, &to)) {
563 0 : move(imm, to);
564 0 : availInCall.takeRegUnchecked(to);
565 : } else {
566 17073 : storePtr(imm, addressOfArg(i));
567 : }
568 17073 : }
569 :
570 : // High-level call helper, given an optional function pointer and a
571 : // calling convention. setupABICall() must have been called beforehand,
572 : // as well as each numbered argument stored with storeArg().
573 : //
574 : // After callWithABI(), the call state is reset, so a new call may begin.
575 2683842 : Call callWithABI(void *fun, bool canThrow) {
576 : #ifdef JS_CPU_ARM
577 : // the repatcher requires that these instructions are adjacent in
578 : // memory, make sure that they are in fact adjacent.
579 : // Theoretically, this requires only 12 bytes of space, however
580 : // there are at least a couple of off-by-one errors that I've noticed
581 : // that make 12 insufficent. In case 16 is also insufficent, I've bumped
582 : // it to 20.
583 : ensureSpace(20);
584 : int initFlushCount = flushCount();
585 : #endif
586 : // [Bug 614953]: This can only be made conditional once the ARM back-end
587 : // is able to distinguish and patch both call sequences. Other
588 : // architecutres are unaffected regardless.
589 : //if (canThrow) {
590 : // Some platforms (such as ARM) require a call veneer if the target
591 : // might THROW. For other platforms, getFallibleCallTarget does
592 : // nothing.
593 2683842 : fun = getFallibleCallTarget(fun);
594 : //}
595 :
596 2683842 : JS_ASSERT(callIsAligned);
597 :
598 2683842 : Call cl = call();
599 2683842 : callPatches.append(CallPatch(cl, fun));
600 : #ifdef JS_CPU_ARM
601 : JS_ASSERT(initFlushCount == flushCount());
602 : #endif
603 2683842 : if (stackAdjust)
604 17314 : addPtr(Imm32(stackAdjust), stackPointerRegister);
605 :
606 2683842 : stackAdjust = 0;
607 :
608 : #ifdef DEBUG
609 2683842 : callIsAligned = false;
610 : #endif
611 : return cl;
612 : }
613 :
614 : // Frees stack space allocated by allocStack().
615 216 : void freeStack(const StackMarker &mark) {
616 216 : JS_ASSERT(!callIsAligned);
617 216 : JS_ASSERT(mark.bytes <= extraStackSpace);
618 :
619 216 : extraStackSpace -= mark.bytes;
620 216 : addPtr(Imm32(mark.bytes), stackPointerRegister);
621 216 : }
622 :
623 : // Wrap AbstractMacroAssembler::getLinkerCallReturnOffset which is protected.
624 2606267 : unsigned callReturnOffset(Call call) {
625 2606267 : return getLinkerCallReturnOffset(call);
626 : }
627 :
628 :
629 : #define STUB_CALL_TYPE(type) \
630 : Call callWithVMFrame(bool inlining, type stub, jsbytecode *pc, \
631 : DataLabelPtr *pinlined, uint32_t fd) { \
632 : return fallibleVMCall(inlining, JS_FUNC_TO_DATA_PTR(void *, stub), \
633 : pc, pinlined, fd); \
634 : }
635 :
636 : STUB_CALL_TYPE(JSObjStub);
637 : STUB_CALL_TYPE(VoidPtrStubUInt32);
638 : STUB_CALL_TYPE(VoidStubUInt32);
639 : STUB_CALL_TYPE(VoidStub);
640 :
641 : #undef STUB_CALL_TYPE
642 :
643 2683626 : void setupFrameDepth(int32_t frameDepth) {
644 : // |frameDepth < 0| implies ic::SplatApplyArgs has been called which
645 : // means regs.sp has already been set in the VMFrame.
646 2683626 : if (frameDepth >= 0) {
647 : // sp = fp->slots() + frameDepth
648 : // regs->sp = sp
649 : addPtr(Imm32(sizeof(StackFrame) + frameDepth * sizeof(jsval)),
650 : JSFrameReg,
651 2674893 : Registers::ClobberInCall);
652 2674893 : storePtr(Registers::ClobberInCall, FrameAddress(VMFrame::offsetOfRegsSp()));
653 : }
654 2683626 : }
655 :
656 2666383 : void setupInfallibleVMFrame(int32_t frameDepth) {
657 2666383 : setupFrameDepth(frameDepth);
658 :
659 : // The JIT has moved Arg1 already, and we've guaranteed to not clobber
660 : // it. Move ArgReg0 into place now. setupFallibleVMFrame will not
661 : // clobber it either.
662 2666383 : move(MacroAssembler::stackPointerRegister, Registers::ArgReg0);
663 2666383 : }
664 :
665 2634419 : void setupFallibleVMFrame(bool inlining, jsbytecode *pc,
666 : DataLabelPtr *pinlined, int32_t frameDepth) {
667 2634419 : setupInfallibleVMFrame(frameDepth);
668 :
669 : /* regs->fp = fp */
670 2634419 : storePtr(JSFrameReg, FrameAddress(VMFrame::offsetOfFp));
671 :
672 : /* PC -> regs->pc :( */
673 2634419 : storePtr(ImmPtr(pc), FrameAddress(VMFrame::offsetOfRegsPc()));
674 :
675 2634419 : if (inlining) {
676 : /* inlined -> regs->inlined :( */
677 : DataLabelPtr ptr = storePtrWithPatch(ImmPtr(NULL),
678 1458793 : FrameAddress(VMFrame::offsetOfInlined));
679 1458793 : if (pinlined)
680 1431730 : *pinlined = ptr;
681 : }
682 :
683 2634419 : restoreStackBase();
684 2634419 : }
685 :
686 17243 : void setupFallibleABICall(bool inlining, jsbytecode *pc, int32_t frameDepth) {
687 17243 : setupFrameDepth(frameDepth);
688 :
689 : /* Store fp and pc */
690 17243 : storePtr(JSFrameReg, FrameAddress(VMFrame::offsetOfFp));
691 17243 : storePtr(ImmPtr(pc), FrameAddress(VMFrame::offsetOfRegsPc()));
692 :
693 17243 : if (inlining) {
694 : /* ABI calls cannot be made from inlined frames. */
695 11144 : storePtr(ImmPtr(NULL), FrameAddress(VMFrame::offsetOfInlined));
696 : }
697 17243 : }
698 :
699 2651662 : void restoreStackBase() {
700 : #if defined(JS_CPU_X86)
701 : /*
702 : * We use the %ebp base stack pointer on x86 to store the JSStackFrame.
703 : * Restore this before calling so that debuggers can construct a
704 : * coherent stack if we crash outside of JIT code.
705 : */
706 : JS_STATIC_ASSERT(JSFrameReg == JSC::X86Registers::ebp);
707 2651662 : move(JSC::X86Registers::esp, JSFrameReg);
708 2651662 : addPtr(Imm32(VMFrame::STACK_BASE_DIFFERENCE), JSFrameReg);
709 : #endif
710 2651662 : }
711 :
712 : // An infallible VM call is a stub call (taking a VMFrame & and one
713 : // optional parameter) that does not need |pc| and |fp| updated, since
714 : // the call is guaranteed to not fail. However, |sp| is always coherent.
715 31964 : Call infallibleVMCall(void *ptr, int32_t frameDepth) {
716 31964 : setupInfallibleVMFrame(frameDepth);
717 31964 : return wrapVMCall(ptr);
718 : }
719 :
720 : // A fallible VM call is a stub call (taking a VMFrame & and one optional
721 : // parameter) that needs the entire VMFrame to be coherent, meaning that
722 : // |pc|, |inlined| and |fp| are guaranteed to be up-to-date.
723 2634419 : Call fallibleVMCall(bool inlining, void *ptr, jsbytecode *pc,
724 : DataLabelPtr *pinlined, int32_t frameDepth) {
725 2634419 : setupFallibleVMFrame(inlining, pc, pinlined, frameDepth);
726 2634419 : Call call = wrapVMCall(ptr);
727 :
728 : // Restore the frame pointer from the VM.
729 2634419 : loadPtr(FrameAddress(VMFrame::offsetOfFp), JSFrameReg);
730 :
731 : return call;
732 : }
733 :
734 2666383 : Call wrapVMCall(void *ptr) {
735 2666383 : JS_ASSERT(!callIsAligned);
736 :
737 : // Every stub call has at most two arguments.
738 2666383 : setupABICall(Registers::FastCall, 2);
739 :
740 : // On x86, if JS_NO_FASTCALL is present, these will result in actual
741 : // pushes to the stack, which the caller will clean up. Otherwise,
742 : // they'll be ignored because the registers fit into the calling
743 : // sequence.
744 2666383 : storeArg(0, Registers::ArgReg0);
745 2666383 : storeArg(1, Registers::ArgReg1);
746 :
747 : // [Bug 614953]: The second argument, 'canThrow', can be set to 'false'
748 : // for infallibleVMCall invocations. However, this changes the call
749 : // sequence on ARM, and the ARM repatcher cannot currently distinguish
750 : // between the two sequences. The argument does not affect the code
751 : // generated by x86 or amd64.
752 2666383 : return callWithABI(ptr, true);
753 : }
754 :
755 : // Constant doubles can't be directly moved into a register, we need to put
756 : // them in memory and load them back with.
757 48984 : void slowLoadConstantDouble(double d, FPRegisterID fpreg) {
758 48984 : DoublePatch patch;
759 48984 : patch.d = d;
760 48984 : patch.label = loadDouble(NULL, fpreg);
761 48984 : doublePatches.append(patch);
762 48984 : }
763 :
764 374608 : size_t numDoubles() { return doublePatches.length(); }
765 :
766 266769 : void finalize(JSC::LinkBuffer &linker, double *doubleVec = NULL) {
767 2950027 : for (size_t i = 0; i < callPatches.length(); i++) {
768 2683258 : CallPatch &patch = callPatches[i];
769 2683258 : linker.link(patch.call, JSC::FunctionPtr(patch.fun));
770 : }
771 315751 : for (size_t i = 0; i < doublePatches.length(); i++) {
772 48982 : DoublePatch &patch = doublePatches[i];
773 48982 : doubleVec[i] = patch.d;
774 48982 : linker.patch(patch.label, &doubleVec[i]);
775 : }
776 266769 : }
777 :
778 20351 : struct FastArrayLoadFails {
779 : Jump rangeCheck;
780 : Jump holeCheck;
781 : };
782 :
783 : // Guard an extent (capacity, length or initialized length) on an array or typed array.
784 85834 : Jump guardArrayExtent(int offset, RegisterID reg,
785 : const Int32Key &key, Condition cond) {
786 85834 : Address extent(reg, offset);
787 85834 : if (key.isConstant())
788 24126 : return branch32(cond, extent, Imm32(key.index()));
789 61708 : return branch32(cond, extent, key.reg());
790 : }
791 :
792 22 : Jump guardElementNotHole(RegisterID elements, const Int32Key &key) {
793 22 : Jump jmp;
794 :
795 22 : if (key.isConstant()) {
796 8 : Address slot(elements, key.index() * sizeof(Value));
797 8 : jmp = guardNotHole(slot);
798 : } else {
799 14 : BaseIndex slot(elements, key.reg(), JSVAL_SCALE);
800 14 : jmp = guardNotHole(slot);
801 : }
802 :
803 : return jmp;
804 : }
805 :
806 : // Load a jsval from an array slot, given a key. |objReg| is clobbered.
807 20351 : FastArrayLoadFails fastArrayLoad(RegisterID objReg, const Int32Key &key,
808 : RegisterID typeReg, RegisterID dataReg) {
809 20351 : JS_ASSERT(objReg != typeReg);
810 :
811 20351 : RegisterID elementsReg = objReg;
812 20351 : loadPtr(Address(objReg, JSObject::offsetOfElements()), elementsReg);
813 :
814 20351 : FastArrayLoadFails fails;
815 : fails.rangeCheck = guardArrayExtent(ObjectElements::offsetOfInitializedLength(),
816 20351 : objReg, key, BelowOrEqual);
817 :
818 : // Load the slot out of the array.
819 20351 : if (key.isConstant()) {
820 10180 : Address slot(elementsReg, key.index() * sizeof(Value));
821 10180 : fails.holeCheck = fastArrayLoadSlot(slot, true, typeReg, dataReg);
822 : } else {
823 10171 : BaseIndex slot(elementsReg, key.reg(), JSVAL_SCALE);
824 10171 : fails.holeCheck = fastArrayLoadSlot(slot, true, typeReg, dataReg);
825 : }
826 :
827 : return fails;
828 : }
829 :
830 16128 : void storeKey(const Int32Key &key, Address address) {
831 16128 : if (key.isConstant())
832 2320 : store32(Imm32(key.index()), address);
833 : else
834 13808 : store32(key.reg(), address);
835 16128 : }
836 :
837 17430 : void bumpKey(Int32Key &key, int32_t delta) {
838 17430 : if (key.isConstant())
839 2320 : key.index_ += delta;
840 : else
841 15110 : add32(Imm32(delta), key.reg());
842 17430 : }
843 :
844 750 : void loadFrameActuals(JSFunction *fun, RegisterID reg) {
845 : /* Bias for the case where there was an arguments overflow. */
846 750 : load32(Address(JSFrameReg, StackFrame::offsetOfNumActual()), reg);
847 750 : add32(Imm32(fun->nargs + 2), reg);
848 : Jump overflowArgs = branchTest32(Assembler::NonZero,
849 750 : Address(JSFrameReg, StackFrame::offsetOfFlags()),
850 1500 : Imm32(StackFrame::OVERFLOW_ARGS));
851 750 : move(Imm32(fun->nargs), reg);
852 750 : overflowArgs.linkTo(label(), this);
853 750 : lshiftPtr(Imm32(3), reg);
854 750 : negPtr(reg);
855 750 : addPtr(JSFrameReg, reg);
856 750 : }
857 :
858 113046 : void loadBaseShape(RegisterID obj, RegisterID dest) {
859 113046 : loadPtr(Address(obj, JSObject::offsetOfShape()), dest);
860 113046 : loadPtr(Address(dest, Shape::offsetOfBase()), dest);
861 113046 : }
862 :
863 5253 : void loadObjClass(RegisterID obj, RegisterID dest) {
864 5253 : loadBaseShape(obj, dest);
865 5253 : loadPtr(Address(dest, BaseShape::offsetOfClass()), dest);
866 5253 : }
867 :
868 10398 : Jump testClass(Condition cond, RegisterID claspReg, js::Class *clasp) {
869 10398 : return branchPtr(cond, claspReg, ImmPtr(clasp));
870 : }
871 :
872 106178 : Jump testObjClass(Condition cond, RegisterID obj, RegisterID temp, js::Class *clasp) {
873 106178 : loadBaseShape(obj, temp);
874 106178 : return branchPtr(cond, Address(temp, BaseShape::offsetOfClass()), ImmPtr(clasp));
875 : }
876 :
877 93030 : Jump testFunction(Condition cond, RegisterID fun, RegisterID temp) {
878 93030 : return testObjClass(cond, fun, temp, &js::FunctionClass);
879 : }
880 :
881 1158 : void branchValue(Condition cond, RegisterID reg, int32_t value, RegisterID result)
882 : {
883 1158 : if (Registers::maskReg(result) & Registers::SingleByteRegs) {
884 773 : set32(cond, reg, Imm32(value), result);
885 : } else {
886 385 : Jump j = branch32(cond, reg, Imm32(value));
887 385 : move(Imm32(0), result);
888 385 : Jump skip = jump();
889 385 : j.linkTo(label(), this);
890 385 : move(Imm32(1), result);
891 385 : skip.linkTo(label(), this);
892 : }
893 1158 : }
894 :
895 959 : void branchValue(Condition cond, RegisterID lreg, RegisterID rreg, RegisterID result)
896 : {
897 959 : if (Registers::maskReg(result) & Registers::SingleByteRegs) {
898 817 : set32(cond, lreg, rreg, result);
899 : } else {
900 142 : Jump j = branch32(cond, lreg, rreg);
901 142 : move(Imm32(0), result);
902 142 : Jump skip = jump();
903 142 : j.linkTo(label(), this);
904 142 : move(Imm32(1), result);
905 142 : skip.linkTo(label(), this);
906 : }
907 959 : }
908 :
909 1179 : void rematPayload(const StateRemat &remat, RegisterID reg) {
910 1179 : if (remat.inMemory())
911 343 : loadPayload(remat.address(), reg);
912 : else
913 836 : move(remat.reg(), reg);
914 1179 : }
915 :
916 25436 : void loadDynamicSlot(RegisterID objReg, uint32_t index,
917 : RegisterID typeReg, RegisterID dataReg) {
918 25436 : loadPtr(Address(objReg, JSObject::offsetOfSlots()), dataReg);
919 25436 : loadValueAsComponents(Address(dataReg, index * sizeof(Value)), typeReg, dataReg);
920 25436 : }
921 :
922 30894 : void loadObjProp(JSObject *obj, RegisterID objReg,
923 : const js::Shape *shape,
924 : RegisterID typeReg, RegisterID dataReg)
925 : {
926 30894 : if (obj->isFixedSlot(shape->slot()))
927 5458 : loadInlineSlot(objReg, shape->slot(), typeReg, dataReg);
928 : else
929 25436 : loadDynamicSlot(objReg, obj->dynamicSlotIndex(shape->slot()), typeReg, dataReg);
930 30894 : }
931 :
932 : #ifdef JS_METHODJIT_TYPED_ARRAY
933 : // Load a value from a typed array's packed data vector into dataReg.
934 : // This function expects the following combinations of typeReg, dataReg and tempReg:
935 : // 1) for all INT arrays other than UINT32:
936 : // - dataReg is a GP-register
937 : // - typeReg is optional
938 : // - tempReg is not set
939 : // 2) for UINT32:
940 : // - dataReg is either a FP-register or a GP-register
941 : // - typeReg is set if dataReg is a GP-register
942 : // - tempReg is set if dataReg is a FP-register
943 : // 3) for FLOAT32 and FLOAT64:
944 : // - dataReg is either a FP-register or a GP-register
945 : // - typeReg is set if dataReg is a GP-register
946 : // - tempReg is not set
947 : template <typename T>
948 1793 : void loadFromTypedArray(int atype, T address, MaybeRegisterID typeReg,
949 : AnyRegisterID dataReg, MaybeRegisterID tempReg)
950 : {
951 : // If dataReg is an FP-register we don't use typeReg.
952 1793 : JS_ASSERT_IF(dataReg.isFPReg(), !typeReg.isSet());
953 :
954 : // We only need tempReg for Uint32Array and only if dataReg is an FP-register.
955 1793 : JS_ASSERT_IF(atype != js::TypedArray::TYPE_UINT32 || dataReg.isReg(), !tempReg.isSet());
956 :
957 1793 : switch (atype) {
958 : case js::TypedArray::TYPE_INT8:
959 192 : load8SignExtend(address, dataReg.reg());
960 192 : if (typeReg.isSet())
961 95 : move(ImmType(JSVAL_TYPE_INT32), typeReg.reg());
962 192 : break;
963 : case js::TypedArray::TYPE_UINT8:
964 : case js::TypedArray::TYPE_UINT8_CLAMPED:
965 369 : load8ZeroExtend(address, dataReg.reg());
966 369 : if (typeReg.isSet())
967 180 : move(ImmType(JSVAL_TYPE_INT32), typeReg.reg());
968 369 : break;
969 : case js::TypedArray::TYPE_INT16:
970 185 : load16SignExtend(address, dataReg.reg());
971 185 : if (typeReg.isSet())
972 95 : move(ImmType(JSVAL_TYPE_INT32), typeReg.reg());
973 185 : break;
974 : case js::TypedArray::TYPE_UINT16:
975 202 : load16(address, dataReg.reg());
976 202 : if (typeReg.isSet())
977 102 : move(ImmType(JSVAL_TYPE_INT32), typeReg.reg());
978 202 : break;
979 : case js::TypedArray::TYPE_INT32:
980 135 : load32(address, dataReg.reg());
981 135 : if (typeReg.isSet())
982 72 : move(ImmType(JSVAL_TYPE_INT32), typeReg.reg());
983 135 : break;
984 : case js::TypedArray::TYPE_UINT32:
985 : {
986 : // For Uint32Array the result is either int32_t or double.
987 : // If dataReg is a GP-register, load a double or int32_t into dataReg/typeReg.
988 : // If dataReg is a FP-register, load the value as double.
989 195 : if (dataReg.isReg()) {
990 176 : load32(address, dataReg.reg());
991 176 : move(ImmType(JSVAL_TYPE_INT32), typeReg.reg());
992 176 : Jump safeInt = branch32(Assembler::Below, dataReg.reg(), Imm32(0x80000000));
993 176 : convertUInt32ToDouble(dataReg.reg(), Registers::FPConversionTemp);
994 176 : breakDouble(Registers::FPConversionTemp, typeReg.reg(), dataReg.reg());
995 176 : safeInt.linkTo(label(), this);
996 : } else {
997 19 : load32(address, tempReg.reg());
998 19 : convertUInt32ToDouble(tempReg.reg(), dataReg.fpreg());
999 : }
1000 195 : break;
1001 : }
1002 : case js::TypedArray::TYPE_FLOAT32:
1003 : case js::TypedArray::TYPE_FLOAT64:
1004 : {
1005 : FPRegisterID fpreg = dataReg.isReg()
1006 : ? Registers::FPConversionTemp
1007 515 : : dataReg.fpreg();
1008 515 : if (atype == js::TypedArray::TYPE_FLOAT32)
1009 341 : loadFloat(address, fpreg);
1010 : else
1011 174 : loadDouble(address, fpreg);
1012 : // Make sure NaN gets canonicalized. If dataReg is not an FP-register
1013 : // we have to use loadStaticDouble as we were probably called from an
1014 : // IC and we can't use slowLoadConstantDouble.
1015 515 : Jump notNaN = branchDouble(Assembler::DoubleEqual, fpreg, fpreg);
1016 515 : if (dataReg.isReg())
1017 209 : loadStaticDouble(&js_NaN, Registers::FPConversionTemp, dataReg.reg());
1018 : else
1019 306 : slowLoadConstantDouble(js_NaN, fpreg);
1020 515 : notNaN.linkTo(label(), this);
1021 515 : if (dataReg.isReg())
1022 209 : breakDouble(Registers::FPConversionTemp, typeReg.reg(), dataReg.reg());
1023 515 : break;
1024 : }
1025 : }
1026 1793 : }
1027 :
1028 1793 : void loadFromTypedArray(int atype, RegisterID objReg, Int32Key key,
1029 : MaybeRegisterID typeReg, AnyRegisterID dataReg,
1030 : MaybeRegisterID tempReg)
1031 : {
1032 1793 : int shift = TypedArray::slotWidth(atype);
1033 :
1034 1793 : if (key.isConstant()) {
1035 1459 : Address addr(objReg, key.index() * shift);
1036 1459 : loadFromTypedArray(atype, addr, typeReg, dataReg, tempReg);
1037 : } else {
1038 334 : Assembler::Scale scale = Assembler::TimesOne;
1039 334 : switch (shift) {
1040 : case 2:
1041 75 : scale = Assembler::TimesTwo;
1042 75 : break;
1043 : case 4:
1044 116 : scale = Assembler::TimesFour;
1045 116 : break;
1046 : case 8:
1047 50 : scale = Assembler::TimesEight;
1048 50 : break;
1049 : }
1050 334 : BaseIndex addr(objReg, key.reg(), scale);
1051 334 : loadFromTypedArray(atype, addr, typeReg, dataReg, tempReg);
1052 : }
1053 1793 : }
1054 :
1055 : template <typename S, typename T>
1056 1139 : void storeToTypedIntArray(int atype, S src, T address)
1057 : {
1058 1139 : switch (atype) {
1059 : case js::TypedArray::TYPE_INT8:
1060 : case js::TypedArray::TYPE_UINT8:
1061 : case js::TypedArray::TYPE_UINT8_CLAMPED:
1062 598 : store8(src, address);
1063 598 : break;
1064 : case js::TypedArray::TYPE_INT16:
1065 : case js::TypedArray::TYPE_UINT16:
1066 260 : store16(src, address);
1067 260 : break;
1068 : case js::TypedArray::TYPE_INT32:
1069 : case js::TypedArray::TYPE_UINT32:
1070 281 : store32(src, address);
1071 281 : break;
1072 : default:
1073 0 : JS_NOT_REACHED("unknown int array type");
1074 : }
1075 1139 : }
1076 :
1077 : template <typename S, typename T>
1078 253 : void storeToTypedFloatArray(int atype, S src, T address)
1079 : {
1080 253 : if (atype == js::TypedArray::TYPE_FLOAT32)
1081 174 : storeFloat(src, address);
1082 : else
1083 79 : storeDouble(src, address);
1084 253 : }
1085 :
1086 : template <typename T>
1087 752 : void storeToTypedArray(int atype, ValueRemat vr, T address)
1088 : {
1089 752 : if (atype == js::TypedArray::TYPE_FLOAT32 || atype == js::TypedArray::TYPE_FLOAT64) {
1090 262 : if (vr.isConstant())
1091 53 : storeToTypedFloatArray(atype, ImmDouble(vr.value().toDouble()), address);
1092 : else
1093 78 : storeToTypedFloatArray(atype, vr.fpReg(), address);
1094 : } else {
1095 621 : if (vr.isConstant())
1096 399 : storeToTypedIntArray(atype, Imm32(vr.value().toInt32()), address);
1097 : else
1098 222 : storeToTypedIntArray(atype, vr.dataReg(), address);
1099 : }
1100 752 : }
1101 :
1102 752 : void storeToTypedArray(int atype, RegisterID objReg, Int32Key key, ValueRemat vr)
1103 : {
1104 752 : int shift = TypedArray::slotWidth(atype);
1105 752 : if (key.isConstant()) {
1106 264 : Address addr(objReg, key.index() * shift);
1107 264 : storeToTypedArray(atype, vr, addr);
1108 : } else {
1109 488 : Assembler::Scale scale = Assembler::TimesOne;
1110 488 : switch (shift) {
1111 : case 2:
1112 70 : scale = Assembler::TimesTwo;
1113 70 : break;
1114 : case 4:
1115 144 : scale = Assembler::TimesFour;
1116 144 : break;
1117 : case 8:
1118 18 : scale = Assembler::TimesEight;
1119 18 : break;
1120 : }
1121 488 : BaseIndex addr(objReg, key.reg(), scale);
1122 488 : storeToTypedArray(atype, vr, addr);
1123 : }
1124 752 : }
1125 :
1126 52 : void clampInt32ToUint8(RegisterID reg)
1127 : {
1128 52 : Jump j = branch32(Assembler::GreaterThanOrEqual, reg, Imm32(0));
1129 52 : move(Imm32(0), reg);
1130 52 : Jump done = jump();
1131 52 : j.linkTo(label(), this);
1132 52 : j = branch32(Assembler::LessThanOrEqual, reg, Imm32(255));
1133 52 : move(Imm32(255), reg);
1134 52 : j.linkTo(label(), this);
1135 52 : done.linkTo(label(), this);
1136 52 : }
1137 :
1138 : // Inline version of js_TypedArray_uint8_clamp_double.
1139 11 : void clampDoubleToUint8(FPRegisterID fpReg, FPRegisterID fpTemp, RegisterID reg)
1140 : {
1141 11 : JS_ASSERT(fpTemp != Registers::FPConversionTemp);
1142 :
1143 : // <= 0 or NaN ==> 0
1144 11 : zeroDouble(fpTemp);
1145 11 : Jump positive = branchDouble(Assembler::DoubleGreaterThan, fpReg, fpTemp);
1146 11 : move(Imm32(0), reg);
1147 11 : Jump done1 = jump();
1148 :
1149 : // Add 0.5 and truncate.
1150 11 : positive.linkTo(label(), this);
1151 11 : slowLoadConstantDouble(0.5, fpTemp);
1152 11 : addDouble(fpReg, fpTemp);
1153 11 : Jump notInt = branchTruncateDoubleToInt32(fpTemp, reg);
1154 :
1155 : // > 255 ==> 255
1156 11 : Jump inRange = branch32(Assembler::BelowOrEqual, reg, Imm32(255));
1157 11 : notInt.linkTo(label(), this);
1158 11 : move(Imm32(255), reg);
1159 11 : Jump done2 = jump();
1160 :
1161 : // Check if we had a tie.
1162 11 : inRange.linkTo(label(), this);
1163 11 : convertInt32ToDouble(reg, Registers::FPConversionTemp);
1164 11 : Jump done3 = branchDouble(Assembler::DoubleNotEqual, fpTemp, Registers::FPConversionTemp);
1165 :
1166 : // It was a tie. Mask out the ones bit to get an even value.
1167 : // See js_TypedArray_uint8_clamp_double for the reasoning behind this.
1168 11 : and32(Imm32(~1), reg);
1169 :
1170 11 : done1.linkTo(label(), this);
1171 11 : done2.linkTo(label(), this);
1172 11 : done3.linkTo(label(), this);
1173 11 : }
1174 : #endif /* JS_METHODJIT_TYPED_ARRAY */
1175 :
1176 14002 : Address objPropAddress(JSObject *obj, RegisterID objReg, uint32_t slot)
1177 : {
1178 14002 : if (obj->isFixedSlot(slot))
1179 9569 : return Address(objReg, JSObject::getFixedSlotOffset(slot));
1180 4433 : loadPtr(Address(objReg, JSObject::offsetOfSlots()), objReg);
1181 4433 : return Address(objReg, obj->dynamicSlotIndex(slot) * sizeof(Value));
1182 : }
1183 :
1184 0 : static uint32_t maskAddress(Address address) {
1185 0 : return Registers::maskReg(address.base);
1186 : }
1187 :
1188 63 : static uint32_t maskAddress(BaseIndex address) {
1189 63 : return Registers::maskReg(address.base) |
1190 63 : Registers::maskReg(address.index);
1191 : }
1192 :
1193 : /*
1194 : * Generate code testing whether an in memory value at address has a type
1195 : * in the specified set. Updates mismatches with any failure jumps. Assumes
1196 : * that no temporary (caller save) registers are live.
1197 : */
1198 156385 : bool generateTypeCheck(JSContext *cx, Address address,
1199 : types::TypeSet *types, Vector<Jump> *mismatches)
1200 : {
1201 156385 : if (types->unknown())
1202 249 : return true;
1203 :
1204 312272 : Vector<Jump> matches(cx);
1205 :
1206 156136 : if (types->hasType(types::Type::DoubleType())) {
1207 : /* Type sets containing double also contain int. */
1208 10278 : if (!matches.append(testNumber(Assembler::Equal, address)))
1209 0 : return false;
1210 145858 : } else if (types->hasType(types::Type::Int32Type())) {
1211 13702 : if (!matches.append(testInt32(Assembler::Equal, address)))
1212 0 : return false;
1213 : }
1214 :
1215 156136 : if (types->hasType(types::Type::UndefinedType())) {
1216 4187 : if (!matches.append(testUndefined(Assembler::Equal, address)))
1217 0 : return false;
1218 : }
1219 :
1220 156136 : if (types->hasType(types::Type::BooleanType())) {
1221 3500 : if (!matches.append(testBoolean(Assembler::Equal, address)))
1222 0 : return false;
1223 : }
1224 :
1225 156136 : if (types->hasType(types::Type::StringType())) {
1226 8407 : if (!matches.append(testString(Assembler::Equal, address)))
1227 0 : return false;
1228 : }
1229 :
1230 156136 : if (types->hasType(types::Type::NullType())) {
1231 4203 : if (!matches.append(testNull(Assembler::Equal, address)))
1232 0 : return false;
1233 : }
1234 :
1235 156136 : unsigned count = 0;
1236 156136 : if (types->hasType(types::Type::AnyObjectType())) {
1237 4296 : if (!matches.append(testObject(Assembler::Equal, address)))
1238 0 : return false;
1239 : } else {
1240 151840 : count = types->getObjectCount();
1241 : }
1242 :
1243 156136 : if (count != 0) {
1244 31261 : if (!mismatches->append(testObject(Assembler::NotEqual, address)))
1245 0 : return false;
1246 31261 : RegisterID reg = Registers::ArgReg1;
1247 :
1248 31261 : loadPayload(address, reg);
1249 :
1250 133582 : for (unsigned i = 0; i < count; i++) {
1251 102321 : if (JSObject *object = types->getSingleObject(i)) {
1252 27372 : if (!matches.append(branchPtr(Assembler::Equal, reg, ImmPtr(object))))
1253 0 : return false;
1254 : }
1255 : }
1256 :
1257 31261 : loadPtr(Address(reg, JSObject::offsetOfType()), reg);
1258 :
1259 133582 : for (unsigned i = 0; i < count; i++) {
1260 102321 : if (types::TypeObject *object = types->getTypeObject(i)) {
1261 34685 : if (!matches.append(branchPtr(Assembler::Equal, reg, ImmPtr(object))))
1262 0 : return false;
1263 : }
1264 : }
1265 : }
1266 :
1267 156136 : if (!mismatches->append(jump()))
1268 0 : return false;
1269 :
1270 266766 : for (unsigned i = 0; i < matches.length(); i++)
1271 110630 : matches[i].linkTo(label(), this);
1272 :
1273 156136 : return true;
1274 : }
1275 :
1276 : /*
1277 : * Get a free object for the specified GC kind in compartment, writing it
1278 : * to result and filling it in according to templateObject. Returns a jump
1279 : * taken if a free thing was not retrieved.
1280 : */
1281 17865 : Jump getNewObject(JSContext *cx, RegisterID result, JSObject *templateObject)
1282 : {
1283 17865 : gc::AllocKind allocKind = templateObject->getAllocKind();
1284 :
1285 17865 : JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
1286 17865 : int thingSize = (int)gc::Arena::thingSize(allocKind);
1287 :
1288 17865 : JS_ASSERT(cx->typeInferenceEnabled());
1289 17865 : JS_ASSERT(!templateObject->hasDynamicSlots());
1290 17865 : JS_ASSERT(!templateObject->hasDynamicElements());
1291 :
1292 : #ifdef JS_GC_ZEAL
1293 17865 : if (cx->runtime->needZealousGC())
1294 2 : return jump();
1295 : #endif
1296 :
1297 : /*
1298 : * Inline FreeSpan::allocate. Only the case where the current freelist
1299 : * span is not empty is handled.
1300 : */
1301 : gc::FreeSpan *list = const_cast<gc::FreeSpan *>
1302 17863 : (cx->compartment->arenas.getFreeList(allocKind));
1303 17863 : loadPtr(&list->first, result);
1304 :
1305 17863 : Jump jump = branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(&list->last), result);
1306 :
1307 17863 : addPtr(Imm32(thingSize), result);
1308 17863 : storePtr(result, &list->first);
1309 :
1310 : /*
1311 : * Fill in the blank object. Order doesn't matter here, from here
1312 : * everything is infallible. Note that this bakes GC thing pointers
1313 : * into the code without explicitly pinning them. With type inference
1314 : * enabled, JIT code is collected on GC except when analysis or
1315 : * compilation is active, in which case type objects won't be collected
1316 : * but other things may be. The shape held by templateObject *must* be
1317 : * pinned against GC either by the script or by some type object.
1318 : */
1319 :
1320 17863 : int elementsOffset = JSObject::offsetOfFixedElements();
1321 :
1322 : /*
1323 : * Write out the elements pointer before readjusting the result register,
1324 : * as for dense arrays we will need to get the address of the fixed
1325 : * elements first.
1326 : */
1327 17863 : if (templateObject->isDenseArray()) {
1328 9693 : JS_ASSERT(!templateObject->getDenseArrayInitializedLength());
1329 9693 : addPtr(Imm32(-thingSize + elementsOffset), result);
1330 9693 : storePtr(result, Address(result, -elementsOffset + JSObject::offsetOfElements()));
1331 9693 : addPtr(Imm32(-elementsOffset), result);
1332 : } else {
1333 8170 : addPtr(Imm32(-thingSize), result);
1334 8170 : storePtr(ImmPtr(emptyObjectElements), Address(result, JSObject::offsetOfElements()));
1335 : }
1336 :
1337 17863 : storePtr(ImmPtr(templateObject->lastProperty()), Address(result, JSObject::offsetOfShape()));
1338 17863 : storePtr(ImmPtr(templateObject->type()), Address(result, JSObject::offsetOfType()));
1339 17863 : storePtr(ImmPtr(NULL), Address(result, JSObject::offsetOfSlots()));
1340 :
1341 17863 : if (templateObject->isDenseArray()) {
1342 : /* Fill in the elements header. */
1343 9693 : store32(Imm32(templateObject->getDenseArrayCapacity()),
1344 19386 : Address(result, elementsOffset + ObjectElements::offsetOfCapacity()));
1345 9693 : store32(Imm32(templateObject->getDenseArrayInitializedLength()),
1346 19386 : Address(result, elementsOffset + ObjectElements::offsetOfInitializedLength()));
1347 9693 : store32(Imm32(templateObject->getArrayLength()),
1348 19386 : Address(result, elementsOffset + ObjectElements::offsetOfLength()));
1349 : } else {
1350 : /*
1351 : * Fixed slots of non-array objects are required to be initialized;
1352 : * Use the values currently in the template object.
1353 : */
1354 39975 : for (unsigned i = 0; i < templateObject->slotSpan(); i++) {
1355 31805 : storeValue(templateObject->getFixedSlot(i),
1356 63610 : Address(result, JSObject::getFixedSlotOffset(i)));
1357 : }
1358 : }
1359 :
1360 17863 : if (templateObject->hasPrivate()) {
1361 4939 : uint32_t nfixed = templateObject->numFixedSlots();
1362 4939 : storePtr(ImmPtr(templateObject->getPrivate()),
1363 9878 : Address(result, JSObject::getPrivateDataOffset(nfixed)));
1364 : }
1365 :
1366 17863 : return jump;
1367 : }
1368 :
1369 : /* Add the value stored in 'value' to the accumulator 'count'. */
1370 0 : void addCount(const double *value, double *count, RegisterID scratch)
1371 : {
1372 0 : loadDouble(value, Registers::FPConversionTemp);
1373 0 : move(ImmPtr(count), scratch);
1374 0 : addDouble(Address(scratch), Registers::FPConversionTemp);
1375 0 : storeDouble(Registers::FPConversionTemp, Address(scratch));
1376 0 : }
1377 :
1378 : /* Add one to the accumulator |count|. */
1379 0 : void bumpCount(double *count, RegisterID scratch)
1380 : {
1381 0 : addCount(&oneDouble, count, scratch);
1382 0 : }
1383 :
1384 : /* Bump the stub call count for script/pc if they are being counted. */
1385 2624958 : void bumpStubCount(JSScript *script, jsbytecode *pc, RegisterID scratch)
1386 : {
1387 2624958 : if (script->scriptCounts) {
1388 0 : PCCounts counts = script->getPCCounts(pc);
1389 0 : double *count = &counts.get(PCCounts::BASE_METHODJIT_STUBS);
1390 0 : bumpCount(count, scratch);
1391 : }
1392 2624958 : }
1393 :
1394 : static const double oneDouble;
1395 : };
1396 :
1397 : /* Return f<true> if the script is strict mode code, f<false> otherwise. */
1398 : #define STRICT_VARIANT(f) \
1399 : (FunctionTemplateConditional(script->strictModeCode, \
1400 : f<true>, f<false>))
1401 :
1402 : /* Save some typing. */
1403 : static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = Assembler::JSReturnReg_Type;
1404 : static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = Assembler::JSReturnReg_Data;
1405 : static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = Assembler::JSParamReg_Argc;
1406 :
1407 : struct FrameFlagsAddress : JSC::MacroAssembler::Address
1408 : {
1409 164452 : FrameFlagsAddress()
1410 164452 : : Address(JSFrameReg, StackFrame::offsetOfFlags())
1411 164452 : {}
1412 : };
1413 :
1414 : class PreserveRegisters {
1415 : typedef JSC::MacroAssembler::RegisterID RegisterID;
1416 :
1417 : Assembler &masm;
1418 : uint32_t count;
1419 : RegisterID regs[JSC::MacroAssembler::TotalRegisters];
1420 :
1421 : public:
1422 1252 : PreserveRegisters(Assembler &masm) : masm(masm), count(0) { }
1423 1252 : ~PreserveRegisters() { JS_ASSERT(!count); }
1424 :
1425 272 : void preserve(Registers mask) {
1426 272 : JS_ASSERT(!count);
1427 :
1428 1114 : while (!mask.empty()) {
1429 570 : RegisterID reg = mask.takeAnyReg().reg();
1430 570 : regs[count++] = reg;
1431 570 : masm.saveReg(reg);
1432 : }
1433 272 : }
1434 :
1435 1252 : void restore() {
1436 3074 : while (count)
1437 570 : masm.restoreReg(regs[--count]);
1438 1252 : }
1439 : };
1440 :
1441 : } /* namespace mjit */
1442 : } /* namespace js */
1443 :
1444 : #endif
1445 :
|