1# Copyright (C) 2015 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15.class public LRuntime;
16.super Ljava/lang/Object;
17
18# The following tests all share the same structure, signature and return values:
19#  - foo(false, false):  normal path,         returns 42
20#  - foo(true, false):   exceptional path #1, returns 3
21#  - foo(false, true):   exceptional path #2, returns 8
22#  - foo(true, true):    undefined
23
24
25# Test register allocation of 32-bit core intervals crossing catch block positions.
26
27## CHECK-START: int Runtime.testUseAfterCatch_int(boolean, boolean) register (after)
28## CHECK-NOT:     Phi is_catch_phi:true
29
30.method public static testUseAfterCatch_int(ZZ)I
31  .registers 6
32
33  sget-object v0, LRuntime;->intArray:[I
34  const/4 v1, 0
35  aget v1, v0, v1
36  const/4 v2, 1
37  aget v2, v0, v2
38  const/4 v3, 2
39  aget v3, v0, v3
40
41  :try_start
42  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
43  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
44  :try_end
45  .catchall {:try_start .. :try_end} :catch_all
46
47  return v3  # Normal path return.
48
49  :catch_all
50  if-eqz p0, :second_throw
51  return v1  # Exceptional path #1 return.
52
53  :second_throw
54  return v2  # Exceptional path #2 return.
55.end method
56
57
58# Test register allocation of 64-bit core intervals crossing catch block positions.
59
60# The sum of the low and high 32 bits treated as integers is returned to prove
61# that both vregs allocated correctly.
62
63## CHECK-START: int Runtime.testUseAfterCatch_long(boolean, boolean) register (after)
64## CHECK-NOT:     Phi is_catch_phi:true
65
66.method public static testUseAfterCatch_long(ZZ)I
67  .registers 10
68
69  sget-object v0, LRuntime;->longArray:[J
70  const/4 v1, 0
71  aget-wide v1, v0, v1
72  const/4 v3, 1
73  aget-wide v3, v0, v3
74  const/4 v5, 2
75  aget-wide v5, v0, v5
76
77  :try_start
78  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
79  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
80  :try_end
81  .catchall {:try_start .. :try_end} :catch_all
82
83  const v0, 32
84  ushr-long v7, v5, v0
85  long-to-int v5, v5
86  long-to-int v7, v7
87  add-int/2addr v5, v7
88  return v5  # Normal path return.
89
90  :catch_all
91  const v0, 32
92  if-eqz p0, :second_throw
93
94  ushr-long v7, v1, v0
95  long-to-int v1, v1
96  long-to-int v7, v7
97  add-int/2addr v1, v7
98  return v1  # Exceptional path #1 return.
99
100  :second_throw
101  ushr-long v7, v3, v0
102  long-to-int v3, v3
103  long-to-int v7, v7
104  add-int/2addr v3, v7
105  return v3  # Exceptional path #2 return.
106.end method
107
108
109# Test register allocation of 32-bit floating-point intervals crossing catch block positions.
110
111## CHECK-START: int Runtime.testUseAfterCatch_float(boolean, boolean) register (after)
112## CHECK-NOT:     Phi is_catch_phi:true
113
114.method public static testUseAfterCatch_float(ZZ)I
115  .registers 6
116
117  sget-object v0, LRuntime;->floatArray:[F
118  const/4 v1, 0
119  aget v1, v0, v1
120  const/4 v2, 1
121  aget v2, v0, v2
122  const/4 v3, 2
123  aget v3, v0, v3
124
125  :try_start
126  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
127  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
128  :try_end
129  .catchall {:try_start .. :try_end} :catch_all
130
131  float-to-int v3, v3
132  return v3  # Normal path return.
133
134  :catch_all
135  if-eqz p0, :second_throw
136  float-to-int v1, v1
137  return v1  # Exceptional path #1 return.
138
139  :second_throw
140  float-to-int v2, v2
141  return v2  # Exceptional path #2 return.
142.end method
143
144
145# Test register allocation of 64-bit floating-point intervals crossing catch block positions.
146
147## CHECK-START: int Runtime.testUseAfterCatch_double(boolean, boolean) register (after)
148## CHECK-NOT:     Phi is_catch_phi:true
149
150.method public static testUseAfterCatch_double(ZZ)I
151  .registers 10
152
153  sget-object v0, LRuntime;->doubleArray:[D
154  const/4 v1, 0
155  aget-wide v1, v0, v1
156  const/4 v3, 1
157  aget-wide v3, v0, v3
158  const/4 v5, 2
159  aget-wide v5, v0, v5
160
161  :try_start
162  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
163  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
164  :try_end
165  .catchall {:try_start .. :try_end} :catch_all
166
167  double-to-int v5, v5
168  return v5  # Normal path return.
169
170  :catch_all
171  if-eqz p0, :second_throw
172  double-to-int v1, v1
173  return v1  # Exceptional path #1 return.
174
175  :second_throw
176  double-to-int v3, v3
177  return v3  # Exceptional path #2 return.
178.end method
179
180
181# Test catch-phi runtime support for constant values.
182
183# Register v0 holds different constants at two throwing instructions. Runtime is
184# expected to load them from stack map and copy to the catch phi's location.
185
186## CHECK-START: int Runtime.testCatchPhi_const(boolean, boolean) register (after)
187## CHECK-DAG:     <<Const3:i\d+>> IntConstant 3
188## CHECK-DAG:     <<Const8:i\d+>> IntConstant 8
189## CHECK-DAG:                     Phi [<<Const3>>,<<Const8>>] is_catch_phi:true
190
191.method public static testCatchPhi_const(ZZ)I
192  .registers 3
193
194  :try_start
195  const v0, 3
196  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
197
198  const v0, 8
199  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
200  :try_end
201  .catchall {:try_start .. :try_end} :catch_all
202
203  const v0, 42
204  return v0  # Normal path return.
205
206  :catch_all
207  return v0  # Exceptional path #1/#2 return.
208.end method
209
210
211# Test catch-phi runtime support for 32-bit values stored in core registers.
212
213# Register v0 holds different integer values at two throwing instructions.
214# Runtime is expected to find their location in the stack map and copy the value
215# to the location of the catch phi.
216
217## CHECK-START: int Runtime.testCatchPhi_int(boolean, boolean) register (after)
218## CHECK-DAG:     <<Val1:i\d+>> ArrayGet
219## CHECK-DAG:     <<Val2:i\d+>> ArrayGet
220## CHECK-DAG:                   Phi [<<Val1>>,<<Val2>>] is_catch_phi:true
221
222.method public static testCatchPhi_int(ZZ)I
223  .registers 6
224
225  sget-object v0, LRuntime;->intArray:[I
226  const/4 v1, 0
227  aget v1, v0, v1
228  const/4 v2, 1
229  aget v2, v0, v2
230  const/4 v3, 2
231  aget v3, v0, v3
232
233  :try_start
234  move v0, v1  # Set catch phi value
235  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
236
237  move v0, v2  # Set catch phi value
238  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
239  :try_end
240  .catchall {:try_start .. :try_end} :catch_all
241
242  return v3  # Normal path return.
243
244  :catch_all
245  return v0  # Exceptional path #1/#2 return.
246.end method
247
248
249# Test catch-phi runtime support for 64-bit values stored in core registers.
250
251# Register pair (v0, v1) holds different long values at two throwing instructions.
252# Runtime is expected to find their location in the stack map and copy the value
253# to the location of the catch phi. The sum of the low and high 32 bits treated
254# as integers is returned to prove that both vregs were copied.
255
256# Note: values will be spilled on x86 because of too few callee-save core registers.
257
258## CHECK-START: int Runtime.testCatchPhi_long(boolean, boolean) register (after)
259## CHECK-DAG:     <<Val1:j\d+>> ArrayGet
260## CHECK-DAG:     <<Val2:j\d+>> ArrayGet
261## CHECK-DAG:                   Phi [<<Val1>>,<<Val2>>] is_catch_phi:true
262
263.method public static testCatchPhi_long(ZZ)I
264  .registers 10
265
266  sget-object v0, LRuntime;->longArray:[J
267  const/4 v2, 0
268  aget-wide v2, v0, v2
269  const/4 v4, 1
270  aget-wide v4, v0, v4
271  const/4 v6, 2
272  aget-wide v6, v0, v6
273
274  :try_start
275  move-wide v0, v2  # Set catch phi value
276  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
277
278  move-wide v0, v4  # Set catch phi value
279  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
280  :try_end
281  .catchall {:try_start .. :try_end} :catch_all
282
283  const v2, 32
284  ushr-long v2, v6, v2
285  long-to-int v2, v2
286  long-to-int v6, v6
287  add-int/2addr v6, v2
288  return v6  # Normal path return.
289
290  :catch_all
291  const v2, 32
292  ushr-long v2, v0, v2
293  long-to-int v2, v2
294  long-to-int v0, v0
295  add-int/2addr v0, v2
296  return v0  # Exceptional path #1/#2 return.
297.end method
298
299
300# Test catch-phi runtime support for 32-bit values stored in FPU registers.
301
302# Register v0 holds different float values at two throwing instructions. Runtime
303# is expected to find their location in the stack map and copy the value to the
304# location of the catch phi. The value is converted to int and returned.
305
306# Note: values will be spilled on x86 as there are no callee-save FPU registers.
307
308## CHECK-START: int Runtime.testCatchPhi_float(boolean, boolean) register (after)
309## CHECK-DAG:     <<Val1:f\d+>> ArrayGet
310## CHECK-DAG:     <<Val2:f\d+>> ArrayGet
311## CHECK-DAG:                   Phi [<<Val1>>,<<Val2>>] is_catch_phi:true
312
313.method public static testCatchPhi_float(ZZ)I
314  .registers 6
315
316  sget-object v0, LRuntime;->floatArray:[F
317  const/4 v1, 0
318  aget v1, v0, v1
319  const/4 v2, 1
320  aget v2, v0, v2
321  const/4 v3, 2
322  aget v3, v0, v3
323
324  :try_start
325  move v0, v1  # Set catch phi value
326  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
327
328  move v0, v2  # Set catch phi value
329  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
330  :try_end
331  .catchall {:try_start .. :try_end} :catch_all
332
333  float-to-int v3, v3
334  return v3  # Normal path return.
335
336  :catch_all
337  float-to-int v0, v0
338  return v0  # Exceptional path #1/#2 return.
339.end method
340
341
342# Test catch-phi runtime support for 64-bit values stored in FPU registers.
343
344# Register pair (v0, v1) holds different double values at two throwing instructions.
345# Runtime is expected to find their location in the stack map and copy the value
346# to the location of the catch phi. The value is converted to int and returned.
347# Values were chosen so that all 64 bits are used.
348
349# Note: values will be spilled on x86 as there are no callee-save FPU registers.
350
351## CHECK-START: int Runtime.testCatchPhi_double(boolean, boolean) register (after)
352## CHECK-DAG:     <<Val1:d\d+>> ArrayGet
353## CHECK-DAG:     <<Val2:d\d+>> ArrayGet
354## CHECK-DAG:                   Phi [<<Val1>>,<<Val2>>] is_catch_phi:true
355
356.method public static testCatchPhi_double(ZZ)I
357  .registers 10
358
359  sget-object v0, LRuntime;->doubleArray:[D
360  const/4 v2, 0
361  aget-wide v2, v0, v2
362  const/4 v4, 1
363  aget-wide v4, v0, v4
364  const/4 v6, 2
365  aget-wide v6, v0, v6
366
367  :try_start
368  move-wide v0, v2  # Set catch phi value
369  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
370
371  move-wide v0, v4  # Set catch phi value
372  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
373  :try_end
374  .catchall {:try_start .. :try_end} :catch_all
375
376  double-to-int v6, v6
377  return v6
378
379  :catch_all
380  double-to-int v0, v0
381  return v0
382.end method
383
384# Test catch-phi runtime support for 32-bit values stored on the stack.
385
386# Register v0 holds different integer values at two throwing instructions.
387# These values were forced to spill by an always-throwing try/catch after their
388# definition. Runtime is expected to find their location in the stack map and
389# copy the value to the location of the catch phi. The value is then returned.
390
391## CHECK-START: int Runtime.testCatchPhi_singleSlot(boolean, boolean) register (after)
392## CHECK:         <<Val1:i\d+>> ArrayGet
393## CHECK-NEXT:                  ParallelMove moves:[{{.*->}}{{\d+}}(sp)]
394## CHECK:         <<Val2:i\d+>> ArrayGet
395## CHECK-NEXT:                  ParallelMove moves:[{{.*->}}{{\d+}}(sp)]
396## CHECK:                       Phi [<<Val1>>,<<Val2>>] is_catch_phi:true
397
398.method public static testCatchPhi_singleSlot(ZZ)I
399  .registers 6
400
401  sget-object v0, LRuntime;->intArray:[I
402  const/4 v1, 0
403  aget v1, v0, v1
404  const/4 v2, 1
405  aget v2, v0, v2
406  const/4 v3, 2
407  aget v3, v0, v3
408
409  # Insert a try/catch to force v1,v2,v3 to spill.
410  :try_start_spill
411  const/4 v0, 1
412  invoke-static {v0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
413  :try_end_spill
414  .catchall {:try_start_spill .. :try_end_spill} :catch_all_spill
415  return v0         # Unreachable
416  :catch_all_spill  # Catch and continue
417
418  :try_start
419  move v0, v1  # Set catch phi value
420  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
421
422  move v0, v2  # Set catch phi value
423  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
424  :try_end
425  .catchall {:try_start .. :try_end} :catch_all
426
427  return v3  # Normal path return.
428
429  :catch_all
430  return v0  # Exceptional path #1/#2 return.
431.end method
432
433# Test catch-phi runtime support for 64-bit values stored on the stack.
434
435# Register pair (v0, v1) holds different double values at two throwing instructions.
436# These values were forced to spill by an always-throwing try/catch after their
437# definition. Runtime is expected to find their location in the stack map and
438# copy the value to the location of the catch phi. The value is converted to int
439# and returned. Values were chosen so that all 64 bits are used.
440
441## CHECK-START: int Runtime.testCatchPhi_doubleSlot(boolean, boolean) register (after)
442## CHECK:         <<Val1:d\d+>> ArrayGet
443## CHECK-NEXT:                  ParallelMove moves:[{{.*->}}2x{{\d+}}(sp)]
444## CHECK:         <<Val2:d\d+>> ArrayGet
445## CHECK-NEXT:                  ParallelMove moves:[{{.*->}}2x{{\d+}}(sp)]
446## CHECK:                       Phi [<<Val1>>,<<Val2>>] is_catch_phi:true
447
448.method public static testCatchPhi_doubleSlot(ZZ)I
449  .registers 10
450
451  sget-object v0, LRuntime;->doubleArray:[D
452  const/4 v2, 0
453  aget-wide v2, v0, v2
454  const/4 v4, 1
455  aget-wide v4, v0, v4
456  const/4 v6, 2
457  aget-wide v6, v0, v6
458
459  # Insert a try/catch to force (v2, v3), (v4, v5), (v6, v7) to spill.
460  :try_start_spill
461  const/4 v0, 1
462  invoke-static {v0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
463  :try_end_spill
464  .catchall {:try_start_spill .. :try_end_spill} :catch_all_spill
465  return v0         # Unreachable
466  :catch_all_spill  # Catch and continue
467
468  :try_start
469  move-wide v0, v2  # Set catch phi value
470  invoke-static {p0}, LRuntime;->$noinline$ThrowIfTrue(Z)V
471
472  move-wide v0, v4  # Set catch phi value
473  invoke-static {p1}, LRuntime;->$noinline$ThrowIfTrue(Z)V
474  :try_end
475  .catchall {:try_start .. :try_end} :catch_all
476
477  double-to-int v6, v6
478  return v6  # Normal path return.
479
480  :catch_all
481  double-to-int v0, v0
482  return v0  # Exceptional path #1/#2 return.
483.end method
484
485
486
487# Helper methods and initialization.
488
489.method public static $noinline$ThrowIfTrue(Z)V
490  .registers 2
491  if-nez p0, :throw
492  return-void
493
494  :throw
495  new-instance v0, Ljava/lang/Exception;
496  invoke-direct {v0}, Ljava/lang/Exception;-><init>()V
497  throw v0
498.end method
499
500.method public static constructor <clinit>()V
501  .registers 2
502
503  const/4 v1, 4
504
505  new-array v0, v1, [I
506  fill-array-data v0, :array_int
507  sput-object v0, LRuntime;->intArray:[I
508
509  new-array v0, v1, [J
510  fill-array-data v0, :array_long
511  sput-object v0, LRuntime;->longArray:[J
512
513  new-array v0, v1, [F
514  fill-array-data v0, :array_float
515  sput-object v0, LRuntime;->floatArray:[F
516
517  new-array v0, v1, [D
518  fill-array-data v0, :array_double
519  sput-object v0, LRuntime;->doubleArray:[D
520
521  return-void
522
523:array_int
524.array-data 4
525  0x03  # int 3
526  0x08  # int 8
527  0x2a  # int 42
528.end array-data
529
530:array_long
531.array-data 8
532  0x0000000100000002L # long (1 << 32) + 2
533  0x0000000500000003L # long (5 << 32) + 3
534  0x0000001e0000000cL # long (30 << 32) + 12
535.end array-data
536
537:array_float
538.array-data 4
539  0x40400000  # float 3
540  0x41000000  # float 8
541  0x42280000  # float 42
542.end array-data
543
544:array_double
545.array-data 8
546  0x400b333333333333L  # double 3.4
547  0x4020cccccccccccdL  # double 8.4
548  0x4045333333333333L  # double 42.4
549.end array-data
550.end method
551
552
553## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after)
554## CHECK-DAG: <<Const1:i\d+>>       IntConstant 1
555## CHECK-DAG: <<Offset:i\d+>>       IntConstant 12
556## CHECK-DAG: <<IndexParam:i\d+>>   ParameterValue
557## CHECK-DAG: <<Array:l\d+>>        ParameterValue
558
559## CHECK-DAG: <<NullCh1:l\d+>>      NullCheck [<<Array>>]
560## CHECK-DAG: <<Length:i\d+>>       ArrayLength
561## CHECK-DAG: <<BoundsCh1:i\d+>>    BoundsCheck [<<IndexParam>>,<<Length>>]
562## CHECK-DAG: <<IntAddr1:i\d+>>     IntermediateAddress [<<NullCh1>>,<<Offset>>]
563## CHECK-DAG:                       ArrayGet [<<IntAddr1>>,<<BoundsCh1>>]
564## CHECK-DAG:                       TryBoundary
565
566## CHECK-DAG: <<Xplus1:i\d+>>       Add [<<IndexParam>>,<<Const1>>]
567## CHECK-DAG: <<BoundsCh2:i\d+>>    BoundsCheck [<<Xplus1>>,<<Length>>]
568## CHECK-DAG:                       ArrayGet [<<IntAddr1>>,<<BoundsCh2>>]
569## CHECK-DAG:                       TryBoundary
570
571## CHECK-DAG: <<Phi:i\d+>>          Phi [<<Xplus1>>]
572## CHECK-DAG: <<Phiplus1:i\d+>>     Add [<<Phi>>,<<Const1>>]
573## CHECK-DAG: <<BoundsCh3:i\d+>>    BoundsCheck [<<Phiplus1>>,<<Length>>]
574## CHECK-DAG: <<IntAddr3:i\d+>>     IntermediateAddress [<<NullCh1>>,<<Offset>>]
575## CHECK-DAG:                       ArrayGet [<<IntAddr3>>,<<BoundsCh3>>]
576
577## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after)
578## CHECK:                           NullCheck
579## CHECK-NOT:                       NullCheck
580
581## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after)
582## CHECK:                           IntermediateAddress
583## CHECK:                           IntermediateAddress
584## CHECK-NOT:                       IntermediateAddress
585
586## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after)
587## CHECK:                           BoundsCheck
588## CHECK:                           BoundsCheck
589## CHECK:                           BoundsCheck
590## CHECK-NOT:                       BoundsCheck
591
592## CHECK-START-{ARM,ARM64}: int Runtime.testIntAddressCatch(int, int[]) GVN$after_arch (after)
593## CHECK:                           ArrayGet
594## CHECK:                           ArrayGet
595## CHECK:                           ArrayGet
596## CHECK-NOT:                       ArrayGet
597.method public static testIntAddressCatch(I[I)I
598    .registers 4
599    aget v0, p1, p0
600    add-int v1, v0, v0
601
602    :try_start
603    const/4 v0, 0x1
604    add-int p0, p0, v0
605    aget v0, p1, p0
606
607    :try_end
608    .catch Ljava/lang/ArithmException; {:try_start .. :try_end} :catch_block
609
610    :return
611    add-int v1, v1, v0
612    return v1
613
614    :catch_block
615    const/4 v0, 0x1
616    add-int p0, p0, v0
617    aget v0, p1, p0
618
619    goto :return
620.end method
621
622.field public static intArray:[I
623.field public static longArray:[J
624.field public static floatArray:[F
625.field public static doubleArray:[D
626