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