From 16ba2b4726cafc2d83cae4a65132aac15f372689 Mon Sep 17 00:00:00 2001 From: Chris Larsen Date: Mon, 2 Nov 2015 10:58:31 -0800 Subject: [PATCH] MIPS32: java.lang.String.equals Add intrinsic support for String.equals on MIPS32. Change-Id: I2d184aa4d5dae7cdd4a89c2c902535692c9e7393 --- compiler/optimizing/code_generator_mips.cc | 4 -- compiler/optimizing/intrinsics.cc | 7 +- compiler/optimizing/intrinsics_mips.cc | 103 ++++++++++++++++++++++++++++- runtime/arch/mips/registers_mips.h | 2 + 4 files changed, 106 insertions(+), 10 deletions(-) diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 8106499c0..959adb423 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -40,12 +40,8 @@ static constexpr int kCurrentMethodStackOffset = 0; static constexpr Register kMethodRegisterArgument = A0; // We need extra temporary/scratch registers (in addition to AT) in some cases. -static constexpr Register TMP = T8; static constexpr FRegister FTMP = F8; -// ART Thread Register. -static constexpr Register TR = S1; - Location MipsReturnLocation(Primitive::Type return_type) { switch (return_type) { case Primitive::kPrimBoolean: diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index dbe75249b..b01324ec3 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -89,10 +89,7 @@ static Primitive::Type GetType(uint64_t data, bool is_op_size) { } } -static Intrinsics GetIntrinsic(InlineMethod method, InstructionSet instruction_set) { - if (instruction_set == kMips) { - return Intrinsics::kNone; - } +static Intrinsics GetIntrinsic(InlineMethod method) { switch (method.opcode) { // Floating-point conversions. case kIntrinsicDoubleCvt: @@ -431,7 +428,7 @@ void IntrinsicsRecognizer::Run() { DexFileMethodInliner* inliner = driver_->GetMethodInlinerMap()->GetMethodInliner(&dex_file); DCHECK(inliner != nullptr); if (inliner->IsIntrinsic(invoke->GetDexMethodIndex(), &method)) { - Intrinsics intrinsic = GetIntrinsic(method, graph_->GetInstructionSet()); + Intrinsics intrinsic = GetIntrinsic(method); if (intrinsic != Intrinsics::kNone) { if (!CheckInvokeType(intrinsic, invoke, dex_file)) { diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 5efcf4ead..a94e3a8c2 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -138,6 +138,108 @@ bool IntrinsicLocationsBuilderMIPS::TryDispatch(HInvoke* invoke) { #define __ assembler-> +// boolean java.lang.String.equals(Object anObject) +void IntrinsicLocationsBuilderMIPS::VisitStringEquals(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + + // Temporary registers to store lengths of strings and for calculations. + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorMIPS::VisitStringEquals(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register str = locations->InAt(0).AsRegister(); + Register arg = locations->InAt(1).AsRegister(); + Register out = locations->Out().AsRegister(); + + Register temp1 = locations->GetTemp(0).AsRegister(); + Register temp2 = locations->GetTemp(1).AsRegister(); + Register temp3 = locations->GetTemp(2).AsRegister(); + + MipsLabel loop; + MipsLabel end; + MipsLabel return_true; + MipsLabel return_false; + + // Get offsets of count, value, and class fields within a string object. + const uint32_t count_offset = mirror::String::CountOffset().Uint32Value(); + const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value(); + const uint32_t class_offset = mirror::Object::ClassOffset().Uint32Value(); + + // Note that the null check must have been done earlier. + DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); + + // If the register containing the pointer to "this", and the register + // containing the pointer to "anObject" are the same register then + // "this", and "anObject" are the same object and we can + // short-circuit the logic to a true result. + if (str == arg) { + __ LoadConst32(out, 1); + return; + } + + // Check if input is null, return false if it is. + __ Beqz(arg, &return_false); + + // Reference equality check, return true if same reference. + __ Beq(str, arg, &return_true); + + // Instanceof check for the argument by comparing class fields. + // All string objects must have the same type since String cannot be subclassed. + // Receiver must be a string object, so its class field is equal to all strings' class fields. + // If the argument is a string object, its class field must be equal to receiver's class field. + __ Lw(temp1, str, class_offset); + __ Lw(temp2, arg, class_offset); + __ Bne(temp1, temp2, &return_false); + + // Load lengths of this and argument strings. + __ Lw(temp1, str, count_offset); + __ Lw(temp2, arg, count_offset); + // Check if lengths are equal, return false if they're not. + __ Bne(temp1, temp2, &return_false); + // Return true if both strings are empty. + __ Beqz(temp1, &return_true); + + // Don't overwrite input registers + __ Move(TMP, str); + __ Move(temp3, arg); + + // Assertions that must hold in order to compare strings 2 characters at a time. + DCHECK_ALIGNED(value_offset, 4); + static_assert(IsAligned<4>(kObjectAlignment), "String of odd length is not zero padded"); + + // Loop to compare strings 2 characters at a time starting at the beginning of the string. + // Ok to do this because strings are zero-padded. + __ Bind(&loop); + __ Lw(out, TMP, value_offset); + __ Lw(temp2, temp3, value_offset); + __ Bne(out, temp2, &return_false); + __ Addiu(TMP, TMP, 4); + __ Addiu(temp3, temp3, 4); + __ Addiu(temp1, temp1, -2); + __ Bgtz(temp1, &loop); + + // Return true and exit the function. + // If loop does not result in returning false, we return true. + __ Bind(&return_true); + __ LoadConst32(out, 1); + __ B(&end); + + // Return false and exit the function. + __ Bind(&return_false); + __ LoadConst32(out, 0); + __ Bind(&end); +} + // Unimplemented intrinsics. #define UNIMPLEMENTED_INTRINSIC(Name) \ @@ -204,7 +306,6 @@ UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) UNIMPLEMENTED_INTRINSIC(UnsafeCASObject) UNIMPLEMENTED_INTRINSIC(StringCharAt) UNIMPLEMENTED_INTRINSIC(StringCompareTo) -UNIMPLEMENTED_INTRINSIC(StringEquals) UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(StringNewStringFromBytes) diff --git a/runtime/arch/mips/registers_mips.h b/runtime/arch/mips/registers_mips.h index 0f784ed43..1096af013 100644 --- a/runtime/arch/mips/registers_mips.h +++ b/runtime/arch/mips/registers_mips.h @@ -59,6 +59,8 @@ enum Register { SP = 29, // Stack pointer. FP = 30, // Saved value/frame pointer. RA = 31, // Return address. + TR = S1, // ART Thread Register + TMP = T8, // scratch register (in addition to AT) kNumberOfCoreRegisters = 32, kNoRegister = -1 // Signals an illegal register. }; -- 2.11.0