1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 import java.lang.reflect.Array;
17 import java.lang.reflect.Method;
18 
19 /**
20  * Tests for SIMD related optimizations.
21  */
22 public class Main {
23 
24   /// CHECK-START: void Main.unroll(float[], float[]) loop_optimization (before)
25   /// CHECK-DAG: <<Cons:f\d+>> FloatConstant 2.5                   loop:none
26   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                 loop:<<Loop:B\d+>> outer_loop:none
27   /// CHECK-DAG: <<Get:f\d+>>  ArrayGet                            loop:<<Loop>>      outer_loop:none
28   /// CHECK-DAG: <<Mul:f\d+>>  Mul [<<Get>>,<<Cons>>]              loop:<<Loop>>      outer_loop:none
29   /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Mul>>] loop:<<Loop>>      outer_loop:none
30   //
31   /// CHECK-START-ARM64: void Main.unroll(float[], float[]) loop_optimization (after)
32   /// CHECK-DAG: <<Cons:f\d+>> FloatConstant 2.5                    loop:none
33   /// CHECK-DAG: <<Incr:i\d+>> IntConstant 4                        loop:none
34   /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Cons>>]        loop:none
35   /// CHECK-NOT:               VecReplicateScalar
36   /// CHECK-DAG: <<Phi:i\d+>>  Phi                                  loop:<<Loop:B\d+>> outer_loop:none
37   /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi>>]           loop:<<Loop>>      outer_loop:none
38   /// CHECK-DAG: <<Mul1:d\d+>> VecMul [<<Get1>>,<<Repl>>]           loop:<<Loop>>      outer_loop:none
39   /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Mul1>>] loop:<<Loop>>      outer_loop:none
40   /// CHECK-DAG: <<Add:i\d+>>  Add [<<Phi>>,<<Incr>>]               loop:<<Loop>>      outer_loop:none
41   /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Add>>]           loop:<<Loop>>      outer_loop:none
42   /// CHECK-DAG: <<Mul2:d\d+>> VecMul [<<Get2>>,<<Repl>>]           loop:<<Loop>>      outer_loop:none
43   /// CHECK-DAG:               VecStore [{{l\d+}},<<Add>>,<<Mul2>>] loop:<<Loop>>      outer_loop:none
44   /// CHECK-DAG:               Add [<<Add>>,<<Incr>>]               loop:<<Loop>>      outer_loop:none
unroll(float[] x, float[] y)45   private static void unroll(float[] x, float[] y) {
46     for (int i = 0; i < 100; i++) {
47       x[i] = y[i] * 2.5f;
48     }
49   }
50 
51   /// CHECK-START-ARM64: void Main.stencil(int[], int[], int) loop_optimization (after)
52   /// CHECK-DAG: <<CP1:i\d+>>   IntConstant 1                         loop:none
53   /// CHECK-DAG: <<CP2:i\d+>>   IntConstant 2                         loop:none
54   /// CHECK-DAG: <<Phi:i\d+>>   Phi                                   loop:<<Loop:B\d+>> outer_loop:none
55   /// CHECK-DAG: <<Add1:i\d+>>  Add [<<Phi>>,<<CP1>>]                 loop:<<Loop>>      outer_loop:none
56   /// CHECK-DAG: <<Get1:d\d+>>  VecLoad [{{l\d+}},<<Phi>>]            loop:<<Loop>>      outer_loop:none
57   /// CHECK-DAG: <<Get2:d\d+>>  VecLoad [{{l\d+}},<<Add1>>]           loop:<<Loop>>      outer_loop:none
58   /// CHECK-DAG: <<Add2:d\d+>>  VecAdd [<<Get1>>,<<Get2>>]            loop:<<Loop>>      outer_loop:none
59   /// CHECK-DAG: <<Add3:i\d+>>  Add [<<Phi>>,<<CP2>>]                 loop:<<Loop>>      outer_loop:none
60   /// CHECK-DAG: <<Get3:d\d+>>  VecLoad [{{l\d+}},<<Add3>>]           loop:<<Loop>>      outer_loop:none
61   /// CHECK-DAG: <<Add4:d\d+>>  VecAdd [<<Add2>>,<<Get3>>]            loop:<<Loop>>      outer_loop:none
62   /// CHECK-DAG:                VecStore [{{l\d+}},<<Add1>>,<<Add4>>] loop:<<Loop>>      outer_loop:none
stencil(int[] a, int[] b, int n)63   private static void stencil(int[] a, int[] b, int n) {
64     for (int i = 1; i < n - 1; i++) {
65       a[i] = b[i - 1] + b[i] + b[i + 1];
66     }
67   }
68 
69   /// CHECK-START: void Main.stencilAddInt(int[], int[], int) loop_optimization (before)
70   /// CHECK-DAG: <<CP1:i\d+>>   IntConstant 1                        loop:none
71   /// CHECK-DAG: <<CM1:i\d+>>   IntConstant -1                       loop:none
72   /// CHECK-DAG: <<Phi:i\d+>>   Phi                                  loop:<<Loop:B\d+>> outer_loop:none
73   /// CHECK-DAG: <<Add1:i\d+>>  Add [<<Phi>>,<<CM1>>]                loop:<<Loop>>      outer_loop:none
74   /// CHECK-DAG: <<Get1:i\d+>>  ArrayGet [{{l\d+}},<<Add1>>]         loop:<<Loop>>      outer_loop:none
75   /// CHECK-DAG: <<Get2:i\d+>>  ArrayGet [{{l\d+}},<<Phi>>]          loop:<<Loop>>      outer_loop:none
76   /// CHECK-DAG: <<Add2:i\d+>>  Add [<<Get1>>,<<Get2>>]              loop:<<Loop>>      outer_loop:none
77   /// CHECK-DAG: <<Add3:i\d+>>  Add [<<Phi>>,<<CP1>>]                loop:<<Loop>>      outer_loop:none
78   /// CHECK-DAG: <<Get3:i\d+>>  ArrayGet [{{l\d+}},<<Add3>>]         loop:<<Loop>>      outer_loop:none
79   /// CHECK-DAG: <<Add4:i\d+>>  Add [<<Add2>>,<<Get3>>]              loop:<<Loop>>      outer_loop:none
80   /// CHECK-DAG:                ArraySet [{{l\d+}},<<Phi>>,<<Add4>>] loop:<<Loop>>      outer_loop:none
81 
82   /// CHECK-START-{X86_64,ARM64}: void Main.stencilAddInt(int[], int[], int) loop_optimization (after)
83   /// CHECK-DAG: <<CP1:i\d+>>   IntConstant 1                         loop:none
84   /// CHECK-DAG: <<CP2:i\d+>>   IntConstant 2                         loop:none
85   /// CHECK-DAG: <<Phi:i\d+>>   Phi                                   loop:<<Loop:B\d+>> outer_loop:none
86   /// CHECK-DAG: <<Add1:i\d+>>  Add [<<Phi>>,<<CP1>>]                 loop:<<Loop>>      outer_loop:none
87   /// CHECK-DAG: <<Get1:d\d+>>  VecLoad [{{l\d+}},<<Phi>>]            loop:<<Loop>>      outer_loop:none
88   /// CHECK-DAG: <<Get2:d\d+>>  VecLoad [{{l\d+}},<<Add1>>]           loop:<<Loop>>      outer_loop:none
89   /// CHECK-DAG: <<Add2:d\d+>>  VecAdd [<<Get1>>,<<Get2>>]            loop:<<Loop>>      outer_loop:none
90   /// CHECK-DAG: <<Add3:i\d+>>  Add [<<Phi>>,<<CP2>>]                 loop:<<Loop>>      outer_loop:none
91   /// CHECK-DAG: <<Get3:d\d+>>  VecLoad [{{l\d+}},<<Add3>>]           loop:<<Loop>>      outer_loop:none
92   /// CHECK-DAG: <<Add4:d\d+>>  VecAdd [<<Add2>>,<<Get3>>]            loop:<<Loop>>      outer_loop:none
93   /// CHECK-DAG:                VecStore [{{l\d+}},<<Add1>>,<<Add4>>] loop:<<Loop>>      outer_loop:none
stencilAddInt(int[] a, int[] b, int n)94   private static void stencilAddInt(int[] a, int[] b, int n) {
95     int minus1 = $inline$constMinus1();
96     for (int i = 1; i < n + minus1; i++) {
97       a[i] = b[i + minus1] + b[i] + b[i + 1];
98     }
99   }
100 
$inline$constMinus1()101   private static int $inline$constMinus1() {
102     return -1;
103   }
104 
105   /// CHECK-START: void Main.stencilSubInt(int[], int[], int) loop_optimization (before)
106   /// CHECK-DAG: <<PAR3:i\d+>>  ParameterValue                       loop:none
107   /// CHECK-DAG: <<CP1:i\d+>>   IntConstant 1                        loop:none
108   /// CHECK-DAG: <<Sub1:i\d+>>  Sub [<<PAR3>>,<<CP1>>]               loop:none
109   /// CHECK-DAG: <<Phi:i\d+>>   Phi                                  loop:<<Loop:B\d+>> outer_loop:none
110   /// CHECK-DAG: <<Sub2:i\d+>>  Sub [<<Phi>>,<<CP1>>]                loop:<<Loop>>      outer_loop:none
111   /// CHECK-DAG: <<Get1:i\d+>>  ArrayGet [{{l\d+}},<<Sub2>>]         loop:<<Loop>>      outer_loop:none
112   /// CHECK-DAG: <<Get2:i\d+>>  ArrayGet [{{l\d+}},<<Phi>>]          loop:<<Loop>>      outer_loop:none
113   /// CHECK-DAG: <<Add1:i\d+>>  Add [<<Get1>>,<<Get2>>]              loop:<<Loop>>      outer_loop:none
114   /// CHECK-DAG: <<Add2:i\d+>>  Add [<<Phi>>,<<CP1>>]                loop:<<Loop>>      outer_loop:none
115   /// CHECK-DAG: <<Get3:i\d+>>  ArrayGet [{{l\d+}},<<Add2>>]         loop:<<Loop>>      outer_loop:none
116   /// CHECK-DAG: <<Add3:i\d+>>  Add [<<Add1>>,<<Get3>>]              loop:<<Loop>>      outer_loop:none
117   /// CHECK-DAG:                ArraySet [{{l\d+}},<<Phi>>,<<Add3>>] loop:<<Loop>>      outer_loop:none
118 
119   /// CHECK-START-{X86_64,ARM64}: void Main.stencilSubInt(int[], int[], int) loop_optimization (after)
120   /// CHECK-DAG: <<CP1:i\d+>>   IntConstant 1                         loop:none
121   /// CHECK-DAG: <<CP2:i\d+>>   IntConstant 2                         loop:none
122   /// CHECK-DAG: <<Phi:i\d+>>   Phi                                   loop:<<Loop:B\d+>> outer_loop:none
123   /// CHECK-DAG: <<Add1:i\d+>>  Add [<<Phi>>,<<CP1>>]                 loop:<<Loop>>      outer_loop:none
124   /// CHECK-DAG: <<Get1:d\d+>>  VecLoad [{{l\d+}},<<Phi>>]            loop:<<Loop>>      outer_loop:none
125   /// CHECK-DAG: <<Get2:d\d+>>  VecLoad [{{l\d+}},<<Add1>>]           loop:<<Loop>>      outer_loop:none
126   /// CHECK-DAG: <<Add2:d\d+>>  VecAdd [<<Get1>>,<<Get2>>]            loop:<<Loop>>      outer_loop:none
127   /// CHECK-DAG: <<Add3:i\d+>>  Add [<<Phi>>,<<CP2>>]                 loop:<<Loop>>      outer_loop:none
128   /// CHECK-DAG: <<Get3:d\d+>>  VecLoad [{{l\d+}},<<Add3>>]           loop:<<Loop>>      outer_loop:none
129   /// CHECK-DAG: <<Add4:d\d+>>  VecAdd [<<Add2>>,<<Get3>>]            loop:<<Loop>>      outer_loop:none
130   /// CHECK-DAG:                VecStore [{{l\d+}},<<Add1>>,<<Add4>>] loop:<<Loop>>      outer_loop:none
stencilSubInt(int[] a, int[] b, int n)131   private static void stencilSubInt(int[] a, int[] b, int n) {
132     int plus1 = $inline$constPlus1();
133     for (int i = 1; i < n - plus1; i++) {
134       a[i] = b[i - plus1] + b[i] + b[i + 1];
135     }
136   }
137 
$inline$constPlus1()138   private static int $inline$constPlus1() {
139     return 1;
140   }
141 
142   /// CHECK-START: long Main.longInductionReduction(long[]) loop_optimization (before)
143   /// CHECK-DAG: <<L0:j\d+>>    LongConstant 0             loop:none
144   /// CHECK-DAG: <<L1:j\d+>>    LongConstant 1             loop:none
145   /// CHECK-DAG: <<I0:i\d+>>    IntConstant 0              loop:none
146   /// CHECK-DAG: <<Get:j\d+>>   ArrayGet [{{l\d+}},<<I0>>] loop:none
147   /// CHECK-DAG: <<Phi1:j\d+>>  Phi [<<L0>>,<<Add1:j\d+>>] loop:<<Loop:B\d+>> outer_loop:none
148   /// CHECK-DAG: <<Phi2:j\d+>>  Phi [<<L1>>,<<Add2:j\d+>>] loop:<<Loop>>      outer_loop:none
149   /// CHECK-DAG: <<Add2>>       Add [<<Phi2>>,<<Get>>]     loop:<<Loop>>      outer_loop:none
150   /// CHECK-DAG: <<Add1>>       Add [<<Phi1>>,<<L1>>]      loop:<<Loop>>      outer_loop:none
151   //
152   /// CHECK-START-ARM64: long Main.longInductionReduction(long[]) loop_optimization (after)
153   /// CHECK-DAG: <<L0:j\d+>>    LongConstant 0               loop:none
154   /// CHECK-DAG: <<L1:j\d+>>    LongConstant 1               loop:none
155   /// CHECK-DAG: <<L2:j\d+>>    LongConstant 2               loop:none
156   /// CHECK-DAG: <<I0:i\d+>>    IntConstant 0                loop:none
157   /// CHECK-DAG: <<Get:j\d+>>   ArrayGet [{{l\d+}},<<I0>>]   loop:none
158   /// CHECK-DAG: <<Rep:d\d+>>   VecReplicateScalar [<<Get>>] loop:none
159   /// CHECK-DAG: <<Set:d\d+>>   VecSetScalars [<<L1>>]       loop:none
160   /// CHECK-DAG: <<Phi1:j\d+>>  Phi [<<L0>>,{{j\d+}}]        loop:<<Loop:B\d+>> outer_loop:none
161   /// CHECK-DAG: <<Phi2:d\d+>>  Phi [<<Set>>,{{d\d+}}]       loop:<<Loop>>      outer_loop:none
162   /// CHECK-DAG:                VecAdd [<<Phi2>>,<<Rep>>]    loop:<<Loop>>      outer_loop:none
163   /// CHECK-DAG:                Add [<<Phi1>>,<<L2>>]        loop:<<Loop>>      outer_loop:none
longInductionReduction(long[] y)164   static long longInductionReduction(long[] y) {
165     long x = 1;
166     for (long i = 0; i < 10; i++) {
167       x += y[0];
168     }
169     return x;
170   }
171 
172   /// CHECK-START: void Main.intVectorLongInvariant(int[], long[]) loop_optimization (before)
173   /// CHECK-DAG: <<I0:i\d+>>    IntConstant 0                       loop:none
174   /// CHECK-DAG: <<I1:i\d+>>    IntConstant 1                       loop:none
175   /// CHECK-DAG: <<Get:j\d+>>   ArrayGet [{{l\d+}},<<I0>>]          loop:none
176   /// CHECK-DAG: <<Phi:i\d+>>   Phi [<<I0>>,<<Add:i\d+>>]           loop:<<Loop:B\d+>> outer_loop:none
177   /// CHECK-DAG: <<Cnv:i\d+>>   TypeConversion [<<Get>>]            loop:<<Loop>>      outer_loop:none
178   /// CHECK-DAG:                ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>>      outer_loop:none
179   /// CHECK-DAG: <<Add>>        Add [<<Phi>>,<<I1>>]                loop:<<Loop>>      outer_loop:none
180   //
181   /// CHECK-START-ARM64: void Main.intVectorLongInvariant(int[], long[]) loop_optimization (after)
182   /// CHECK-DAG: <<I0:i\d+>>    IntConstant 0                       loop:none
183   /// CHECK-DAG: <<I1:i\d+>>    IntConstant 1                       loop:none
184   /// CHECK-DAG: <<I4:i\d+>>    IntConstant 4                       loop:none
185   /// CHECK-DAG: <<Get:j\d+>>   ArrayGet [{{l\d+}},<<I0>>]          loop:none
186   /// CHECK-DAG: <<Cnv:i\d+>>   TypeConversion [<<Get>>]            loop:none
187   /// CHECK-DAG: <<Rep:d\d+>>   VecReplicateScalar [<<Cnv>>]        loop:none
188   /// CHECK-DAG: <<Phi:i\d+>>   Phi [<<I0>>,{{i\d+}}]               loop:<<Loop:B\d+>> outer_loop:none
189   /// CHECK-DAG:                VecStore [{{l\d+}},<<Phi>>,<<Rep>>] loop:<<Loop>>      outer_loop:none
190   /// CHECK-DAG:                Add [<<Phi>>,<<I4>>]                loop:<<Loop>>      outer_loop:none
intVectorLongInvariant(int[] x, long[] y)191   static void intVectorLongInvariant(int[] x, long[] y) {
192     for (int i = 0; i < 100; i++) {
193       x[i] = (int) y[0];
194     }
195   }
196 
197   /// CHECK-START: void Main.longCanBeDoneWithInt(int[], int[]) loop_optimization (before)
198   /// CHECK-DAG: <<I0:i\d+>>    IntConstant 0                        loop:none
199   /// CHECK-DAG: <<I1:i\d+>>    IntConstant 1                        loop:none
200   /// CHECK-DAG: <<L1:j\d+>>    LongConstant 1                       loop:none
201   /// CHECK-DAG: <<Phi:i\d+>>   Phi [<<I0>>,<<Add:i\d+>>]            loop:<<Loop:B\d+>> outer_loop:none
202   /// CHECK-DAG: <<Get:i\d+>>   ArrayGet [{{l\d+}},<<Phi>>]          loop:<<Loop>>      outer_loop:none
203   /// CHECK-DAG: <<Cnv1:j\d+>>  TypeConversion [<<Get>>]             loop:<<Loop>>      outer_loop:none
204   /// CHECK-DAG: <<AddL:j\d+>>  Add [<<Cnv1>>,<<L1>>]                loop:<<Loop>>      outer_loop:none
205   /// CHECK-DAG: <<Cnv2:i\d+>>  TypeConversion [<<AddL>>]            loop:<<Loop>>      outer_loop:none
206   /// CHECK-DAG:                ArraySet [{{l\d+}},<<Phi>>,<<Cnv2>>] loop:<<Loop>>      outer_loop:none
207   /// CHECK-DAG: <<Add>>        Add [<<Phi>>,<<I1>>]                 loop:<<Loop>>      outer_loop:none
208   //
209   /// CHECK-START-ARM64: void Main.longCanBeDoneWithInt(int[], int[]) loop_optimization (after)
210   /// CHECK-DAG: <<I0:i\d+>>    IntConstant 0                       loop:none
211   /// CHECK-DAG: <<I4:i\d+>>    IntConstant 4                       loop:none
212   /// CHECK-DAG: <<L1:j\d+>>    LongConstant 1                      loop:none
213   /// CHECK-DAG: <<Cnv:i\d+>>   TypeConversion [<<L1>>]             loop:none
214   /// CHECK-DAG: <<Rep:d\d+>>   VecReplicateScalar [<<Cnv>>]        loop:none
215   /// CHECK-DAG: <<Phi:i\d+>>   Phi [<<I0>>,{{i\d+}}]               loop:<<Loop:B\d+>> outer_loop:none
216   /// CHECK-DAG: <<Load:d\d+>>  VecLoad [{{l\d+}},<<Phi>>]          loop:<<Loop>>      outer_loop:none
217   /// CHECK-DAG: <<Add:d\d+>>   VecAdd [<<Load>>,<<Rep>>]           loop:<<Loop>>      outer_loop:none
218   /// CHECK-DAG:                VecStore [{{l\d+}},<<Phi>>,<<Add>>] loop:<<Loop>>      outer_loop:none
219   /// CHECK-DAG:                Add [<<Phi>>,<<I4>>]                loop:<<Loop>>      outer_loop:none
longCanBeDoneWithInt(int[] x, int[] y)220   static void longCanBeDoneWithInt(int[] x, int[] y) {
221     for (int i = 0; i < 100; i++) {
222       x[i] = (int) (y[i] + 1L);
223     }
224   }
225 
testUnroll()226   static void testUnroll() {
227     float[] x = new float[100];
228     float[] y = new float[100];
229     for (int i = 0; i < 100; i++) {
230       x[i] = 0.0f;
231       y[i] = 2.0f;
232     }
233     unroll(x, y);
234     for (int i = 0; i < 100; i++) {
235       expectEquals(5.0f, x[i]);
236       expectEquals(2.0f, y[i]);
237     }
238   }
239 
testStencil1()240   static void testStencil1() {
241     int[] a = new int[100];
242     int[] b = new int[100];
243     for (int i = 0; i < 100; i++) {
244       a[i] = 0;
245       b[i] = i;
246     }
247     stencil(a, b, 100);
248     for (int i = 1; i < 99; i++) {
249       int e = i + i + i;
250       expectEquals(e, a[i]);
251       expectEquals(i, b[i]);
252     }
253   }
254 
testStencil2()255   static void testStencil2() {
256     int[] a = new int[100];
257     int[] b = new int[100];
258     for (int i = 0; i < 100; i++) {
259       a[i] = 0;
260       b[i] = i;
261     }
262     stencilSubInt(a, b, 100);
263     for (int i = 1; i < 99; i++) {
264       int e = i + i + i;
265       expectEquals(e, a[i]);
266       expectEquals(i, b[i]);
267     }
268   }
269 
testStencil3()270   static void testStencil3() {
271     int[] a = new int[100];
272     int[] b = new int[100];
273     for (int i = 0; i < 100; i++) {
274       a[i] = 0;
275       b[i] = i;
276     }
277     stencilAddInt(a, b, 100);
278     for (int i = 1; i < 99; i++) {
279       int e = i + i + i;
280       expectEquals(e, a[i]);
281       expectEquals(i, b[i]);
282     }
283   }
284 
testTypes()285   static void testTypes() {
286     int[] a = new int[100];
287     int[] b = new int[100];
288     long[] l = { 3 };
289     expectEquals(31, longInductionReduction(l));
290     intVectorLongInvariant(a, l);
291     for (int i = 0; i < 100; i++) {
292       expectEquals(3, a[i]);
293     }
294     longCanBeDoneWithInt(b, a);
295     for (int i = 0; i < 100; i++) {
296       expectEquals(4, b[i]);
297     }
298   }
299 
main(String[] args)300   public static void main(String[] args) {
301     testUnroll();
302     testStencil1();
303     testStencil2();
304     testStencil3();
305     testTypes();
306     System.out.println("passed");
307   }
308 
expectEquals(int expected, int result)309   private static void expectEquals(int expected, int result) {
310     if (expected != result) {
311       throw new Error("Expected: " + expected + ", found: " + result);
312     }
313   }
314 
expectEquals(long expected, long result)315   private static void expectEquals(long expected, long result) {
316     if (expected != result) {
317       throw new Error("Expected: " + expected + ", found: " + result);
318     }
319   }
320 
expectEquals(float expected, float result)321   private static void expectEquals(float expected, float result) {
322     if (expected != result) {
323       throw new Error("Expected: " + expected + ", found: " + result);
324     }
325   }
326 }
327