std::vector get_string_patterns()

in opt/peephole/Peephole.cpp [852:1228]


std::vector<Pattern> get_string_patterns() {
  return {
      // It coalesces init(void) and append(string) into init(string).
      // new StringBuilder().append("...") = new StringBuilder("...")
      {"Coalesce_InitVoid_AppendString",
       {invoke_StringBuilder_init(Register::A),
        const_string(String::A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        move_result_object(Register::C)},
       {const_string(String::A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_init_String(Register::A, Register::B),
        move_object(Register::C, Register::A)}},

      // It coalesces init(void) and append(string) into init(string).
      // new StringBuilder().append("...") = new StringBuilder("...")
      // Difference from Coalesce_InitVoid_AppendString is it don't have
      // trailing move_result_object
      {"Coalesce_InitVoid_AppendString_WithoutMoveResult",
       {invoke_StringBuilder_init(Register::A),
        const_string(String::A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString)},
       {const_string(String::A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_init_String(Register::A, Register::B)}},

      // It coalesces consecutive two append(string) to a single append call.
      // StringBuilder.append("A").append("B") = StringBuilder.append("AB")
      {"Coalesce_AppendString_AppendString",
       {const_string(String::A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        move_result_object(Register::C),
        const_string(String::B),
        move_result_pseudo_object(Register::D),
        invoke_StringBuilder_append(Register::C, Register::D, LjavaString),
        move_result_object(Register::E)},
       // pre opt write order: B, C, D, E
       {const_string(String::concat_A_B_strings),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        const_string(String::A), // maybe dead
        move_result_pseudo_object(Register::B),
        move_object(Register::C, Register::A), // maybe dead
        const_string(String::B), // maybe dead
        move_result_pseudo_object(Register::D),
        move_object(Register::E, Register::C)}}, // maybe dead
      // post opt write order B, B, C, D, E

      // Explanation of WithoutMoveResult
      // A variation of the above optimization. The result of append isn't
      // always moved with move-result-object. But we want to capture both forms
      // of this pattern. This optimization would not be safe if
      // AppendString_AppendString doesn't run first because
      // (1) the last instruction of the pattern is an invoke AND
      // (2) the last instruction of the replacement is not an invoke AND
      // (3) the instruction after the pattern may be a move_result_object
      {"Coalesce_AppendString_AppendString_WithoutMoveResult",
       {const_string(String::A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        move_result_object(Register::C),
        const_string(String::B),
        move_result_pseudo_object(Register::D),
        invoke_StringBuilder_append(Register::C, Register::D, LjavaString)},
       // pre opt write order: B, C, D
       {const_string(String::concat_A_B_strings),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        const_string(String::A), // maybe dead
        move_result_pseudo_object(Register::B),
        move_object(Register::C, Register::A), // maybe dead
        const_string(String::B), // maybe dead
        move_result_pseudo_object(Register::D)}},
      // there shouldn't be a move-result-object here because of the
      // previous pattern
      // post opt write order: B, B, C, D

      // It evaluates the length of a literal in compile time.
      // "stringA".length() ==> length_of_stringA
      {"CompileTime_StringLength",
       {const_string(String::A),
        move_result_pseudo_object(Register::A),
        invoke_String_length(Register::A),
        move_result(Register::B)},
       {const_string(String::A), // maybe dead
        move_result_pseudo_object(Register::A),
        const_literal(OPCODE_CONST, Register::B, Literal::Length_String_A)}},

      // Evaluate the hashCode of a String at compile time.
      // "stringA".hashCode() ==> hashcode_of_stringA
      {"CompileTime_StringHashCode",
        {const_string(String::A),
         move_result_pseudo_object(Register::A),
         invoke_String_hashCode(Register::A),
         move_result(Register::B)},
        {const_string(String::A), // maybe dead
         move_result_pseudo_object(Register::A),
         const_literal(OPCODE_CONST, Register::B, Literal::HashCode_String_A)}},

      // It removes an append call with an empty string.
      // StringBuilder.append("") = nothing
      {"Remove_AppendEmptyString",
       {const_string(String::empty),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        move_result_object(Register::C)},
       {const_string(String::empty), // maybe dead
        move_result_pseudo_object(Register::B),
        move_object(Register::C, Register::A)}}, // maybe dead

      {"Remove_AppendEmptyString_WithoutMoveResult",
       {const_string(String::empty),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString)},
       {const_string(String::empty),
        move_result_pseudo_object(Register::B)}}, // maybe dead

      // It coalesces init(void) and append(char) into init(string).
      // StringBuilder().append(C) = new StringBuilder("....")
      {"Coalesce_Init_AppendChar",
       {invoke_StringBuilder_init(Register::A),
        const_char(Register::B, Literal::A),
        invoke_StringBuilder_append(Register::A, Register::B, "C"),
        move_result_object(Register::C)},
       {const_string(String::char_A_to_string),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_init_String(Register::A, Register::B),
        DexPattern::copy_matched_instruction(1), // const_char. maybe dead
        move_object(Register::C, Register::A)}}, // maybe dead

      {"Coalesce_Init_AppendChar_WithoutMoveResult",
       {invoke_StringBuilder_init(Register::A),
        const_char(Register::B, Literal::A),
        invoke_StringBuilder_append(Register::A, Register::B, "C")},
       {const_string(String::char_A_to_string),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_init_String(Register::A, Register::B),
        DexPattern::copy_matched_instruction(1)}}, // const_char. maybe dead

      // It coalesces append(string) and append(integer) into append(string).
      // StringBuilder.append("...").append(I) = StringBuilder.append("....")
      {"Coalesce_AppendString_AppendInt",
       {const_string(String::A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        move_result_object(Register::C),
        const_integer(Register::D, Literal::A),
        invoke_StringBuilder_append(Register::C, Register::D, "I"),
        move_result_object(Register::E)},
       // pre opt write order: B, C, D, E
       {// (2 + 3 + 1 + [1, 2, 3] + 3 + 1) - (2 + 3 + 2 + 1 + [1, 2, 3] + 1) = 1
        const_string(String::concat_string_A_int_A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        const_string(String::A), // maybe dead
        move_result_pseudo_object(Register::B),
        move_object(Register::C, Register::A), // maybe dead
        DexPattern::copy_matched_instruction(4), // const_integer. maybe dead
        move_object(Register::E, Register::C)}}, // maybe dead
      // post opt write order B, B, C, D, E

      {"Coalesce_AppendString_AppendInt_WithoutMoveResult",
       {const_string(String::A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        move_result_object(Register::C),
        const_integer(Register::D, Literal::A),
        invoke_StringBuilder_append(Register::C, Register::D, "I")},
       // pre opt write order: B, C, D
       {// (2 + 3 + 1 + [1, 2, 3] + 3) - (2 + 3 + 2 + 1 + [1, 2, 3]) = 1
        const_string(String::concat_string_A_int_A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        const_string(String::A), // maybe dead
        move_result_pseudo_object(Register::B),
        move_object(Register::C, Register::A), // maybe dead
        DexPattern::copy_matched_instruction(4)}}, // const_integer. maybe dead
      // post opt write order: B, B, C, D

      // It coalesces append(string) and append(char) into append(string).
      // StringBuilder.append("...").append(C) = StringBuilder.append("....")
      {"Coalesce_AppendString_AppendChar",
       {const_string(String::A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        move_result_object(Register::C),
        const_char(Register::D, Literal::A),
        invoke_StringBuilder_append(Register::C, Register::D, "C"),
        move_result_object(Register::E)},
       // pre opt write order: B, C, D, A
       {// (2 + 3 + 1 + [1, 2, 3] + 3 + 1) - (2 + 3 + 2 + 1 + [1, 2, 3] + 1) = 1
        const_string(String::concat_string_A_char_A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        const_string(String::A), // maybe dead
        move_result_pseudo_object(Register::B),
        move_object(Register::C, Register::A), // maybe dead
        DexPattern::copy_matched_instruction(4), // const_integer. maybe dead
        move_object(Register::E, Register::C)}}, // maybe dead
      // post opt write order: B, B, C, D, E

      {"Coalesce_AppendString_AppendChar_WithoutMoveResult",
       {const_string(String::A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        move_result_object(Register::C),
        const_char(Register::D, Literal::A),
        invoke_StringBuilder_append(Register::C, Register::D, "C")},
       // pre opt write order: B, C, D
       {// (2 + 3 + 1 + [1, 2, 3] + 3) - (2 + 3 + 2 + 1 + [1, 2, 3]) = 1
        const_string(String::concat_string_A_char_A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        const_string(String::A), // maybe dead
        move_result_pseudo_object(Register::B),
        move_object(Register::C, Register::A), // maybe dead
        DexPattern::copy_matched_instruction(4)}}, // const_integer. maybe dead
      // post opt write order: B, B, C, D

      // It coalesces append(string) and append(boolean) into append(string).
      // StringBuilder.append("...").append(Z) = StringBuilder.append("....")
      {"Coalesce_AppendString_AppendBoolean",
       {const_string(String::A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        move_result_object(Register::C),
        const_literal(OPCODE_CONST, Register::D, Literal::A),
        invoke_StringBuilder_append(Register::C, Register::D, "Z"),
        move_result_object(Register::E)},
       // pre opt write order: B, C, D, E
       {const_string(String::concat_string_A_boolean_A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        const_string(String::A), // maybe dead
        move_result_pseudo_object(Register::B),
        move_object(Register::C, Register::A), // maybe dead
        const_literal(OPCODE_CONST, Register::D, Literal::A), // maybe dead
        move_object(Register::E, Register::C)}}, // maybe dead
      // post opt write order: B, B, C, D, E

      {"Coalesce_AppendString_AppendBoolean_WithoutMoveResult",
       {const_string(String::A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        move_result_object(Register::C),
        const_literal(OPCODE_CONST, Register::D, Literal::A),
        invoke_StringBuilder_append(Register::C, Register::D, "Z")},
       // pre opt write order: B, C, D
       {const_string(String::concat_string_A_boolean_A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        const_string(String::A), // maybe dead
        move_result_pseudo_object(Register::B),
        move_object(Register::C, Register::A), // maybe dead
        const_literal(OPCODE_CONST, Register::D, Literal::A)}}, // maybe dead
      // post opt write order: B, B, C, D

      // It coalesces append(string) and append(long int) into append(string).
      // StringBuilder.append("...").append(J) = StringBuilder.append("....")
      {"Coalesce_AppendString_AppendLongInt",
       {const_string(String::A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        move_result_object(Register::C),
        const_wide(Register::D, Literal::A),
        invoke_StringBuilder_append(Register::C, Register::D, "J"),
        move_result_object(Register::E)},
       // pre opt write order: B, C, D, E
       {// (2 + 3 + 1 + [2, 3, 5] + 3 + 1) - (2 + 3 + 2 + 1 + [2, 3, 5] + 1) = 1
        const_string(String::concat_string_A_long_int_A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        const_string(String::A), // maybe dead
        move_result_pseudo_object(Register::B),
        move_object(Register::C, Register::A), // maybe dead
        DexPattern::copy_matched_instruction(4), // const_wide. maybe dead
        move_object(Register::E, Register::C)}}, // maybe dead
      // post opt write order: B, B, C, D, E

      {"Coalesce_AppendString_AppendLongInt_WithoutMoveResult",
       {const_string(String::A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        move_result_object(Register::C),
        const_wide(Register::D, Literal::A),
        invoke_StringBuilder_append(Register::C, Register::D, "J")},
       // pre opt write order: B, C, D
       {// (2 + 3 + 1 + [2, 3, 5] + 3) - (2 + 3 + 2 + 1 + [2, 3, 5]) = 1
        const_string(String::concat_string_A_long_int_A),
        move_result_pseudo_object(Register::B),
        invoke_StringBuilder_append(Register::A, Register::B, LjavaString),
        const_string(String::A), // maybe dead
        move_result_pseudo_object(Register::B),
        move_object(Register::C, Register::A), // maybe dead
        DexPattern::copy_matched_instruction(4)}}, // const_wide. maybe dead
      // post opt write order: B, B, C, D

      // It evaluates the identify of two literal strings in compile time.
      // "stringA".equals("stringB") ==> true or false
      {"CompileTime_StringCompare",
       {const_string(String::A),
        move_result_pseudo_object(Register::A),
        const_string(String::B),
        move_result_pseudo_object(Register::B),
        invoke_String_equals(Register::A, Register::B),
        move_result(Register::C)},
       {const_string(String::A), // maybe dead
        move_result_pseudo_object(Register::A),
        const_string(String::B), // maybe dead
        move_result_pseudo_object(Register::B),
        const_literal(
            OPCODE_CONST, Register::C, Literal::Compare_Strings_A_B)}},

      // It replaces valueOf on a boolean value by "true" or "false" directly.
      // String.valueof(true/false) ==> "true" or "false"
      {"Replace_ValueOfBoolean",
       {const_literal(OPCODE_CONST, Register::A, Literal::A),
        invoke_String_valueOf(Register::A, "Z"),
        move_result_object(Register::B)},
       {const_literal(OPCODE_CONST, Register::A, Literal::A), // maybe dead
        const_string(String::boolean_A_to_string),
        move_result_pseudo_object(Register::B)}},

      // It replaces valueOf on a literal character by the character itself.
      // String.valueOf(char) ==> "char"
      {"Replace_ValueOfChar",
       {const_char(Register::A, Literal::A),
        invoke_String_valueOf(Register::A, "C"),
        move_result_object(Register::B)},
       {DexPattern::copy_matched_instruction(0), // const_char. maybe dead
        const_string(String::char_A_to_string), // maybe dead
        move_result_pseudo_object(Register::B)}},

      // It replaces valueOf on an integer literal by the integer itself.
      // String.valueof(int) ==> "int"
      {"Replace_ValueOfInt",
       {const_integer(Register::A, Literal::A),
        invoke_String_valueOf(Register::A, "I"),
        move_result_object(Register::B)},
       {DexPattern::copy_matched_instruction(0), // const_integer. maybe dead
        const_string(String::int_A_to_string),
        move_result_pseudo_object(Register::B)}},

      // It replaces valueOf on a long integer literal by the number itself.
      // String.valueof(long int) ==> "long int"
      {"Replace_ValueOfLongInt",
       {const_wide(Register::A, Literal::A),
        invoke_String_valueOf(Register::A, "J"),
        move_result_object(Register::B)},
       {DexPattern::copy_matched_instruction(0), // const_wide. maybe dead
        const_string(String::long_int_A_to_string),
        move_result_pseudo_object(Register::B)}},

      // It replaces valueOf on a float literal by the float itself.
      // String.valueof(float) ==> "float"
      {"Replace_ValueOfFloat",
       {const_float(Register::A, Literal::A),
        invoke_String_valueOf(Register::A, "F"),
        move_result_object(Register::B)},
       {DexPattern::copy_matched_instruction(0), // const_float. maybe dead
        const_string(String::float_A_to_string),
        move_result_pseudo_object(Register::B)}},

      // It replaces valueOf on a double literal by the double itself.
      // String.valueof(double) ==> "double"
      {"Replace_ValueOfDouble",
       {const_wide(Register::A, Literal::A),
        invoke_String_valueOf(Register::A, "D"),
        move_result_object(Register::B)},
       {DexPattern::copy_matched_instruction(0), // const_wide. maybe dead
        const_string(String::double_A_to_string),
        move_result_pseudo_object(Register::B)}},
  };
}