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