blob: 055a4d7ec97e4936e86bd76851a7df53b7014a16 [file] [log] [blame]
Aart Bikff7d89c2016-11-07 08:49:28 -08001/*
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 */
Alan Leung4a509be2017-10-03 22:33:47 -070016import java.lang.reflect.Method;
Aart Bikff7d89c2016-11-07 08:49:28 -080017
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() {
Ian Zerny51e52c02018-09-20 07:12:46 +000041 int k = ABC.length() + XYZ.length(); // does LoadString before loops
Aart Bikff7d89c2016-11-07 08:49:28 -080042 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
Aart Bik71bf7b42016-11-16 10:17:46 -0800102 //
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
Alan Leung4a509be2017-10-03 22:33:47 -0700109 /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<New>>,<<String1>>] intrinsic:StringBufferAppend
Aart Bik71bf7b42016-11-16 10:17:46 -0800110 /// CHECK-DAG: <<String2:l\d+>> LoadString
Alan Leung4a509be2017-10-03 22:33:47 -0700111 /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [{{l\d+}},<<String2>>] intrinsic:StringBufferAppend
112 /// CHECK-DAG: InvokeVirtual [{{l\d+}}] intrinsic:StringBufferLength
Aart Bik71bf7b42016-11-16 10:17:46 -0800113 //
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();
Jinseong Jeonbf44f152018-11-01 22:45:29 -0700123 return s.append("x").append("y").length();
Aart Bik71bf7b42016-11-16 10:17:46 -0800124 }
125
Alan Leung4a509be2017-10-03 22:33:47 -0700126 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
Aart Bik71bf7b42016-11-16 10:17:46 -0800132 //
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
Vladimir Markod4561172017-10-30 17:48:25 +0000139 /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<New>>,<<String1>>] intrinsic:StringBuilderAppendString
Aart Bik71bf7b42016-11-16 10:17:46 -0800140 /// CHECK-DAG: <<String2:l\d+>> LoadString
Vladimir Markod4561172017-10-30 17:48:25 +0000141 /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [{{l\d+}},<<String2>>] intrinsic:StringBuilderAppendString
Alan Leung4a509be2017-10-03 22:33:47 -0700142 /// CHECK-DAG: InvokeVirtual [{{l\d+}}] intrinsic:StringBuilderLength
Aart Bik71bf7b42016-11-16 10:17:46 -0800143 //
144 /// CHECK-START: int Main.builderLen2() instruction_simplifier (after)
145 /// CHECK-DAG: <<New:l\d+>> NewInstance
146 /// CHECK-DAG: <<String1:l\d+>> LoadString
Vladimir Markod4561172017-10-30 17:48:25 +0000147 /// CHECK-DAG: <<Append1:l\d+>> InvokeVirtual [<<New>>,<<String1>>] intrinsic:StringBuilderAppendString
Aart Bik71bf7b42016-11-16 10:17:46 -0800148 /// CHECK-DAG: <<String2:l\d+>> LoadString
Vladimir Markod4561172017-10-30 17:48:25 +0000149 /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<New>>,<<String2>>] intrinsic:StringBuilderAppendString
Aart Bik71bf7b42016-11-16 10:17:46 -0800150 /// CHECK-DAG: InvokeVirtual [<<New>>] intrinsic:StringBuilderLength
151 static int builderLen2() {
152 StringBuilder s = new StringBuilder();
Jinseong Jeonbf44f152018-11-01 22:45:29 -0700153 return s.append("x").append("y").length();
Aart Bik71bf7b42016-11-16 10:17:46 -0800154 }
155
Alan Leung4a509be2017-10-03 22:33:47 -0700156 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
Aart Bik71bf7b42016-11-16 10:17:46 -0800162 //
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>>
Alan Leung4a509be2017-10-03 22:33:47 -0700171 /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [{{l\d+}},<<String2>>] intrinsic:StringBufferAppend loop:<<Loop>>
Aart Bik71bf7b42016-11-16 10:17:46 -0800172 /// CHECK-DAG: <<String3:l\d+>> LoadString loop:<<Loop>>
Alan Leung4a509be2017-10-03 22:33:47 -0700173 /// CHECK-DAG: <<Append3:l\d+>> InvokeVirtual [{{l\d+}},<<String3>>] intrinsic:StringBufferAppend loop:<<Loop>>
174 /// CHECK-DAG: InvokeVirtual [{{l\d+}}] intrinsic:StringBufferLength loop:none
Aart Bik71bf7b42016-11-16 10:17:46 -0800175 //
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
Alan Leung4a509be2017-10-03 22:33:47 -0700193 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
Aart Bik71bf7b42016-11-16 10:17:46 -0800199 //
200 // Similar situation in a loop.
201 //
202 /// CHECK-START: int Main.builderLoopAppender() instruction_simplifier (before)
Vladimir Markod4561172017-10-30 17:48:25 +0000203 /// 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:StringBuilderAppendString loop:<<Loop>>
207 /// CHECK-DAG: <<String2:l\d+>> LoadString loop:<<Loop>>
208 /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [{{l\d+}},<<String2>>] intrinsic:StringBuilderAppendString loop:<<Loop>>
209 /// CHECK-DAG: <<String3:l\d+>> LoadString loop:<<Loop>>
210 /// CHECK-DAG: <<Append3:l\d+>> InvokeVirtual [{{l\d+}},<<String3>>] intrinsic:StringBuilderAppendString loop:<<Loop>>
211 /// CHECK-DAG: InvokeVirtual [{{l\d+}}] intrinsic:StringBuilderLength loop:none
Aart Bik71bf7b42016-11-16 10:17:46 -0800212 //
213 /// CHECK-START: int Main.builderLoopAppender() instruction_simplifier (after)
Vladimir Markod4561172017-10-30 17:48:25 +0000214 /// 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:StringBuilderAppendString loop:<<Loop>>
217 /// CHECK-DAG: <<String2:l\d+>> LoadString loop:<<Loop>>
218 /// CHECK-DAG: <<Append2:l\d+>> InvokeVirtual [<<New>>,<<String2>>] intrinsic:StringBuilderAppendString loop:<<Loop>>
219 /// CHECK-DAG: <<String3:l\d+>> LoadString loop:<<Loop>>
220 /// CHECK-DAG: <<Append3:l\d+>> InvokeVirtual [<<New>>,<<String3>>] intrinsic:StringBuilderAppendString loop:<<Loop>>
221 /// CHECK-DAG: InvokeVirtual [<<New>>] intrinsic:StringBuilderLength loop:none
Aart Bik71bf7b42016-11-16 10:17:46 -0800222 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
Alan Leung4a509be2017-10-03 22:33:47 -0700230 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
Aart Bik71bf7b42016-11-16 10:17:46 -0800236 //
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();
Vladimir Marko3e86bb02017-02-16 12:47:06 +0000250 String x = "x";
Aart Bik71bf7b42016-11-16 10:17:46 -0800251 for (int i = 0; i < 10; i++) {
Vladimir Marko3e86bb02017-02-16 12:47:06 +0000252 int d = b.toString().indexOf(x, 1);
Aart Bik71bf7b42016-11-16 10:17:46 -0800253 }
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();
Vladimir Marko3e86bb02017-02-16 12:47:06 +0000271 String x = "x";
Aart Bik71bf7b42016-11-16 10:17:46 -0800272 for (int i = 0; i < 10; i++) {
Vladimir Marko3e86bb02017-02-16 12:47:06 +0000273 int d = b.toString().indexOf(x, 1);
Aart Bik71bf7b42016-11-16 10:17:46 -0800274 }
275 return b.length();
276 }
277
Aart Bikab2270f2016-12-15 09:36:31 -0800278 // 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
Alan Leung4a509be2017-10-03 22:33:47 -0700292 public static void main(String[] args) throws Exception {
Ian Zerny51e52c02018-09-20 07:12:46 +0000293 expectEquals(1865, liveIndexOf());
Aart Bikff7d89c2016-11-07 08:49:28 -0800294 expectEquals(29, deadIndexOf());
Aart Bik71bf7b42016-11-16 10:17:46 -0800295
Aart Bikff7d89c2016-11-07 08:49:28 -0800296 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
Aart Bik71bf7b42016-11-16 10:17:46 -0800308 expectEquals(2, bufferLen2());
Alan Leung4a509be2017-10-03 22:33:47 -0700309 expectEquals(2, bufferLen2Smali());
Aart Bik71bf7b42016-11-16 10:17:46 -0800310 expectEquals(2, builderLen2());
Alan Leung4a509be2017-10-03 22:33:47 -0700311 expectEquals(2, builderLen2Smali());
Aart Bik71bf7b42016-11-16 10:17:46 -0800312 expectEquals(30, bufferLoopAppender());
Alan Leung4a509be2017-10-03 22:33:47 -0700313 expectEquals(30, bufferLoopAppenderSmali());
Aart Bik71bf7b42016-11-16 10:17:46 -0800314 expectEquals(30, builderLoopAppender());
Alan Leung4a509be2017-10-03 22:33:47 -0700315 expectEquals(30, builderLoopAppenderSmali());
Aart Bik71bf7b42016-11-16 10:17:46 -0800316 expectEquals(0, bufferDeadLoop());
317 expectEquals(0, builderDeadLoop());
318
Aart Bikab2270f2016-12-15 09:36:31 -0800319 doesNothing();
320
Aart Bikff7d89c2016-11-07 08:49:28 -0800321 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}