Bug 1261826 part 8 - Add JSOP_JUMPTARGET opcode. r=jandem,jorendorff,shu

This commit is contained in:
Nicolas B. Pierron 2016-05-17 17:15:52 +00:00
Родитель d02bd6369a
Коммит e037534d4b
11 изменённых файлов: 147 добавлений и 22 удалений

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

@ -331,6 +331,8 @@ bool
BytecodeEmitter::emitJumpTarget(JumpTarget* target)
{
target->offset = offset();
if (!emit1(JSOP_JUMPTARGET))
return false;
return true;
}
@ -402,6 +404,8 @@ BytecodeEmitter::patchJumpsToTarget(JumpList jump, JumpTarget target)
{
MOZ_ASSERT(-1 <= jump.offset && jump.offset <= offset());
MOZ_ASSERT(0 <= target.offset && target.offset <= offset());
MOZ_ASSERT_IF(jump.offset != -1 && target.offset + 4 <= offset(),
BytecodeIsJumpTarget(JSOp(*code(target.offset))));
jump.patchAll(code(0), target);
}

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

@ -1260,12 +1260,16 @@ BaselineCompiler::emit_JSOP_POS()
bool
BaselineCompiler::emit_JSOP_LOOPHEAD()
{
if (!emit_JSOP_JUMPTARGET())
return false;
return emitInterruptCheck();
}
bool
BaselineCompiler::emit_JSOP_LOOPENTRY()
{
if (!emit_JSOP_JUMPTARGET())
return false;
frame.syncStack(0);
return emitWarmUpCounterIncrement(LoopEntryCanIonOsr(pc));
}
@ -3345,6 +3349,9 @@ BaselineCompiler::emit_JSOP_THROWING()
bool
BaselineCompiler::emit_JSOP_TRY()
{
if (!emit_JSOP_JUMPTARGET())
return false;
// Ionmonkey can't inline function with JSOP_TRY.
script->setUninlineable();
return true;
@ -3776,6 +3783,8 @@ BaselineCompiler::emit_JSOP_ISNOITER()
bool
BaselineCompiler::emit_JSOP_ENDITER()
{
if (!emit_JSOP_JUMPTARGET())
return false;
frame.popRegsAndSync(1);
ICIteratorClose_Fallback::Compiler compiler(cx);
@ -4326,3 +4335,9 @@ BaselineCompiler::emit_JSOP_DEBUGCHECKSELFHOSTED()
return true;
}
bool
BaselineCompiler::emit_JSOP_JUMPTARGET()
{
return true;
}

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

@ -223,7 +223,8 @@ namespace jit {
_(JSOP_INITHIDDENELEM_GETTER) \
_(JSOP_INITHIDDENELEM_SETTER) \
_(JSOP_CHECKOBJCOERCIBLE) \
_(JSOP_DEBUGCHECKSELFHOSTED)
_(JSOP_DEBUGCHECKSELFHOSTED) \
_(JSOP_JUMPTARGET)
class BaselineCompiler : public BaselineCompilerSpecific
{

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

@ -1645,6 +1645,7 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_NOP_DESTRUCTURING:
case JSOP_LINENO:
case JSOP_LOOPENTRY:
case JSOP_JUMPTARGET:
return true;
case JSOP_LABEL:
@ -3006,7 +3007,8 @@ IonBuilder::processContinue(JSOp op)
CFGState* found = nullptr;
jsbytecode* target = pc + GetJumpOffset(pc);
for (size_t i = loops_.length() - 1; i < loops_.length(); i--) {
if (loops_[i].continuepc == target ||
// +1 to skip JSOP_JUMPTARGET.
if (loops_[i].continuepc == target + 1 ||
EffectiveContinue(loops_[i].continuepc) == target)
{
found = &cfgStack_[loops_[i].cfgEntry];
@ -4071,7 +4073,8 @@ IonBuilder::jsop_condswitch()
jssrcnote* caseSn = info().getNote(gsn, curCase);
MOZ_ASSERT(caseSn && SN_TYPE(caseSn) == SRC_NEXTCASE);
ptrdiff_t off = GetSrcNoteOffset(caseSn, 0);
curCase = off ? curCase + off : GetNextPc(curCase);
MOZ_ASSERT_IF(off == 0, JSOp(*GetNextPc(curCase)) == JSOP_JUMPTARGET);
curCase = off ? curCase + off : GetNextPc(GetNextPc(curCase));
MOZ_ASSERT(pc < curCase && curCase <= exitpc);
// Count non-aliased cases.
@ -4151,7 +4154,8 @@ IonBuilder::processCondSwitchCase(CFGState& state)
// Fetch the following case in which we will continue.
jssrcnote* sn = info().getNote(gsn, pc);
ptrdiff_t off = GetSrcNoteOffset(sn, 0);
jsbytecode* casePc = off ? pc + off : GetNextPc(pc);
MOZ_ASSERT_IF(off == 0, JSOp(*GetNextPc(pc)) == JSOP_JUMPTARGET);
jsbytecode* casePc = off ? pc + off : GetNextPc(GetNextPc(pc));
bool caseIsDefault = JSOp(*casePc) == JSOP_DEFAULT;
MOZ_ASSERT(JSOp(*casePc) == JSOP_CASE || caseIsDefault);

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

@ -529,6 +529,14 @@ BytecodeParser::parse()
continue;
}
// On a jump target, we reload the offsetStack saved for the current
// bytecode, as it contains either the original offset stack, or the
// merged offset stack.
if (BytecodeIsJumpTarget(op)) {
for (uint32_t n = 0; n < code->stackDepth; ++n)
offsetStack[n] = code->offsetStack[n];
}
if (code->parsed) {
// No need to reparse.
continue;

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

@ -431,6 +431,21 @@ BytecodeFallsThrough(JSOp op)
}
}
static inline bool
BytecodeIsJumpTarget(JSOp op)
{
switch (op) {
case JSOP_JUMPTARGET:
case JSOP_LOOPHEAD:
case JSOP_LOOPENTRY:
case JSOP_ENDITER:
case JSOP_TRY:
return true;
default:
return false;
}
}
class SrcNoteLineScanner
{
/* offset of the current JSOp in the bytecode */

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

@ -3072,11 +3072,13 @@ JSScript::assertValidJumpTargets() const
if (IsJumpOpcode(JSOp(*pc))) {
jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
MOZ_ASSERT(mainEntry <= target && target < end);
MOZ_ASSERT(BytecodeIsJumpTarget(JSOp(*target)));
// Check fallthrough of conditional jump instructions.
if (BytecodeFallsThrough(JSOp(*pc))) {
jsbytecode* fallthrough = GetNextPc(pc);
MOZ_ASSERT(mainEntry <= fallthrough && fallthrough < end);
MOZ_ASSERT(BytecodeIsJumpTarget(JSOp(*fallthrough)));
}
}
@ -3087,6 +3089,7 @@ JSScript::assertValidJumpTargets() const
// Default target.
MOZ_ASSERT(mainEntry <= pc + len && pc + len < end);
MOZ_ASSERT(BytecodeIsJumpTarget(JSOp(*(pc + len))));
pc2 += JUMP_OFFSET_LEN;
int32_t low = GET_JUMP_OFFSET(pc2);
@ -3098,6 +3101,7 @@ JSScript::assertValidJumpTargets() const
int32_t off = (int32_t) GET_JUMP_OFFSET(pc2);
// Case (i + low)
MOZ_ASSERT_IF(off, mainEntry <= pc + off && pc + off < end);
MOZ_ASSERT_IF(off, BytecodeIsJumpTarget(JSOp(*(pc + off))));
}
}
}
@ -3114,6 +3118,7 @@ JSScript::assertValidJumpTargets() const
jsbytecode* tryTarget = tryStart + tn->length;
MOZ_ASSERT(mainEntry <= tryTarget && tryTarget < end);
MOZ_ASSERT(BytecodeIsJumpTarget(JSOp(*tryTarget)));
}
}
}

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

