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