diff options
-rw-r--r-- | compiler/optimizing/code_generator_riscv64.h | 1 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics_riscv64.cc | 179 | ||||
-rw-r--r-- | test/536-checker-intrinsic-optimization/src/Main.java | 44 |
3 files changed, 223 insertions, 1 deletions
diff --git a/compiler/optimizing/code_generator_riscv64.h b/compiler/optimizing/code_generator_riscv64.h index c49e2b4771..7f4f352341 100644 --- a/compiler/optimizing/code_generator_riscv64.h +++ b/compiler/optimizing/code_generator_riscv64.h @@ -71,7 +71,6 @@ static constexpr int32_t kFClassNaNMinValue = 0x100; V(FP16LessEquals) \ V(FP16Min) \ V(FP16Max) \ - V(StringGetCharsNoCheck) \ V(StringStringIndexOf) \ V(StringStringIndexOfAfter) \ V(StringBufferAppend) \ diff --git a/compiler/optimizing/intrinsics_riscv64.cc b/compiler/optimizing/intrinsics_riscv64.cc index 4e248a2b7c..9aa4b9415b 100644 --- a/compiler/optimizing/intrinsics_riscv64.cc +++ b/compiler/optimizing/intrinsics_riscv64.cc @@ -5043,6 +5043,185 @@ void IntrinsicCodeGeneratorRISCV64::VisitMathMultiplyHigh(HInvoke* invoke) { __ Mulh(out, x, y); } +void IntrinsicLocationsBuilderRISCV64::VisitStringGetCharsNoCheck(HInvoke* invoke) { + LocationSummary* locations = + new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); + + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RequiresRegister()); + locations->SetInAt(4, Location::RequiresRegister()); + + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorRISCV64::VisitStringGetCharsNoCheck(HInvoke* invoke) { + Riscv64Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // In Java sizeof(Char) is 2. + constexpr size_t char_size = DataType::Size(DataType::Type::kUint16); + static_assert(char_size == 2u); + + // Location of data in the destination char array buffer. + const uint32_t array_data_offset = mirror::Array::DataOffset(char_size).Uint32Value(); + + // Location of char array data in the source string. + const uint32_t string_value_offset = mirror::String::ValueOffset().Uint32Value(); + + // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin); + + // The source string. + XRegister source_string_object = locations->InAt(0).AsRegister<XRegister>(); + // Index of the first character. + XRegister source_begin_index = locations->InAt(1).AsRegister<XRegister>(); + // Index that immediately follows the last character. + XRegister source_end_index = locations->InAt(2).AsRegister<XRegister>(); + // The destination array. + XRegister destination_array_object = locations->InAt(3).AsRegister<XRegister>(); + // The start offset in the destination array. + XRegister destination_begin_offset = locations->InAt(4).AsRegister<XRegister>(); + + XRegister source_ptr = locations->GetTemp(0).AsRegister<XRegister>(); + XRegister destination_ptr = locations->GetTemp(1).AsRegister<XRegister>(); + XRegister number_of_chars = locations->GetTemp(2).AsRegister<XRegister>(); + + ScratchRegisterScope temps(assembler); + XRegister tmp = temps.AllocateXRegister(); + + Riscv64Label done; + + // Calculate the length(number_of_chars) of the string. + __ Subw(number_of_chars, source_end_index, source_begin_index); + + // If the string has zero length then exit. + __ Beqz(number_of_chars, &done); + + // Prepare a register with the destination address + // to start copying to the address: + // 1. set the address from which the data in the + // destination array begins (destination_array_object + array_data_offset); + __ Addi(destination_ptr, destination_array_object, array_data_offset); + // 2. it is necessary to add the start offset relative to the beginning + // of the data in the destination array, + // yet, due to sizeof(Char) being 2, formerly scaling must be performed + // (destination_begin_offset * 2 that equals to destination_begin_offset << 1); + __ Sh1Add(destination_ptr, destination_begin_offset, destination_ptr); + + // Prepare a register with the source address + // to start copying from the address: + // 1. set the address from which the data in the + // source string begins (source_string_object + string_value_offset). + // Other manipulations will be performed later, + // since they depend on whether the string is compressed or not. + __ Addi(source_ptr, source_string_object, string_value_offset); + + // The string can be compressed. It is a way to store strings more compactly. + // In this instance, every character is located in one byte (instead of two). + Riscv64Label compressed_string_preloop; + + // Information about whether the string is compressed or not is located + // in the area intended for storing the length of the string. + // The least significant bit of the string's length is used + // as the compression flag if STRING_COMPRESSION_ENABLED. + if (mirror::kUseStringCompression) { + // Location of count in string. + const uint32_t count_offset = mirror::String::CountOffset().Uint32Value(); + // String's length. + __ Loadwu(tmp, source_string_object, count_offset); + + // Checking the string for compression. + // If so, move to the "compressed_string_preloop". + __ Andi(tmp, tmp, 0x1); + __ Beqz(tmp, &compressed_string_preloop); + } + + // Continue preparing the source register: + // proceed similarly to what was done for the destination register. + __ Sh1Add(source_ptr, source_begin_index, source_ptr); + + // If the string is not compressed, then perform ordinary copying. + // Copying will occur 4 characters (8 bytes) at a time, immediately after there are + // less than 4 characters left, move to the "remainder_loop" and copy the remaining + // characters one character (2 bytes) at a time. + // Note: Unaligned addresses are acceptable here and it is not required to embed + // additional code to correct them. + Riscv64Label main_loop; + Riscv64Label remainder_loop; + + // If initially there are less than 4 characters, + // then we directly calculate the remainder. + __ Addi(tmp, number_of_chars, -4); + __ Bltz(tmp, &remainder_loop); + + // Otherwise, save the value to the counter and continue. + __ Mv(number_of_chars, tmp); + + // Main loop. Loads and stores 4 16-bit Java characters at a time. + __ Bind(&main_loop); + + __ Loadd(tmp, source_ptr, 0); + __ Addi(source_ptr, source_ptr, char_size * 4); + __ Stored(tmp, destination_ptr, 0); + __ Addi(destination_ptr, destination_ptr, char_size * 4); + + __ Addi(number_of_chars, number_of_chars, -4); + + __ Bgez(number_of_chars, &main_loop); + + // Restore the previous counter value. + __ Addi(number_of_chars, number_of_chars, 4); + __ Beqz(number_of_chars, &done); + + // Remainder loop for < 4 characters case and remainder handling. + // Loads and stores one 16-bit Java character at a time. + __ Bind(&remainder_loop); + + __ Loadhu(tmp, source_ptr, 0); + __ Addi(source_ptr, source_ptr, char_size); + + __ Storeh(tmp, destination_ptr, 0); + __ Addi(destination_ptr, destination_ptr, char_size); + + __ Addi(number_of_chars, number_of_chars, -1); + __ Bgtz(number_of_chars, &remainder_loop); + + Riscv64Label compressed_string_loop; + if (mirror::kUseStringCompression) { + __ J(&done); + + // Below is the copying under the string compression circumstance mentioned above. + // Every character in the source string occupies only one byte (instead of two). + constexpr size_t compressed_char_size = DataType::Size(DataType::Type::kInt8); + static_assert(compressed_char_size == 1u); + + __ Bind(&compressed_string_preloop); + + // Continue preparing the source register: + // proceed identically to what was done for the destination register, + // yet take into account that only one byte yields for every source character, + // hence we need to extend it to two ones when copying it to the destination address. + // Against this background scaling for source_begin_index is not needed. + __ Add(source_ptr, source_ptr, source_begin_index); + + // Copy loop for compressed strings. Copying one 8-bit character to 16-bit one at a time. + __ Bind(&compressed_string_loop); + + __ Loadbu(tmp, source_ptr, 0); + __ Addi(source_ptr, source_ptr, compressed_char_size); + __ Storeh(tmp, destination_ptr, 0); + __ Addi(destination_ptr, destination_ptr, char_size); + + __ Addi(number_of_chars, number_of_chars, -1); + __ Bgtz(number_of_chars, &compressed_string_loop); + } + + __ Bind(&done); +} + #define MARK_UNIMPLEMENTED(Name) UNIMPLEMENTED_INTRINSIC(RISCV64, Name) UNIMPLEMENTED_INTRINSIC_LIST_RISCV64(MARK_UNIMPLEMENTED); #undef MARK_UNIMPLEMENTED diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java index c809278d8e..6519e5c2ad 100644 --- a/test/536-checker-intrinsic-optimization/src/Main.java +++ b/test/536-checker-intrinsic-optimization/src/Main.java @@ -19,6 +19,10 @@ public class Main { public static String mediumString = generateString(300); public static String largeString = generateString(2000); + public static String smallNonAsciiString = generateNonAsciiString(100); + public static String mediumNonAsciiString = generateNonAsciiString(300); + public static String largeNonAsciiString = generateNonAsciiString(2000); + public static String generateString(int length) { // Generate a string in the ASCII range that will // use string compression. @@ -30,6 +34,14 @@ public class Main { return sb.toString(); } + public static String generateNonAsciiString(int length) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) { + sb.append(Character.valueOf('\uFFFF')); + } + return sb.toString(); + } + public static void assertIntEquals(int expected, int result) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); @@ -102,6 +114,38 @@ public class Main { // Substring > 8 characters. assertStringEquals(smallString.substring(7, 28), stringGetCharsRange(smallString, 7, 28, 17)); + // Single character. + assertStringEquals("\uFFFF", stringGetCharsAndBack("\uFFFF")); + + // Strings < 8 characters. + assertStringEquals("\uFFFF\uFFFF\uFFFF\uFFFF\uFFFF", + stringGetCharsAndBack("\uFFFF\uFFFF\uFFFF\uFFFF\uFFFF")); + + // Strings > 8 characters of various lengths. + assertStringEquals(smallNonAsciiString, stringGetCharsAndBack(smallNonAsciiString)); + assertStringEquals(mediumNonAsciiString, stringGetCharsAndBack(mediumNonAsciiString)); + assertStringEquals(largeNonAsciiString, stringGetCharsAndBack(largeNonAsciiString)); + + // Get only a substring: + // Substring < 8 characters. + assertStringEquals(smallNonAsciiString.substring(5, 10), + stringGetCharsRange(smallNonAsciiString, 5, 10, 0)); + // Substring > 8 characters. + assertStringEquals(smallNonAsciiString.substring(7, 28), + stringGetCharsRange(smallNonAsciiString, 7, 28, 0)); + + // Get full string with offset in the char array. + assertStringEquals(smallNonAsciiString, + stringGetCharsAndBackOffset(smallNonAsciiString, 17)); + + // Get a substring with an offset in the char array. + // Substring < 8 characters. + assertStringEquals(smallNonAsciiString.substring(5, 10), + stringGetCharsRange(smallNonAsciiString, 5, 10, 17)); + // Substring > 8 characters. + assertStringEquals(smallNonAsciiString.substring(7, 28), + stringGetCharsRange(smallNonAsciiString, 7, 28, 17)); + try { $opt$noinline$stringCharAt("abc", -1); throw new Error("Should throw SIOOB."); |