1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
24 * David Anderson <danderson@mozilla.com>
25 * David Mandelin <dmandelin@mozilla.com>
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.
39 * ***** END LICENSE BLOCK ***** */
41 #if !defined jsjaeger_baseassembler_h__ && defined JS_METHODJIT
42 #define jsjaeger_baseassembler_h__
46 #include "assembler/assembler/MacroAssemblerCodeRef.h"
47 #include "assembler/assembler/MacroAssembler.h"
48 #include "assembler/assembler/LinkBuffer.h"
49 #include "assembler/moco/MocoStubs.h"
50 #include "methodjit/MethodJIT.h"
51 #include "methodjit/MachineRegs.h"
52 #include "CodeGenIncludes.h"
53 #include "jsobjinlines.h"
54 #include "jsscopeinlines.h"
59 class MaybeRegisterID
{
60 typedef JSC::MacroAssembler::RegisterID RegisterID
;
64 : reg_(Registers::ReturnReg
), set(false)
67 MaybeRegisterID(RegisterID reg
)
68 : reg_(reg
), set(true)
71 inline RegisterID
reg() const { JS_ASSERT(set
); return reg_
; }
72 inline void setReg(const RegisterID r
) { reg_
= r
; set
= true; }
73 inline bool isSet() const { return set
; }
75 MaybeRegisterID
& operator =(const MaybeRegisterID
&other
) {
81 MaybeRegisterID
& operator =(RegisterID r
) {
91 // Represents an int32 property name in generated code, which must be either
92 // a RegisterID or a constant value.
94 typedef JSC::MacroAssembler::RegisterID RegisterID
;
99 Int32Key() : index_(0) { }
101 static Int32Key
FromRegister(RegisterID reg
) {
106 static Int32Key
FromConstant(int32 index
) {
112 int32
index() const {
113 JS_ASSERT(!reg_
.isSet());
117 RegisterID
reg() const { return reg_
.reg(); }
118 bool isConstant() const { return !reg_
.isSet(); }
122 typedef JSC::MacroAssembler::Jump Jump
;
128 inline Jump
getJump() const { JS_ASSERT(set
); return jump
; }
129 inline Jump
get() const { JS_ASSERT(set
); return jump
; }
130 inline void setJump(const Jump
&j
) { jump
= j
; set
= true; }
131 inline bool isSet() const { return set
; }
133 inline MaybeJump
&operator=(Jump j
) { setJump(j
); return *this; }
140 struct FrameAddress
: JSC::MacroAssembler::Address
142 FrameAddress(int32 offset
)
143 : Address(JSC::MacroAssembler::stackPointerRegister
, offset
)
147 struct ImmIntPtr
: public JSC::MacroAssembler::ImmPtr
149 ImmIntPtr(intptr_t val
)
150 : ImmPtr(reinterpret_cast<void*>(val
))
154 class Assembler
: public ValueAssembler
157 CallPatch(Call cl
, void *fun
)
162 JSC::FunctionPtr fun
;
165 /* Need a temp reg that is not ArgReg1. */
166 #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
167 static const RegisterID ClobberInCall
= JSC::X86Registers::ecx
;
168 #elif defined(JS_CPU_ARM)
169 static const RegisterID ClobberInCall
= JSC::ARMRegisters::r2
;
174 Vector
<CallPatch
, 64, SystemAllocPolicy
> callPatches
;
176 // List and count of registers that will be saved and restored across a call.
178 RegisterID savedRegs
[TotalRegisters
];
180 // Calling convention used by the currently in-progress call.
181 Registers::CallConvention callConvention
;
183 // Amount of stack space reserved for the currently in-progress call. This
184 // includes alignment and parameters.
187 // Debug flag to make sure calls do not nest.
194 : callPatches(SystemAllocPolicy()),
197 , callIsAligned(false)
200 startLabel
= label();
203 /* Total number of floating-point registers. */
204 static const uint32 TotalFPRegisters
= FPRegisters::TotalFPRegisters
;
206 /* Register pair storing returned type/data for calls. */
207 #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
208 static const JSC::MacroAssembler::RegisterID JSReturnReg_Type
= JSC::X86Registers::ecx
;
209 static const JSC::MacroAssembler::RegisterID JSReturnReg_Data
= JSC::X86Registers::edx
;
210 static const JSC::MacroAssembler::RegisterID JSParamReg_Argc
= JSC::X86Registers::ecx
;
211 #elif defined(JS_CPU_ARM)
212 static const JSC::MacroAssembler::RegisterID JSReturnReg_Type
= JSC::ARMRegisters::r2
;
213 static const JSC::MacroAssembler::RegisterID JSReturnReg_Data
= JSC::ARMRegisters::r1
;
214 static const JSC::MacroAssembler::RegisterID JSParamReg_Argc
= JSC::ARMRegisters::r1
;
217 size_t distanceOf(Label l
) {
218 return differenceBetween(startLabel
, l
);
221 void load32FromImm(void *ptr
, RegisterID reg
) {
225 void loadShape(RegisterID obj
, RegisterID shape
) {
226 load32(Address(obj
, offsetof(JSObject
, objShape
)), shape
);
229 Jump
guardShape(RegisterID objReg
, JSObject
*obj
) {
230 return branch32(NotEqual
, Address(objReg
, offsetof(JSObject
, objShape
)),
231 Imm32(obj
->shape()));
234 Jump
testFunction(Condition cond
, RegisterID fun
) {
235 return branchPtr(cond
, Address(fun
, offsetof(JSObject
, clasp
)),
236 ImmPtr(&js_FunctionClass
));
240 * Finds and returns the address of a known object and slot.
242 Address
objSlotRef(JSObject
*obj
, RegisterID reg
, uint32 slot
) {
243 move(ImmPtr(&obj
->slots
), reg
);
245 return Address(reg
, slot
* sizeof(Value
));
249 void idiv(RegisterID reg
) {
251 m_assembler
.idivl_r(reg
);
254 void fastLoadDouble(RegisterID lo
, RegisterID hi
, FPRegisterID fpReg
) {
255 if (MacroAssemblerX86Common::getSSEState() >= HasSSE4_1
) {
256 m_assembler
.movd_rr(lo
, fpReg
);
257 m_assembler
.pinsrd_rr(hi
, fpReg
);
259 m_assembler
.movd_rr(lo
, fpReg
);
260 m_assembler
.movd_rr(hi
, FPRegisters::Temp0
);
261 m_assembler
.unpcklps_rr(FPRegisters::Temp0
, fpReg
);
266 // Prepares for a stub call.
267 void *getCallTarget(void *fun
) {
270 * Insert a veneer for ARM to allow it to catch exceptions. There is no
271 * reliable way to determine the location of the return address on the
272 * stack, so it cannot be hijacked.
274 * :TODO: It wouldn't surprise me if GCC always pushes LR first. In that
275 * case, this looks like the x86-style call, and we can hijack the stack
276 * slot accordingly, thus avoiding the cost of a veneer. This should be
280 void *pfun
= JS_FUNC_TO_DATA_PTR(void *, JaegerStubVeneer
);
283 * We put the real target address into IP, as this won't conflict with
284 * the EABI argument-passing mechanism. Technically, this isn't ABI-
287 move(Imm32(intptr_t(fun
)), JSC::ARMRegisters::ip
);
290 * Architectures that push the return address to an easily-determined
291 * location on the stack can hijack C++'s return mechanism by overwriting
292 * that address, so a veneer is not required.
299 // Save all registers in the given mask.
300 void saveRegs(uint32 volatileMask
) {
301 // Only one use per call.
302 JS_ASSERT(saveCount
== 0);
303 // Must save registers before pushing arguments or setting up calls.
304 JS_ASSERT(!callIsAligned
);
306 Registers
set(volatileMask
);
307 while (!set
.empty()) {
308 JS_ASSERT(saveCount
< TotalRegisters
);
310 RegisterID reg
= set
.takeAnyReg();
311 savedRegs
[saveCount
++] = reg
;
316 static const uint32 StackAlignment
= 16;
318 static inline uint32
alignForCall(uint32 stackBytes
) {
319 #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
320 // If StackAlignment is a power of two, % is just two shifts.
321 // 16 - (x % 16) gives alignment, extra % 16 handles total == 0.
322 return (StackAlignment
- (stackBytes
% StackAlignment
)) % StackAlignment
;
328 // Some platforms require stack manipulation before making stub calls.
329 // When using THROW/V, the return address is replaced, meaning the
330 // stack de-adjustment will not have occured. JaegerThrowpoline accounts
331 // for this. For stub calls, which are always invoked as if they use
332 // two parameters, the stack adjustment is constant.
334 // When using callWithABI() manually, for example via an IC, it might
335 // be necessary to jump directly to JaegerThrowpoline. In this case,
336 // the constant is provided here in order to appropriately adjust the
339 static const uint32 ReturnStackAdjustment
= 32;
340 #elif defined(JS_CPU_X86) && defined(JS_NO_FASTCALL)
341 static const uint32 ReturnStackAdjustment
= 16;
343 static const uint32 ReturnStackAdjustment
= 0;
347 if (ReturnStackAdjustment
)
348 subPtr(Imm32(ReturnStackAdjustment
), stackPointerRegister
);
349 move(ImmPtr(JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline
)), Registers::ReturnReg
);
350 jump(Registers::ReturnReg
);
353 // Windows x64 requires extra space in between calls.
355 static const uint32 ShadowStackSpace
= 32;
357 static const uint32 ShadowStackSpace
= 0;
360 // Prepare the stack for a call sequence. This must be called AFTER all
361 // volatile regs have been saved, and BEFORE pushArg() is used. The stack
362 // is assumed to be aligned to 16-bytes plus any pushes that occured via
363 // saveVolatileRegs().
364 void setupABICall(Registers::CallConvention convention
, uint32 generalArgs
) {
365 JS_ASSERT(!callIsAligned
);
367 uint32 numArgRegs
= Registers::numArgRegs(convention
);
368 uint32 pushCount
= (generalArgs
> numArgRegs
)
369 ? generalArgs
- numArgRegs
372 // Adjust the stack for alignment and parameters all at once.
373 stackAdjust
= (pushCount
+ saveCount
) * sizeof(void *);
374 stackAdjust
+= alignForCall(stackAdjust
);
377 // Windows x64 ABI requires 32 bytes of "shadow space" for the callee
378 // to spill its parameters.
379 stackAdjust
+= ShadowStackSpace
;
383 subPtr(Imm32(stackAdjust
), stackPointerRegister
);
385 callConvention
= convention
;
387 callIsAligned
= true;
391 // This is an internal function only for use inside a startABICall(),
392 // callWithABI() sequence, and only for arguments known to fit in
394 Address
addressOfArg(uint32 i
) {
395 uint32 numArgRegs
= Registers::numArgRegs(callConvention
);
396 JS_ASSERT(i
>= numArgRegs
);
398 // Note that shadow space is for the callee to spill, and thus it must
399 // be skipped when writing its arguments.
400 int32 spOffset
= ((i
- numArgRegs
) * sizeof(void *)) + ShadowStackSpace
;
401 return Address(stackPointerRegister
, spOffset
);
404 // Push an argument for a call.
405 void storeArg(uint32 i
, RegisterID reg
) {
406 JS_ASSERT(callIsAligned
);
408 if (Registers::regForArg(callConvention
, i
, &to
)) {
412 storePtr(reg
, addressOfArg(i
));
416 void storeArg(uint32 i
, Imm32 imm
) {
417 JS_ASSERT(callIsAligned
);
419 if (Registers::regForArg(callConvention
, i
, &to
))
422 store32(imm
, addressOfArg(i
));
425 // High-level call helper, given an optional function pointer and a
426 // calling convention. setupABICall() must have been called beforehand,
427 // as well as each numbered argument stored with storeArg().
429 // After callWithABI(), the call state is reset, so a new call may begin.
430 Call
callWithABI(void *fun
) {
431 JS_ASSERT(callIsAligned
);
434 callPatches
.append(CallPatch(cl
, fun
));
437 addPtr(Imm32(stackAdjust
), stackPointerRegister
);
440 callIsAligned
= false;
445 // Restore registers after a call.
447 // Note that saveCount will safely decrement back to 0.
449 pop(savedRegs
[--saveCount
]);
452 // Wrap AbstractMacroAssembler::getLinkerCallReturnOffset which is protected.
453 unsigned callReturnOffset(Call call
) {
454 return getLinkerCallReturnOffset(call
);
458 #define STUB_CALL_TYPE(type) \
459 Call callWithVMFrame(type stub, jsbytecode *pc, uint32 fd) { \
460 return fallibleVMCall(JS_FUNC_TO_DATA_PTR(void *, stub), pc, fd); \
463 STUB_CALL_TYPE(JSObjStub
);
464 STUB_CALL_TYPE(VoidPtrStubUInt32
);
465 STUB_CALL_TYPE(VoidStubUInt32
);
466 STUB_CALL_TYPE(VoidStub
);
468 #undef STUB_CALL_TYPE
470 void setupInfallibleVMFrame(int32 frameDepth
) {
471 // |frameDepth < 0| implies ic::SplatApplyArgs has been called which
472 // means regs.sp has already been set in the VMFrame.
473 if (frameDepth
>= 0) {
474 // sp = fp->slots() + frameDepth
476 addPtr(Imm32(sizeof(JSStackFrame
) + frameDepth
* sizeof(jsval
)),
479 storePtr(ClobberInCall
, FrameAddress(offsetof(VMFrame
, regs
.sp
)));
482 // The JIT has moved Arg1 already, and we've guaranteed to not clobber
483 // it. Move ArgReg0 into place now. setupFallibleVMFrame will not
484 // clobber it either.
485 move(MacroAssembler::stackPointerRegister
, Registers::ArgReg0
);
488 void setupFallibleVMFrame(jsbytecode
*pc
, int32 frameDepth
) {
489 setupInfallibleVMFrame(frameDepth
);
492 storePtr(JSFrameReg
, FrameAddress(offsetof(VMFrame
, regs
.fp
)));
494 /* PC -> regs->pc :( */
496 FrameAddress(offsetof(VMFrame
, regs
) + offsetof(JSFrameRegs
, pc
)));
499 // An infallible VM call is a stub call (taking a VMFrame & and one
500 // optional parameter) that does not need |pc| and |fp| updated, since
501 // the call is guaranteed to not fail. However, |sp| is always coherent.
502 Call
infallibleVMCall(void *ptr
, int32 frameDepth
) {
503 setupInfallibleVMFrame(frameDepth
);
504 return wrapVMCall(ptr
);
507 // A fallible VM call is a stub call (taking a VMFrame & and one optional
508 // parameter) that needs the entire VMFrame to be coherent, meaning that
509 // |pc| and |fp| are guaranteed to be up-to-date.
510 Call
fallibleVMCall(void *ptr
, jsbytecode
*pc
, int32 frameDepth
) {
511 setupFallibleVMFrame(pc
, frameDepth
);
512 return wrapVMCall(ptr
);
515 Call
wrapVMCall(void *ptr
) {
516 JS_ASSERT(!saveCount
);
517 JS_ASSERT(!callIsAligned
);
519 // Every stub call has at most two arguments.
520 setupABICall(Registers::FastCall
, 2);
522 // On x86, if JS_NO_FASTCALL is present, these will result in actual
523 // pushes to the stack, which the caller will clean up. Otherwise,
524 // they'll be ignored because the registers fit into the calling
526 storeArg(0, Registers::ArgReg0
);
527 storeArg(1, Registers::ArgReg1
);
529 return callWithABI(getCallTarget(ptr
));
532 void finalize(JSC::LinkBuffer
&linker
) {
533 for (size_t i
= 0; i
< callPatches
.length(); i
++) {
534 CallPatch
&patch
= callPatches
[i
];
535 linker
.link(patch
.call
, JSC::FunctionPtr(patch
.fun
));
539 struct FastArrayLoadFails
{
544 Jump
guardArrayCapacity(RegisterID objReg
, const Int32Key
&key
) {
545 Address
capacity(objReg
, offsetof(JSObject
, capacity
));
546 if (key
.isConstant()) {
547 JS_ASSERT(key
.index() >= 0);
548 return branch32(BelowOrEqual
, payloadOf(capacity
), Imm32(key
.index()));
550 return branch32(BelowOrEqual
, payloadOf(capacity
), key
.reg());
553 // Load a jsval from an array slot, given a key. |objReg| is clobbered.
554 FastArrayLoadFails
fastArrayLoad(RegisterID objReg
, const Int32Key
&key
,
555 RegisterID typeReg
, RegisterID dataReg
) {
556 JS_ASSERT(objReg
!= typeReg
);
558 FastArrayLoadFails fails
;
559 fails
.rangeCheck
= guardArrayCapacity(objReg
, key
);
561 RegisterID dslotsReg
= objReg
;
562 loadPtr(Address(objReg
, offsetof(JSObject
, slots
)), dslotsReg
);
564 // Load the slot out of the array.
565 if (key
.isConstant()) {
566 Address
slot(objReg
, key
.index() * sizeof(Value
));
567 fails
.holeCheck
= fastArrayLoadSlot(slot
, typeReg
, dataReg
);
569 BaseIndex
slot(objReg
, key
.reg(), JSVAL_SCALE
);
570 fails
.holeCheck
= fastArrayLoadSlot(slot
, typeReg
, dataReg
);
576 void loadObjClass(RegisterID objReg
, RegisterID destReg
) {
577 loadPtr(Address(objReg
, offsetof(JSObject
, clasp
)), destReg
);
580 Jump
testClass(Condition cond
, RegisterID claspReg
, js::Class
*clasp
) {
581 return branchPtr(cond
, claspReg
, ImmPtr(clasp
));
584 Jump
testObjClass(Condition cond
, RegisterID objReg
, js::Class
*clasp
) {
585 return branchPtr(cond
, Address(objReg
, offsetof(JSObject
, clasp
)), ImmPtr(clasp
));
588 void rematPayload(const StateRemat
&remat
, RegisterID reg
) {
589 if (remat
.inMemory())
590 loadPayload(remat
.address(), reg
);
592 move(remat
.reg(), reg
);
595 void loadDynamicSlot(RegisterID objReg
, uint32 slot
,
596 RegisterID typeReg
, RegisterID dataReg
) {
597 loadPtr(Address(objReg
, offsetof(JSObject
, slots
)), dataReg
);
598 loadValueAsComponents(Address(dataReg
, slot
* sizeof(Value
)), typeReg
, dataReg
);
601 void loadObjProp(JSObject
*obj
, RegisterID objReg
,
602 const js::Shape
*shape
,
603 RegisterID typeReg
, RegisterID dataReg
)
605 if (shape
->isMethod())
606 loadValueAsComponents(ObjectValue(shape
->methodObject()), typeReg
, dataReg
);
607 else if (obj
->hasSlotsArray())
608 loadDynamicSlot(objReg
, shape
->slot
, typeReg
, dataReg
);
610 loadInlineSlot(objReg
, shape
->slot
, typeReg
, dataReg
);
614 /* Return f<true> if the script is strict mode code, f<false> otherwise. */
615 #define STRICT_VARIANT(f) \
616 (FunctionTemplateConditional(script->strictModeCode, \
619 /* Save some typing. */
620 static const JSC::MacroAssembler::RegisterID JSReturnReg_Type
= Assembler::JSReturnReg_Type
;
621 static const JSC::MacroAssembler::RegisterID JSReturnReg_Data
= Assembler::JSReturnReg_Data
;
622 static const JSC::MacroAssembler::RegisterID JSParamReg_Argc
= Assembler::JSParamReg_Argc
;
624 struct FrameFlagsAddress
: JSC::MacroAssembler::Address
627 : Address(JSFrameReg
, JSStackFrame::offsetOfFlags())
631 } /* namespace mjit */