content: /*
   * Copyright (C) 2015 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  
  public class Main {
    public static boolean doThrow = false;
  
    public static void assertIntEquals(int expected, int result) {
      if (expected != result) {
        throw new Error("Expected: " + expected + ", found: " + result);
      }
    }
  
    public static void assertBooleanEquals(boolean expected, boolean result) {
      if (expected != result) {
        throw new Error("Expected: " + expected + ", found: " + result);
      }
    }
  
    public static void assertCharEquals(char expected, char result) {
      if (expected != result) {
        throw new Error("Expected: " + expected + ", found: " + result);
      }
    }
  
    public static void assertStringContains(String searchTerm, String result) {
      if (result == null || !result.contains(searchTerm)) {
        throw new Error("Search term: " + searchTerm + ", not found in: " + result);
      }
    }
  
    public static void main(String[] args) {
      stringEqualsSame();
      stringArgumentNotNull("Foo");
  
      assertIntEquals(0, $opt$noinline$getStringLength(""));
      assertIntEquals(3, $opt$noinline$getStringLength("abc"));
      assertIntEquals(10, $opt$noinline$getStringLength("0123456789"));
  
      assertBooleanEquals(true, $opt$noinline$isStringEmpty(""));
      assertBooleanEquals(false, $opt$noinline$isStringEmpty("abc"));
      assertBooleanEquals(false, $opt$noinline$isStringEmpty("0123456789"));
  
      assertCharEquals('a', $opt$noinline$stringCharAt("a", 0));
      assertCharEquals('a', $opt$noinline$stringCharAt("abc", 0));
      assertCharEquals('b', $opt$noinline$stringCharAt("abc", 1));
      assertCharEquals('c', $opt$noinline$stringCharAt("abc", 2));
      assertCharEquals('7', $opt$noinline$stringCharAt("0123456789", 7));
  
      try {
        $opt$noinline$stringCharAt("abc", -1);
        throw new Error("Should throw SIOOB.");
      } catch (StringIndexOutOfBoundsException sioob) {
        assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
        assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString());
      }
      try {
        $opt$noinline$stringCharAt("abc", 3);
        throw new Error("Should throw SIOOB.");
      } catch (StringIndexOutOfBoundsException sioob) {
        assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
        assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString());
      }
      try {
        $opt$noinline$stringCharAt("abc", Integer.MAX_VALUE);
        throw new Error("Should throw SIOOB.");
      } catch (StringIndexOutOfBoundsException sioob) {
        assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
        assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString());
      }
  
      assertCharEquals('7', $opt$noinline$stringCharAtCatch("0123456789", 7));
      assertCharEquals('\0', $opt$noinline$stringCharAtCatch("0123456789", 10));
  
      assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumChars("abc"));
      assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumLeadingChars("abcdef", 3));
      try {
        $opt$noinline$stringSumLeadingChars("abcdef", 7);
        throw new Error("Should throw SIOOB.");
      } catch (StringIndexOutOfBoundsException sioob) {
        assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
        assertStringContains("Main.$opt$noinline$stringSumLeadingChars",
                             sioob.getStackTrace()[1].toString());
      }
      assertIntEquals('a' + 'b' + 'c' + 'd', $opt$noinline$stringSum4LeadingChars("abcdef"));
      try {
        $opt$noinline$stringSum4LeadingChars("abc");
        throw new Error("Should throw SIOOB.");
      } catch (StringIndexOutOfBoundsException sioob) {
        assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
        assertStringContains("Main.$opt$noinline$stringSum4LeadingChars",
                             sioob.getStackTrace()[1].toString());
      }
    }
  
    /// CHECK-START: int Main.$opt$noinline$getStringLength(java.lang.String) instruction_simplifier (before)
    /// CHECK-DAG:  <<Length:i\d+>>   InvokeVirtual intrinsic:StringLength
    /// CHECK-DAG:                    Return [<<Length>>]
  
    /// CHECK-START: int Main.$opt$noinline$getStringLength(java.lang.String) instruction_simplifier (after)
    /// CHECK-DAG:  <<String:l\d+>>   ParameterValue
    /// CHECK-DAG:  <<NullCk:l\d+>>   NullCheck [<<String>>]
    /// CHECK-DAG:  <<Length:i\d+>>   ArrayLength [<<NullCk>>] is_string_length:true
    /// CHECK-DAG:                    Return [<<Length>>]
  
    /// CHECK-START: int Main.$opt$noinline$getStringLength(java.lang.String) instruction_simplifier (after)
    /// CHECK-NOT:                    InvokeVirtual intrinsic:StringLength
  
    static public int $opt$noinline$getStringLength(String s) {
      if (doThrow) { throw new Error(); }
      return s.length();
    }
  
    /// CHECK-START: boolean Main.$opt$noinline$isStringEmpty(java.lang.String) instruction_simplifier (before)
    /// CHECK-DAG:  <<IsEmpty:z\d+>>  InvokeVirtual intrinsic:StringIsEmpty
    /// CHECK-DAG:                    Return [<<IsEmpty>>]
  
    /// CHECK-START: boolean Main.$opt$noinline$isStringEmpty(java.lang.String) instruction_simplifier (after)
    /// CHECK-DAG:  <<String:l\d+>>   ParameterValue
    /// CHECK-DAG:  <<Const0:i\d+>>   IntConstant 0
    /// CHECK-DAG:  <<NullCk:l\d+>>   NullCheck [<<String>>]
    /// CHECK-DAG:  <<Length:i\d+>>   ArrayLength [<<NullCk>>] is_string_length:true
    /// CHECK-DAG:  <<IsEmpty:z\d+>>  Equal [<<Length>>,<<Const0>>]
    /// CHECK-DAG:                    Return [<<IsEmpty>>]
  
    /// CHECK-START: boolean Main.$opt$noinline$isStringEmpty(java.lang.String) instruction_simplifier (after)
    /// CHECK-NOT:                    InvokeVirtual intrinsic:StringIsEmpty
  
    static public boolean $opt$noinline$isStringEmpty(String s) {
      if (doThrow) { throw new Error(); }
      return s.isEmpty();
    }
  
    /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) instruction_simplifier (before)
    /// CHECK-DAG:  <<Char:c\d+>>     InvokeVirtual intrinsic:StringCharAt
    /// CHECK-DAG:                    Return [<<Char>>]
  
    /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) instruction_simplifier (after)
    /// CHECK-DAG:  <<String:l\d+>>   ParameterValue
    /// CHECK-DAG:  <<Pos:i\d+>>      ParameterValue
    /// CHECK-DAG:  <<NullCk:l\d+>>   NullCheck [<<String>>]
    /// CHECK-DAG:  <<Length:i\d+>>   ArrayLength [<<NullCk>>] is_string_length:true
    /// CHECK-DAG:  <<Bounds:i\d+>>   BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
    /// CHECK-DAG:  <<Char:c\d+>>     ArrayGet [<<NullCk>>,<<Bounds>>] is_string_char_at:true
    /// CHECK-DAG:                    Return [<<Char>>]
  
    /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) instruction_simplifier (after)
    /// CHECK-NOT:                    InvokeVirtual intrinsic:StringCharAt
  
    static public char $opt$noinline$stringCharAt(String s, int pos) {
      if (doThrow) { throw new Error(); }
      return s.charAt(pos);
    }
  
    /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (before)
    /// CHECK-DAG:  <<Char:c\d+>>     InvokeVirtual intrinsic:StringCharAt
    /// CHECK-DAG:                    Return [<<Char>>]
  
    /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (after)
    /// CHECK-DAG:  <<String:l\d+>>   ParameterValue
    /// CHECK-DAG:  <<Pos:i\d+>>      ParameterValue
    /// CHECK-DAG:  <<NullCk:l\d+>>   NullCheck [<<String>>]
    /// CHECK-DAG:  <<Length:i\d+>>   ArrayLength [<<NullCk>>] is_string_length:true
    /// CHECK-DAG:  <<Bounds:i\d+>>   BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
    /// CHECK-DAG:  <<Char:c\d+>>     ArrayGet [<<NullCk>>,<<Bounds>>] is_string_char_at:true
    /// CHECK-DAG:                    Return [<<Char>>]
  
    /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (after)
    /// CHECK-NOT:                    InvokeVirtual intrinsic:StringCharAt
  
    static public char $opt$noinline$stringCharAtCatch(String s, int pos) {
      if (doThrow) { throw new Error(); }
      try {
        return s.charAt(pos);
      } catch (StringIndexOutOfBoundsException ignored) {
        return '\0';
      }
    }
  
    /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) instruction_simplifier (before)
    /// CHECK-DAG:                    InvokeVirtual intrinsic:StringLength
    /// CHECK-DAG:                    InvokeVirtual intrinsic:StringCharAt
  
    /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) instruction_simplifier (after)
    /// CHECK-DAG:                    ArrayLength is_string_length:true
    /// CHECK-DAG:                    ArrayLength is_string_length:true
    /// CHECK-DAG:                    BoundsCheck is_string_char_at:true
    /// CHECK-DAG:                    ArrayGet is_string_char_at:true
  
    /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) instruction_simplifier (after)
    /// CHECK-NOT:                    InvokeVirtual intrinsic:StringLength
    /// CHECK-NOT:                    InvokeVirtual intrinsic:StringCharAt
  
    /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) GVN (after)
    /// CHECK-DAG:                    ArrayLength is_string_length:true
    /// CHECK-NOT:                    ArrayLength is_string_length:true
  
    /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) BCE (after)
    /// CHECK-NOT:                    BoundsCheck
  
    static public int $opt$noinline$stringSumChars(String s) {
      if (doThrow) { throw new Error(); }
      int sum = 0;
      int len = s.length();
      for (int i = 0; i < len; ++i) {
        sum += s.charAt(i);
      }
      return sum;
    }
  
    /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) instruction_simplifier (before)
    /// CHECK-DAG:                    InvokeVirtual intrinsic:StringCharAt
  
    /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) instruction_simplifier (after)
    /// CHECK-DAG:                    ArrayLength is_string_length:true
    /// CHECK-DAG:                    BoundsCheck is_string_char_at:true
    /// CHECK-DAG:                    ArrayGet is_string_char_at:true
  
    /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) instruction_simplifier (after)
    /// CHECK-NOT:                    InvokeVirtual intrinsic:StringCharAt
  
    /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) BCE (after)
    /// CHECK-DAG:                    Deoptimize env:[[{{[^\]]*}}]]
  
    /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) BCE (after)
    /// CHECK-NOT:                    BoundsCheck is_string_char_at:true
  
    static public int $opt$noinline$stringSumLeadingChars(String s, int n) {
      if (doThrow) { throw new Error(); }
      int sum = 0;
      for (int i = 0; i < n; ++i) {
        sum += s.charAt(i);
      }
      return sum;
    }
  
    /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) instruction_simplifier (before)
    /// CHECK-DAG:                    InvokeVirtual intrinsic:StringCharAt
    /// CHECK-DAG:                    InvokeVirtual intrinsic:StringCharAt
    /// CHECK-DAG:                    InvokeVirtual intrinsic:StringCharAt
    /// CHECK-DAG:                    InvokeVirtual intrinsic:StringCharAt
  
    /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) instruction_simplifier (after)
    /// CHECK-DAG:                    ArrayLength is_string_length:true
    /// CHECK-DAG:                    BoundsCheck is_string_char_at:true
    /// CHECK-DAG:                    ArrayGet is_string_char_at:true
    /// CHECK-DAG:                    ArrayLength is_string_length:true
    /// CHECK-DAG:                    BoundsCheck is_string_char_at:true
    /// CHECK-DAG:                    ArrayGet is_string_char_at:true
    /// CHECK-DAG:                    ArrayLength is_string_length:true
    /// CHECK-DAG:                    BoundsCheck is_string_char_at:true
    /// CHECK-DAG:                    ArrayGet is_string_char_at:true
    /// CHECK-DAG:                    ArrayLength is_string_length:true
    /// CHECK-DAG:                    BoundsCheck is_string_char_at:true
    /// CHECK-DAG:                    ArrayGet is_string_char_at:true
  
    /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) instruction_simplifier (after)
    /// CHECK-NOT:                    InvokeVirtual intrinsic:StringCharAt
  
    /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) BCE (after)
    /// CHECK-DAG:                    Deoptimize env:[[{{[^\]]*}}]]
  
    /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) BCE (after)
    /// CHECK-NOT:                    BoundsCheck is_string_char_at:true
  
    static public int $opt$noinline$stringSum4LeadingChars(String s) {
      if (doThrow) { throw new Error(); }
      int sum = s.charAt(0) + s.charAt(1) + s.charAt(2) + s.charAt(3);
      return sum;
    }
  
    /// CHECK-START: boolean Main.stringEqualsSame() instruction_simplifier (before)
    /// CHECK:      InvokeStaticOrDirect
  
    /// CHECK-START: boolean Main.stringEqualsSame() register (before)
    /// CHECK:      <<Const1:i\d+>> IntConstant 1
    /// CHECK:      Return [<<Const1>>]
  
    /// CHECK-START: boolean Main.stringEqualsSame() register (before)
    /// CHECK-NOT:  InvokeStaticOrDirect
    public static boolean stringEqualsSame() {
      return $inline$callStringEquals("obj", "obj");
    }
  
    /// CHECK-START: boolean Main.stringEqualsNull() register (after)
    /// CHECK:      <<Invoke:z\d+>> InvokeVirtual
    /// CHECK:      Return [<<Invoke>>]
    public static boolean stringEqualsNull() {
      String o = (String)myObject;
      return $inline$callStringEquals(o, o);
    }
  
    public static boolean $inline$callStringEquals(String a, String b) {
      return a.equals(b);
    }
  
    /// CHECK-START-X86: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after)
    /// CHECK:          InvokeVirtual {{.*\.equals.*}} intrinsic:StringEquals
    /// CHECK-NOT:      test
  
    /// CHECK-START-X86_64: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after)
    /// CHECK:          InvokeVirtual {{.*\.equals.*}} intrinsic:StringEquals
    /// CHECK-NOT:      test
  
    /// CHECK-START-ARM: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after)
    /// CHECK:          InvokeVirtual {{.*\.equals.*}} intrinsic:StringEquals
    // CompareAndBranchIfZero() may emit either CBZ or CMP+BEQ.
    /// CHECK-NOT:      cbz
    /// CHECK-NOT:      cmp {{r\d+}}, #0
    // Terminate the scope for the CHECK-NOT search at the reference or length comparison,
    // whichever comes first.
    /// CHECK:          cmp {{r\d+}}, {{r\d+}}
  
    /// CHECK-START-ARM64: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after)
    /// CHECK:          InvokeVirtual {{.*\.equals.*}} intrinsic:StringEquals
    /// CHECK-NOT:      cbz
    // Terminate the scope for the CHECK-NOT search at the reference or length comparison,
    // whichever comes first.
    /// CHECK:          cmp {{w.*,}} {{w.*|#.*}}
  
    /// CHECK-START-MIPS: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after)
    /// CHECK:          InvokeVirtual {{.*\.equals.*}} intrinsic:StringEquals
    /// CHECK-NOT:      beq r0,
    /// CHECK-NOT:      beqz
    /// CHECK-NOT:      beqzc
    // Terminate the scope for the CHECK-NOT search at the class field or length comparison,
    // whichever comes first.
    /// CHECK:          lw
  
    /// CHECK-START-MIPS64: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after)
    /// CHECK:          InvokeVirtual {{.*\.equals.*}} intrinsic:StringEquals
    /// CHECK-NOT:      beqzc
    // Terminate the scope for the CHECK-NOT search at the reference comparison.
    /// CHECK:          beqc
    public static boolean stringArgumentNotNull(Object obj) {
      obj.getClass();
      return "foo".equals(obj);
    }
  
    // Test is very brittle as it depends on the order we emit instructions.
    /// CHECK-START-X86: boolean Main.stringArgumentIsString() disassembly (after)
    /// CHECK:          InvokeVirtual intrinsic:StringEquals
    /// CHECK:          test
    /// CHECK:          jz/eq
    // Check that we don't try to compare the classes.
    /// CHECK-NOT:      mov
    /// CHECK:          cmp
  
    // Test is very brittle as it depends on the order we emit instructions.
    /// CHECK-START-X86_64: boolean Main.stringArgumentIsString() disassembly (after)
    /// CHECK:          InvokeVirtual intrinsic:StringEquals
    /// CHECK:          test
    /// CHECK:          jz/eq
    // Check that we don't try to compare the classes.
    /// CHECK-NOT:      mov
    /// CHECK:          cmp
  
    // Test is brittle as it depends on the class offset being 0.
    /// CHECK-START-ARM: boolean Main.stringArgumentIsString() disassembly (after)
    /// CHECK:          InvokeVirtual intrinsic:StringEquals
    /// CHECK:          {{cbz|cmp}}
    // Check that we don't try to compare the classes.
    // The dissassembler currently explicitly emits the offset 0 but don't rely on it.
    // We want to terminate the CHECK-NOT search after two CMPs, one for reference
    // equality and one for length comparison but these may be emitted in different order,
    // so repeat the check twice.
    /// CHECK-NOT:      ldr{{(|.w)}} {{r\d+}}, [{{r\d+}}]
    /// CHECK-NOT:      ldr{{(|.w)}} {{r\d+}}, [{{r\d+}}, #0]
    /// CHECK:          cmp {{r\d+}}, {{r\d+}}
    /// CHECK-NOT:      ldr{{(|.w)}} {{r\d+}}, [{{r\d+}}]
    /// CHECK-NOT:      ldr{{(|.w)}} {{r\d+}}, [{{r\d+}}, #0]
    /// CHECK:          cmp {{r\d+}}, {{r\d+}}
  
    // Test is brittle as it depends on the class offset being 0.
    /// CHECK-START-ARM64: boolean Main.stringArgumentIsString() disassembly (after)
    /// CHECK:          InvokeVirtual intrinsic:StringEquals
    /// CHECK:          cbz
    // Check that we don't try to compare the classes.
    // The dissassembler currently does not explicitly emits the offset 0 but don't rely on it.
    // We want to terminate the CHECK-NOT search after two CMPs, one for reference
    // equality and one for length comparison but these may be emitted in different order,
    // so repeat the check twice.
    /// CHECK-NOT:      ldr {{w\d+}}, [{{x\d+}}]
    /// CHECK-NOT:      ldr {{w\d+}}, [{{x\d+}}, #0]
    /// CHECK:          cmp {{w\d+}}, {{w\d+|#.*}}
    /// CHECK-NOT:      ldr {{w\d+}}, [{{x\d+}}]
    /// CHECK-NOT:      ldr {{w\d+}}, [{{x\d+}}, #0]
    /// CHECK:          cmp {{w\d+}}, {{w\d+|#.*}}
  
    // Test is brittle as it depends on the class offset being 0.
    /// CHECK-START-MIPS: boolean Main.stringArgumentIsString() disassembly (after)
    /// CHECK:          InvokeVirtual intrinsic:StringEquals
    /// CHECK:          beq{{(zc)?}}
    // Check that we don't try to compare the classes.
    /// CHECK-NOT:      lw {{r\d+}}, +0({{r\d+}})
    /// CHECK:          bne{{c?}}
  
    // Test is brittle as it depends on the class offset being 0.
    /// CHECK-START-MIPS64: boolean Main.stringArgumentIsString() disassembly (after)
    /// CHECK:          InvokeVirtual intrinsic:StringEquals
    /// CHECK:          beqzc
    // Check that we don't try to compare the classes.
    /// CHECK-NOT:      lw {{r\d+}}, +0({{r\d+}})
    /// CHECK:          bnec
    public static boolean stringArgumentIsString() {
      return "foo".equals(myString);
    }
  
    static String myString;
    static Object myObject;
  }
  