Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2016 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 /**
     18  * Tests properties of some string operations represented by intrinsics.
     19  */
     20 public class Main {
     21 
     22   static final String ABC = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
     23   static final String XYZ = "XYZ";
     24 
     25   //
     26   // Variant intrinsics remain in the loop, but invariant references are hoisted out of the loop.
     27   //
     28   /// CHECK-START: int Main.liveIndexOf() licm (before)
     29   /// CHECK-DAG: InvokeVirtual intrinsic:StringIndexOf            loop:{{B\d+}} outer_loop:none
     30   /// CHECK-DAG: InvokeVirtual intrinsic:StringIndexOfAfter       loop:{{B\d+}} outer_loop:none
     31   /// CHECK-DAG: InvokeVirtual intrinsic:StringStringIndexOf      loop:{{B\d+}} outer_loop:none
     32   /// CHECK-DAG: InvokeVirtual intrinsic:StringStringIndexOfAfter loop:{{B\d+}} outer_loop:none
     33   //
     34   /// CHECK-START: int Main.liveIndexOf() licm (after)
     35   /// CHECK-DAG: InvokeVirtual intrinsic:StringIndexOf            loop:{{B\d+}} outer_loop:none
     36   /// CHECK-DAG: InvokeVirtual intrinsic:StringIndexOfAfter       loop:{{B\d+}} outer_loop:none
     37   /// CHECK-DAG: InvokeVirtual intrinsic:StringStringIndexOf      loop:none
     38   /// CHECK-DAG: InvokeVirtual intrinsic:StringStringIndexOfAfter loop:none
     39   static int liveIndexOf() {
     40     int k = ABC.length() + XYZ.length();  // does LoadString before loops
     41     for (char c = 'A'; c <= 'Z'; c++) {
     42       k += ABC.indexOf(c);
     43     }
     44     for (char c = 'A'; c <= 'Z'; c++) {
     45       k += ABC.indexOf(c, 4);
     46     }
     47     for (char c = 'A'; c <= 'Z'; c++) {
     48       k += ABC.indexOf(XYZ);
     49     }
     50     for (char c = 'A'; c <= 'Z'; c++) {
     51       k += ABC.indexOf(XYZ, 2);
     52     }
     53     return k;
     54   }
     55 
     56   //
     57   // All dead intrinsics can be removed completely.
     58   //
     59   /// CHECK-START: int Main.deadIndexOf() dead_code_elimination$initial (before)
     60   /// CHECK-DAG: InvokeVirtual intrinsic:StringIndexOf            loop:{{B\d+}} outer_loop:none
     61   /// CHECK-DAG: InvokeVirtual intrinsic:StringIndexOfAfter       loop:{{B\d+}} outer_loop:none
     62   /// CHECK-DAG: InvokeVirtual intrinsic:StringStringIndexOf      loop:{{B\d+}} outer_loop:none
     63   /// CHECK-DAG: InvokeVirtual intrinsic:StringStringIndexOfAfter loop:{{B\d+}} outer_loop:none
     64   //
     65   /// CHECK-START: int Main.deadIndexOf() dead_code_elimination$initial (after)
     66   /// CHECK-NOT: InvokeVirtual intrinsic:StringIndexOf
     67   /// CHECK-NOT: InvokeVirtual intrinsic:StringIndexOfAfter
     68   /// CHECK-NOT: InvokeVirtual intrinsic:StringStringIndexOf
     69   /// CHECK-NOT: InvokeVirtual intrinsic:StringStringIndexOfAfter
     70   static int deadIndexOf() {
     71     int k = ABC.length() + XYZ.length();  // does LoadString before loops
     72     for (char c = 'A'; c <= 'Z'; c++) {
     73       int d = ABC.indexOf(c);
     74     }
     75     for (char c = 'A'; c <= 'Z'; c++) {
     76       int d = ABC.indexOf(c, 4);
     77     }
     78     for (char c = 'A'; c <= 'Z'; c++) {
     79       int d = ABC.indexOf(XYZ);
     80     }
     81     for (char c = 'A'; c <= 'Z'; c++) {
     82       int d = ABC.indexOf(XYZ, 2);
     83     }
     84     return k;
     85   }
     86 
     87   //
     88   // Explicit null check on receiver, implicit null check on argument prevents hoisting.
     89   //
     90   /// CHECK-START: int Main.indexOfExceptions(java.lang.String, java.lang.String) licm (after)
     91   /// CHECK-DAG: <<String:l\d+>> NullCheck                                                         loop:<<Loop:B\d+>> outer_loop:none
     92   /// CHECK-DAG:                 InvokeVirtual [<<String>>,{{l\d+}}] intrinsic:StringStringIndexOf loop:<<Loop>>      outer_loop:none
     93   static int indexOfExceptions(String s, String t) {
     94     int k = 0;
     95     for (char c = 'A'; c <= 'Z'; c++) {
     96       k += s.indexOf(t);
     97     }
     98     return k;
     99   }
    100 
    101   //
    102   // Allows combining of returned "this". Also ensures that similar looking append() calls
    103   // are not combined somehow through returned result.
    104   //
    105   /// CHECK-START: int Main.bufferLen2() instruction_simplifier (before)
    106   /// CHECK-DAG: <<New:l\d+>>     NewInstance
    107   /// CHECK-DAG: <<String1:l\d+>> LoadString
    108   /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<New>>,<<String1>>]   intrinsic:StringBufferAppend
    109   /// CHECK-DAG: <<String2:l\d+>> LoadString
    110   /// CHECK-DAG: <<Null1:l\d+>>   NullCheck     [<<Append1>>]
    111   /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<Null1>>,<<String2>>] intrinsic:StringBufferAppend
    112   /// CHECK-DAG: <<Null2:l\d+>>   NullCheck     [<<Append2>>]
    113   /// CHECK-DAG:                  InvokeVirtual [<<Null2>>]             intrinsic:StringBufferLength
    114   //
    115   /// CHECK-START: int Main.bufferLen2() instruction_simplifier (after)
    116   /// CHECK-DAG: <<New:l\d+>>     NewInstance
    117   /// CHECK-DAG: <<String1:l\d+>> LoadString
    118   /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<New>>,<<String1>>] intrinsic:StringBufferAppend
    119   /// CHECK-DAG: <<String2:l\d+>> LoadString
    120   /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<New>>,<<String2>>] intrinsic:StringBufferAppend
    121   /// CHECK-DAG:                  InvokeVirtual [<<New>>]             intrinsic:StringBufferLength
    122   static int bufferLen2() {
    123     StringBuffer s = new StringBuffer();
    124     return s.append("x").append("x").length();
    125   }
    126 
    127   //
    128   // Allows combining of returned "this". Also ensures that similar looking append() calls
    129   // are not combined somehow through returned result.
    130   //
    131   /// CHECK-START: int Main.builderLen2() instruction_simplifier (before)
    132   /// CHECK-DAG: <<New:l\d+>>     NewInstance
    133   /// CHECK-DAG: <<String1:l\d+>> LoadString
    134   /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<New>>,<<String1>>]   intrinsic:StringBuilderAppend
    135   /// CHECK-DAG: <<String2:l\d+>> LoadString
    136   /// CHECK-DAG: <<Null2:l\d+>>   NullCheck     [<<Append1>>]
    137   /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<Null2>>,<<String2>>] intrinsic:StringBuilderAppend
    138   /// CHECK-DAG: <<Null3:l\d+>>   NullCheck     [<<Append2>>]
    139   /// CHECK-DAG:                  InvokeVirtual [<<Null3>>]             intrinsic:StringBuilderLength
    140   //
    141   /// CHECK-START: int Main.builderLen2() instruction_simplifier (after)
    142   /// CHECK-DAG: <<New:l\d+>>     NewInstance
    143   /// CHECK-DAG: <<String1:l\d+>> LoadString
    144   /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<New>>,<<String1>>] intrinsic:StringBuilderAppend
    145   /// CHECK-DAG: <<String2:l\d+>> LoadString
    146   /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<New>>,<<String2>>] intrinsic:StringBuilderAppend
    147   /// CHECK-DAG:                  InvokeVirtual [<<New>>]             intrinsic:StringBuilderLength
    148   static int builderLen2() {
    149     StringBuilder s = new StringBuilder();
    150     return s.append("x").append("x").length();
    151   }
    152 
    153   //
    154   // Similar situation in a loop.
    155   //
    156   /// CHECK-START: int Main.bufferLoopAppender() instruction_simplifier (before)
    157   /// CHECK-DAG: <<New:l\d+>>     NewInstance                                                         loop:none
    158   /// CHECK-DAG: <<String1:l\d+>> LoadString                                                          loop:<<Loop:B\d+>>
    159   /// CHECK-DAG: <<Null1:l\d+>>   NullCheck     [<<New>>]                                             loop:<<Loop>>
    160   /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<Null1>>,<<String1>>] intrinsic:StringBufferAppend  loop:<<Loop>>
    161   /// CHECK-DAG: <<String2:l\d+>> LoadString                                                          loop:<<Loop>>
    162   /// CHECK-DAG: <<Null2:l\d+>>   NullCheck     [<<Append1>>]                                         loop:<<Loop>>
    163   /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<Null2>>,<<String2>>] intrinsic:StringBufferAppend  loop:<<Loop>>
    164   /// CHECK-DAG: <<String3:l\d+>> LoadString                                                          loop:<<Loop>>
    165   /// CHECK-DAG: <<Null3:l\d+>>   NullCheck     [<<Append2>>]                                         loop:<<Loop>>
    166   /// CHECK-DAG: <<Append3:l\d+>> InvokeVirtual [<<Null3>>,<<String3>>] intrinsic:StringBufferAppend  loop:<<Loop>>
    167   /// CHECK-DAG: <<Null4:l\d+>>   NullCheck     [<<New>>]                                             loop:none
    168   /// CHECK-DAG:                  InvokeVirtual [<<Null4>>]             intrinsic:StringBufferLength  loop:none
    169   //
    170   /// CHECK-START: int Main.bufferLoopAppender() instruction_simplifier (after)
    171   /// CHECK-DAG: <<New:l\d+>>     NewInstance                                                       loop:none
    172   /// CHECK-DAG: <<String1:l\d+>> LoadString                                                        loop:<<Loop:B\d+>>
    173   /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<New>>,<<String1>>] intrinsic:StringBufferAppend  loop:<<Loop>>
    174   /// CHECK-DAG: <<String2:l\d+>> LoadString                                                        loop:<<Loop>>
    175   /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<New>>,<<String2>>] intrinsic:StringBufferAppend  loop:<<Loop>>
    176   /// CHECK-DAG: <<String3:l\d+>> LoadString                                                        loop:<<Loop>>
    177   /// CHECK-DAG: <<Append3:l\d+>> InvokeVirtual [<<New>>,<<String3>>] intrinsic:StringBufferAppend  loop:<<Loop>>
    178   /// CHECK-DAG:                  InvokeVirtual [<<New>>]             intrinsic:StringBufferLength  loop:none
    179   static int bufferLoopAppender() {
    180     StringBuffer b = new StringBuffer();
    181     for (int i = 0; i < 10; i++) {
    182       b.append("x").append("y").append("z");
    183     }
    184     return b.length();
    185   }
    186 
    187   //
    188   // Similar situation in a loop.
    189   //
    190   /// CHECK-START: int Main.builderLoopAppender() instruction_simplifier (before)
    191   /// CHECK-DAG: <<New:l\d+>>     NewInstance                                                         loop:none
    192   /// CHECK-DAG: <<String1:l\d+>> LoadString                                                          loop:<<Loop:B\d+>>
    193   /// CHECK-DAG: <<Null1:l\d+>>   NullCheck     [<<New>>]                                             loop:<<Loop>>
    194   /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<Null1>>,<<String1>>] intrinsic:StringBuilderAppend loop:<<Loop>>
    195   /// CHECK-DAG: <<String2:l\d+>> LoadString                                                          loop:<<Loop>>
    196   /// CHECK-DAG: <<Null2:l\d+>>   NullCheck     [<<Append1>>]                                         loop:<<Loop>>
    197   /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<Null2>>,<<String2>>] intrinsic:StringBuilderAppend loop:<<Loop>>
    198   /// CHECK-DAG: <<String3:l\d+>> LoadString                                                          loop:<<Loop>>
    199   /// CHECK-DAG: <<Null3:l\d+>>   NullCheck     [<<Append2>>]                                         loop:<<Loop>>
    200   /// CHECK-DAG: <<Append3:l\d+>> InvokeVirtual [<<Null3>>,<<String3>>] intrinsic:StringBuilderAppend loop:<<Loop>>
    201   /// CHECK-DAG: <<Null4:l\d+>>   NullCheck     [<<New>>]                                             loop:none
    202   /// CHECK-DAG:                  InvokeVirtual [<<Null4>>]             intrinsic:StringBuilderLength loop:none
    203   //
    204   /// CHECK-START: int Main.builderLoopAppender() instruction_simplifier (after)
    205   /// CHECK-DAG: <<New:l\d+>>     NewInstance                                                       loop:none
    206   /// CHECK-DAG: <<String1:l\d+>> LoadString                                                        loop:<<Loop:B\d+>>
    207   /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<New>>,<<String1>>] intrinsic:StringBuilderAppend loop:<<Loop>>
    208   /// CHECK-DAG: <<String2:l\d+>> LoadString                                                        loop:<<Loop>>
    209   /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<New>>,<<String2>>] intrinsic:StringBuilderAppend loop:<<Loop>>
    210   /// CHECK-DAG: <<String3:l\d+>> LoadString                                                        loop:<<Loop>>
    211   /// CHECK-DAG: <<Append3:l\d+>> InvokeVirtual [<<New>>,<<String3>>] intrinsic:StringBuilderAppend loop:<<Loop>>
    212   /// CHECK-DAG:                  InvokeVirtual [<<New>>]             intrinsic:StringBuilderLength loop:none
    213   static int builderLoopAppender() {
    214     StringBuilder b = new StringBuilder();
    215     for (int i = 0; i < 10; i++) {
    216       b.append("x").append("y").append("z");
    217     }
    218     return b.length();
    219   }
    220 
    221   //
    222   // All calls in the loop-body and thus loop can be eliminated.
    223   //
    224   /// CHECK-START: int Main.bufferDeadLoop() instruction_simplifier (before)
    225   /// CHECK-DAG: Phi                                              loop:<<Loop:B\d+>>
    226   /// CHECK-DAG: InvokeVirtual intrinsic:StringBufferToString     loop:<<Loop>>
    227   /// CHECK-DAG: InvokeVirtual intrinsic:StringStringIndexOfAfter loop:<<Loop>>
    228   //
    229   /// CHECK-START: int Main.bufferDeadLoop() loop_optimization (after)
    230   /// CHECK-NOT: Phi
    231   /// CHECK-NOT: InvokeVirtual intrinsic:StringBufferToString
    232   /// CHECK-NOT: InvokeVirtual intrinsic:StringStringIndexOfAfter
    233   static int bufferDeadLoop() {
    234     StringBuffer b = new StringBuffer();
    235     String x = "x";
    236     for (int i = 0; i < 10; i++) {
    237       int d = b.toString().indexOf(x, 1);
    238     }
    239     return b.length();
    240   }
    241 
    242   //
    243   // All calls in the loop-body and thus loop can be eliminated.
    244   //
    245   /// CHECK-START: int Main.builderDeadLoop() instruction_simplifier (before)
    246   /// CHECK-DAG: Phi                                              loop:<<Loop:B\d+>>
    247   /// CHECK-DAG: InvokeVirtual intrinsic:StringBuilderToString    loop:<<Loop>>
    248   /// CHECK-DAG: InvokeVirtual intrinsic:StringStringIndexOfAfter loop:<<Loop>>
    249   //
    250   /// CHECK-START: int Main.builderDeadLoop() loop_optimization (after)
    251   /// CHECK-NOT: Phi
    252   /// CHECK-NOT: InvokeVirtual intrinsic:StringBuilderToString
    253   /// CHECK-NOT: InvokeVirtual intrinsic:StringStringIndexOfAfter
    254   static int builderDeadLoop() {
    255     StringBuilder b = new StringBuilder();
    256     String x = "x";
    257     for (int i = 0; i < 10; i++) {
    258       int d = b.toString().indexOf(x, 1);
    259     }
    260     return b.length();
    261   }
    262 
    263   // Regression b/33656359: StringBuffer x is passed to constructor of String
    264   // (this caused old code to crash due to missing nullptr check).
    265   //
    266   /// CHECK-START: void Main.doesNothing() instruction_simplifier (before)
    267   /// CHECK-DAG: InvokeVirtual intrinsic:StringBufferToString
    268   //
    269   /// CHECK-START: void Main.doesNothing() instruction_simplifier (after)
    270   /// CHECK-DAG: InvokeVirtual intrinsic:StringBufferToString
    271   static void doesNothing() {
    272     StringBuffer x = new StringBuffer();
    273     String y = new String(x);
    274     x.toString();
    275   }
    276 
    277   public static void main(String[] args) {
    278     expectEquals(1865, liveIndexOf());
    279     expectEquals(29, deadIndexOf());
    280 
    281     try {
    282       indexOfExceptions(null, XYZ);
    283       throw new Error("Expected: NPE");
    284     } catch (NullPointerException e) {
    285     }
    286     try {
    287       indexOfExceptions(ABC, null);
    288       throw new Error("Expected: NPE");
    289     } catch (NullPointerException e) {
    290     }
    291     expectEquals(598, indexOfExceptions(ABC, XYZ));
    292 
    293     expectEquals(2, bufferLen2());
    294     expectEquals(2, builderLen2());
    295     expectEquals(30, bufferLoopAppender());
    296     expectEquals(30, builderLoopAppender());
    297     expectEquals(0, bufferDeadLoop());
    298     expectEquals(0, builderDeadLoop());
    299 
    300     doesNothing();
    301 
    302     System.out.println("passed");
    303   }
    304 
    305   private static void expectEquals(int expected, int result) {
    306     if (expected != result) {
    307       throw new Error("Expected: " + expected + ", found: " + result);
    308     }
    309   }
    310 }
    311