/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "BaselineCompiler-shared.h"
#include "jit/BaselineIC.h"
#include "jit/VMFunctions.h"

using namespace js;
using namespace js::jit;

BaselineCompilerShared::BaselineCompilerShared(JSContext *cx, HandleScript script)
  : cx(cx),
    script(cx, script),
    pc(script->code),
    ionCompileable_(jit::IsIonEnabled(cx) && CanIonCompileScript(cx, script, false)),
    ionOSRCompileable_(jit::IsIonEnabled(cx) && CanIonCompileScript(cx, script, true)),
    debugMode_(cx->compartment()->debugMode()),
    analysis_(script),
    frame(cx, script, masm),
    stubSpace_(),
    icEntries_(),
    pcMappingEntries_(),
    icLoadLabels_(),
    pushedBeforeCall_(0),
    inCall_(false),
    spsPushToggleOffset_()
{ }

bool
BaselineCompilerShared::callVM(const VMFunction &fun)
{
    IonCompartment *ion = cx->compartment()->ionCompartment();
    IonCode *code = ion->getVMWrapper(fun);
    if (!code)
        return false;

#ifdef DEBUG
    // Assert prepareVMCall() has been called.
    JS_ASSERT(inCall_);
    inCall_ = false;
#endif

    // Compute argument size. Note that this include the size of the frame pointer
    // pushed by prepareVMCall.
    uint32_t argSize = fun.explicitStackSlots() * sizeof(void *) + sizeof(void *);

    // Assert all arguments were pushed.
    JS_ASSERT(masm.framePushed() - pushedBeforeCall_ == argSize);

    uint32_t frameSize = BaselineFrame::FramePointerOffset + BaselineFrame::Size() +
        (frame.nlocals() + frame.stackDepth()) * sizeof(Value);

    masm.store32(Imm32(frameSize), Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));

    uint32_t descriptor = MakeFrameDescriptor(frameSize + argSize, IonFrame_BaselineJS);
    masm.push(Imm32(descriptor));

    // Perform the call.
    masm.call(code);
    uint32_t callOffset = masm.currentOffset();
    masm.pop(BaselineFrameReg);

    // Add a fake ICEntry (without stubs), so that the return offset to
    // pc mapping works.
    ICEntry entry(pc - script->code, false);
    entry.setReturnOffset(callOffset);

    return icEntries_.append(entry);
}
