mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-27 02:29:35 +00:00
562 lines
16 KiB
C++
562 lines
16 KiB
C++
/* -*- 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 "jit/Safepoints.h"
|
|
|
|
#include "mozilla/MathAlgorithms.h"
|
|
|
|
#include "jit/BitSet.h"
|
|
#include "jit/JitSpewer.h"
|
|
#include "jit/LIR.h"
|
|
|
|
using namespace js;
|
|
using namespace jit;
|
|
|
|
using mozilla::FloorLog2;
|
|
|
|
SafepointWriter::SafepointWriter(uint32_t slotCount, uint32_t argumentCount)
|
|
: frameSlots_((slotCount / sizeof(intptr_t)) + 1), // Stack slot counts are inclusive.
|
|
argumentSlots_(argumentCount / sizeof(intptr_t))
|
|
{ }
|
|
|
|
bool
|
|
SafepointWriter::init(TempAllocator& alloc)
|
|
{
|
|
return frameSlots_.init(alloc) && argumentSlots_.init(alloc);
|
|
}
|
|
|
|
uint32_t
|
|
SafepointWriter::startEntry()
|
|
{
|
|
JitSpew(JitSpew_Safepoints, "Encoding safepoint (position %d):", stream_.length());
|
|
return uint32_t(stream_.length());
|
|
}
|
|
|
|
void
|
|
SafepointWriter::writeOsiCallPointOffset(uint32_t osiCallPointOffset)
|
|
{
|
|
stream_.writeUnsigned(osiCallPointOffset);
|
|
}
|
|
|
|
static void
|
|
WriteRegisterMask(CompactBufferWriter& stream, uint32_t bits)
|
|
{
|
|
if (sizeof(PackedRegisterMask) == 1)
|
|
stream.writeByte(bits);
|
|
else
|
|
stream.writeUnsigned(bits);
|
|
}
|
|
|
|
static int32_t
|
|
ReadRegisterMask(CompactBufferReader& stream)
|
|
{
|
|
if (sizeof(PackedRegisterMask) == 1)
|
|
return stream.readByte();
|
|
return stream.readUnsigned();
|
|
}
|
|
|
|
static void
|
|
WriteFloatRegisterMask(CompactBufferWriter& stream, uint64_t bits)
|
|
{
|
|
if (sizeof(FloatRegisters::SetType) == 1) {
|
|
stream.writeByte(bits);
|
|
} else if (sizeof(FloatRegisters::SetType) == 4) {
|
|
stream.writeUnsigned(bits);
|
|
} else {
|
|
MOZ_ASSERT(sizeof(FloatRegisters::SetType) == 8);
|
|
stream.writeUnsigned(bits & 0xffffffff);
|
|
stream.writeUnsigned(bits >> 32);
|
|
}
|
|
}
|
|
|
|
static int64_t
|
|
ReadFloatRegisterMask(CompactBufferReader& stream)
|
|
{
|
|
if (sizeof(FloatRegisters::SetType) == 1)
|
|
return stream.readByte();
|
|
if (sizeof(FloatRegisters::SetType) <= 4)
|
|
return stream.readUnsigned();
|
|
MOZ_ASSERT(sizeof(FloatRegisters::SetType) == 8);
|
|
uint64_t ret = stream.readUnsigned();
|
|
ret |= uint64_t(stream.readUnsigned()) << 32;
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
SafepointWriter::writeGcRegs(LSafepoint* safepoint)
|
|
{
|
|
LiveGeneralRegisterSet gc(safepoint->gcRegs());
|
|
LiveGeneralRegisterSet spilledGpr(safepoint->liveRegs().gprs());
|
|
LiveFloatRegisterSet spilledFloat(safepoint->liveRegs().fpus());
|
|
LiveGeneralRegisterSet slots(safepoint->slotsOrElementsRegs());
|
|
LiveGeneralRegisterSet valueRegs;
|
|
|
|
WriteRegisterMask(stream_, spilledGpr.bits());
|
|
if (!spilledGpr.empty()) {
|
|
WriteRegisterMask(stream_, gc.bits());
|
|
WriteRegisterMask(stream_, slots.bits());
|
|
|
|
#ifdef JS_PUNBOX64
|
|
valueRegs = safepoint->valueRegs();
|
|
WriteRegisterMask(stream_, valueRegs.bits());
|
|
#endif
|
|
}
|
|
|
|
// GC registers are a subset of the spilled registers.
|
|
MOZ_ASSERT((valueRegs.bits() & ~spilledGpr.bits()) == 0);
|
|
MOZ_ASSERT((gc.bits() & ~spilledGpr.bits()) == 0);
|
|
|
|
WriteFloatRegisterMask(stream_, spilledFloat.bits());
|
|
|
|
#ifdef JS_JITSPEW
|
|
if (JitSpewEnabled(JitSpew_Safepoints)) {
|
|
for (GeneralRegisterForwardIterator iter(spilledGpr); iter.more(); iter++) {
|
|
const char* type = gc.has(*iter)
|
|
? "gc"
|
|
: slots.has(*iter)
|
|
? "slots"
|
|
: valueRegs.has(*iter)
|
|
? "value"
|
|
: "any";
|
|
JitSpew(JitSpew_Safepoints, " %s reg: %s", type, (*iter).name());
|
|
}
|
|
for (FloatRegisterForwardIterator iter(spilledFloat); iter.more(); iter++)
|
|
JitSpew(JitSpew_Safepoints, " float reg: %s", (*iter).name());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
WriteBitset(const BitSet& set, CompactBufferWriter& stream)
|
|
{
|
|
size_t count = set.rawLength();
|
|
const uint32_t* words = set.raw();
|
|
for (size_t i = 0; i < count; i++)
|
|
stream.writeUnsigned(words[i]);
|
|
}
|
|
|
|
static void
|
|
MapSlotsToBitset(BitSet& stackSet, BitSet& argumentSet,
|
|
CompactBufferWriter& stream, const LSafepoint::SlotList& slots)
|
|
{
|
|
stackSet.clear();
|
|
argumentSet.clear();
|
|
|
|
for (uint32_t i = 0; i < slots.length(); i++) {
|
|
// Slots are represented at a distance from |fp|. We divide by the
|
|
// pointer size, since we only care about pointer-sized/aligned slots
|
|
// here.
|
|
MOZ_ASSERT(slots[i].slot % sizeof(intptr_t) == 0);
|
|
size_t index = slots[i].slot / sizeof(intptr_t);
|
|
(slots[i].stack ? stackSet : argumentSet).insert(index);
|
|
}
|
|
|
|
WriteBitset(stackSet, stream);
|
|
WriteBitset(argumentSet, stream);
|
|
}
|
|
|
|
void
|
|
SafepointWriter::writeGcSlots(LSafepoint* safepoint)
|
|
{
|
|
LSafepoint::SlotList& slots = safepoint->gcSlots();
|
|
|
|
#ifdef JS_JITSPEW
|
|
for (uint32_t i = 0; i < slots.length(); i++)
|
|
JitSpew(JitSpew_Safepoints, " gc slot: %d", slots[i]);
|
|
#endif
|
|
|
|
MapSlotsToBitset(frameSlots_, argumentSlots_, stream_, slots);
|
|
}
|
|
|
|
void
|
|
SafepointWriter::writeSlotsOrElementsSlots(LSafepoint* safepoint)
|
|
{
|
|
LSafepoint::SlotList& slots = safepoint->slotsOrElementsSlots();
|
|
|
|
stream_.writeUnsigned(slots.length());
|
|
|
|
for (uint32_t i = 0; i < slots.length(); i++) {
|
|
if (!slots[i].stack)
|
|
MOZ_CRASH();
|
|
#ifdef JS_JITSPEW
|
|
JitSpew(JitSpew_Safepoints, " slots/elements slot: %d", slots[i].slot);
|
|
#endif
|
|
stream_.writeUnsigned(slots[i].slot);
|
|
}
|
|
}
|
|
|
|
void
|
|
SafepointWriter::writeValueSlots(LSafepoint* safepoint)
|
|
{
|
|
LSafepoint::SlotList& slots = safepoint->valueSlots();
|
|
|
|
#ifdef JS_JITSPEW
|
|
for (uint32_t i = 0; i < slots.length(); i++)
|
|
JitSpew(JitSpew_Safepoints, " gc value: %d", slots[i]);
|
|
#endif
|
|
|
|
MapSlotsToBitset(frameSlots_, argumentSlots_, stream_, slots);
|
|
}
|
|
|
|
#if defined(JS_JITSPEW) && defined(JS_NUNBOX32)
|
|
static void
|
|
DumpNunboxPart(const LAllocation& a)
|
|
{
|
|
Fprinter& out = JitSpewPrinter();
|
|
if (a.isStackSlot()) {
|
|
out.printf("stack %d", a.toStackSlot()->slot());
|
|
} else if (a.isArgument()) {
|
|
out.printf("arg %d", a.toArgument()->index());
|
|
} else {
|
|
out.printf("reg %s", a.toGeneralReg()->reg().name());
|
|
}
|
|
}
|
|
#endif // DEBUG
|
|
|
|
// Nunbox part encoding:
|
|
//
|
|
// Reg = 000
|
|
// Stack = 001
|
|
// Arg = 010
|
|
//
|
|
// [vwu] nentries:
|
|
// uint16_t: tttp ppXX XXXY YYYY
|
|
//
|
|
// If ttt = Reg, type is reg XXXXX
|
|
// If ppp = Reg, payload is reg YYYYY
|
|
//
|
|
// If ttt != Reg, type is:
|
|
// XXXXX if not 11111, otherwise followed by [vwu]
|
|
// If ppp != Reg, payload is:
|
|
// YYYYY if not 11111, otherwise followed by [vwu]
|
|
//
|
|
enum NunboxPartKind {
|
|
Part_Reg,
|
|
Part_Stack,
|
|
Part_Arg
|
|
};
|
|
|
|
static const uint32_t PART_KIND_BITS = 3;
|
|
static const uint32_t PART_KIND_MASK = (1 << PART_KIND_BITS) - 1;
|
|
static const uint32_t PART_INFO_BITS = 5;
|
|
static const uint32_t PART_INFO_MASK = (1 << PART_INFO_BITS) - 1;
|
|
|
|
static const uint32_t MAX_INFO_VALUE = (1 << PART_INFO_BITS) - 1;
|
|
static const uint32_t TYPE_KIND_SHIFT = 16 - PART_KIND_BITS;
|
|
static const uint32_t PAYLOAD_KIND_SHIFT = TYPE_KIND_SHIFT - PART_KIND_BITS;
|
|
static const uint32_t TYPE_INFO_SHIFT = PAYLOAD_KIND_SHIFT - PART_INFO_BITS;
|
|
static const uint32_t PAYLOAD_INFO_SHIFT = TYPE_INFO_SHIFT - PART_INFO_BITS;
|
|
|
|
JS_STATIC_ASSERT(PAYLOAD_INFO_SHIFT == 0);
|
|
|
|
#ifdef JS_NUNBOX32
|
|
static inline NunboxPartKind
|
|
AllocationToPartKind(const LAllocation& a)
|
|
{
|
|
if (a.isRegister())
|
|
return Part_Reg;
|
|
if (a.isStackSlot())
|
|
return Part_Stack;
|
|
MOZ_ASSERT(a.isArgument());
|
|
return Part_Arg;
|
|
}
|
|
|
|
// gcc 4.5 doesn't actually inline CanEncodeInfoInHeader when only
|
|
// using the "inline" keyword, and miscompiles the function as well
|
|
// when doing block reordering with branch prediction information.
|
|
// See bug 799295 comment 71.
|
|
static MOZ_ALWAYS_INLINE bool
|
|
CanEncodeInfoInHeader(const LAllocation& a, uint32_t* out)
|
|
{
|
|
if (a.isGeneralReg()) {
|
|
*out = a.toGeneralReg()->reg().code();
|
|
return true;
|
|
}
|
|
|
|
if (a.isStackSlot())
|
|
*out = a.toStackSlot()->slot();
|
|
else
|
|
*out = a.toArgument()->index();
|
|
|
|
return *out < MAX_INFO_VALUE;
|
|
}
|
|
|
|
void
|
|
SafepointWriter::writeNunboxParts(LSafepoint* safepoint)
|
|
{
|
|
LSafepoint::NunboxList& entries = safepoint->nunboxParts();
|
|
|
|
# ifdef JS_JITSPEW
|
|
if (JitSpewEnabled(JitSpew_Safepoints)) {
|
|
for (uint32_t i = 0; i < entries.length(); i++) {
|
|
SafepointNunboxEntry& entry = entries[i];
|
|
if (entry.type.isUse() || entry.payload.isUse())
|
|
continue;
|
|
JitSpewHeader(JitSpew_Safepoints);
|
|
Fprinter& out = JitSpewPrinter();
|
|
out.printf(" nunbox (type in ");
|
|
DumpNunboxPart(entry.type);
|
|
out.printf(", payload in ");
|
|
DumpNunboxPart(entry.payload);
|
|
out.printf(")\n");
|
|
}
|
|
}
|
|
# endif
|
|
|
|
// Safepoints are permitted to have partially filled in entries for nunboxes,
|
|
// provided that only the type is live and not the payload. Omit these from
|
|
// the written safepoint.
|
|
|
|
size_t pos = stream_.length();
|
|
stream_.writeUnsigned(entries.length());
|
|
|
|
size_t count = 0;
|
|
for (size_t i = 0; i < entries.length(); i++) {
|
|
SafepointNunboxEntry& entry = entries[i];
|
|
|
|
if (entry.payload.isUse()) {
|
|
// No allocation associated with the payload.
|
|
continue;
|
|
}
|
|
|
|
if (entry.type.isUse()) {
|
|
// No allocation associated with the type. Look for another
|
|
// safepoint entry with an allocation for the type.
|
|
entry.type = safepoint->findTypeAllocation(entry.typeVreg);
|
|
if (entry.type.isUse())
|
|
continue;
|
|
}
|
|
|
|
count++;
|
|
|
|
uint16_t header = 0;
|
|
|
|
header |= (AllocationToPartKind(entry.type) << TYPE_KIND_SHIFT);
|
|
header |= (AllocationToPartKind(entry.payload) << PAYLOAD_KIND_SHIFT);
|
|
|
|
uint32_t typeVal;
|
|
bool typeExtra = !CanEncodeInfoInHeader(entry.type, &typeVal);
|
|
if (!typeExtra)
|
|
header |= (typeVal << TYPE_INFO_SHIFT);
|
|
else
|
|
header |= (MAX_INFO_VALUE << TYPE_INFO_SHIFT);
|
|
|
|
uint32_t payloadVal;
|
|
bool payloadExtra = !CanEncodeInfoInHeader(entry.payload, &payloadVal);
|
|
if (!payloadExtra)
|
|
header |= (payloadVal << PAYLOAD_INFO_SHIFT);
|
|
else
|
|
header |= (MAX_INFO_VALUE << PAYLOAD_INFO_SHIFT);
|
|
|
|
stream_.writeFixedUint16_t(header);
|
|
if (typeExtra)
|
|
stream_.writeUnsigned(typeVal);
|
|
if (payloadExtra)
|
|
stream_.writeUnsigned(payloadVal);
|
|
}
|
|
|
|
// Update the stream with the actual number of safepoint entries written.
|
|
stream_.writeUnsignedAt(pos, count, entries.length());
|
|
}
|
|
#endif
|
|
|
|
void
|
|
SafepointWriter::encode(LSafepoint* safepoint)
|
|
{
|
|
uint32_t safepointOffset = startEntry();
|
|
|
|
MOZ_ASSERT(safepoint->osiCallPointOffset());
|
|
|
|
writeOsiCallPointOffset(safepoint->osiCallPointOffset());
|
|
writeGcRegs(safepoint);
|
|
writeGcSlots(safepoint);
|
|
writeValueSlots(safepoint);
|
|
|
|
#ifdef JS_NUNBOX32
|
|
writeNunboxParts(safepoint);
|
|
#endif
|
|
|
|
writeSlotsOrElementsSlots(safepoint);
|
|
|
|
endEntry();
|
|
safepoint->setOffset(safepointOffset);
|
|
}
|
|
|
|
void
|
|
SafepointWriter::endEntry()
|
|
{
|
|
JitSpew(JitSpew_Safepoints, " -- entry ended at %d", uint32_t(stream_.length()));
|
|
}
|
|
|
|
SafepointReader::SafepointReader(IonScript* script, const SafepointIndex* si)
|
|
: stream_(script->safepoints() + si->safepointOffset(),
|
|
script->safepoints() + script->safepointsSize()),
|
|
frameSlots_((script->frameSlots() / sizeof(intptr_t)) + 1), // Stack slot counts are inclusive.
|
|
argumentSlots_(script->argumentSlots() / sizeof(intptr_t))
|
|
{
|
|
osiCallPointOffset_ = stream_.readUnsigned();
|
|
|
|
// gcSpills is a subset of allGprSpills.
|
|
allGprSpills_ = GeneralRegisterSet(ReadRegisterMask(stream_));
|
|
if (allGprSpills_.empty()) {
|
|
gcSpills_ = allGprSpills_;
|
|
valueSpills_ = allGprSpills_;
|
|
slotsOrElementsSpills_ = allGprSpills_;
|
|
} else {
|
|
gcSpills_ = GeneralRegisterSet(ReadRegisterMask(stream_));
|
|
slotsOrElementsSpills_ = GeneralRegisterSet(ReadRegisterMask(stream_));
|
|
#ifdef JS_PUNBOX64
|
|
valueSpills_ = GeneralRegisterSet(ReadRegisterMask(stream_));
|
|
#endif
|
|
}
|
|
allFloatSpills_ = FloatRegisterSet(ReadFloatRegisterMask(stream_));
|
|
|
|
advanceFromGcRegs();
|
|
}
|
|
|
|
uint32_t
|
|
SafepointReader::osiReturnPointOffset() const
|
|
{
|
|
return osiCallPointOffset_ + Assembler::PatchWrite_NearCallSize();
|
|
}
|
|
|
|
CodeLocationLabel
|
|
SafepointReader::InvalidationPatchPoint(IonScript* script, const SafepointIndex* si)
|
|
{
|
|
SafepointReader reader(script, si);
|
|
|
|
return CodeLocationLabel(script->method(), CodeOffset(reader.osiCallPointOffset()));
|
|
}
|
|
|
|
void
|
|
SafepointReader::advanceFromGcRegs()
|
|
{
|
|
currentSlotChunk_ = 0;
|
|
nextSlotChunkNumber_ = 0;
|
|
currentSlotsAreStack_ = true;
|
|
}
|
|
|
|
bool
|
|
SafepointReader::getSlotFromBitmap(SafepointSlotEntry* entry)
|
|
{
|
|
while (currentSlotChunk_ == 0) {
|
|
// Are there any more chunks to read?
|
|
if (currentSlotsAreStack_) {
|
|
if (nextSlotChunkNumber_ == BitSet::RawLengthForBits(frameSlots_)) {
|
|
nextSlotChunkNumber_ = 0;
|
|
currentSlotsAreStack_ = false;
|
|
continue;
|
|
}
|
|
} else if (nextSlotChunkNumber_ == BitSet::RawLengthForBits(argumentSlots_)) {
|
|
return false;
|
|
}
|
|
|
|
// Yes, read the next chunk.
|
|
currentSlotChunk_ = stream_.readUnsigned();
|
|
nextSlotChunkNumber_++;
|
|
}
|
|
|
|
// The current chunk still has bits in it, so get the next bit, then mask
|
|
// it out of the slot chunk.
|
|
uint32_t bit = FloorLog2(currentSlotChunk_);
|
|
currentSlotChunk_ &= ~(1 << bit);
|
|
|
|
// Return the slot, and re-scale it by the pointer size, reversing the
|
|
// transformation in MapSlotsToBitset.
|
|
entry->stack = currentSlotsAreStack_;
|
|
entry->slot = (((nextSlotChunkNumber_ - 1) * BitSet::BitsPerWord) + bit) * sizeof(intptr_t);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
SafepointReader::getGcSlot(SafepointSlotEntry* entry)
|
|
{
|
|
if (getSlotFromBitmap(entry))
|
|
return true;
|
|
advanceFromGcSlots();
|
|
return false;
|
|
}
|
|
|
|
void
|
|
SafepointReader::advanceFromGcSlots()
|
|
{
|
|
// No, reset the counter.
|
|
currentSlotChunk_ = 0;
|
|
nextSlotChunkNumber_ = 0;
|
|
currentSlotsAreStack_ = true;
|
|
}
|
|
|
|
bool
|
|
SafepointReader::getValueSlot(SafepointSlotEntry* entry)
|
|
{
|
|
if (getSlotFromBitmap(entry))
|
|
return true;
|
|
advanceFromValueSlots();
|
|
return false;
|
|
}
|
|
|
|
void
|
|
SafepointReader::advanceFromValueSlots()
|
|
{
|
|
#ifdef JS_NUNBOX32
|
|
nunboxSlotsRemaining_ = stream_.readUnsigned();
|
|
#else
|
|
nunboxSlotsRemaining_ = 0;
|
|
advanceFromNunboxSlots();
|
|
#endif
|
|
}
|
|
|
|
static inline LAllocation
|
|
PartFromStream(CompactBufferReader& stream, NunboxPartKind kind, uint32_t info)
|
|
{
|
|
if (kind == Part_Reg)
|
|
return LGeneralReg(Register::FromCode(info));
|
|
|
|
if (info == MAX_INFO_VALUE)
|
|
info = stream.readUnsigned();
|
|
|
|
if (kind == Part_Stack)
|
|
return LStackSlot(info);
|
|
|
|
MOZ_ASSERT(kind == Part_Arg);
|
|
return LArgument(info);
|
|
}
|
|
|
|
bool
|
|
SafepointReader::getNunboxSlot(LAllocation* type, LAllocation* payload)
|
|
{
|
|
if (!nunboxSlotsRemaining_--) {
|
|
advanceFromNunboxSlots();
|
|
return false;
|
|
}
|
|
|
|
uint16_t header = stream_.readFixedUint16_t();
|
|
NunboxPartKind typeKind = (NunboxPartKind)((header >> TYPE_KIND_SHIFT) & PART_KIND_MASK);
|
|
NunboxPartKind payloadKind = (NunboxPartKind)((header >> PAYLOAD_KIND_SHIFT) & PART_KIND_MASK);
|
|
uint32_t typeInfo = (header >> TYPE_INFO_SHIFT) & PART_INFO_MASK;
|
|
uint32_t payloadInfo = (header >> PAYLOAD_INFO_SHIFT) & PART_INFO_MASK;
|
|
|
|
*type = PartFromStream(stream_, typeKind, typeInfo);
|
|
*payload = PartFromStream(stream_, payloadKind, payloadInfo);
|
|
return true;
|
|
}
|
|
|
|
void
|
|
SafepointReader::advanceFromNunboxSlots()
|
|
{
|
|
slotsOrElementsSlotsRemaining_ = stream_.readUnsigned();
|
|
}
|
|
|
|
bool
|
|
SafepointReader::getSlotsOrElementsSlot(SafepointSlotEntry* entry)
|
|
{
|
|
if (!slotsOrElementsSlotsRemaining_--)
|
|
return false;
|
|
entry->stack = true;
|
|
entry->slot = stream_.readUnsigned();
|
|
return true;
|
|
}
|