Fix call mechanism and recompilation (bug 609222, r=dmandelin,adrake,m_kato).
[mozilla-central.git] / js / src / methodjit / BaseAssembler.h
blob7c7d924062b9457e3a2de8c201ce7d06326dd410
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
15 * License.
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 * May 28, 2008.
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
23 * Contributor(s):
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__
44 #include "jscntxt.h"
45 #include "jstl.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"
56 namespace js {
57 namespace mjit {
59 class MaybeRegisterID {
60 typedef JSC::MacroAssembler::RegisterID RegisterID;
62 public:
63 MaybeRegisterID()
64 : reg_(Registers::ReturnReg), set(false)
65 { }
67 MaybeRegisterID(RegisterID reg)
68 : reg_(reg), set(true)
69 { }
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) {
76 set = other.set;
77 reg_ = other.reg_;
78 return *this;
81 MaybeRegisterID & operator =(RegisterID r) {
82 setReg(r);
83 return *this;
86 private:
87 RegisterID reg_;
88 bool set;
91 // Represents an int32 property name in generated code, which must be either
92 // a RegisterID or a constant value.
93 struct Int32Key {
94 typedef JSC::MacroAssembler::RegisterID RegisterID;
96 MaybeRegisterID reg_;
97 int32 index_;
99 Int32Key() : index_(0) { }
101 static Int32Key FromRegister(RegisterID reg) {
102 Int32Key key;
103 key.reg_ = reg;
104 return key;
106 static Int32Key FromConstant(int32 index) {
107 Int32Key key;
108 key.index_ = index;
109 return key;
112 int32 index() const {
113 JS_ASSERT(!reg_.isSet());
114 return index_;
117 RegisterID reg() const { return reg_.reg(); }
118 bool isConstant() const { return !reg_.isSet(); }
121 class MaybeJump {
122 typedef JSC::MacroAssembler::Jump Jump;
123 public:
124 MaybeJump()
125 : set(false)
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; }
135 private:
136 Jump jump;
137 bool set;
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
156 struct CallPatch {
157 CallPatch(Call cl, void *fun)
158 : call(cl), fun(fun)
161 Call call;
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;
170 #endif
172 /* :TODO: OOM */
173 Label startLabel;
174 Vector<CallPatch, 64, SystemAllocPolicy> callPatches;
176 // List and count of registers that will be saved and restored across a call.
177 uint32 saveCount;
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.
185 uint32 stackAdjust;
187 // Debug flag to make sure calls do not nest.
188 #ifdef DEBUG
189 bool callIsAligned;
190 #endif
192 public:
193 Assembler()
194 : callPatches(SystemAllocPolicy()),
195 saveCount(0)
196 #ifdef DEBUG
197 , callIsAligned(false)
198 #endif
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;
215 #endif
217 size_t distanceOf(Label l) {
218 return differenceBetween(startLabel, l);
221 void load32FromImm(void *ptr, RegisterID reg) {
222 load32(ptr, 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);
244 loadPtr(reg, reg);
245 return Address(reg, slot * sizeof(Value));
248 #ifdef JS_CPU_X86
249 void idiv(RegisterID reg) {
250 m_assembler.cdq();
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);
258 } else {
259 m_assembler.movd_rr(lo, fpReg);
260 m_assembler.movd_rr(hi, FPRegisters::Temp0);
261 m_assembler.unpcklps_rr(FPRegisters::Temp0, fpReg);
264 #endif
266 // Prepares for a stub call.
267 void *getCallTarget(void *fun) {
268 #ifdef JS_CPU_ARM
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
277 * investigated.
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-
285 * compliant.
287 move(Imm32(intptr_t(fun)), JSC::ARMRegisters::ip);
288 #else
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.
294 void *pfun = fun;
295 #endif
296 return pfun;
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;
312 push(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;
323 #else
324 return 0;
325 #endif
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
337 // stack.
338 #ifdef _WIN64
339 static const uint32 ReturnStackAdjustment = 32;
340 #elif defined(JS_CPU_X86) && defined(JS_NO_FASTCALL)
341 static const uint32 ReturnStackAdjustment = 16;
342 #else
343 static const uint32 ReturnStackAdjustment = 0;
344 #endif
346 void throwInJIT() {
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.
354 #ifdef _WIN64
355 static const uint32 ShadowStackSpace = 32;
356 #else
357 static const uint32 ShadowStackSpace = 0;
358 #endif
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
370 : 0;
372 // Adjust the stack for alignment and parameters all at once.
373 stackAdjust = (pushCount + saveCount) * sizeof(void *);
374 stackAdjust += alignForCall(stackAdjust);
376 #ifdef _WIN64
377 // Windows x64 ABI requires 32 bytes of "shadow space" for the callee
378 // to spill its parameters.
379 stackAdjust += ShadowStackSpace;
380 #endif
382 if (stackAdjust)
383 subPtr(Imm32(stackAdjust), stackPointerRegister);
385 callConvention = convention;
386 #ifdef DEBUG
387 callIsAligned = true;
388 #endif
391 // This is an internal function only for use inside a startABICall(),
392 // callWithABI() sequence, and only for arguments known to fit in
393 // registers.
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);
407 RegisterID to;
408 if (Registers::regForArg(callConvention, i, &to)) {
409 if (reg != to)
410 move(reg, to);
411 } else {
412 storePtr(reg, addressOfArg(i));
416 void storeArg(uint32 i, Imm32 imm) {
417 JS_ASSERT(callIsAligned);
418 RegisterID to;
419 if (Registers::regForArg(callConvention, i, &to))
420 move(imm, to);
421 else
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);
433 Call cl = call();
434 callPatches.append(CallPatch(cl, fun));
436 if (stackAdjust)
437 addPtr(Imm32(stackAdjust), stackPointerRegister);
439 #ifdef DEBUG
440 callIsAligned = false;
441 #endif
442 return cl;
445 // Restore registers after a call.
446 void restoreRegs() {
447 // Note that saveCount will safely decrement back to 0.
448 while (saveCount)
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
475 // regs->sp = sp
476 addPtr(Imm32(sizeof(JSStackFrame) + frameDepth * sizeof(jsval)),
477 JSFrameReg,
478 ClobberInCall);
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);
491 /* regs->fp = fp */
492 storePtr(JSFrameReg, FrameAddress(offsetof(VMFrame, regs.fp)));
494 /* PC -> regs->pc :( */
495 storePtr(ImmPtr(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
525 // sequence.
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 {
540 Jump rangeCheck;
541 Jump holeCheck;
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);
568 } else {
569 BaseIndex slot(objReg, key.reg(), JSVAL_SCALE);
570 fails.holeCheck = fastArrayLoadSlot(slot, typeReg, dataReg);
573 return fails;
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);
591 else
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);
609 else
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, \
617 f<true>, f<false>))
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
626 FrameFlagsAddress()
627 : Address(JSFrameReg, JSStackFrame::offsetOfFlags())
631 } /* namespace mjit */
632 } /* namespace js */
634 #endif