1 /* 2 * Copyright (C) 2015 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 public class Main { 18 public static String smallString = generateString(100); 19 public static String mediumString = generateString(300); 20 public static String largeString = generateString(2000); 21 generateString(int length)22 public static String generateString(int length) { 23 // Generate a string in the ASCII range that will 24 // use string compression. 25 StringBuilder sb = new StringBuilder(); 26 for (int i = 0; i < length; i++) { 27 // Generate repeating alphabet. 28 sb.append(Character.valueOf((char)('a' + (i % 26)))); 29 } 30 return sb.toString(); 31 } 32 assertIntEquals(int expected, int result)33 public static void assertIntEquals(int expected, int result) { 34 if (expected != result) { 35 throw new Error("Expected: " + expected + ", found: " + result); 36 } 37 } 38 assertBooleanEquals(boolean expected, boolean result)39 public static void assertBooleanEquals(boolean expected, boolean result) { 40 if (expected != result) { 41 throw new Error("Expected: " + expected + ", found: " + result); 42 } 43 } 44 assertCharEquals(char expected, char result)45 public static void assertCharEquals(char expected, char result) { 46 if (expected != result) { 47 throw new Error("Expected: " + expected + ", found: " + result); 48 } 49 } 50 assertStringEquals(String expected, String result)51 public static void assertStringEquals(String expected, String result) { 52 if (!expected.equals(result)) { 53 throw new Error("Expected: " + expected + ", found: " + result); 54 } 55 } 56 assertStringContains(String searchTerm, String result)57 public static void assertStringContains(String searchTerm, String result) { 58 if (result == null || !result.contains(searchTerm)) { 59 throw new Error("Search term: " + searchTerm + ", not found in: " + result); 60 } 61 } 62 main(String[] args)63 public static void main(String[] args) { 64 stringEqualsSame(); 65 stringArgumentNotNull("Foo"); 66 67 assertIntEquals(0, $opt$noinline$getStringLength("")); 68 assertIntEquals(3, $opt$noinline$getStringLength("abc")); 69 assertIntEquals(10, $opt$noinline$getStringLength("0123456789")); 70 71 assertBooleanEquals(true, $opt$noinline$isStringEmpty("")); 72 assertBooleanEquals(false, $opt$noinline$isStringEmpty("abc")); 73 assertBooleanEquals(false, $opt$noinline$isStringEmpty("0123456789")); 74 75 assertCharEquals('a', $opt$noinline$stringCharAt("a", 0)); 76 assertCharEquals('a', $opt$noinline$stringCharAt("abc", 0)); 77 assertCharEquals('b', $opt$noinline$stringCharAt("abc", 1)); 78 assertCharEquals('c', $opt$noinline$stringCharAt("abc", 2)); 79 assertCharEquals('7', $opt$noinline$stringCharAt("0123456789", 7)); 80 81 // Single character. 82 assertStringEquals("a", stringGetCharsAndBack("a")); 83 // Strings < 8 characters. 84 assertStringEquals("foobar", stringGetCharsAndBack("foobar")); 85 // Strings > 8 characters of various lengths. 86 assertStringEquals(smallString, stringGetCharsAndBack(smallString)); 87 assertStringEquals(mediumString, stringGetCharsAndBack(mediumString)); 88 assertStringEquals(largeString, stringGetCharsAndBack(largeString)); 89 90 // Get only a substring: 91 // Substring < 8 characters. 92 assertStringEquals(smallString.substring(5, 10), stringGetCharsRange(smallString, 5, 10, 0)); 93 // Substring > 8 characters. 94 assertStringEquals(smallString.substring(7, 28), stringGetCharsRange(smallString, 7, 28, 0)); 95 96 // Get full string with offset in the char array. 97 assertStringEquals(smallString, stringGetCharsAndBackOffset(smallString, 17)); 98 99 // Get a substring with an offset in the char array. 100 // Substring < 8 characters. 101 assertStringEquals(smallString.substring(5, 10), stringGetCharsRange(smallString, 5, 10, 17)); 102 // Substring > 8 characters. 103 assertStringEquals(smallString.substring(7, 28), stringGetCharsRange(smallString, 7, 28, 17)); 104 105 try { 106 $opt$noinline$stringCharAt("abc", -1); 107 throw new Error("Should throw SIOOB."); 108 } catch (StringIndexOutOfBoundsException sioob) { 109 assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString()); 110 assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString()); 111 } 112 try { 113 $opt$noinline$stringCharAt("abc", 3); 114 throw new Error("Should throw SIOOB."); 115 } catch (StringIndexOutOfBoundsException sioob) { 116 assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString()); 117 assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString()); 118 } 119 try { 120 $opt$noinline$stringCharAt("abc", Integer.MAX_VALUE); 121 throw new Error("Should throw SIOOB."); 122 } catch (StringIndexOutOfBoundsException sioob) { 123 assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString()); 124 assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString()); 125 } 126 127 assertCharEquals('7', $opt$noinline$stringCharAtCatch("0123456789", 7)); 128 assertCharEquals('\0', $opt$noinline$stringCharAtCatch("0123456789", 10)); 129 130 assertCharEquals('7', $opt$noinline$stringCharAtCatchPhiReturn("0123456789", 7)); 131 assertCharEquals('\0', $opt$noinline$stringCharAtCatchPhiReturn("0123456789", 10)); 132 133 assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumChars("abc")); 134 assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumLeadingChars("abcdef", 3)); 135 try { 136 $opt$noinline$stringSumLeadingChars("abcdef", 7); 137 throw new Error("Should throw SIOOB."); 138 } catch (StringIndexOutOfBoundsException sioob) { 139 assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString()); 140 assertStringContains("Main.$opt$noinline$stringSumLeadingChars", 141 sioob.getStackTrace()[1].toString()); 142 } 143 assertIntEquals('a' + 'b' + 'c' + 'd', $opt$noinline$stringSum4LeadingChars("abcdef")); 144 try { 145 $opt$noinline$stringSum4LeadingChars("abc"); 146 throw new Error("Should throw SIOOB."); 147 } catch (StringIndexOutOfBoundsException sioob) { 148 assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString()); 149 assertStringContains("Main.$opt$noinline$stringSum4LeadingChars", 150 sioob.getStackTrace()[1].toString()); 151 } 152 } 153 154 /// CHECK-START: int Main.$opt$noinline$getStringLength(java.lang.String) builder (after) 155 /// CHECK-DAG: <<String:l\d+>> ParameterValue 156 /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>] 157 /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true 158 /// CHECK-DAG: Return [<<Length>>] 159 160 /// CHECK-START: int Main.$opt$noinline$getStringLength(java.lang.String) builder (after) 161 /// CHECK-NOT: InvokeVirtual 162 $opt$noinline$getStringLength(String s)163 static public int $opt$noinline$getStringLength(String s) { 164 return s.length(); 165 } 166 167 /// CHECK-START: boolean Main.$opt$noinline$isStringEmpty(java.lang.String) builder (after) 168 /// CHECK-DAG: <<String:l\d+>> ParameterValue 169 /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 170 /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>] 171 /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true 172 /// CHECK-DAG: <<IsEmpty:z\d+>> Equal [<<Length>>,<<Const0>>] 173 /// CHECK-DAG: Return [<<IsEmpty>>] 174 175 /// CHECK-START: boolean Main.$opt$noinline$isStringEmpty(java.lang.String) builder (after) 176 /// CHECK-NOT: InvokeVirtual 177 $opt$noinline$isStringEmpty(String s)178 static public boolean $opt$noinline$isStringEmpty(String s) { 179 return s.isEmpty(); 180 } 181 182 /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) builder (after) 183 /// CHECK-DAG: <<String:l\d+>> ParameterValue 184 /// CHECK-DAG: <<Pos:i\d+>> ParameterValue 185 /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>] 186 /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true 187 /// CHECK-DAG: <<Bounds:i\d+>> BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true 188 /// CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Bounds>>] is_string_char_at:true 189 /// CHECK-DAG: Return [<<Char>>] 190 191 /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) builder (after) 192 /// CHECK-NOT: InvokeVirtual 193 $opt$noinline$stringCharAt(String s, int pos)194 static public char $opt$noinline$stringCharAt(String s, int pos) { 195 return s.charAt(pos); 196 } 197 198 /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) builder (after) 199 /// CHECK-DAG: <<String:l\d+>> ParameterValue 200 /// CHECK-DAG: <<Pos:i\d+>> ParameterValue 201 /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>] 202 /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true 203 /// CHECK-DAG: <<Bounds:i\d+>> BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true 204 /// CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Bounds>>] is_string_char_at:true 205 /// CHECK-DAG: Return [{{(c|i)\d+}}] 206 207 /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) builder (after) 208 /// CHECK-NOT: InvokeVirtual 209 $opt$noinline$stringCharAtCatch(String s, int pos)210 static public char $opt$noinline$stringCharAtCatch(String s, int pos) { 211 try { 212 return s.charAt(pos); 213 } catch (StringIndexOutOfBoundsException ignored) { 214 return '\0'; 215 } 216 } 217 218 /// CHECK-START: char Main.$opt$noinline$stringCharAtCatchPhiReturn(java.lang.String, int) builder (after) 219 /// CHECK-DAG: <<String:l\d+>> ParameterValue 220 /// CHECK-DAG: <<Pos:i\d+>> ParameterValue 221 /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 222 /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>] 223 /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true 224 /// CHECK-DAG: <<Bounds:i\d+>> BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true 225 /// CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Bounds>>] is_string_char_at:true 226 /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Char>>,<<Int>>] 227 /// CHECK-DAG: Return [<<Phi>>] 228 229 /// CHECK-START: char Main.$opt$noinline$stringCharAtCatchPhiReturn(java.lang.String, int) instruction_simplifier (after) 230 /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt 231 $opt$noinline$stringCharAtCatchPhiReturn(String s, int pos)232 static public char $opt$noinline$stringCharAtCatchPhiReturn(String s, int pos) { 233 char result; 234 try { 235 result = s.charAt(pos); 236 } catch (StringIndexOutOfBoundsException ignored) { 237 result = '\0'; 238 } 239 return result; 240 } 241 242 /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) builder (after) 243 /// CHECK-DAG: ArrayLength is_string_length:true 244 /// CHECK-DAG: ArrayLength is_string_length:true 245 /// CHECK-DAG: BoundsCheck is_string_char_at:true 246 /// CHECK-DAG: ArrayGet is_string_char_at:true 247 248 /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) builder (after) 249 /// CHECK-NOT: InvokeVirtual 250 251 /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) GVN (after) 252 /// CHECK-DAG: ArrayLength is_string_length:true 253 /// CHECK-NOT: ArrayLength is_string_length:true 254 255 /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) BCE (after) 256 /// CHECK-NOT: BoundsCheck 257 $opt$noinline$stringSumChars(String s)258 static public int $opt$noinline$stringSumChars(String s) { 259 int sum = 0; 260 int len = s.length(); 261 for (int i = 0; i < len; ++i) { 262 sum += s.charAt(i); 263 } 264 return sum; 265 } 266 267 /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) builder (after) 268 /// CHECK-DAG: ArrayLength is_string_length:true 269 /// CHECK-DAG: BoundsCheck is_string_char_at:true 270 /// CHECK-DAG: ArrayGet is_string_char_at:true 271 272 /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) builder (after) 273 /// CHECK-NOT: InvokeVirtual 274 275 /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) BCE (after) 276 /// CHECK-DAG: Deoptimize env:[[{{[^\]]*}}]] 277 278 /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) BCE (after) 279 /// CHECK-NOT: BoundsCheck is_string_char_at:true 280 $opt$noinline$stringSumLeadingChars(String s, int n)281 static public int $opt$noinline$stringSumLeadingChars(String s, int n) { 282 int sum = 0; 283 for (int i = 0; i < n; ++i) { 284 sum += s.charAt(i); 285 } 286 return sum; 287 } 288 289 /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) builder (after) 290 /// CHECK-DAG: ArrayLength is_string_length:true 291 /// CHECK-DAG: BoundsCheck is_string_char_at:true 292 /// CHECK-DAG: ArrayGet is_string_char_at:true 293 /// CHECK-DAG: ArrayLength is_string_length:true 294 /// CHECK-DAG: BoundsCheck is_string_char_at:true 295 /// CHECK-DAG: ArrayGet is_string_char_at:true 296 /// CHECK-DAG: ArrayLength is_string_length:true 297 /// CHECK-DAG: BoundsCheck is_string_char_at:true 298 /// CHECK-DAG: ArrayGet is_string_char_at:true 299 /// CHECK-DAG: ArrayLength is_string_length:true 300 /// CHECK-DAG: BoundsCheck is_string_char_at:true 301 /// CHECK-DAG: ArrayGet is_string_char_at:true 302 303 /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) builder (after) 304 /// CHECK-NOT: InvokeVirtual 305 306 /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) BCE (after) 307 /// CHECK-DAG: Deoptimize env:[[{{[^\]]*}}]] 308 309 /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) BCE (after) 310 /// CHECK-NOT: BoundsCheck is_string_char_at:true 311 $opt$noinline$stringSum4LeadingChars(String s)312 static public int $opt$noinline$stringSum4LeadingChars(String s) { 313 int sum = s.charAt(0) + s.charAt(1) + s.charAt(2) + s.charAt(3); 314 return sum; 315 } 316 317 /// CHECK-START: boolean Main.stringEqualsSame() instruction_simplifier (before) 318 /// CHECK: InvokeStaticOrDirect 319 320 /// CHECK-START: boolean Main.stringEqualsSame() register (before) 321 /// CHECK: <<Const1:i\d+>> IntConstant 1 322 /// CHECK: Return [<<Const1>>] 323 324 /// CHECK-START: boolean Main.stringEqualsSame() register (before) 325 /// CHECK-NOT: InvokeStaticOrDirect stringEqualsSame()326 public static boolean stringEqualsSame() { 327 return $inline$callStringEquals("obj", "obj"); 328 } 329 330 /// CHECK-START: boolean Main.stringEqualsNull() register (after) 331 /// CHECK: <<Invoke:z\d+>> InvokeVirtual 332 /// CHECK: Return [<<Invoke>>] stringEqualsNull()333 public static boolean stringEqualsNull() { 334 String o = (String)myObject; 335 return $inline$callStringEquals(o, o); 336 } 337 $inline$callStringEquals(String a, String b)338 public static boolean $inline$callStringEquals(String a, String b) { 339 return a.equals(b); 340 } 341 342 /// CHECK-START-X86: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after) 343 /// CHECK: InvokeVirtual {{.*\.equals.*}} intrinsic:StringEquals 344 /// CHECK-NOT: test 345 346 /// CHECK-START-X86_64: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after) 347 /// CHECK: InvokeVirtual {{.*\.equals.*}} intrinsic:StringEquals 348 /// CHECK-NOT: test 349 350 /// CHECK-START-ARM: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after) 351 /// CHECK: InvokeVirtual {{.*\.equals.*}} intrinsic:StringEquals 352 // CompareAndBranchIfZero() may emit either CBZ or CMP+BEQ. 353 /// CHECK-NOT: cbz 354 /// CHECK-NOT: cmp {{r\d+}}, #0 355 // Terminate the scope for the CHECK-NOT search at the reference or length comparison, 356 // whichever comes first. 357 /// CHECK: cmp {{r\d+}}, {{r\d+}} 358 359 /// CHECK-START-ARM64: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after) 360 /// CHECK: InvokeVirtual {{.*\.equals.*}} intrinsic:StringEquals 361 /// CHECK-NOT: cbz 362 // Terminate the scope for the CHECK-NOT search at the reference or length comparison, 363 // whichever comes first. 364 /// CHECK: cmp {{w.*,}} {{w.*|#.*}} stringArgumentNotNull(Object obj)365 public static boolean stringArgumentNotNull(Object obj) { 366 obj.getClass(); 367 return "foo".equals(obj); 368 } 369 370 // Test is very brittle as it depends on the order we emit instructions. 371 /// CHECK-START-X86: boolean Main.stringArgumentIsString() disassembly (after) 372 /// CHECK: InvokeVirtual intrinsic:StringEquals 373 /// CHECK: test 374 /// CHECK: jz/eq 375 // Check that we don't try to compare the classes. 376 /// CHECK-NOT: mov 377 /// CHECK: cmp 378 379 // Test is very brittle as it depends on the order we emit instructions. 380 /// CHECK-START-X86_64: boolean Main.stringArgumentIsString() disassembly (after) 381 /// CHECK: InvokeVirtual intrinsic:StringEquals 382 /// CHECK: test 383 /// CHECK: jz/eq 384 // Check that we don't try to compare the classes. 385 /// CHECK-NOT: mov 386 /// CHECK: cmp 387 388 // Test is brittle as it depends on the class offset being 0. 389 /// CHECK-START-ARM: boolean Main.stringArgumentIsString() disassembly (after) 390 /// CHECK: InvokeVirtual intrinsic:StringEquals 391 /// CHECK: {{cbz|cmp}} 392 // Check that we don't try to compare the classes. 393 // The dissassembler currently explicitly emits the offset 0 but don't rely on it. 394 // We want to terminate the CHECK-NOT search after two CMPs, one for reference 395 // equality and one for length comparison but these may be emitted in different order, 396 // so repeat the check twice. 397 /// CHECK-NOT: ldr{{(|.w)}} {{r\d+}}, [{{r\d+}}] 398 /// CHECK-NOT: ldr{{(|.w)}} {{r\d+}}, [{{r\d+}}, #0] 399 /// CHECK: cmp {{r\d+}}, {{r\d+}} 400 /// CHECK-NOT: ldr{{(|.w)}} {{r\d+}}, [{{r\d+}}] 401 /// CHECK-NOT: ldr{{(|.w)}} {{r\d+}}, [{{r\d+}}, #0] 402 /// CHECK: cmp {{r\d+}}, {{r\d+}} 403 404 // Test is brittle as it depends on the class offset being 0. 405 /// CHECK-START-ARM64: boolean Main.stringArgumentIsString() disassembly (after) 406 /// CHECK: InvokeVirtual intrinsic:StringEquals 407 /// CHECK: cbz 408 // Check that we don't try to compare the classes. 409 // The dissassembler currently does not explicitly emits the offset 0 but don't rely on it. 410 // We want to terminate the CHECK-NOT search after two CMPs, one for reference 411 // equality and one for length comparison but these may be emitted in different order, 412 // so repeat the check twice. 413 /// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}] 414 /// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}, #0] 415 /// CHECK: cmp {{w\d+}}, {{w\d+|#.*}} 416 /// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}] 417 /// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}, #0] 418 /// CHECK: cmp {{w\d+}}, {{w\d+|#.*}} stringArgumentIsString()419 public static boolean stringArgumentIsString() { 420 return "foo".equals(myString); 421 } 422 423 static String myString; 424 static Object myObject; 425 stringGetCharsAndBack(String src)426 public static String stringGetCharsAndBack(String src) { 427 char[] dst = new char[src.length()]; 428 src.getChars(0, src.length(), dst, 0); 429 return new String(dst); 430 } 431 stringGetCharsAndBackOffset(String src, int offset)432 public static String stringGetCharsAndBackOffset(String src, int offset) { 433 char[] dst = new char[src.length() + offset]; 434 src.getChars(0, src.length(), dst, offset); 435 return new String(dst, offset, src.length()); 436 } 437 stringGetCharsRange(String src, int srcBegin, int srcEnd, int offset)438 public static String stringGetCharsRange(String src, int srcBegin, int srcEnd, int offset) { 439 char[] dst = new char[srcEnd - srcBegin + offset]; 440 src.getChars(srcBegin, srcEnd, dst, offset); 441 return new String(dst, offset, srcEnd - srcBegin); 442 } 443 } 444