1   /*
2   * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4   *
5   * This code is free software; you can redistribute it and/or modify it
6   * under the terms of the GNU General Public License version 2 only, as
7   * published by the Free Software Foundation.
8   *
9   * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23  
24  /**
25  * @test
26  * @bug 8325469
27  * @summary Test freeze/thaw with OSR frames
28  * @requires vm.continuations
29  * @requires vm.compMode != "Xint" & vm.compMode != "Xcomp"
30  * @modules java.base/jdk.internal.vm
31  * @library /test/lib /test/hotspot/jtreg
32  * @build jdk.test.whitebox.WhiteBox
33  * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
34  *
35  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=dontinline,*::foo* OSRTest true true true
36  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=dontinline,*::foo* -XX:CompileCommand=inline,*::yield0 OSRTest true true false
37  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=dontinline,*::foo* -XX:CompileCommand=dontinline,*::yield* OSRTest true true false
38  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=dontinline,*::foo* -XX:CompileCommand=exclude,*::bar() OSRTest true false false
39  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=dontinline,*::foo* OSRTest false true true
40  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=dontinline,*::foo* OSRTest false true false
41  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=dontinline,*::foo* -XX:CompileCommand=exclude,*::bar() OSRTest false false false
42  *
43  */
44  
45  import jdk.internal.vm.Continuation;
46  import jdk.internal.vm.ContinuationScope;
47  
48  import java.lang.reflect.Method;
49  import jdk.test.whitebox.WhiteBox;
50  
51  public class OSRTest {
52      static final WhiteBox wb = WhiteBox.getWhiteBox();
53      static final ContinuationScope FOO = new ContinuationScope() {};
54      static final Method foo = getMethod("foo");
55      static final Method fooBigFrame = getMethod("fooBigFrame");
56      boolean osrAtBottom;
57      boolean freezeFast;
58      boolean thawFast;
59      int fooCallCount;
60  
61      public static void main(String[] args) {
62          if (args.length != 3) {
63              throw new Error("Error: args.length must be 3");
64          }
65          boolean TEST_OSR_AT_BOTTOM = Boolean.parseBoolean(args[0]);
66          boolean FREEZE_FAST = Boolean.parseBoolean(args[1]);
67          boolean THAW_FAST = Boolean.parseBoolean(args[2]);
68          assert !THAW_FAST || FREEZE_FAST : "THAW_FAST requires FREEZE_FAST";
69  
70          OSRTest test = new OSRTest(TEST_OSR_AT_BOTTOM, FREEZE_FAST, THAW_FAST);
71          test.runTest();
72      }
73  
74      public OSRTest(boolean osrAtBottom, boolean freezeFast, boolean thawFast) {
75          this.osrAtBottom = osrAtBottom;
76          this.freezeFast = freezeFast;
77          this.thawFast = thawFast;
78      }
79  
80      public void runTest() {
81          Runnable testCase =  osrAtBottom ? ()-> testOSRAtStackBottom() : ()-> TestOSRNotAtStackBottom();
82          Continuation cont = new Continuation(FOO, testCase);
83  
84          while (!cont.isDone()) {
85              cont.run();
86              if (freezeFast && !thawFast && fooCallCount == 2) {
87                  // All frames frozen in last yield should be compiled
88                  // including OSR version of foo. Invoke full GC now so
89                  // that chunk is marked and we force thaw slow path.
90                  System.gc();
91                  fooCallCount++; // Don't call again
92              }
93          }
94      }
95  
96      public void testOSRAtStackBottom() {
97          if (freezeFast) {
98              // Trigger compilation of Continuation.yield/yield0
99              for (int i = 0; i < 10_000; i++) {
100                 Continuation.yield(FOO);
101             }
102         }
103         for (int i = 0; i < 2; i++) {
104             if (freezeFast && !thawFast) {
105                 foo(new Object(), new Object(), new Object(), new Object(), new Object(),
106                     new Object(), new Object(), new Object(), new Object(), new Object(),
107                     1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f);
108             } else {
109                 fooBigFrame(new Object(), new Object(), new Object(), new Object(), new Object(),
110                             new Object(), new Object(), new Object(), new Object(), new Object(),
111                             1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f);
112             }
113         }
114     }
115 
116     // Declare many parameters and unused locals so that size of interpreter frame is bigger
117     // than size of OSR frame + size of Continuation.yield/yield0 frames. This is so that once
118     // foo is OSR, on yield we clear cont_fastpath() forcing the freeze fast path.
119     public void foo(Object o1, Object o2, Object o3, Object o4, Object o5,
120                     Object o6, Object o7, Object o8, Object o9, Object o10,
121                     float f1, float f2, float f3, float f4, float f5, float f6, float f7) {
122         int i1 = 1;
123         int i2 = i1 + 1;
124         int i3 = i2 + 1;
125         int i4 = i3 + 1;
126         int i5 = i4 + 1;
127         int i6 = i5 + 1;
128         int i7 = i6 + 1;
129         long ll = 2*(long)i1;
130         float ff = ll + 1.2f;
131         double dd = ff + 1.3D;
132 
133         if (osrAtBottom) {
134             // freeze all frames so that we only run with foo on the stack
135             Continuation.yield(FOO);
136         }
137 
138         // Provoke OSR compilation. After we verified the method was compiled keep looping
139         // until we trigger the _backedge_counter overflow to actually trigger OSR.
140         for (int i = 0; fooCallCount > 0 && (!wb.isMethodCompiled(foo, true) || i++ < 2_000);) {
141         }
142         fooCallCount++;
143 
144         if (freezeFast) {
145             Continuation.yield(FOO);
146         } else {
147             bar();
148         }
149     }
150 
151     public void bar() {
152         Continuation.yield(FOO);
153     }
154 
155     public double fooBigFrame(Object o1, Object o2, Object o3, Object o4, Object o5,
156                               Object o6, Object o7, Object o8, Object o9, Object o10,
157                               float f1, float f2, float f3, float f4, float f5, float f6, float f7) {
158         double d1=1,d2=2,d3=3,d4=4,d5=5,d6=6,d7=7,d8=8,d9=9,d10=10,d11=11,d12=12,d13=13,d14=14,d15=15,d16=16,d17=17,d18=18,d19=19,d20=20,d21=21,d22=22,d23=23,d24=24,d25=25;
159         double d26=26,d27=27,d28=28,d29=29,d30=30,d31=31,d32=32,d33=33,d34=34,d35=35,d36=36,d37=37,d38=38,d39=39,d40=40,d41=41,d42=42,d43=43,d44=44,d45=45,d46=46,d47=47,d48=48,d49=49,d50=50;
160         double d51=51,d52=52,d53=53,d54=54,d55=55,d56=56,d57=57,d58=58,d59=59,d60=60,d61=61,d62=62,d63=63,d64=64,d65=65,d66=66,d67=67,d68=68,d69=69,d70=70,d71=71,d72=72,d73=73,d74=74,d75=75;
161         double d76=76,d77=77,d78=78,d79=79,d80=80,d81=81,d82=82,d83=83,d84=84,d85=85,d86=86,d87=87,d88=88,d89=89,d90=90,d91=91,d92=92,d93=93,d94=94,d95=95,d96=96,d97=97,d98=98,d99=99,d100=100;
162         double d101=101,d102=102,d103=103,d104=104,d105=105,d106=106,d107=107,d108=108,d109=109,d110=110,d111=111,d112=112,d113=113,d114=114,d115=115,d116=116,d117=117,d118=118,d119=119,d120=120,d121=121,d122=122,d123=123,d124=124,d125=125;
163         double d126=126,d127=127,d128=128,d129=129,d130=130,d131=131,d132=132,d133=133,d134=134,d135=135,d136=136,d137=137,d138=138,d139=139,d140=140,d141=141,d142=142,d143=143,d144=144,d145=145,d146=146,d147=147,d148=148,d149=149,d150=150;
164         double d151=151,d152=152,d153=153,d154=154,d155=155,d156=156,d157=157,d158=158,d159=159,d160=160,d161=161,d162=162,d163=163,d164=164,d165=165,d166=166,d167=167,d168=168,d169=169,d170=170,d171=171,d172=172,d173=173,d174=174,d175=175;
165         double d176=176,d177=177,d178=178,d179=179,d180=180,d181=181,d182=182,d183=183,d184=184,d185=185,d186=186,d187=187,d188=188,d189=189,d190=190,d191=191,d192=192,d193=193,d194=194,d195=195,d196=196,d197=197,d198=198,d199=199,d200=200;
166         double d201=201,d202=202,d203=203,d204=204,d205=205,d206=206,d207=207,d208=208,d209=209,d210=210,d211=211,d212=212,d213=213,d214=214,d215=215,d216=216,d217=217,d218=218,d219=219,d220=220,d221=221,d222=222,d223=223,d224=224,d225=225;
167         double d226=226,d227=227,d228=228,d229=229,d230=230,d231=231,d232=232,d233=233,d234=234,d235=235,d236=236,d237=237,d238=238,d239=239,d240=240,d241=241,d242=242,d243=243,d244=244,d245=245,d246=246,d247=247,d248=248,d249=249,d250=250;
168         double d251=251,d252=252,d253=253,d254=254,d255=255,d256=256,d257=257,d258=258,d259=259,d260=260,d261=261,d262=262,d263=263,d264=264,d265=265,d266=266,d267=267,d268=268,d269=269,d270=270,d271=271,d272=272,d273=273,d274=274,d275=275;
169         double d276=276,d277=277,d278=278,d279=279,d280=280,d281=281,d282=282,d283=283,d284=284,d285=285,d286=286,d287=287,d288=288,d289=289,d290=290,d291=291,d292=292,d293=293,d294=294,d295=295,d296=296,d297=297,d298=298,d299=299,d300=300;
170 
171         // freeze all frames so that we only run with fooBigFrame on the stack
172         Continuation.yield(FOO);
173 
174         // Provoke OSR compilation. After we verified the method was compiled keep looping
175         // until we trigger the _backedge_counter overflow to actually trigger OSR.
176         for (int i = 0; fooCallCount > 0 && (!wb.isMethodCompiled(fooBigFrame, true) || i++ < 2_000);) {
177         }
178         fooCallCount++;
179 
180         Continuation.yield(FOO);
181 
182         // For the thaw fast case we want to trigger the case of thawing one
183         // frame at a time. Because the OSR frame is at the bottom we have to
184         // make its size > 500 words, so we use a lot of locals. We also want
185         // the interpreted frame size be bigger than OSR frame + size of
186         // Continuation.yield/yield0, so that we clear cont_fastpath() on yield
187         // forcing the freeze fast path (same as with foo). For that, we just
188         // declare more locals than the ones we use after OSR happens.
189         // For the freeze slow case we also want the interpreted frame size to
190         // be bigger than OSR frame + size of Continuation.yield/yield0, so the
191         // last technique serves for this case too.
192         double res = d1*d2*d3*d4*d5*d6*d7*d8*d9*d10*d11*d12*d13*d14*d15*d16*d17*d18*d19*d20*d21*d22*d23*d24*d25*d26*d27*d28*d29*d30*d31*d32*d33*d34*d35*d36*d37*d38*d39*d40*d41*d42*d43*d44*d45*d46*d47*d48*d49*d50*
193                      d51*d52*d53*d54*d55*d56*d57*d58*d59*d60*d61*d62*d63*d64*d65*d66*d67*d68*d69*d70*d71*d72*d73*d74*d75*d76*d77*d78*d79*d80*d81*d82*d83*d84*d85*d86*d87*d88*d89*d90*d91*d92*d93*d94*d95*d96*d97*d98*d99*d100*
194                      d101*d102*d103*d104*d105*d106*d107*d108*d109*d110*d111*d112*d113*d114*d115*d116*d117*d118*d119*d120*d121*d122*d123*d124*d125*d126*d127*d128*d129*d130*d131*d132*d133*d134*d135*d136*d137*d138*d139*d140*
195                      d141*d142*d143*d144*d145*d146*d147*d148*d149*d150*d151*d152*d153*d154*d155*d156*d157*d158*d159*d160*d161*d162*d163*d164*d165*d166*d167*d168*d169*d170*d171*d172*d173*d174*d175*d176*d177*d178*d179*d180*
196                      d181*d182*d183*d184*d185*d186*d187*d188*d189*d190*d191*d192*d193*d194*d195*d196*d197*d198*d199*d200*d201*d202*d203*d204*d205*d206*d207*d208*d209*d210*d211*d212*d213*d214*d215*d216*d217*d218*d219*d220*
197                      d221*d222*d223*d224*d225*d226*d227*d228*d229*d230*d231*d232*d233*d234*d235*d236*d237*d238*d239*d240*d241*d242*d243*d244*d245*d246*d247*d248*d249*d250*d251*d252*d253*d254*d255*d256*d257*d258*d259*d260*
198                      d261*d262*d263*d264*d265*d266*d267*d268*d269*d270*d271*d272*d273*d274*d275;
199         return res;
200     }
201 
202     public void TestOSRNotAtStackBottom() {
203         // freeze all frames currently in the stack
204         boolean res = Continuation.yield(FOO);
205 
206         for (int i = 1; i < 100000; i++) {
207             // When testing the thaw fast path make recursion big enough so that
208             // the size of all frames at freeze time is more than 500 words. This
209             // way we later force thawing one frame at a time.
210             recurse(thawFast ? 60 : 5, i);
211         }
212     }
213 
214     public void recurse(int depth, int iteration) {
215         if (depth > 0) {
216             recurse(depth - 1, iteration);
217         } else {
218             // Make compiler see this branch but not enough times to avoid foo
219             // getting compiled, since we want the OSR version.
220             if (iteration % 45000 == 0) {
221                 foo(new Object(), new Object(), new Object(), new Object(), new Object(),
222                     new Object(), new Object(), new Object(), new Object(), new Object(),
223                     1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f);
224             } else {
225                 Continuation.yield(FOO);
226             }
227         }
228     }
229 
230     static Method getMethod(String method) {
231         try {
232             return OSRTest.class.getMethod(method, Object.class, Object.class, Object.class, Object.class, Object.class, Object.class, Object.class, Object.class,
233                                            Object.class, Object.class, Float.TYPE, Float.TYPE, Float.TYPE, Float.TYPE, Float.TYPE, Float.TYPE, Float.TYPE);
234         } catch (Exception e) {
235             throw new RuntimeException("Exception: couldn't found method " + method + ". " + e.getMessage());
236         }
237     }
238 }