Bug 1213732 part 3 - SM LCov: Add code coverage support for TableSwitch statements. r=bhackett

This commit is contained in:
Nicolas B. Pierron 2015-11-25 16:56:13 +00:00
Родитель 95b6d67041
Коммит ba81d44fff
4 изменённых файлов: 341 добавлений и 1 удалений

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

@ -249,5 +249,130 @@ checkLcov(function () { //FN:$,top-level //FNDA:1,%
//BRH:0
});
// Test TableSwitch opcode
checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,- //BRDA:$,0,2,1 //BRDA:$,0,3,- //BRDA:$,0,4,-
case 0:
l.push('0'); //DA:$,0
break;
case 1:
l.push('1'); //DA:$,0
break;
case 2:
l.push('2'); //DA:$,1
break;
case 3:
l.push('3'); //DA:$,0
break;
}
l.pop(); //DA:$,1
//FNF:1
//FNH:1
//LF:7
//LH:4
//BRF:5
//BRH:1
});
checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,- //BRDA:$,0,2,1 //BRDA:$,0,3,- //BRDA:$,0,4,-
case 0:
l.push('0'); //DA:$,0
case 1:
l.push('1'); //DA:$,0
case 2:
l.push('2'); //DA:$,1
case 3:
l.push('3'); //DA:$,1
}
l.pop(); //DA:$,1
//FNF:1
//FNH:1
//LF:7
//LH:5
//BRF:5
//BRH:1
});
checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
// Branches are ordered, and starting at 0
switch (l.length) { //DA:$,1 //BRDA:$,0,0,1 //BRDA:$,0,1,- //BRDA:$,0,2,- //BRDA:$,0,3,- //BRDA:$,0,4,-
case 5:
l.push('5'); //DA:$,0
case 4:
l.push('4'); //DA:$,0
case 3:
l.push('3'); //DA:$,0
case 2:
l.push('2'); //DA:$,1
}
l.pop(); //DA:$,1
//FNF:1
//FNH:1
//LF:7
//LH:4
//BRF:5
//BRH:1
});
checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1 //BRDA:$,0,0,1 //BRDA:$,0,3,- //BRDA:$,0,4,-
case 2:
l.push('2'); //DA:$,1
case 5:
l.push('5'); //DA:$,1
}
l.pop(); //DA:$,1
//FNF:1
//FNH:1
//LF:5
//LH:5
//BRF:3
//BRH:1
});
checkLcov(function () { //FN:$,top-level //FNDA:1,%
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,2,- //BRDA:$,0,3,1
case 3:
l.push('1'); //DA:$,0
case 5:
l.push('5'); //DA:$,0
}
l.pop(); //DA:$,1
//FNF:1
//FNH:1
//LF:5
//LH:3
//BRF:3
//BRH:1
});
// Unfortunately the differences between switch implementations leaks in the
// code coverage reports.
checkLcov(function () { //FN:$,top-level //FNDA:1,%
function f(a) { //FN:$,f //FNDA:2,%
return a; //DA:$,2
}
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1
case f(-42): //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,1
l.push('1'); //DA:$,0
case f(51): //DA:$,1 //BRDA:$,1,0,- //BRDA:$,1,1,1
l.push('5'); //DA:$,0
}
l.pop(); //DA:$,1
//FNF:2
//FNH:2
//LF:8
//LH:6
//BRF:4
//BRH:2
});
// If you add a test case here, do the same in
// jit-test/tests/debug/Script-getOffsetsCoverage-01.js

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

@ -251,5 +251,94 @@ checkGetOffsetsCoverage(function () { //FN:$,top-level
}
});
// Test TableSwitch opcode
checkGetOffsetsCoverage(function () { //FN:$,top-level
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1
case 0:
l.push('0'); //DA:$,0
break;
case 1:
l.push('1'); //DA:$,0
break;
case 2:
l.push('2'); //DA:$,1
break;
case 3:
l.push('3'); //DA:$,0
break;
}
l.pop(); //DA:$,1
});
checkGetOffsetsCoverage(function () { //FN:$,top-level
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1
case 0:
l.push('0'); //DA:$,0
case 1:
l.push('1'); //DA:$,0
case 2:
l.push('2'); //DA:$,1
case 3:
l.push('3'); //DA:$,1
}
l.pop(); //DA:$,1
});
checkGetOffsetsCoverage(function () { //FN:$,top-level
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1
case 5:
l.push('5'); //DA:$,0
case 4:
l.push('4'); //DA:$,0
case 3:
l.push('3'); //DA:$,0
case 2:
l.push('2'); //DA:$,1
}
l.pop(); //DA:$,1
});
checkGetOffsetsCoverage(function () { //FN:$,top-level
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1
case 2:
l.push('2'); //DA:$,1
case 5:
l.push('5'); //DA:$,1
}
l.pop(); //DA:$,1
});
checkGetOffsetsCoverage(function () { //FN:$,top-level
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1
case 3:
l.push('1'); //DA:$,0
case 5:
l.push('5'); //DA:$,0
}
l.pop(); //DA:$,1
});
// Unfortunately the differences between switch implementations leaks in the
// code coverage reports.
checkGetOffsetsCoverage(function () { //FN:$,top-level
function f(a) { //FN:$,f
return a; //DA:$,2
}
var l = ",".split(','); //DA:$,1
switch (l.length) { //DA:$,1
case f(-42): //DA:$,1
l.push('1'); //DA:$,0
case f(51): //DA:$,1
l.push('5'); //DA:$,0
}
l.pop(); //DA:$,1
});
// If you add a test case here, do the same in
// jit-test/tests/coverage/simple.js

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

