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)}},
};
}