Bug 990106 part 3 - Recover RInstructions during bailouts. r=jandem

This commit is contained in:
Nicolas B. Pierron 2014-04-29 10:17:51 -07:00
Родитель a0e1f8e563
Коммит f2ea1dcb9f
10 изменённых файлов: 179 добавлений и 21 удалений

Просмотреть файл

@ -236,7 +236,7 @@ typedef enum JSWhyMagic
JS_BLOCK_NEEDS_CLONE, /* value of static block object slot */
JS_HASH_KEY_EMPTY, /* see class js::HashableValue */
JS_ION_ERROR, /* error while running Ion code */
JS_ION_BAILOUT, /* status code to signal EnterIon will OSR into Interpret */
JS_ION_BAILOUT, /* missing recover instruction result */
JS_OPTIMIZED_OUT, /* optimized out slot */
JS_GENERIC_MAGIC /* for local use */
} JSWhyMagic;

Просмотреть файл

@ -47,7 +47,8 @@ SnapshotIterator::SnapshotIterator(const IonBailoutIterator &iter)
iter.ionScript()->recoversSize()),
fp_(iter.jsFrame()),
machine_(iter.machineState()),
ionScript_(iter.ionScript())
ionScript_(iter.ionScript()),
instructionResults_(nullptr)
{
}

Просмотреть файл

@ -1336,8 +1336,12 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
return BAILOUT_RETURN_FATAL_ERROR;
IonSpew(IonSpew_BaselineBailouts, " Incoming frame ptr = %p", builder.startFrame());
AutoValueVector instructionResults(cx);
SnapshotIterator snapIter(iter);
if (!snapIter.initIntructionResults(instructionResults))
return BAILOUT_RETURN_FATAL_ERROR;
RootedFunction callee(cx, iter.maybeCallee());
RootedScript scr(cx, iter.script());
if (callee) {
@ -1365,7 +1369,12 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIt
jsbytecode *topCallerPC = nullptr;
while (true) {
MOZ_ASSERT(snapIter.instruction()->isResumePoint());
if (!snapIter.instruction()->isResumePoint()) {
if (!snapIter.instruction()->recover(cx, snapIter))
return BAILOUT_RETURN_FATAL_ERROR;
snapIter.nextInstruction();
continue;
}
if (frameNo > 0) {
TraceLogStartEvent(logger, TraceLogCreateTextId(logger, scr));

Просмотреть файл

@ -1338,7 +1338,8 @@ SnapshotIterator::SnapshotIterator(IonScript *ionScript, SnapshotOffset snapshot
ionScript->recoversSize()),
fp_(fp),
machine_(machine),
ionScript_(ionScript)
ionScript_(ionScript),
instructionResults_(nullptr)
{
JS_ASSERT(snapshotOffset < ionScript->snapshotsListSize());
}
@ -1353,7 +1354,8 @@ SnapshotIterator::SnapshotIterator(const JitFrameIterator &iter)
iter.ionScript()->recoversSize()),
fp_(iter.jsFrame()),
machine_(iter.machineState()),
ionScript_(iter.ionScript())
ionScript_(iter.ionScript()),
instructionResults_(nullptr)
{
}
@ -1361,7 +1363,8 @@ SnapshotIterator::SnapshotIterator()
: snapshot_(nullptr, 0, 0, 0),
recover_(snapshot_, nullptr, 0),
fp_(nullptr),
ionScript_(nullptr)
ionScript_(nullptr),
instructionResults_(nullptr)
{
}
@ -1426,6 +1429,9 @@ SnapshotIterator::allocationReadable(const RValueAllocation &alloc)
return hasStack(alloc.stackOffset());
#endif
case RValueAllocation::RECOVER_INSTRUCTION:
return hasInstructionResult(alloc.index());
default:
return true;
}
@ -1531,6 +1537,9 @@ SnapshotIterator::allocationValue(const RValueAllocation &alloc)
}
#endif
case RValueAllocation::RECOVER_INSTRUCTION:
return fromInstructionResult(alloc.index());
default:
MOZ_ASSUME_UNREACHABLE("huh?");
}
@ -1545,7 +1554,7 @@ SnapshotIterator::resumePoint() const
uint32_t
SnapshotIterator::numAllocations() const
{
return resumePoint()->numOperands();
return instruction()->numOperands();
}
uint32_t
@ -1564,6 +1573,43 @@ SnapshotIterator::skipInstruction()
nextInstruction();
}
bool
SnapshotIterator::initIntructionResults(AutoValueVector &results)
{
MOZ_ASSERT(recover_.numInstructionsRead() == 1);
// The last instruction will always be a resume point, no need to allocate
// space for it.
if (recover_.numInstructions() == 1)
return true;
MOZ_ASSERT(recover_.numInstructions() > 1);
size_t numResults = recover_.numInstructions() - 1;
if (!results.reserve(numResults))
return false;
for (size_t i = 0; i < numResults; i++)
results.infallibleAppend(MagicValue(JS_ION_BAILOUT));
instructionResults_ = &results;
return true;
}
void
SnapshotIterator::storeInstructionResult(Value v)
{
uint32_t currIns = recover_.numInstructionsRead() - 1;
MOZ_ASSERT((*instructionResults_)[currIns].isMagic(JS_ION_BAILOUT));
(*instructionResults_)[currIns].set(v);
}
Value
SnapshotIterator::fromInstructionResult(uint32_t index) const
{
MOZ_ASSERT(!(*instructionResults_)[index].isMagic(JS_ION_BAILOUT));
return (*instructionResults_)[index];
}
void
SnapshotIterator::nextFrame()
{

Просмотреть файл

@ -258,6 +258,7 @@ class SnapshotIterator
IonJSFrameLayout *fp_;
MachineState machine_;
IonScript *ionScript_;
AutoValueVector *instructionResults_;
private:
// Read a spilled register from the machine state.
@ -281,6 +282,11 @@ class SnapshotIterator
}
uintptr_t fromStack(int32_t offset) const;
bool hasInstructionResult(uint32_t index) const {
return instructionResults_;
}
Value fromInstructionResult(uint32_t index) const;
Value allocationValue(const RValueAllocation &a);
bool allocationReadable(const RValueAllocation &a);
void warnUnreadableAllocation();
@ -338,6 +344,14 @@ class SnapshotIterator
return recover_.moreInstructions();
}
// Register a vector used for storing the results of the evaluation of
// recover instructions. This vector should be registered before the
// beginning of the iteration. This function is in charge of allocating
// enough space for all instructions results, and return false iff it fails.
bool initIntructionResults(AutoValueVector &results);
void storeInstructionResult(Value v);
public:
// Handle iterating over frames of the snapshots.
void nextFrame();