@ -252,19 +252,20 @@ LCovSource::writeScript(JSScript* script)
// Get the low and high from the tableswitch
int32_t low = GET_JUMP_OFFSET(pc + JUMP_OFFSET_LEN * 1);
int32_t high = GET_JUMP_OFFSET(pc + JUMP_OFFSET_LEN * 2);
int32_t numCases = high - low + 1;
MOZ_ASSERT(high > low);
size_t numCases = high - low + 1;
jsbytecode* jumpTable = pc + JUMP_OFFSET_LEN * 3;
jsbytecode* firstcasepc = exitpc;
for (int j = 0; j < numCases; j++) {
for (size_t j = 0; j < numCases; j++) {
jsbytecode* testpc = pc + GET_JUMP_OFFSET(jumpTable + JUMP_OFFSET_LEN * j);
if (testpc < firstcasepc)
firstcasepc = testpc;
}
jsbytecode* lastcasepc = firstcasepc;
uint64_t allCaseHits = 0;
for (int i = 0; i < numCases; i++) {
uint64_t fallsThroughHits = 0;
for (size_t i = 0; i < numCases; i++) {
jsbytecode* casepc = pc + GET_JUMP_OFFSET(jumpTable + JUMP_OFFSET_LEN * i);
// The case is not present, and jumps to the default pc if used.
if (casepc == pc)
@ -272,7 +273,7 @@ LCovSource::writeScript(JSScript* script)
// PCs might not be in increasing order of case indexes.
lastcasepc = firstcasepc - 1;
for (int j = 0; j < numCases; j++) {
for (size_t j = 0; j < numCases; j++) {
jsbytecode* testpc = pc + GET_JUMP_OFFSET(jumpTable + JUMP_OFFSET_LEN * j);
if (lastcasepc < testpc && testpc < casepc)
lastcasepc = testpc;
@ -287,16 +288,17 @@ LCovSource::writeScript(JSScript* script)
caseHits = counts->numExec();
// Remove fallthrough.
fallsThroughHits = 0;
if (casepc != firstcasepc) {
jsbytecode* endpc = lastcasepc;
while (GetNextPc(endpc) < casepc)
endpc = GetNextPc(endpc);
if (BytecodeFallsThrough(JSOp(*endpc)))
caseHits -= script->getHitCount(endpc);
fallsThroughHits = script->getHitCount(endpc);
}
allCaseHits += caseHits;
caseHits -= fallsThroughHits;
}
outBRDA_.printf("BRDA:%d,%d,%d,", lineno, branchId, i);
@ -315,14 +317,30 @@ LCovSource::writeScript(JSScript* script)
uint64_t defaultHits = 0;
if (sc) {
// Look for the last case entry before the default pc.
lastcasepc = firstcasepc - 1;
for (size_t j = 0; j < numCases; j++) {
jsbytecode* testpc = pc + GET_JUMP_OFFSET(jumpTable + JUMP_OFFSET_LEN * j);
if (lastcasepc < testpc && testpc < defaultpc)
lastcasepc = testpc;
}
// Look if the last case entry fallthrough to the default case,
// in which case we have to remove the number of fallthrough
// hits out of the default case hits.
if (lastcasepc != pc) {
jsbytecode* endpc = lastcasepc;
while (GetNextPc(endpc) < defaultpc)
endpc = GetNextPc(endpc);
if (BytecodeFallsThrough(JSOp(*endpc)))
fallsThroughHits = script->getHitCount(endpc);
}
const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(defaultpc));
if (counts)
defaultHits = counts->numExec();
// Note: currently we do not track edges, so we might have
// false-positive if we have any throw / return inside some
// of the case statements.
defaultHits -= allCaseHits;
defaultHits -= fallsThroughHits;
}
outBRDA_.printf("BRDA:%d,%d,%d,", lineno, branchId, numCases);

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

@ -5398,14 +5398,19 @@ class BytecodeRangeWithPosition : private BytecodeRange
BytecodeRangeWithPosition(JSContext* cx, JSScript* script)
: BytecodeRange(cx, script), lineno(script->lineno()), column(0),
sn(script->notes()), snpc(script->code()), isEntryPoint(false)
sn(script->notes()), snpc(script->code()), isEntryPoint(false),
wasArtifactEntryPoint(false)
{
if (!SN_IS_TERMINATOR(sn))
snpc += SN_DELTA(sn);
updatePosition();
while (frontPC() != script->main())
popFront();
isEntryPoint = true;
if (frontOpcode() != JSOP_JUMPTARGET)
isEntryPoint = true;
else
wasArtifactEntryPoint = true;
}
void popFront() {
@ -5414,6 +5419,19 @@ class BytecodeRangeWithPosition : private BytecodeRange
isEntryPoint = false;
else
updatePosition();
// The following conditions are handling artifacts introduced by the
// bytecode emitter, such that we do not add breakpoints on empty
// statements of the source code of the user.
if (wasArtifactEntryPoint) {
wasArtifactEntryPoint = false;
isEntryPoint = true;
}
if (isEntryPoint && frontOpcode() == JSOP_JUMPTARGET) {
wasArtifactEntryPoint = isEntryPoint;
isEntryPoint = false;
}
}
size_t frontLineNumber() const { return lineno; }
@ -5463,6 +5481,7 @@ class BytecodeRangeWithPosition : private BytecodeRange
jssrcnote* sn;
jsbytecode* snpc;
bool isEntryPoint;
bool wasArtifactEntryPoint;
};
/*
@ -5579,13 +5598,23 @@ class FlowGraphSummary {
size_t prevColumn = 0;
JSOp prevOp = JSOP_NOP;
for (BytecodeRangeWithPosition r(cx, script); !r.empty(); r.popFront()) {
size_t lineno = r.frontLineNumber();
size_t column = r.frontColumnNumber();
size_t lineno = prevLineno;
size_t column = prevColumn;
JSOp op = r.frontOpcode();
if (FlowsIntoNext(prevOp))
addEdge(prevLineno, prevColumn, r.frontOffset());
if (BytecodeIsJumpTarget(op)) {
lineno = entries_[r.frontOffset()].lineno();
column = entries_[r.frontOffset()].column();
}
if (r.frontIsEntryPoint()) {
lineno = r.frontLineNumber();
column = r.frontColumnNumber();
}
if (CodeSpec[op].type() == JOF_JUMP) {
addEdge(lineno, column, r.frontOffset() + GET_JUMP_OFFSET(r.frontPC()));
} else if (op == JSOP_TABLESWITCH) {
@ -5606,6 +5635,22 @@ class FlowGraphSummary {
addEdge(lineno, column, target);
pc += step;
}
} else if (op == JSOP_TRY) {
// As there is no literal incoming edge into the catch block, we
// make a fake one by copying the JSOP_TRY location, as-if this
// was an incoming edge of the catch block. This is needed
// because we only report offsets of entry points which have
// valid incoming edges.
JSTryNote* tn = script->trynotes()->vector;
JSTryNote* tnlimit = tn + script->trynotes()->length;
for (; tn < tnlimit; tn++) {
uint32_t startOffset = script->mainOffset() + tn->start;
if (startOffset == r.frontOffset() + 1) {
uint32_t catchOffset = startOffset + tn->length;
if (tn->kind == JSTRY_CATCH || tn->kind == JSTRY_FINALLY)
addEdge(lineno, column, catchOffset);
}
}
}
prevLineno = lineno;

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

@ -1811,6 +1811,7 @@ CASE(JSOP_UNUSED222)
CASE(JSOP_UNUSED223)
CASE(JSOP_CONDSWITCH)
CASE(JSOP_TRY)
CASE(JSOP_JUMPTARGET)
{
MOZ_ASSERT(CodeSpec[*REGS.pc].length == 1);
ADVANCE_AND_DISPATCH(1);

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

@ -2180,14 +2180,23 @@
* Operands:
* Stack: =>
*/ \
macro(JSOP_NOP_DESTRUCTURING, 229, "nop-destructuring", NULL, 1, 0, 0, JOF_BYTE)
macro(JSOP_NOP_DESTRUCTURING, 229, "nop-destructuring", NULL, 1, 0, 0, JOF_BYTE) \
/*
* This opcode is a no-op and it indicates the location of a jump
* instruction target. Some other opcodes act as jump targets, such as
* LOOPENTRY, as well as all which are matched by BytecodeIsJumpTarget
* function.
* Category: Other
* Operands:
* Stack: =>
*/ \
macro(JSOP_JUMPTARGET, 230, "jumptarget", NULL, 1, 0, 0, JOF_BYTE)
/*
* In certain circumstances it may be useful to "pad out" the opcode space to
* a power of two. Use this macro to do so.
*/
#define FOR_EACH_TRAILING_UNUSED_OPCODE(macro) \
macro(230) \
macro(231) \
macro(232) \
macro(233) \