Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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
#include "jit/BaselineCacheIRCompiler.h"
#include "gc/GC.h"
#include "jit/CacheIR.h"
#include "jit/CacheIRCloner.h"
#include "jit/CacheIRWriter.h"
#include "jit/JitFrames.h"
#include "jit/JitRuntime.h"
#include "jit/JitZone.h"
#include "jit/Linker.h"
#include "jit/MoveEmitter.h"
#include "jit/RegExpStubConstants.h"
#include "jit/SharedICHelpers.h"
#include "jit/VMFunctions.h"
#include "js/experimental/JitInfo.h" // JSJitInfo
#include "js/friend/DOMProxy.h" // JS::ExpandoAndGeneration
#include "proxy/DeadObjectProxy.h"
#include "proxy/Proxy.h"
#include "util/Unicode.h"
#include "vm/PortableBaselineInterpret.h"
#include "vm/StaticStrings.h"
#include "jit/JitScript-inl.h"
#include "jit/MacroAssembler-inl.h"
#include "jit/SharedICHelpers-inl.h"
#include "jit/VMFunctionList-inl.h"
#include "vm/List-inl.h"
using namespace js;
using namespace js::jit;
using mozilla::Maybe;
using JS::ExpandoAndGeneration;
namespace js {
namespace jit {
static uint32_t GetICStackValueOffset() {
uint32_t offset = ICStackValueOffset;
if (JitOptions.enableICFramePointers) {
#ifdef JS_USE_LINK_REGISTER
// The frame pointer and return address are also on the stack.
offset += 2 * sizeof(uintptr_t);
#else
// The frame pointer is also on the stack.
offset += sizeof(uintptr_t);
#endif
}
return offset;
}
static void PushICFrameRegs(MacroAssembler& masm) {
MOZ_ASSERT(JitOptions.enableICFramePointers);
#ifdef JS_USE_LINK_REGISTER
masm.pushReturnAddress();
#endif
masm.push(FramePointer);
}
static void PopICFrameRegs(MacroAssembler& masm) {
MOZ_ASSERT(JitOptions.enableICFramePointers);
masm.pop(FramePointer);
#ifdef JS_USE_LINK_REGISTER
masm.popReturnAddress();
#endif
}
Address CacheRegisterAllocator::addressOf(MacroAssembler& masm,
BaselineFrameSlot slot) const {
uint32_t offset =
stackPushed_ + GetICStackValueOffset() + slot.slot() * sizeof(JS::Value);
return Address(masm.getStackPointer(), offset);
}
BaseValueIndex CacheRegisterAllocator::addressOf(MacroAssembler& masm,
Register argcReg,
BaselineFrameSlot slot) const {
uint32_t offset =
stackPushed_ + GetICStackValueOffset() + slot.slot() * sizeof(JS::Value);
return BaseValueIndex(masm.getStackPointer(), argcReg, offset);
}
// BaselineCacheIRCompiler compiles CacheIR to BaselineIC native code.
BaselineCacheIRCompiler::BaselineCacheIRCompiler(JSContext* cx,
TempAllocator& alloc,
const CacheIRWriter& writer,
uint32_t stubDataOffset)
: CacheIRCompiler(cx, alloc, writer, stubDataOffset, Mode::Baseline,
StubFieldPolicy::Address),
makesGCCalls_(false) {}
// AutoStubFrame methods
AutoStubFrame::AutoStubFrame(BaselineCacheIRCompiler& compiler)
: compiler(compiler)
#ifdef DEBUG
,
framePushedAtEnterStubFrame_(0)
#endif
{
}
void AutoStubFrame::enter(MacroAssembler& masm, Register scratch) {
MOZ_ASSERT(compiler.allocator.stackPushed() == 0);
if (JitOptions.enableICFramePointers) {
// If we have already pushed the frame pointer, pop it
// before creating the stub frame.
PopICFrameRegs(masm);
}
EmitBaselineEnterStubFrame(masm, scratch);
#ifdef DEBUG
framePushedAtEnterStubFrame_ = masm.framePushed();
#endif
MOZ_ASSERT(!compiler.enteredStubFrame_);
compiler.enteredStubFrame_ = true;
// All current uses of this are to call VM functions that can GC.
compiler.makesGCCalls_ = true;
}
void AutoStubFrame::leave(MacroAssembler& masm) {
MOZ_ASSERT(compiler.enteredStubFrame_);
compiler.enteredStubFrame_ = false;
#ifdef DEBUG
masm.setFramePushed(framePushedAtEnterStubFrame_);
#endif
EmitBaselineLeaveStubFrame(masm);
if (JitOptions.enableICFramePointers) {
// We will pop the frame pointer when we return,
// so we have to push it again now.
PushICFrameRegs(masm);
}
}
void AutoStubFrame::storeTracedValue(MacroAssembler& masm, ValueOperand value) {
MOZ_ASSERT(compiler.localTracingSlots_ < 255);
MOZ_ASSERT(masm.framePushed() - framePushedAtEnterStubFrame_ ==
compiler.localTracingSlots_ * sizeof(Value));
masm.Push(value);
compiler.localTracingSlots_++;
}
void AutoStubFrame::loadTracedValue(MacroAssembler& masm, uint8_t slotIndex,
ValueOperand value) {
MOZ_ASSERT(slotIndex <= compiler.localTracingSlots_);
int32_t offset = BaselineStubFrameLayout::LocallyTracedValueOffset +
slotIndex * sizeof(Value);
masm.loadValue(Address(FramePointer, -offset), value);
}
#ifdef DEBUG
AutoStubFrame::~AutoStubFrame() { MOZ_ASSERT(!compiler.enteredStubFrame_); }
#endif
} // namespace jit
} // namespace js
bool BaselineCacheIRCompiler::makesGCCalls() const { return makesGCCalls_; }
Address BaselineCacheIRCompiler::stubAddress(uint32_t offset) const {
return Address(ICStubReg, stubDataOffset_ + offset);
}
template <typename Fn, Fn fn>
void BaselineCacheIRCompiler::callVM(MacroAssembler& masm) {
VMFunctionId id = VMFunctionToId<Fn, fn>::id;
callVMInternal(masm, id);
}
JitCode* BaselineCacheIRCompiler::compile() {
AutoCreatedBy acb(masm, "BaselineCacheIRCompiler::compile");
#ifndef JS_USE_LINK_REGISTER
masm.adjustFrame(sizeof(intptr_t));
#endif
#ifdef JS_CODEGEN_ARM
AutoNonDefaultSecondScratchRegister andssr(masm, BaselineSecondScratchReg);
#endif
if (JitOptions.enableICFramePointers) {
/* [SMDOC] Baseline IC Frame Pointers
*
* In general, ICs don't have frame pointers until just before
* doing a VM call, at which point we retroactively create a stub
* frame. However, for the sake of external profilers, we
* optionally support full-IC frame pointers in baseline ICs, with
* the following approach:
* 1. We push a frame pointer when we enter an IC.
* 2. We pop the frame pointer when we return from an IC, or
* when we jump to the next IC.
* 3. Entering a stub frame for a VM call already pushes a
* frame pointer, so we pop our existing frame pointer
* just before entering a stub frame and push it again
* just after leaving a stub frame.
* Some ops take advantage of the fact that the frame pointer is
* not updated until we enter a stub frame to read values from
* the caller's frame. To support this, we allocate a separate
* baselineFrame register when IC frame pointers are enabled.
*/
PushICFrameRegs(masm);
masm.moveStackPtrTo(FramePointer);
MOZ_ASSERT(baselineFrameReg() != FramePointer);
masm.loadPtr(Address(FramePointer, 0), baselineFrameReg());
}
// Count stub entries: We count entries rather than successes as it much
// easier to ensure ICStubReg is valid at entry than at exit.
Address enteredCount(ICStubReg, ICCacheIRStub::offsetOfEnteredCount());
masm.add32(Imm32(1), enteredCount);
CacheIRReader reader(writer_);
do {
CacheOp op = reader.readOp();
perfSpewer_.recordInstruction(masm, op);
switch (op) {
#define DEFINE_OP(op, ...) \
case CacheOp::op: \
if (!emit##op(reader)) return nullptr; \
break;