Просмотреть файл

@ -6,7 +6,10 @@
#include "jit/Recover.h"
#include "jscntxt.h"
#include "jit/IonSpewer.h"
#include "jit/JitFrameIterator.h"
#include "jit/MIR.h"
#include "jit/MIRGraph.h"
@ -25,9 +28,17 @@ RInstruction::readRecoverData(CompactBufferReader &reader, RInstructionStorage *
{
uint32_t op = reader.readUnsigned();
switch (Opcode(op)) {
case Recover_ResumePoint:
new (raw->addr()) RResumePoint(reader);
# define MATCH_OPCODES_(op) \
case Recover_##op: \
static_assert(sizeof(R##op) <= sizeof(RInstructionStorage), \
"Storage space is too small to decode R" #op " instructions."); \
new (raw->addr()) R##op(reader); \
break;
RECOVER_OPCODE_LIST(MATCH_OPCODES_)
# undef DEFINE_OPCODES_
case Recover_Invalid:
default:
MOZ_ASSUME_UNREACHABLE("Bad decoding of the previous instruction?");
break;
@ -108,10 +119,14 @@ MResumePoint::writeRecoverData(CompactBufferWriter &writer) const
RResumePoint::RResumePoint(CompactBufferReader &reader)
{
static_assert(sizeof(*this) <= sizeof(RInstructionStorage),
"Storage space is too small to decode this recover instruction.");
pcOffset_ = reader.readUnsigned();
numOperands_ = reader.readUnsigned();
IonSpew(IonSpew_Snapshots, "Read RResumePoint (pc offset %u, nslots %u)",
pcOffset_, numOperands_);
}
bool
RResumePoint::recover(JSContext *cx, SnapshotIterator &iter) const
{
MOZ_ASSUME_UNREACHABLE("This instruction is not recoverable.");
}

Просмотреть файл

@ -11,44 +11,70 @@
#include "jit/Snapshots.h"
class JSContext;
namespace js {
namespace jit {
#define RECOVER_OPCODE_LIST(_) \
_(ResumePoint)
class RResumePoint;
class SnapshotIterator;
class RInstruction
{
public:
enum Opcode
{
Recover_ResumePoint = 0
# define DEFINE_OPCODES_(op) Recover_##op,
RECOVER_OPCODE_LIST(DEFINE_OPCODES_)
# undef DEFINE_OPCODES_
Recover_Invalid
};
virtual Opcode opcode() const = 0;
// As opposed to the MIR, there is no need to add more methods as every
// other instruction is well abstracted under the "recover" method.
bool isResumePoint() const {
return opcode() == Recover_ResumePoint;
}
inline const RResumePoint *toResumePoint() const;
// Number of allocations which are encoded in the Snapshot for recovering
// the current instruction.
virtual uint32_t numOperands() const = 0;
// Function used to recover the value computed by this instruction. This
// function reads its arguments from the allocations listed on the snapshot
// iterator and stores its returned value on the snapshot iterator too.
virtual bool recover(JSContext *cx, SnapshotIterator &iter) const = 0;
// Decode an RInstruction on top of the reserved storage space, based on the
// tag written by the writeRecoverData function of the corresponding MIR
// instruction.
static void readRecoverData(CompactBufferReader &reader, RInstructionStorage *raw);
};
#define RINSTRUCTION_HEADER_(op) \
private: \
friend class RInstruction; \
R##op(CompactBufferReader &reader); \
\
public: \
Opcode opcode() const { \
return RInstruction::Recover_##op; \
}
class RResumePoint MOZ_FINAL : public RInstruction
{
private:
uint32_t pcOffset_; // Offset from script->code.
uint32_t numOperands_; // Number of slots.
friend class RInstruction;
RResumePoint(CompactBufferReader &reader);
public:
virtual Opcode opcode() const {
return Recover_ResumePoint;
}
RINSTRUCTION_HEADER_(ResumePoint)
uint32_t pcOffset() const {
return pcOffset_;
@ -56,8 +82,11 @@ class RResumePoint MOZ_FINAL : public RInstruction
virtual uint32_t numOperands() const {
return numOperands_;
}
bool recover(JSContext *cx, SnapshotIterator &iter) const;
};
#undef RINSTRUCTION_HEADER_
const RResumePoint *
RInstruction::toResumePoint() const
{

Просмотреть файл

@ -76,6 +76,9 @@ using namespace js::jit;
// first register/stack-offset correspond to the holder of the type,
// and the second correspond to the payload of the JS Value.
//
// RECOVER_INSTRUCTION [INDEX]
// Index into the list of recovered instruction results.
//
// TYPED_REG [PACKED_TAG, GPR_REG]:
// Value with statically known type, which payload is stored in a
// register.
@ -219,6 +222,15 @@ RValueAllocation::layoutFromMode(Mode mode)
return layout;
}
#endif
case RECOVER_INSTRUCTION: {
static const RValueAllocation::Layout layout = {
PAYLOAD_INDEX,
PAYLOAD_NONE,
"instruction"
};
return layout;
}
default: {
static const RValueAllocation::Layout regLayout = {
PAYLOAD_PACKED_TAG,

Просмотреть файл

@ -54,6 +54,8 @@ class RValueAllocation
UNTYPED_REG = 0x06,
UNTYPED_STACK = 0x07,
#endif
RECOVER_INSTRUCTION = 0x0a,
// The JSValueType is packed in the Mode.
TYPED_REG_MIN = 0x10,
TYPED_REG_MAX = 0x17,
@ -236,6 +238,11 @@ class RValueAllocation
return RValueAllocation(CONSTANT, payloadOfIndex(index));
}
// Recover instruction's index
static RValueAllocation RecoverInstruction(uint32_t index) {
return RValueAllocation(RECOVER_INSTRUCTION, payloadOfIndex(index));
}
void writeHeader(CompactBufferWriter &writer, JSValueType type, uint32_t regCode) const;
public:
static RValueAllocation read(CompactBufferReader &reader);
@ -473,6 +480,13 @@ class RecoverReader
public:
RecoverReader(SnapshotReader &snapshot, const uint8_t *recovers, uint32_t size);
uint32_t numInstructions() const {
return numInstructions_;
}
uint32_t numInstructionsRead() const {
return numInstructionsRead_;
}
bool moreInstructions() const {
return numInstructionsRead_ < numInstructions_;
}

Просмотреть файл

@ -143,13 +143,31 @@ CodeGeneratorShared::encodeAllocation(LSnapshot *snapshot, MDefinition *mir,
if (mir->isBox())
mir = mir->toBox()->getOperand(0);
MIRType type = mir->isUnused()
? MIRType_MagicOptimizedOut
: mir->type();
MIRType type =
mir->isRecoveredOnBailout() ? MIRType_None :
mir->isUnused() ? MIRType_MagicOptimizedOut :
mir->type();
RValueAllocation alloc;
switch (type) {
case MIRType_None:
{
MOZ_ASSERT(mir->isRecoveredOnBailout());
uint32_t index = 0;
LRecoverInfo *recoverInfo = snapshot->recoverInfo();
MNode **it = recoverInfo->begin(), **end = recoverInfo->end();
while (it != end && mir != *it) {
++it;
++index;
}
// This MDefinition is recovered, thus it should be listed in the
// LRecoverInfo.
MOZ_ASSERT(it != end && mir == *it);
alloc = RValueAllocation::RecoverInstruction(index);
break;
}
case MIRType_Undefined:
alloc = RValueAllocation::Undefined();
break;