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