diff options
| -rw-r--r-- | runtime/interpreter/interpreter_intrinsics.cc | 153 | ||||
| -rw-r--r-- | runtime/jvalue.h | 8 |
2 files changed, 159 insertions, 2 deletions
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc index 5e901cdfd8..ff0c20ec8a 100644 --- a/runtime/interpreter/interpreter_intrinsics.cc +++ b/runtime/interpreter/interpreter_intrinsics.cc @@ -79,6 +79,151 @@ UNARY_SIMPLE_INTRINSIC(MterpMathAcos, std::acos, GetVRegDouble, SetD); // java.lang.Math.atan(D)D UNARY_SIMPLE_INTRINSIC(MterpMathAtan, std::atan, GetVRegDouble, SetD); +// java.lang.String.charAt(I)C +static ALWAYS_INLINE bool MterpStringCharAt(ShadowFrame* shadow_frame, + const Instruction* inst, + uint16_t inst_data, + JValue* result_register) + REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t arg[Instruction::kMaxVarArgRegs] = {}; + inst->GetVarArgs(arg, inst_data); + mirror::String* str = shadow_frame->GetVRegReference(arg[0])->AsString(); + int length = str->GetLength(); + int index = shadow_frame->GetVReg(arg[1]); + uint16_t res; + if (UNLIKELY(index < 0) || (index >= length)) { + return false; // Punt and let non-intrinsic version deal with the throw. + } + if (str->IsCompressed()) { + res = str->GetValueCompressed()[index]; + } else { + res = str->GetValue()[index]; + } + result_register->SetC(res); + return true; +} + +// java.lang.String.compareTo(Ljava/lang/string)I +static ALWAYS_INLINE bool MterpStringCompareTo(ShadowFrame* shadow_frame, + const Instruction* inst, + uint16_t inst_data, + JValue* result_register) + REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t arg[Instruction::kMaxVarArgRegs] = {}; + inst->GetVarArgs(arg, inst_data); + mirror::String* str = shadow_frame->GetVRegReference(arg[0])->AsString(); + mirror::Object* arg1 = shadow_frame->GetVRegReference(arg[1]); + if (arg1 == nullptr) { + return false; + } + result_register->SetI(str->CompareTo(arg1->AsString())); + return true; +} + +#define STRING_INDEXOF_INTRINSIC(name, starting_pos) \ +static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \ + const Instruction* inst, \ + uint16_t inst_data, \ + JValue* result_register) \ + REQUIRES_SHARED(Locks::mutator_lock_) { \ + uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \ + inst->GetVarArgs(arg, inst_data); \ + mirror::String* str = shadow_frame->GetVRegReference(arg[0])->AsString(); \ + int ch = shadow_frame->GetVReg(arg[1]); \ + if (ch >= 0x10000) { \ + /* Punt if supplementary char. */ \ + return false; \ + } \ + result_register->SetI(str->FastIndexOf(ch, starting_pos)); \ + return true; \ +} + +// java.lang.String.indexOf(I)I +STRING_INDEXOF_INTRINSIC(StringIndexOf, 0); + +// java.lang.String.indexOf(II)I +STRING_INDEXOF_INTRINSIC(StringIndexOfAfter, shadow_frame->GetVReg(arg[2])); + +#define SIMPLE_STRING_INTRINSIC(name, operation) \ +static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \ + const Instruction* inst, \ + uint16_t inst_data, \ + JValue* result_register) \ + REQUIRES_SHARED(Locks::mutator_lock_) { \ + uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \ + inst->GetVarArgs(arg, inst_data); \ + mirror::String* str = shadow_frame->GetVRegReference(arg[0])->AsString(); \ + result_register->operation; \ + return true; \ +} + +// java.lang.String.isEmpty()Z +SIMPLE_STRING_INTRINSIC(StringIsEmpty, SetZ(str->GetLength() == 0)) + +// java.lang.String.length()I +SIMPLE_STRING_INTRINSIC(StringLength, SetI(str->GetLength())) + +// java.lang.String.getCharsNoCheck(II[CI)V +static ALWAYS_INLINE bool MterpStringGetCharsNoCheck(ShadowFrame* shadow_frame, + const Instruction* inst, + uint16_t inst_data, + JValue* result_register ATTRIBUTE_UNUSED) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Start, end & index already checked by caller - won't throw. Destination is uncompressed. + uint32_t arg[Instruction::kMaxVarArgRegs] = {}; + inst->GetVarArgs(arg, inst_data); + mirror::String* str = shadow_frame->GetVRegReference(arg[0])->AsString(); + int32_t start = shadow_frame->GetVReg(arg[1]); + int32_t end = shadow_frame->GetVReg(arg[2]); + int32_t index = shadow_frame->GetVReg(arg[4]); + mirror::CharArray* array = shadow_frame->GetVRegReference(arg[3])->AsCharArray(); + uint16_t* dst = array->GetData() + index; + int32_t len = (end - start); + if (str->IsCompressed()) { + const uint8_t* src_8 = str->GetValueCompressed() + start; + for (int i = 0; i < len; i++) { + dst[i] = src_8[i]; + } + } else { + uint16_t* src_16 = str->GetValue() + start; + memcpy(dst, src_16, len * sizeof(uint16_t)); + } + return true; +} + +// java.lang.String.equalsLjava/lang/Object;)Z +static ALWAYS_INLINE bool MterpStringEquals(ShadowFrame* shadow_frame, + const Instruction* inst, + uint16_t inst_data, + JValue* result_register) + REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t arg[Instruction::kMaxVarArgRegs] = {}; + inst->GetVarArgs(arg, inst_data); + mirror::String* str = shadow_frame->GetVRegReference(arg[0])->AsString(); + mirror::Object* obj = shadow_frame->GetVRegReference(arg[1]); + bool res = false; // Assume not equal. + if ((obj != nullptr) && obj->IsString()) { + mirror::String* str2 = obj->AsString(); + if (str->GetCount() == str2->GetCount()) { + // Length & compression status are same. Can use block compare. + void* bytes1; + void* bytes2; + int len = str->GetLength(); + if (str->IsCompressed()) { + bytes1 = str->GetValueCompressed(); + bytes2 = str2->GetValueCompressed(); + } else { + len *= sizeof(uint16_t); + bytes1 = str->GetValue(); + bytes2 = str2->GetValue(); + } + res = (memcmp(bytes1, bytes2, len) == 0); + } + } + result_register->SetZ(res); + return true; +} + #define INTRINSIC_CASE(name) \ case Intrinsics::k##name: \ res = Mterp##name(shadow_frame, inst, inst_data, result_register); \ @@ -110,6 +255,14 @@ bool MterpHandleIntrinsic(ShadowFrame* shadow_frame, INTRINSIC_CASE(MathAsin) INTRINSIC_CASE(MathAcos) INTRINSIC_CASE(MathAtan) + INTRINSIC_CASE(StringCharAt) + INTRINSIC_CASE(StringCompareTo) + INTRINSIC_CASE(StringIndexOf) + INTRINSIC_CASE(StringIndexOfAfter) + INTRINSIC_CASE(StringEquals) + INTRINSIC_CASE(StringGetCharsNoCheck) + INTRINSIC_CASE(StringIsEmpty) + INTRINSIC_CASE(StringLength) default: res = false; // Punt break; diff --git a/runtime/jvalue.h b/runtime/jvalue.h index 398bfbc27a..f61a07c0c0 100644 --- a/runtime/jvalue.h +++ b/runtime/jvalue.h @@ -39,7 +39,9 @@ union PACKED(alignof(mirror::Object*)) JValue { } uint16_t GetC() const { return c; } - void SetC(uint16_t new_c) { c = new_c; } + void SetC(uint16_t new_c) { + j = static_cast<int64_t>(new_c); // Zero-extend to 64 bits. + } double GetD() const { return d; } void SetD(double new_d) { d = new_d; } @@ -66,7 +68,9 @@ union PACKED(alignof(mirror::Object*)) JValue { } uint8_t GetZ() const { return z; } - void SetZ(uint8_t new_z) { z = new_z; } + void SetZ(uint8_t new_z) { + j = static_cast<int64_t>(new_z); // Zero-extend to 64 bits. + } mirror::Object** GetGCRoot() { return &l; } |