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
liveIndexOf()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
deadIndexOf()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
indexOfExceptions(String s, String t)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
bufferLen2()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
builderLen2()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
bufferLoopAppender()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
builderLoopAppender()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
bufferDeadLoop()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
builderDeadLoop()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
doesNothing()271   static void doesNothing() {
272     StringBuffer x = new StringBuffer();
273     String y = new String(x);
274     x.toString();
275   }
276 
main(String[] args)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 
expectEquals(int expected, int result)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