summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Mark <mark.gonopolskiy@syntacore.com> 2024-02-19 18:25:55 +0300
committer VladimĂ­r Marko <vmarko@google.com> 2024-05-06 07:16:27 +0000
commit53da12c60f200d64b08d9c079593da2cff8f43d2 (patch)
treeefee33ddda8f9ad6a7cc4ee7cd0768105876c944
parent60c54c582bc99050e431aa369c3b3a8cae7555eb (diff)
riscv64: StringGetCharsNoCheck intrinsic.
Implemented StringGetCharsNoCheck intrinsic. Added new checks in the 536-checker-intrinsic-optimization test in order to increase the quality of test coverage for the case of uncompressed strings. Test: testrunner.py --target --64 --ndebug --no-image --jit-on-first-use Change-Id: I08bc075440eef1750a057bad5fe6de45f792091f
-rw-r--r--compiler/optimizing/code_generator_riscv64.h1
-rw-r--r--compiler/optimizing/intrinsics_riscv64.cc179
-rw-r--r--test/536-checker-intrinsic-optimization/src/Main.java44
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.");