Improve unit testing of for-loops.
An ES2-compatible for loop supports six separate rel-ops:
< <= > >= != ==
Each rel-op, in addition to its expected usage, is also able to
represent a loop which never terminates, as well as a loop which
terminates instantly. Since SkVM unrolls these loops, we should make
sure we do it properly. We now have unit tests for all of these cases.
Change-Id: Icae04d48bc158bf8c0c98db97f76756a1a29110c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/445756
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
diff --git a/resources/sksl/runtime/LoopFloat.rts b/resources/sksl/runtime/LoopFloat.rts
index 96c7759..53f409f 100644
--- a/resources/sksl/runtime/LoopFloat.rts
+++ b/resources/sksl/runtime/LoopFloat.rts
@@ -1,3 +1,5 @@
+uniform half4 colorRed, colorGreen;
+
// Should return 5
float return_loop() {
for (float i = 0; i < 10; ++i) {
@@ -26,15 +28,89 @@
return sum;
}
-// Should return ~1.725
+// Should return a value close to zero
float float_loop() {
float sum = 0;
for (float i = 0.123; i < 0.6; i += 0.111) {
sum += i;
}
- return sum;
+ return sum - 1.725;
+}
+
+bool loop_operator_le() {
+ // These loops are inside-out and execute zero times.
+ for (float i = 3; i <= 1; ++i) { return false; }
+ for (float i = 3; i <= 1; --i) { return false; }
+
+ float4 result = float4(9);
+ for (float i = 1; i <= 3; ++i) {
+ result = float4(result.yzw, i);
+ }
+ return result == float4(9, 1, 2, 3);
+}
+
+bool loop_operator_lt() {
+ // These loops are inside-out and execute zero times.
+ for (float i = 4; i < 1; ++i) { return false; }
+ for (float i = 4; i < 1; --i) { return false; }
+
+ float4 result = float4(9);
+ for (float i = 1; i < 4; ++i) {
+ result = float4(result.yzw, i);
+ }
+ return result == float4(9, 1, 2, 3);
+}
+
+bool loop_operator_ge() {
+ // These loops are inside-out and execute zero times.
+ for (float i = 1; i >= 3; ++i) { return false; }
+ for (float i = 1; i >= 3; --i) { return false; }
+
+ float4 result = float4(9);
+ for (float i = 3; i >= 1; --i) {
+ result = float4(result.yzw, i);
+ }
+ return result == float4(9, 3, 2, 1);
+}
+
+bool loop_operator_gt() {
+ // These loops are inside-out and execute zero times.
+ for (float i = 0; i > 3; ++i) { return false; }
+ for (float i = 0; i > 3; --i) { return false; }
+
+ float4 result = float4(9);
+ for (float i = 3; i > 0; --i) {
+ result = float4(result.yzw, i);
+ }
+ return result == float4(9, 3, 2, 1);
+}
+
+bool loop_operator_ne() {
+ // This loop executes zero times.
+ for (int i = 1; i != 1; ++i) { return false; }
+
+ float4 result = float4(9);
+ for (float i = 1; i != 4; ++i) {
+ result = float4(result.yzw, i);
+ }
+ return result == float4(9, 1, 2, 3);
+}
+
+bool loop_operator_eq() {
+ // This loops mismatches and executes zero times.
+ for (float i = 1; i == 2; ++i) { return false; }
+
+ float4 result = float4(9);
+ for (float i = 1; i == 1; ++i) {
+ result = float4(result.yzw, i);
+ }
+ return result == float4(9, 9, 9, 1);
}
half4 main(float2 xy) {
- return half4(return_loop(), continue_loop(), break_loop(), float_loop());
+ return (return_loop() == 5 && continue_loop() == 35 &&
+ break_loop() == 15 && abs(float_loop()) < 0.025 &&
+ loop_operator_le() && loop_operator_lt() &&
+ loop_operator_ge() && loop_operator_gt() &&
+ loop_operator_eq() && loop_operator_ne()) ? colorGreen : colorRed;
}
diff --git a/resources/sksl/runtime/LoopInt.rts b/resources/sksl/runtime/LoopInt.rts
index 9faec3d..bda39ca 100644
--- a/resources/sksl/runtime/LoopInt.rts
+++ b/resources/sksl/runtime/LoopInt.rts
@@ -1,3 +1,5 @@
+uniform half4 colorRed, colorGreen;
+
// Should return 5
int return_loop() {
for (int i = 0; i < 10; ++i) {
@@ -26,6 +28,79 @@
return sum;
}
+bool loop_operator_le() {
+ // These loops are inside-out and execute zero times.
+ for (int i = 3; i <= 1; ++i) { return false; }
+ for (int i = 3; i <= 1; --i) { return false; }
+
+ int4 result = int4(9);
+ for (int i = 1; i <= 3; ++i) {
+ result = int4(result.yzw, i);
+ }
+ return result == int4(9, 1, 2, 3);
+}
+
+bool loop_operator_lt() {
+ // These loops are inside-out and execute zero times.
+ for (int i = 4; i < 1; ++i) { return false; }
+ for (int i = 4; i < 1; --i) { return false; }
+
+ int4 result = int4(9);
+ for (int i = 1; i < 4; ++i) {
+ result = int4(result.yzw, i);
+ }
+ return result == int4(9, 1, 2, 3);
+}
+
+bool loop_operator_ge() {
+ // These loops are inside-out and execute zero times.
+ for (int i = 1; i >= 3; ++i) { return false; }
+ for (int i = 1; i >= 3; --i) { return false; }
+
+ int4 result = int4(9);
+ for (int i = 3; i >= 1; --i) {
+ result = int4(result.yzw, i);
+ }
+ return result == int4(9, 3, 2, 1);
+}
+
+bool loop_operator_gt() {
+ // These loops are inside-out and execute zero times.
+ for (int i = 0; i > 3; ++i) { return false; }
+ for (int i = 0; i > 3; --i) { return false; }
+
+ int4 result = int4(9);
+ for (int i = 3; i > 0; --i) {
+ result = int4(result.yzw, i);
+ }
+ return result == int4(9, 3, 2, 1);
+}
+
+bool loop_operator_ne() {
+ // This loop executes zero times.
+ for (int i = 1; i != 1; ++i) { return false; }
+
+ int4 result = int4(9);
+ for (int i = 1; i != 4; ++i) {
+ result = int4(result.yzw, i);
+ }
+ return result == int4(9, 1, 2, 3);
+}
+
+bool loop_operator_eq() {
+ // This loop executes zero times.
+ for (int i = 1; i == 2; ++i) { return false; }
+
+ int4 result = int4(9);
+ for (int i = 1; i == 1; ++i) {
+ result = int4(result.yzw, i);
+ }
+ return result == int4(9, 9, 9, 1);
+}
+
half4 main(float2 xy) {
- return half4(return_loop(), continue_loop(), break_loop(), 1);
+ return (return_loop() == 5 && continue_loop() == 35 && break_loop() == 15 &&
+ loop_operator_le() && loop_operator_lt() &&
+ loop_operator_ge() && loop_operator_gt() &&
+ loop_operator_eq() && loop_operator_ne()) ? colorGreen : colorRed;
}
diff --git a/resources/sksl/runtime_errors/LoopStructureErrors.rts b/resources/sksl/runtime_errors/LoopStructureErrors.rts
index 48a5c60..dd288c9 100644
--- a/resources/sksl/runtime_errors/LoopStructureErrors.rts
+++ b/resources/sksl/runtime_errors/LoopStructureErrors.rts
@@ -1,8 +1,10 @@
-// Expect 5 errors
+// Expect 15 errors
-void loop_length_ok() { for (int i = 0; i < 128; i++) {} } // LEGAL: See kMaxUnrollableLoopLength
-void loop_too_long() { for (int i = 0; i < 129; i++) {} }
-void infinite_loop() { for (int i = 0; i < 1; i += 0) {} }
+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_100000() { for (int i = 0; i < 100000; i++) {} }
+void infinite_loop() { for (int i = 0; i < 1; i += 0) {} }
void set(out int x) { x = 1; }
void inc(inout int x) { x++; }
@@ -10,3 +12,12 @@
void index_modified() { for (int i = 0; i < 2; i++) { i++; } }
void index_out_param() { for (int i = 0; i < 1; i++) { set(i); } }
void index_inout_param() { for (int i = 0; i < 1; i++) { inc(i); } }
+
+void infinite_loop_le() { for (int i = 0; i <= 3; --i) {} }
+void infinite_loop_lt() { for (int i = 0; i < 4; --i) {} }
+void infinite_loop_ge() { for (int i = 3; i >= 0; ++i) {} }
+void infinite_loop_gt() { for (int i = 3; i > -1; ++i) {} }
+void infinite_loop_eq1() { for (int i = 0; i == 0; i-=0) {} }
+void infinite_loop_eq2() { for (int i = 1; i == 1; i+=0) {} }
+void infinite_loop_ne1() { for (int i = 0; i != 4; i--) {} }
+void infinite_loop_ne2() { for (int i = 0; i != 4; i+=3) {} }
diff --git a/tests/SkSLTest.cpp b/tests/SkSLTest.cpp
index 34a07f2..e205c2f 100644
--- a/tests/SkSLTest.cpp
+++ b/tests/SkSLTest.cpp
@@ -230,6 +230,8 @@
SKSL_TEST_ES3(SkSLIntrinsicUintBitsToFloat, "intrinsics/UintBitsToFloat.sksl")
SKSL_TEST_ES3(SkSLArrayNarrowingConversions, "runtime/ArrayNarrowingConversions.rts")
+SKSL_TEST_ES3(SkSLLoopFloat, "runtime/LoopFloat.rts")
+SKSL_TEST_ES3(SkSLLoopInt, "runtime/LoopInt.rts")
SKSL_TEST_ES3(SkSLArrayComparison, "shared/ArrayComparison.sksl")
SKSL_TEST_ES3(SkSLArrayConstructors, "shared/ArrayConstructors.sksl")
diff --git a/tests/sksl/runtime/LoopFloat.skvm b/tests/sksl/runtime/LoopFloat.skvm
index 85795b9..17da1ad 100644
--- a/tests/sksl/runtime/LoopFloat.skvm
+++ b/tests/sksl/runtime/LoopFloat.skvm
@@ -1,8 +1,8 @@
4 registers, 8 instructions:
-0 r0 = splat 40A00000 (5)
-1 r1 = splat 420C0000 (35)
-2 r2 = splat 41700000 (15)
-3 r3 = splat 3FDCCCCD (1.725)
+0 r0 = uniform32 ptr0 14
+1 r1 = uniform32 ptr0 18
+2 r2 = uniform32 ptr0 1C
+3 r3 = uniform32 ptr0 20
loop:
4 store32 ptr1 r0
5 store32 ptr2 r1
diff --git a/tests/sksl/runtime/LoopFloat.stage b/tests/sksl/runtime/LoopFloat.stage
index 9793dbe..c2e47b0 100644
--- a/tests/sksl/runtime/LoopFloat.stage
+++ b/tests/sksl/runtime/LoopFloat.stage
@@ -1,3 +1,5 @@
+uniform half4 colorRed;
+uniform half4 colorGreen;
float return_loop_0()
{
for (float i = 0.0;i < 10.0; ++i)
@@ -9,30 +11,136 @@
}
return 0.0;
}
-half4 main(float2 xy)
+float continue_loop_0()
{
- float _0_sum = 0.0;
- for (float _1_i = 0.0;_1_i < 10.0; ++_1_i)
+ float sum = 0.0;
+ for (float i = 0.0;i < 10.0; ++i)
{
- if (_1_i < 5.0)
+ if (i < 5.0)
{
continue;
}
- _0_sum += _1_i;
+ sum += i;
}
- float _2_sum = 0.0;
- for (float _3_i = 0.0;_3_i < 10.0; ++_3_i)
+ return sum;
+}
+float break_loop_0()
+{
+ float sum = 0.0;
+ for (float i = 0.0;i < 10.0; ++i)
{
- if (_3_i > 5.0)
+ if (i > 5.0)
{
break;
}
- _2_sum += _3_i;
+ sum += i;
}
- float _4_sum = 0.0;
- for (float _5_i = 0.12300000339746475;_5_i < 0.60000002384185791; _5_i += 0.11100000143051147)
+ return sum;
+}
+float float_loop_0()
+{
+ float sum = 0.0;
+ for (float i = 0.12300000339746475;i < 0.60000002384185791; i += 0.11100000143051147)
{
- _4_sum += _5_i;
+ sum += i;
}
- return half4(half4(half(return_loop_0()), half(_0_sum), half(_2_sum), half(_4_sum)));
+ return sum - 1.7250000238418579;
+}
+bool loop_operator_le_0()
+{
+ for (float i = 3.0;i <= 1.0; ++i)
+ {
+ return false;
+ }
+ for (float i = 3.0;i <= 1.0; --i)
+ {
+ return false;
+ }
+ float4 result = float4(9.0);
+ for (float i = 1.0;i <= 3.0; ++i)
+ {
+ result = float4(result.yzw, i);
+ }
+ return result == float4(9.0, 1.0, 2.0, 3.0);
+}
+bool loop_operator_lt_0()
+{
+ for (float i = 4.0;i < 1.0; ++i)
+ {
+ return false;
+ }
+ for (float i = 4.0;i < 1.0; --i)
+ {
+ return false;
+ }
+ float4 result = float4(9.0);
+ for (float i = 1.0;i < 4.0; ++i)
+ {
+ result = float4(result.yzw, i);
+ }
+ return result == float4(9.0, 1.0, 2.0, 3.0);
+}
+bool loop_operator_ge_0()
+{
+ for (float i = 1.0;i >= 3.0; ++i)
+ {
+ return false;
+ }
+ for (float i = 1.0;i >= 3.0; --i)
+ {
+ return false;
+ }
+ float4 result = float4(9.0);
+ for (float i = 3.0;i >= 1.0; --i)
+ {
+ result = float4(result.yzw, i);
+ }
+ return result == float4(9.0, 3.0, 2.0, 1.0);
+}
+bool loop_operator_gt_0()
+{
+ for (float i = 0.0;i > 3.0; ++i)
+ {
+ return false;
+ }
+ for (float i = 0.0;i > 3.0; --i)
+ {
+ return false;
+ }
+ float4 result = float4(9.0);
+ for (float i = 3.0;i > 0.0; --i)
+ {
+ result = float4(result.yzw, i);
+ }
+ return result == float4(9.0, 3.0, 2.0, 1.0);
+}
+bool loop_operator_ne_0()
+{
+ for (int i = 1;i != 1; ++i)
+ {
+ return false;
+ }
+ float4 result = float4(9.0);
+ for (float i = 1.0;i != 4.0; ++i)
+ {
+ result = float4(result.yzw, i);
+ }
+ return result == float4(9.0, 1.0, 2.0, 3.0);
+}
+bool loop_operator_eq_0()
+{
+ for (float i = 1.0;i == 2.0; ++i)
+ {
+ return false;
+ }
+ float4 result = float4(9.0);
+ for (float i = 1.0;i == 1.0; ++i)
+ {
+ result = float4(result.yzw, i);
+ }
+ return result == float4(9.0, 9.0, 9.0, 1.0);
+}
+half4 main(float2 xy)
+{
+ return half4(((((((((return_loop_0() == 5.0 && continue_loop_0() == 35.0) && break_loop_0() == 15.0) && abs(float_loop_0()) < 0.02500000037252903) && loop_operator_le_0()) && loop_operator_lt_0()) && loop_operator_ge_0()) && loop_operator_gt_0()) && loop_operator_eq_0()) && loop_operator_ne_0() ? colorGreen : colorRed);
}
diff --git a/tests/sksl/runtime/LoopInt.skvm b/tests/sksl/runtime/LoopInt.skvm
index b1c3c4d..17da1ad 100644
--- a/tests/sksl/runtime/LoopInt.skvm
+++ b/tests/sksl/runtime/LoopInt.skvm
@@ -1,8 +1,8 @@
4 registers, 8 instructions:
-0 r0 = splat 40A00000 (5)
-1 r1 = splat 420C0000 (35)
-2 r2 = splat 41700000 (15)
-3 r3 = splat 3F800000 (1)
+0 r0 = uniform32 ptr0 14
+1 r1 = uniform32 ptr0 18
+2 r2 = uniform32 ptr0 1C
+3 r3 = uniform32 ptr0 20
loop:
4 store32 ptr1 r0
5 store32 ptr2 r1
diff --git a/tests/sksl/runtime/LoopInt.stage b/tests/sksl/runtime/LoopInt.stage
index fe12dd6..bfbffb4 100644
--- a/tests/sksl/runtime/LoopInt.stage
+++ b/tests/sksl/runtime/LoopInt.stage
@@ -1,3 +1,5 @@
+uniform half4 colorRed;
+uniform half4 colorGreen;
int return_loop_0()
{
for (int i = 0;i < 10; ++i)
@@ -9,25 +11,127 @@
}
return 0;
}
-half4 main(float2 xy)
+int continue_loop_0()
{
- int _0_sum = 0;
- for (int _1_i = 0;_1_i < 10; ++_1_i)
+ int sum = 0;
+ for (int i = 0;i < 10; ++i)
{
- if (_1_i < 5)
+ if (i < 5)
{
continue;
}
- _0_sum += _1_i;
+ sum += i;
}
- int _2_sum = 0;
- for (int _3_i = 0;_3_i < 10; ++_3_i)
+ return sum;
+}
+int break_loop_0()
+{
+ int sum = 0;
+ for (int i = 0;i < 10; ++i)
{
- if (_3_i > 5)
+ if (i > 5)
{
break;
}
- _2_sum += _3_i;
+ sum += i;
}
- return half4(half4(half(return_loop_0()), half(_0_sum), half(_2_sum), 1.0));
+ return sum;
+}
+bool loop_operator_le_0()
+{
+ for (int i = 3;i <= 1; ++i)
+ {
+ return false;
+ }
+ for (int i = 3;i <= 1; --i)
+ {
+ return false;
+ }
+ int4 result = int4(9);
+ for (int i = 1;i <= 3; ++i)
+ {
+ result = int4(result.yzw, i);
+ }
+ return result == int4(9, 1, 2, 3);
+}
+bool loop_operator_lt_0()
+{
+ for (int i = 4;i < 1; ++i)
+ {
+ return false;
+ }
+ for (int i = 4;i < 1; --i)
+ {
+ return false;
+ }
+ int4 result = int4(9);
+ for (int i = 1;i < 4; ++i)
+ {
+ result = int4(result.yzw, i);
+ }
+ return result == int4(9, 1, 2, 3);
+}
+bool loop_operator_ge_0()
+{
+ for (int i = 1;i >= 3; ++i)
+ {
+ return false;
+ }
+ for (int i = 1;i >= 3; --i)
+ {
+ return false;
+ }
+ int4 result = int4(9);
+ for (int i = 3;i >= 1; --i)
+ {
+ result = int4(result.yzw, i);
+ }
+ return result == int4(9, 3, 2, 1);
+}
+bool loop_operator_gt_0()
+{
+ for (int i = 0;i > 3; ++i)
+ {
+ return false;
+ }
+ for (int i = 0;i > 3; --i)
+ {
+ return false;
+ }
+ int4 result = int4(9);
+ for (int i = 3;i > 0; --i)
+ {
+ result = int4(result.yzw, i);
+ }
+ return result == int4(9, 3, 2, 1);
+}
+bool loop_operator_ne_0()
+{
+ for (int i = 1;i != 1; ++i)
+ {
+ return false;
+ }
+ int4 result = int4(9);
+ for (int i = 1;i != 4; ++i)
+ {
+ result = int4(result.yzw, i);
+ }
+ return result == int4(9, 1, 2, 3);
+}
+bool loop_operator_eq_0()
+{
+ for (int i = 1;i == 2; ++i)
+ {
+ return false;
+ }
+ int4 result = int4(9);
+ for (int i = 1;i == 1; ++i)
+ {
+ result = int4(result.yzw, i);
+ }
+ return result == int4(9, 9, 9, 1);
+}
+half4 main(float2 xy)
+{
+ return half4((((((((return_loop_0() == 5 && continue_loop_0() == 35) && break_loop_0() == 15) && loop_operator_le_0()) && loop_operator_lt_0()) && loop_operator_ge_0()) && loop_operator_gt_0()) && loop_operator_eq_0()) && loop_operator_ne_0() ? colorGreen : colorRed);
}
diff --git a/tests/sksl/runtime_errors/LoopStructureErrors.skvm b/tests/sksl/runtime_errors/LoopStructureErrors.skvm
index 1c169dc..abd7c5f 100644
--- a/tests/sksl/runtime_errors/LoopStructureErrors.skvm
+++ b/tests/sksl/runtime_errors/LoopStructureErrors.skvm
@@ -1,8 +1,18 @@
### Compilation failed:
error: 4: loop must guarantee termination in fewer iterations
-error: 5: invalid loop expression
-error: 10: loop index must not be modified within body of the loop
-error: 11: loop index must not be modified within body of the loop
+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
-5 errors
+error: 13: loop index must not be modified within body of the loop
+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: 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
+15 errors