1 /*
2  * Copyright (C) 2019 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 
17 // This test checks that FP registers spill offset is correctly recorded in the SlowPath; by causing
18 // asynchronous deoptimization in debuggable mode we observe the FP values in the interpreter.
19 public class FloatLoop implements Runnable {
20     static final int numberOfThreads = 2;
21     volatile static boolean sExitFlag = false;
22     volatile static boolean sEntered = false;
23     int threadIndex;
24 
FloatLoop(int index)25     FloatLoop(int index) {
26         threadIndex = index;
27     }
28 
main()29     public static void main() throws Exception {
30         final Thread[] threads = new Thread[numberOfThreads];
31         for (int t = 0; t < threads.length; t++) {
32             threads[t] = new Thread(new FloatLoop(t));
33             threads[t].start();
34         }
35         for (Thread t : threads) {
36             t.join();
37         }
38 
39         System.out.println("Float loop finishing");
40     }
41 
42     static final float kFloatConst0 = 256.0f;
43     static final float kFloatConst1 = 128.0f;
44     static final int kArraySize = 128;
45     volatile static float floatField;
46 
expectEqualToEither(float value, float expected0, float expected1)47     public void expectEqualToEither(float value, float expected0, float expected1) {
48         if (value != expected0 && value != expected1) {
49             throw new Error("Expected:  " + expected0 + " or "+ expected1 +
50                             ", found: " + value);
51         }
52     }
53 
54     // Create an empty int[] to force loading the int[] class before compiling $noinline$busyLoop.
55     // This makes sure the compiler can properly type int[] and not bail.
56     static int[] emptyArray = new int[0];
57 
$noinline$busyLoop()58     public void $noinline$busyLoop() {
59         Main.assertIsManaged();
60 
61         // On Arm64:
62         // This loop is likely to be vectorized which causes the full 16-byte Q-register to be saved
63         // across slow paths.
64         int[] array = new int[kArraySize];
65         for (int i = 0; i < kArraySize; i++) {
66             array[i]++;
67         }
68 
69         sEntered = true;
70         float s0 = kFloatConst0;
71         float s1 = kFloatConst1;
72         for (int i = 0; !sExitFlag; i++) {
73             if (i % 2 == 0) {
74                 s0 += 2.0;
75                 s1 += 2.0;
76             } else {
77                 s0 -= 2.0;
78                 s1 -= 2.0;
79             }
80             // SuspendCheckSlowPath must record correct stack offset for spilled FP registers.
81         }
82         Main.assertIsInterpreted();
83 
84         expectEqualToEither(s0, kFloatConst0, kFloatConst0 + 2.0f);
85         expectEqualToEither(s1, kFloatConst1, kFloatConst1 + 2.0f);
86 
87         floatField = s0 + s1;
88     }
89 
run()90     public void run() {
91         if (threadIndex == 0) {
92             while (!sEntered) {
93               Thread.yield();
94             }
95             Main.deoptimizeAll();
96             sExitFlag = true;
97         } else {
98             $noinline$busyLoop();
99         }
100     }
101 }
102