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 
$noinline$busyLoop()54     public void $noinline$busyLoop() {
55         Main.assertIsManaged();
56 
57         // On Arm64:
58         // This loop is likely to be vectorized which causes the full 16-byte Q-register to be saved
59         // across slow paths.
60         int[] array = new int[kArraySize];
61         for (int i = 0; i < kArraySize; i++) {
62             array[i]++;
63         }
64 
65         sEntered = true;
66         float s0 = kFloatConst0;
67         float s1 = kFloatConst1;
68         for (int i = 0; !sExitFlag; i++) {
69             if (i % 2 == 0) {
70                 s0 += 2.0;
71                 s1 += 2.0;
72             } else {
73                 s0 -= 2.0;
74                 s1 -= 2.0;
75             }
76             // SuspendCheckSlowPath must record correct stack offset for spilled FP registers.
77         }
78         Main.assertIsInterpreted();
79 
80         expectEqualToEither(s0, kFloatConst0, kFloatConst0 + 2.0f);
81         expectEqualToEither(s1, kFloatConst1, kFloatConst1 + 2.0f);
82 
83         floatField = s0 + s1;
84     }
85 
run()86     public void run() {
87         if (threadIndex == 0) {
88             while (!sEntered) {
89               Thread.yield();
90             }
91             Main.deoptimizeAll();
92             sExitFlag = true;
93         } else {
94             $noinline$busyLoop();
95         }
96     }
97 }
98