Increase GetLoopUnrollInfo loop-count limit to 100,000.

To do this with a clean conscience, I needed to convert the unroll-
counting logic from a linear time algorithm to constant-time. Getting
all the edge cases correct requires a lot of care, and there are now
plenty of unit tests.

Change-Id: I620909d069ac425b7310e345bf80ec844fe035f8
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/445643
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
This commit is contained in:
John Stiles 2021-09-07 11:43:51 -04:00 committed by SkCQ
parent 41a6ace12e
commit 106e0cd412
3 changed files with 85 additions and 29 deletions

View File

@ -1,10 +1,12 @@
// Expect 15 errors
void loop_length_128() { for (int i = 0; i < 128; i++) {} } // OK, under kMaxUnrollableLoopLength
void loop_length_129() { for (int i = 0; i < 129; i++) {} }
void loop_length_99999() { for (int i = 0; i < 99999; i++) {} }
void loop_length_128() { for (int i = 0; i < 128; i++) {} } // OK, under kLoopTerminationLimit
void loop_length_129() { for (int i = 0; i < 129; i++) {} } // OK, under kLoopTerminationLimit
void loop_length_99999() { for (int i = 0; i < 99999; i++) {} } // OK, under kLoopTerminationLimit
void loop_length_100000() { for (int i = 0; i < 100000; i++) {} }
void infinite_loop() { for (int i = 0; i < 1; i += 0) {} }
void infinite_loop1() { for (int i = 0; i < 1; i += 0) {} }
void infinite_loop2() { for (int i = 3; i >= 3; i += 0) {} }
void infinite_loop3() { for (float i = 3; i >= 3; i += 1e-20) {} }
void set(out int x) { x = 1; }
void inc(inout int x) { x++; }

View File

@ -7,6 +7,7 @@
#include "src/sksl/SkSLAnalysis.h"
#include "include/private/SkFloatingPoint.h"
#include "include/private/SkSLModifiers.h"
#include "include/private/SkSLProgramElement.h"
#include "include/private/SkSLSampleUsage.h"
@ -1165,30 +1166,83 @@ static const char* invalid_for_ES2(int offset,
}
// Finally, compute the iteration count, based on the bounds, and the termination operator.
constexpr int kMaxUnrollableLoopLength = 128;
static constexpr int kLoopTerminationLimit = 100000;
loopInfo.fCount = 0;
double val = loopInfo.fStart;
auto evalCond = [&]() {
switch (cond.getOperator().kind()) {
case Token::Kind::TK_GT: return val > loopEnd;
case Token::Kind::TK_GTEQ: return val >= loopEnd;
case Token::Kind::TK_LT: return val < loopEnd;
case Token::Kind::TK_LTEQ: return val <= loopEnd;
case Token::Kind::TK_EQEQ: return val == loopEnd;
case Token::Kind::TK_NEQ: return val != loopEnd;
default: SkUNREACHABLE;
auto calculateCount = [](double start, double end, double delta,
bool forwards, bool inclusive) -> int {
if (forwards != (start < end)) {
// The loop starts in a completed state (the start has already advanced past the end).
return 0;
}
if ((delta == 0.0) || forwards != (delta > 0.0)) {
// The loop does not progress toward a completed state, and will never terminate.
return kLoopTerminationLimit;
}
double iterations = sk_ieee_double_divide(end - start, delta);
double count = std::ceil(iterations);
if (inclusive && (count == iterations)) {
count += 1.0;
}
if (count > kLoopTerminationLimit || !std::isfinite(count)) {
// The loop runs for more iterations than we can safely unroll.
return kLoopTerminationLimit;
}
return (int)count;
};
for (loopInfo.fCount = 0; loopInfo.fCount <= kMaxUnrollableLoopLength; ++loopInfo.fCount) {
if (!evalCond()) {
switch (cond.getOperator().kind()) {
case Token::Kind::TK_LT:
loopInfo.fCount = calculateCount(loopInfo.fStart, loopEnd, loopInfo.fDelta,
/*forwards=*/true, /*inclusive=*/false);
break;
case Token::Kind::TK_GT:
loopInfo.fCount = calculateCount(loopInfo.fStart, loopEnd, loopInfo.fDelta,
/*forwards=*/false, /*inclusive=*/false);
break;
case Token::Kind::TK_LTEQ:
loopInfo.fCount = calculateCount(loopInfo.fStart, loopEnd, loopInfo.fDelta,
/*forwards=*/true, /*inclusive=*/true);
break;
case Token::Kind::TK_GTEQ:
loopInfo.fCount = calculateCount(loopInfo.fStart, loopEnd, loopInfo.fDelta,
/*forwards=*/false, /*inclusive=*/true);
break;
case Token::Kind::TK_NEQ: {
float iterations = sk_ieee_double_divide(loopEnd - loopInfo.fStart, loopInfo.fDelta);
loopInfo.fCount = std::ceil(iterations);
if (loopInfo.fCount < 0 || loopInfo.fCount != iterations ||
!std::isfinite(iterations)) {
// The loop doesn't reach the exact endpoint and so will never terminate.
loopInfo.fCount = kLoopTerminationLimit;
}
break;
}
val += loopInfo.fDelta;
case Token::Kind::TK_EQEQ: {
if (loopInfo.fStart == loopEnd) {
// Start and end begin in the same place, so we can run one iteration...
if (loopInfo.fDelta) {
// ... and then they diverge, so the loop terminates.
loopInfo.fCount = 1;
} else {
// ... but they never diverge, so the loop runs forever.
loopInfo.fCount = kLoopTerminationLimit;
}
} else {
// Start never equals end, so the loop will not run a single iteration.
loopInfo.fCount = 0;
}
break;
}
default: SkUNREACHABLE;
}
if (loopInfo.fCount > kMaxUnrollableLoopLength) {
SkASSERT(loopInfo.fCount >= 0);
if (loopInfo.fCount >= kLoopTerminationLimit) {
return "loop must guarantee termination in fewer iterations";
}

View File

@ -1,18 +1,18 @@
### Compilation failed:
error: 4: loop must guarantee termination in fewer iterations
error: 5: loop must guarantee termination in fewer iterations
error: 6: loop must guarantee termination in fewer iterations
error: 7: invalid loop expression
error: 12: loop index must not be modified within body of the loop
error: 13: loop index must not be modified within body of the loop
error: 8: invalid loop expression
error: 9: loop must guarantee termination in fewer iterations
error: 14: loop index must not be modified within body of the loop
error: 16: loop must guarantee termination in fewer iterations
error: 17: loop must guarantee termination in fewer iterations
error: 15: loop index must not be modified within body of the loop
error: 16: loop index must not be modified within body of the loop
error: 18: loop must guarantee termination in fewer iterations
error: 19: loop must guarantee termination in fewer iterations
error: 20: invalid loop expression
error: 21: invalid loop expression
error: 22: loop must guarantee termination in fewer iterations
error: 23: loop must guarantee termination in fewer iterations
error: 20: loop must guarantee termination in fewer iterations
error: 21: loop must guarantee termination in fewer iterations
error: 22: invalid loop expression
error: 23: invalid loop expression
error: 24: loop must guarantee termination in fewer iterations
error: 25: loop must guarantee termination in fewer iterations
15 errors