@ -1340,6 +1340,30 @@ JSScript::initScriptCounts(JSContext* cx)
return false;
}
}
if (JSOp(*pc) == JSOP_TABLESWITCH) {
jsbytecode* pc2 = pc;
int32_t len = GET_JUMP_OFFSET(pc2);
// Default target.
if (!jumpTargets.append(pc + len))
return false;
pc2 += JUMP_OFFSET_LEN;
int32_t low = GET_JUMP_OFFSET(pc2);
pc2 += JUMP_OFFSET_LEN;
int32_t high = GET_JUMP_OFFSET(pc2);
for (int i = 0; i < high-low+1; i++) {
pc2 += JUMP_OFFSET_LEN;
int32_t off = (int32_t) GET_JUMP_OFFSET(pc2);
if (off) {
// Case (i + low)
if (!jumpTargets.append(pc + off))
return false;
}
}
}
}
// Mark catch/finally blocks as being jump targets.

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

@ -228,9 +228,10 @@ LCovSource::writeScript(JSScript* script)
size_t lineno = script->lineno();
jsbytecode* end = script->codeEnd();
size_t branchId = 0;
size_t tableswitchExitOffset = 0;
for (jsbytecode* pc = script->code(); pc != end; pc = GetNextPc(pc)) {
JSOp op = JSOp(*pc);
bool jump = IsJumpOpcode(op);
bool jump = IsJumpOpcode(op) || op == JSOP_TABLESWITCH;
bool fallsthrough = BytecodeFallsThrough(op) && op != JSOP_GOSUB;
// If the current script & pc has a hit-count report, then update the
@ -251,6 +252,8 @@ LCovSource::writeScript(JSScript* script)
lineno = size_t(GetSrcNoteOffset(sn, 0));
else if (type == SRC_NEWLINE)
lineno++;
else if (type == SRC_TABLESWITCH)
tableswitchExitOffset = GetSrcNoteOffset(sn, 0);
sn = SN_NEXT(sn);
snpc += SN_DELTA(sn);
@ -304,6 +307,105 @@ LCovSource::writeScript(JSScript* script)
numBranchesHit_ += !!taken + !!fallthroughHits;
branchId++;
}
// If the current pc corresponds to a pre-computed switch case, then
// reports branch hits for each case statement.
if (jump && op == JSOP_TABLESWITCH) {
MOZ_ASSERT(tableswitchExitOffset != 0);
// Get the default and exit pc
jsbytecode* exitpc = pc + tableswitchExitOffset;
jsbytecode* defaultpc = pc + GET_JUMP_OFFSET(pc);
MOZ_ASSERT(defaultpc > pc && defaultpc <= exitpc);
// 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;
jsbytecode* jumpTable = pc + JUMP_OFFSET_LEN * 3;
jsbytecode* firstcasepc = exitpc;
for (int 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++) {
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)
continue;
// PCs might not be in increasing order of case indexes.
lastcasepc = firstcasepc - 1;
for (int j = 0; j < numCases; j++) {
jsbytecode* testpc = pc + GET_JUMP_OFFSET(jumpTable + JUMP_OFFSET_LEN * j);
if (lastcasepc < testpc && testpc < casepc)
lastcasepc = testpc;
}
if (casepc != lastcasepc) {
// Case (i + low)
uint64_t caseHits = 0;
if (sc) {
const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(casepc));
if (counts)
caseHits = counts->numExec();
// Remove fallthrough.
if (casepc != firstcasepc) {
jsbytecode* endpc = lastcasepc;
while (GetNextPc(endpc) < casepc)
endpc = GetNextPc(endpc);
if (BytecodeFallsThrough(JSOp(*endpc)))
caseHits -= script->getHitCount(endpc);
}
allCaseHits += caseHits;
}
outBRDA_.printf("BRDA:%d,%d,%d,", lineno, branchId, i);
if (caseHits)
outBRDA_.printf("%d\n", caseHits);
else
outBRDA_.put("-\n", 2);
numBranchesFound_++;
numBranchesHit_ += !!caseHits;
lastcasepc = casepc;
}
}
// Add one branch entry for the default statement.
uint64_t defaultHits = 0;
if (sc) {
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;
}
outBRDA_.printf("BRDA:%d,%d,%d,", lineno, branchId, numCases);
if (defaultHits)
outBRDA_.printf("%d\n", defaultHits);
else
outBRDA_.put("-\n", 2);
numBranchesFound_++;
numBranchesHit_ += !!defaultHits;
// Increment the branch identifier, and go to the next instruction.
branchId++;
tableswitchExitOffset = 0;
}
}
return true;