blob: fa3bb73725de66224070dd5719536c0569cbcf7f [file] [log] [blame]
/* -*- 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 "MIRGenerator.h"
#include "IonFrames.h"
#include "jsscript.h"
#include "IonLinker.h"
#include "IonSpewer.h"
#include "SnapshotReader.h"
#include "SnapshotWriter.h"
#ifdef TRACK_SNAPSHOTS
#include "MIR.h"
#include "LIR.h"
#endif
#include "jsscriptinlines.h"
using namespace js;
using namespace js::jit;
// Snapshot header:
//
// [vwu] bits (n-31]: frame count
// bits [0,n): bailout kind (n = BAILOUT_KIND_BITS)
//
// Snapshot body, repeated "frame count" times, from oldest frame to newest frame.
// Note that the first frame doesn't have the "parent PC" field.
//
// [ptr] Debug only: JSScript *
// [vwu] pc offset
// [vwu] # of slots, including nargs
// [slot*] N slot entries, where N = nargs + nfixed + stackDepth
//
// Encodings:
// [ptr] A fixed-size pointer.
// [vwu] A variable-width unsigned integer.
// [vws] A variable-width signed integer.
// [u8] An 8-bit unsigned integer.
// [slot*] Information on how to reify a js::Value from an Ion frame. The
// first byte is split as thus:
// Bits 0-2: JSValueType
// Bits 3-7: 5-bit register code ("reg").
//
// JSVAL_TYPE_DOUBLE:
// If "reg" is InvalidFloatReg, this byte is followed by a
// [vws] stack offset. Otherwise, "reg" encodes an XMM register.
//
// JSVAL_TYPE_INT32:
// JSVAL_TYPE_OBJECT:
// JSVAL_TYPE_BOOLEAN:
// JSVAL_TYPE_STRING:
// If "reg" is InvalidReg1, this byte is followed by a [vws]
// stack offset. Otherwise, "reg" encodes a GPR register.
//
// JSVAL_TYPE_NULL:
// Reg value:
// 0-29: Constant integer; Int32Value(n)
// 30: NullValue()
// 31: Constant integer; Int32Value([vws])
//
// JSVAL_TYPE_UNDEFINED:
// Reg value:
// 0-29: Constant value, index n into ionScript->constants()
// 30: UndefinedValue()
// 31: Constant value, index [vwu] into
// ionScript->constants()
//
// JSVAL_TYPE_MAGIC: (reg value is 30)
// The value is a lazy argument object. Followed by extra fields
// indicating the location of the payload.
// [vwu] reg2 (0-29)
// [vwu] reg2 (31) [vws] stack index
//
// JSVAL_TYPE_MAGIC:
// The type is not statically known. The meaning of this depends
// on the boxing style.
//
// NUNBOX32:
// Followed by a type and payload indicator that are one of
// the following:
// code=0 [vws] stack slot, [vws] stack slot
// code=1 [vws] stack slot, reg
// code=2 reg, [vws] stack slot
// code=3 reg, reg
//
// PUNBOX64:
// "reg" is InvalidReg1: byte is followed by a [vws] stack
// offset containing a Value.
//
// Otherwise, "reg" is a register containing a Value.
//
SnapshotReader::SnapshotReader(const uint8_t *buffer, const uint8_t *end)
: reader_(buffer, end),
slotCount_(0),
frameCount_(0),
slotsRead_(0)
{
if (!buffer)
return;
IonSpew(IonSpew_Snapshots, "Creating snapshot reader");
readSnapshotHeader();
nextFrame();
}
static const uint32_t BAILOUT_KIND_SHIFT = 0;
static const uint32_t BAILOUT_KIND_MASK = (1 << BAILOUT_KIND_BITS) - 1;
static const uint32_t BAILOUT_RESUME_SHIFT = BAILOUT_KIND_SHIFT + BAILOUT_KIND_BITS;
static const uint32_t BAILOUT_FRAMECOUNT_SHIFT = BAILOUT_KIND_BITS + BAILOUT_RESUME_BITS;
static const uint32_t BAILOUT_FRAMECOUNT_BITS = (8 * sizeof(uint32_t)) - BAILOUT_FRAMECOUNT_SHIFT;
void
SnapshotReader::readSnapshotHeader()
{
uint32_t bits = reader_.readUnsigned();
frameCount_ = bits >> BAILOUT_FRAMECOUNT_SHIFT;
JS_ASSERT(frameCount_ > 0);
bailoutKind_ = BailoutKind((bits >> BAILOUT_KIND_SHIFT) & BAILOUT_KIND_MASK);
resumeAfter_ = !!(bits & (1 << BAILOUT_RESUME_SHIFT));
framesRead_ = 0;
IonSpew(IonSpew_Snapshots, "Read snapshot header with frameCount %u, bailout kind %u (ra: %d)",
frameCount_, bailoutKind_, resumeAfter_);
}
void
SnapshotReader::readFrameHeader()
{
JS_ASSERT(moreFrames());
JS_ASSERT(slotsRead_ == slotCount_);
pcOffset_ = reader_.readUnsigned();
slotCount_ = reader_.readUnsigned();
IonSpew(IonSpew_Snapshots, "Read pc offset %u, nslots %u", pcOffset_, slotCount_);
#ifdef TRACK_SNAPSHOTS
pcOpcode_ = reader_.readUnsigned();
mirOpcode_ = reader_.readUnsigned();
mirId_ = reader_.readUnsigned();
lirOpcode_ = reader_.readUnsigned();
lirId_ = reader_.readUnsigned();
#endif
framesRead_++;
slotsRead_ = 0;
}
#ifdef TRACK_SNAPSHOTS
void
SnapshotReader::spewBailingFrom() const
{
if (IonSpewEnabled(IonSpew_Bailouts)) {
IonSpewHeader(IonSpew_Bailouts);
fprintf(IonSpewFile, " bailing from bytecode: %s, MIR: ", js_CodeName[pcOpcode_]);
MDefinition::PrintOpcodeName(IonSpewFile, MDefinition::Opcode(mirOpcode_));
fprintf(IonSpewFile, " [%u], LIR: ", mirId_);
LInstruction::printName(IonSpewFile, LInstruction::Opcode(lirOpcode_));
fprintf(IonSpewFile, " [%u]", lirId_);
fprintf(IonSpewFile, "\n");
}
}
#endif
#ifdef JS_NUNBOX32
static const uint32_t NUNBOX32_STACK_STACK = 0;
static const uint32_t NUNBOX32_STACK_REG = 1;
static const uint32_t NUNBOX32_REG_STACK = 2;
static const uint32_t NUNBOX32_REG_REG = 3;
#endif
static const uint32_t MAX_TYPE_FIELD_VALUE = 7;
static const uint32_t MAX_REG_FIELD_VALUE = 31;
static const uint32_t ESC_REG_FIELD_INDEX = 31;
static const uint32_t ESC_REG_FIELD_CONST = 30;
static const uint32_t MIN_REG_FIELD_ESC = 30;
SnapshotReader::Slot
SnapshotReader::readSlot()
{
JS_ASSERT(slotsRead_ < slotCount_);
IonSpew(IonSpew_Snapshots, "Reading slot %u", slotsRead_);
slotsRead_++;
uint8_t b = reader_.readByte();
JSValueType type = JSValueType(b & 0x7);
uint32_t code = b >> 3;
switch (type) {
case JSVAL_TYPE_DOUBLE:
if (code < MIN_REG_FIELD_ESC)
return Slot(FloatRegister::FromCode(code));
JS_ASSERT(code == ESC_REG_FIELD_INDEX);
return Slot(TYPED_STACK, type, Location::From(reader_.readSigned()));
case JSVAL_TYPE_INT32:
case JSVAL_TYPE_STRING:
case JSVAL_TYPE_OBJECT:
case JSVAL_TYPE_BOOLEAN:
if (code < MIN_REG_FIELD_ESC)
return Slot(TYPED_REG, type, Location::From(Register::FromCode(code)));
JS_ASSERT(code == ESC_REG_FIELD_INDEX);
return Slot(TYPED_STACK, type, Location::From(reader_.readSigned()));
case JSVAL_TYPE_NULL:
if (code == ESC_REG_FIELD_CONST)
return Slot(JS_NULL);
if (code == ESC_REG_FIELD_INDEX)
return Slot(JS_INT32, reader_.readSigned());
return Slot(JS_INT32, code);
case JSVAL_TYPE_UNDEFINED:
if (code == ESC_REG_FIELD_CONST)
return Slot(JS_UNDEFINED);
if (code == ESC_REG_FIELD_INDEX)
return Slot(CONSTANT, reader_.readUnsigned());
return Slot(CONSTANT, code);
default:
{
JS_ASSERT(type == JSVAL_TYPE_MAGIC);
if (code == ESC_REG_FIELD_CONST) {
uint8_t reg2 = reader_.readUnsigned();
Location loc;
if (reg2 != ESC_REG_FIELD_INDEX)
loc = Location::From(Register::FromCode(reg2));
else
loc = Location::From(reader_.readSigned());
return Slot(TYPED_REG, type, loc);
}
Slot slot(UNTYPED);
#ifdef JS_NUNBOX32
switch (code) {
case NUNBOX32_STACK_STACK:
slot.unknown_type_.type = Location::From(reader_.readSigned());
slot.unknown_type_.payload = Location::From(reader_.readSigned());
return slot;
case NUNBOX32_STACK_REG:
slot.unknown_type_.type = Location::From(reader_.readSigned());
slot.unknown_type_.payload = Location::From(Register::FromCode(reader_.readByte()));
return slot;
case NUNBOX32_REG_STACK:
slot.unknown_type_.type = Location::From(Register::FromCode(reader_.readByte()));
slot.unknown_type_.payload = Location::From(reader_.readSigned());
return slot;
default:
JS_ASSERT(code == NUNBOX32_REG_REG);
slot.unknown_type_.type = Location::From(Register::FromCode(reader_.readByte()));
slot.unknown_type_.payload = Location::From(Register::FromCode(reader_.readByte()));
return slot;
}
#elif JS_PUNBOX64
if (code < MIN_REG_FIELD_ESC) {
slot.unknown_type_.value = Location::From(Register::FromCode(code));
} else {
JS_ASSERT(code == ESC_REG_FIELD_INDEX);
slot.unknown_type_.value = Location::From(reader_.readSigned());
}
return slot;
#endif
}
}
JS_NOT_REACHED("huh?");
return Slot(JS_UNDEFINED);
}
SnapshotOffset
SnapshotWriter::startSnapshot(uint32_t frameCount, BailoutKind kind, bool resumeAfter)
{
nframes_ = frameCount;
framesWritten_ = 0;
lastStart_ = writer_.length();
IonSpew(IonSpew_Snapshots, "starting snapshot with frameCount %u, bailout kind %u",
frameCount, kind);
JS_ASSERT(frameCount > 0);
JS_ASSERT(frameCount < (1 << BAILOUT_FRAMECOUNT_BITS));
JS_ASSERT(uint32_t(kind) < (1 << BAILOUT_KIND_BITS));
uint32_t bits = (uint32_t(kind) << BAILOUT_KIND_SHIFT) |
(frameCount << BAILOUT_FRAMECOUNT_SHIFT);
if (resumeAfter)
bits |= (1 << BAILOUT_RESUME_SHIFT);
writer_.writeUnsigned(bits);
return lastStart_;
}
void
SnapshotWriter::startFrame(JSFunction *fun, JSScript *script, jsbytecode *pc, uint32_t exprStack)
{
JS_ASSERT(CountArgSlots(script, fun) < SNAPSHOT_MAX_NARGS);
uint32_t implicit = StartArgSlot(script, fun);
uint32_t formalArgs = CountArgSlots(script, fun);
nslots_ = formalArgs + script->nfixed + exprStack;
slotsWritten_ = 0;
IonSpew(IonSpew_Snapshots, "Starting frame; implicit %u, formals %u, fixed %u, exprs %u",
implicit, formalArgs - implicit, script->nfixed, exprStack);
JS_ASSERT(script->code <= pc && pc <= script->code + script->length);
uint32_t pcoff = uint32_t(pc - script->code);
IonSpew(IonSpew_Snapshots, "Writing pc offset %u, nslots %u", pcoff, nslots_);
writer_.writeUnsigned(pcoff);
writer_.writeUnsigned(nslots_);
}
#ifdef TRACK_SNAPSHOTS
void
SnapshotWriter::trackFrame(uint32_t pcOpcode, uint32_t mirOpcode, uint32_t mirId,
uint32_t lirOpcode, uint32_t lirId)
{
writer_.writeUnsigned(pcOpcode);
writer_.writeUnsigned(mirOpcode);
writer_.writeUnsigned(mirId);
writer_.writeUnsigned(lirOpcode);
writer_.writeUnsigned(lirId);
}
#endif
void
SnapshotWriter::endFrame()
{
// Check that the last write succeeded.
JS_ASSERT(nslots_ == slotsWritten_);
nslots_ = slotsWritten_ = 0;
framesWritten_++;
}
void
SnapshotWriter::writeSlotHeader(JSValueType type, uint32_t regCode)
{
JS_ASSERT(uint32_t(type) <= MAX_TYPE_FIELD_VALUE);
JS_ASSERT(uint32_t(regCode) <= MAX_REG_FIELD_VALUE);
#if defined(JS_CPU_MIPS)
// Note: This static assert fails on MIPS, but seems safe.
#else
JS_STATIC_ASSERT(Registers::Total < MIN_REG_FIELD_ESC);
#endif
uint8_t byte = uint32_t(type) | (regCode << 3);
writer_.writeByte(byte);
slotsWritten_++;
JS_ASSERT(slotsWritten_ <= nslots_);
}
void
SnapshotWriter::addSlot(const FloatRegister &reg)
{
IonSpew(IonSpew_Snapshots, " slot %u: double (reg %s)", slotsWritten_, reg.name());
writeSlotHeader(JSVAL_TYPE_DOUBLE, reg.code());
}
static const char *
ValTypeToString(JSValueType type)
{
switch (type) {
case JSVAL_TYPE_INT32:
return "int32_t";
case JSVAL_TYPE_DOUBLE:
return "double";
case JSVAL_TYPE_STRING:
return "string";
case JSVAL_TYPE_BOOLEAN:
return "boolean";
case JSVAL_TYPE_OBJECT:
return "object";
case JSVAL_TYPE_MAGIC:
return "magic";
default:
JS_NOT_REACHED("no payload");
return "";
}
}
void
SnapshotWriter::addSlot(JSValueType type, const Register &reg)
{
IonSpew(IonSpew_Snapshots, " slot %u: %s (%s)",
slotsWritten_, ValTypeToString(type), reg.name());
JS_ASSERT(type != JSVAL_TYPE_DOUBLE);
writeSlotHeader(type, reg.code());
}
void
SnapshotWriter::addSlot(JSValueType type, int32_t stackIndex)
{
IonSpew(IonSpew_Snapshots, " slot %u: %s (stack %d)",
slotsWritten_, ValTypeToString(type), stackIndex);
writeSlotHeader(type, ESC_REG_FIELD_INDEX);
writer_.writeSigned(stackIndex);
}
#if defined(JS_NUNBOX32)
void
SnapshotWriter::addSlot(const Register &type, const Register &payload)
{
IonSpew(IonSpew_Snapshots, " slot %u: value (t=%s, d=%s)",
slotsWritten_, type.name(), payload.name());
writeSlotHeader(JSVAL_TYPE_MAGIC, NUNBOX32_REG_REG);
writer_.writeByte(type.code());
writer_.writeByte(payload.code());
}
void
SnapshotWriter::addSlot(const Register &type, int32_t payloadStackIndex)
{
IonSpew(IonSpew_Snapshots, " slot %u: value (t=%s, d=%d)",
slotsWritten_, type.name(), payloadStackIndex);
writeSlotHeader(JSVAL_TYPE_MAGIC, NUNBOX32_REG_STACK);
writer_.writeByte(type.code());
writer_.writeSigned(payloadStackIndex);
}
void
SnapshotWriter::addSlot(int32_t typeStackIndex, const Register &payload)
{
IonSpew(IonSpew_Snapshots, " slot %u: value (t=%d, d=%s)",
slotsWritten_, typeStackIndex, payload.name());
writeSlotHeader(JSVAL_TYPE_MAGIC, NUNBOX32_STACK_REG);
writer_.writeSigned(typeStackIndex);
writer_.writeByte(payload.code());
}
void
SnapshotWriter::addSlot(int32_t typeStackIndex, int32_t payloadStackIndex)
{
IonSpew(IonSpew_Snapshots, " slot %u: value (t=%d, d=%d)",
slotsWritten_, typeStackIndex, payloadStackIndex);
writeSlotHeader(JSVAL_TYPE_MAGIC, NUNBOX32_STACK_STACK);
writer_.writeSigned(typeStackIndex);
writer_.writeSigned(payloadStackIndex);
}
#elif defined(JS_PUNBOX64)
void
SnapshotWriter::addSlot(const Register &value)
{
IonSpew(IonSpew_Snapshots, " slot %u: value (reg %s)", slotsWritten_, value.name());
writeSlotHeader(JSVAL_TYPE_MAGIC, value.code());
}
void
SnapshotWriter::addSlot(int32_t valueStackSlot)
{
IonSpew(IonSpew_Snapshots, " slot %u: value (stack %d)", slotsWritten_, valueStackSlot);
writeSlotHeader(JSVAL_TYPE_MAGIC, ESC_REG_FIELD_INDEX);
writer_.writeSigned(valueStackSlot);
}
#endif
void
SnapshotWriter::addUndefinedSlot()
{
IonSpew(IonSpew_Snapshots, " slot %u: undefined", slotsWritten_);
writeSlotHeader(JSVAL_TYPE_UNDEFINED, ESC_REG_FIELD_CONST);
}
void
SnapshotWriter::addNullSlot()
{
IonSpew(IonSpew_Snapshots, " slot %u: null", slotsWritten_);
writeSlotHeader(JSVAL_TYPE_NULL, ESC_REG_FIELD_CONST);
}
void
SnapshotWriter::endSnapshot()
{
JS_ASSERT(nframes_ == framesWritten_);
// Place a sentinel for asserting on the other end.
#ifdef DEBUG
writer_.writeSigned(-1);
#endif
IonSpew(IonSpew_Snapshots, "ending snapshot total size: %u bytes (start %u)",
uint32_t(writer_.length() - lastStart_), lastStart_);
}
void
SnapshotWriter::addInt32Slot(int32_t value)
{
IonSpew(IonSpew_Snapshots, " slot %u: int32_t %d", slotsWritten_, value);
if (value >= 0 && uint32_t(value) < MIN_REG_FIELD_ESC) {
writeSlotHeader(JSVAL_TYPE_NULL, value);
} else {
writeSlotHeader(JSVAL_TYPE_NULL, ESC_REG_FIELD_INDEX);
writer_.writeSigned(value);
}
}
void
SnapshotWriter::addConstantPoolSlot(uint32_t index)
{
IonSpew(IonSpew_Snapshots, " slot %u: constant pool index %u", slotsWritten_, index);
if (index < MIN_REG_FIELD_ESC) {
writeSlotHeader(JSVAL_TYPE_UNDEFINED, index);
} else {
writeSlotHeader(JSVAL_TYPE_UNDEFINED, ESC_REG_FIELD_INDEX);
writer_.writeUnsigned(index);
}
}