зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1213732 part 3 - SM LCov: Add code coverage support for TableSwitch statements. r=bhackett
This commit is contained in:
Родитель
95b6d67041
Коммит
ba81d44fff
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче