From fd861249f31ab360c12dd1ffb131d50f02b0bfc6 Mon Sep 17 00:00:00 2001 From: Calin Juravle Date: Tue, 25 Nov 2014 20:56:51 +0000 Subject: [PATCH] [optimizing compiler] Add CMP{L,G}_{FLOAT,DOUBLE} - adds float comparison for arm, x86, x86_64 backends. - adds ucomis{s,d} assembly to x86 and x86_64. Change-Id: I232d2b6e9ecf373beb5cc63698dd97a658ff9c83 --- compiler/optimizing/builder.cc | 31 +++- compiler/optimizing/builder.h | 2 + compiler/optimizing/code_generator_arm.cc | 71 +++++--- compiler/optimizing/code_generator_x86.cc | 68 +++++--- compiler/optimizing/code_generator_x86_64.cc | 60 +++++-- compiler/optimizing/nodes.h | 27 ++- compiler/utils/x86/assembler_x86.cc | 17 ++ compiler/utils/x86/assembler_x86.h | 2 + compiler/utils/x86/constants_x86.h | 3 +- compiler/utils/x86_64/assembler_x86_64.cc | 18 ++ compiler/utils/x86_64/assembler_x86_64.h | 2 + compiler/utils/x86_64/assembler_x86_64_test.cc | 8 + compiler/utils/x86_64/constants_x86_64.h | 3 +- test/432-optimizing-cmp/expected.txt | 0 test/432-optimizing-cmp/info.txt | 1 + test/432-optimizing-cmp/smali/cmp.smali | 33 ++++ test/432-optimizing-cmp/src/Main.java | 227 +++++++++++++++++++++++++ test/Android.run-test.mk | 1 + 18 files changed, 507 insertions(+), 67 deletions(-) create mode 100644 test/432-optimizing-cmp/expected.txt create mode 100644 test/432-optimizing-cmp/info.txt create mode 100644 test/432-optimizing-cmp/smali/cmp.smali create mode 100644 test/432-optimizing-cmp/src/Main.java diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index b26146069..1be6e0093 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -313,6 +313,15 @@ void HGraphBuilder::Binop_23x_shift(const Instruction& instruction, UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); } +void HGraphBuilder::Binop_23x_cmp(const Instruction& instruction, + Primitive::Type type, + HCompare::Bias bias) { + HInstruction* first = LoadLocal(instruction.VRegB(), type); + HInstruction* second = LoadLocal(instruction.VRegC(), type); + current_block_->AddInstruction(new (arena_) HCompare(type, first, second, bias)); + UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); +} + template void HGraphBuilder::Binop_12x(const Instruction& instruction, Primitive::Type type) { HInstruction* first = LoadLocal(instruction.VRegA(), type); @@ -1492,7 +1501,27 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; case Instruction::CMP_LONG: { - Binop_23x(instruction, Primitive::kPrimLong); + Binop_23x_cmp(instruction, Primitive::kPrimLong, HCompare::kNoBias); + break; + } + + case Instruction::CMPG_FLOAT: { + Binop_23x_cmp(instruction, Primitive::kPrimFloat, HCompare::kGtBias); + break; + } + + case Instruction::CMPG_DOUBLE: { + Binop_23x_cmp(instruction, Primitive::kPrimDouble, HCompare::kGtBias); + break; + } + + case Instruction::CMPL_FLOAT: { + Binop_23x_cmp(instruction, Primitive::kPrimFloat, HCompare::kLtBias); + break; + } + + case Instruction::CMPL_DOUBLE: { + Binop_23x_cmp(instruction, Primitive::kPrimDouble, HCompare::kLtBias); break; } diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 204005daa..25781b08f 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -107,6 +107,8 @@ class HGraphBuilder : public ValueObject { template void Binop_23x_shift(const Instruction& instruction, Primitive::Type type); + void Binop_23x_cmp(const Instruction& instruction, Primitive::Type type, HCompare::Bias bias); + template void Binop_12x(const Instruction& instruction, Primitive::Type type); diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 890cfdd0e..df65ff054 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -2292,44 +2292,71 @@ void InstructionCodeGeneratorARM::VisitNot(HNot* not_) { void LocationsBuilderARM::VisitCompare(HCompare* compare) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + switch (compare->InputAt(0)->GetType()) { + case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + } + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresRegister()); + break; + } + default: + LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType(); + } } void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) { LocationSummary* locations = compare->GetLocations(); + Register out = locations->Out().As(); + Location left = locations->InAt(0); + Location right = locations->InAt(1); + + Label less, greater, done; switch (compare->InputAt(0)->GetType()) { case Primitive::kPrimLong: { - Register output = locations->Out().As(); - Location left = locations->InAt(0); - Location right = locations->InAt(1); - Label less, greater, done; __ cmp(left.AsRegisterPairHigh(), ShifterOperand(right.AsRegisterPairHigh())); // Signed compare. __ b(&less, LT); __ b(&greater, GT); - // Do LoadImmediate before any `cmp`, as LoadImmediate might affect - // the status flags. - __ LoadImmediate(output, 0); + // Do LoadImmediate before any `cmp`, as LoadImmediate might affect the status flags. + __ LoadImmediate(out, 0); __ cmp(left.AsRegisterPairLow(), ShifterOperand(right.AsRegisterPairLow())); // Unsigned compare. - __ b(&done, EQ); - __ b(&less, CC); - - __ Bind(&greater); - __ LoadImmediate(output, 1); - __ b(&done); - - __ Bind(&less); - __ LoadImmediate(output, -1); - - __ Bind(&done); + break; + } + case Primitive::kPrimFloat: { + __ LoadImmediate(out, 0); + __ vcmps(left.As(), right.As()); + __ b(compare->IsGtBias() ? &greater : &less, VS); // VS for unordered + break; + } + case Primitive::kPrimDouble: { + __ LoadImmediate(out, 0); + __ vcmpd(FromLowSToD(left.AsFpuRegisterPairLow()), + FromLowSToD(right.AsFpuRegisterPairLow())); + __ b(compare->IsGtBias() ? &greater : &less, VS); break; } default: - LOG(FATAL) << "Unimplemented compare type " << compare->InputAt(0)->GetType(); + LOG(FATAL) << "Unexpected compare type " << compare->InputAt(0)->GetType(); } + __ b(&done, EQ); + __ b(&less, CC); // CC is for both: unsigned compare for longs and 'less than' for floats. + + __ Bind(&greater); + __ LoadImmediate(out, 1); + __ b(&done); + + __ Bind(&less); + __ LoadImmediate(out, -1); + + __ Bind(&done); } void LocationsBuilderARM::VisitPhi(HPhi* instruction) { diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 368945223..537e5e10d 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -2355,20 +2355,36 @@ void InstructionCodeGeneratorX86::VisitNot(HNot* not_) { void LocationsBuilderX86::VisitCompare(HCompare* compare) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::Any()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + switch (compare->InputAt(0)->GetType()) { + case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RequiresRegister()); + // TODO: we set any here but we don't handle constants + locations->SetInAt(1, Location::Any()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + } + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresRegister()); + break; + } + default: + LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType(); + } } void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) { LocationSummary* locations = compare->GetLocations(); + Register out = locations->Out().As(); + Location left = locations->InAt(0); + Location right = locations->InAt(1); + + Label less, greater, done; switch (compare->InputAt(0)->GetType()) { case Primitive::kPrimLong: { - Label less, greater, done; - Register output = locations->Out().As(); - Location left = locations->InAt(0); - Location right = locations->InAt(1); - if (right.IsRegister()) { + if (right.IsRegisterPair()) { __ cmpl(left.AsRegisterPairHigh(), right.AsRegisterPairHigh()); } else { DCHECK(right.IsDoubleStackSlot()); @@ -2383,23 +2399,33 @@ void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) { DCHECK(right.IsDoubleStackSlot()); __ cmpl(left.AsRegisterPairLow(), Address(ESP, right.GetStackIndex())); } - __ movl(output, Immediate(0)); - __ j(kEqual, &done); - __ j(kBelow, &less); // Unsigned compare. - - __ Bind(&greater); - __ movl(output, Immediate(1)); - __ jmp(&done); - - __ Bind(&less); - __ movl(output, Immediate(-1)); - - __ Bind(&done); + break; + } + case Primitive::kPrimFloat: { + __ ucomiss(left.As(), right.As()); + __ j(kUnordered, compare->IsGtBias() ? &greater : &less); + break; + } + case Primitive::kPrimDouble: { + __ ucomisd(left.As(), right.As()); + __ j(kUnordered, compare->IsGtBias() ? &greater : &less); break; } default: - LOG(FATAL) << "Unimplemented compare type " << compare->InputAt(0)->GetType(); + LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType(); } + __ movl(out, Immediate(0)); + __ j(kEqual, &done); + __ j(kBelow, &less); // kBelow is for CF (unsigned & floats). + + __ Bind(&greater); + __ movl(out, Immediate(1)); + __ jmp(&done); + + __ Bind(&less); + __ movl(out, Immediate(-1)); + + __ Bind(&done); } void LocationsBuilderX86::VisitPhi(HPhi* instruction) { diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 34fa1e7a3..a39b238d5 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -899,33 +899,61 @@ void InstructionCodeGeneratorX86_64::VisitGreaterThanOrEqual(HGreaterThanOrEqual void LocationsBuilderX86_64::VisitCompare(HCompare* compare) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + switch (compare->InputAt(0)->GetType()) { + case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + } + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresRegister()); + break; + } + default: + LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType(); + } } void InstructionCodeGeneratorX86_64::VisitCompare(HCompare* compare) { - Label greater, done; LocationSummary* locations = compare->GetLocations(); - switch (compare->InputAt(0)->GetType()) { - case Primitive::kPrimLong: - __ cmpq(locations->InAt(0).As(), - locations->InAt(1).As()); + CpuRegister out = locations->Out().As(); + Location left = locations->InAt(0); + Location right = locations->InAt(1); + + Label less, greater, done; + Primitive::Type type = compare->InputAt(0)->GetType(); + switch (type) { + case Primitive::kPrimLong: { + __ cmpq(left.As(), right.As()); + break; + } + case Primitive::kPrimFloat: { + __ ucomiss(left.As(), right.As()); + __ j(kUnordered, compare->IsGtBias() ? &greater : &less); break; + } + case Primitive::kPrimDouble: { + __ ucomisd(left.As(), right.As()); + __ j(kUnordered, compare->IsGtBias() ? &greater : &less); + break; + } default: - LOG(FATAL) << "Unimplemented compare type " << compare->InputAt(0)->GetType(); + LOG(FATAL) << "Unexpected compare type " << type; } - - CpuRegister output = locations->Out().As(); - __ movl(output, Immediate(0)); + __ movl(out, Immediate(0)); __ j(kEqual, &done); - __ j(kGreater, &greater); + __ j(type == Primitive::kPrimLong ? kLess : kBelow, &less); // ucomis{s,d} sets CF (kBelow) - __ movl(output, Immediate(-1)); + __ Bind(&greater); + __ movl(out, Immediate(1)); __ jmp(&done); - __ Bind(&greater); - __ movl(output, Immediate(1)); + __ Bind(&less); + __ movl(out, Immediate(-1)); __ Bind(&done); } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index f562113e6..28496e4ad 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -777,7 +777,7 @@ class HInstruction : public ArenaObject { } // Returns whether two instructions are equal, that is: - // 1) They have the same type and contain the same data, + // 1) They have the same type and contain the same data (InstructionDataEquals). // 2) Their inputs are identical. bool Equals(HInstruction* other) const; @@ -1363,28 +1363,45 @@ class HGreaterThanOrEqual : public HCondition { // Result is 0 if input0 == input1, 1 if input0 > input1, or -1 if input0 < input1. class HCompare : public HBinaryOperation { public: - HCompare(Primitive::Type type, HInstruction* first, HInstruction* second) - : HBinaryOperation(Primitive::kPrimInt, first, second) { + // The bias applies for floating point operations and indicates how NaN + // comparisons are treated: + enum Bias { + kNoBias, // bias is not applicable (i.e. for long operation) + kGtBias, // return 1 for NaN comparisons + kLtBias, // return -1 for NaN comparisons + }; + + HCompare(Primitive::Type type, HInstruction* first, HInstruction* second, Bias bias) + : HBinaryOperation(Primitive::kPrimInt, first, second), bias_(bias) { DCHECK_EQ(type, first->GetType()); DCHECK_EQ(type, second->GetType()); } - virtual int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { + int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { return x == y ? 0 : x > y ? 1 : -1; } - virtual int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { + + int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { return x == y ? 0 : x > y ? 1 : -1; } + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + return bias_ == other->AsCompare()->bias_; + } + + bool IsGtBias() { return bias_ == kGtBias; } + DECLARE_INSTRUCTION(Compare); private: + const Bias bias_; + DISALLOW_COPY_AND_ASSIGN(HCompare); }; diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index a297ea3b6..b118f9a81 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -613,6 +613,23 @@ void X86Assembler::comisd(XmmRegister a, XmmRegister b) { } +void X86Assembler::ucomiss(XmmRegister a, XmmRegister b) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x2E); + EmitXmmRegisterOperand(a, b); +} + + +void X86Assembler::ucomisd(XmmRegister a, XmmRegister b) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x2E); + EmitXmmRegisterOperand(a, b); +} + + void X86Assembler::sqrtsd(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF2); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index 6ea66a5fa..a630ccda4 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -301,6 +301,8 @@ class X86Assembler FINAL : public Assembler { void comiss(XmmRegister a, XmmRegister b); void comisd(XmmRegister a, XmmRegister b); + void ucomiss(XmmRegister a, XmmRegister b); + void ucomisd(XmmRegister a, XmmRegister b); void sqrtsd(XmmRegister dst, XmmRegister src); void sqrtss(XmmRegister dst, XmmRegister src); diff --git a/compiler/utils/x86/constants_x86.h b/compiler/utils/x86/constants_x86.h index 45c3834a9..2dfb65c47 100644 --- a/compiler/utils/x86/constants_x86.h +++ b/compiler/utils/x86/constants_x86.h @@ -96,7 +96,8 @@ enum Condition { kZero = kEqual, kNotZero = kNotEqual, kNegative = kSign, - kPositive = kNotSign + kPositive = kNotSign, + kUnordered = kParityEven }; diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index dff384907..2b7ec0fbd 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -700,6 +700,24 @@ void X86_64Assembler::comisd(XmmRegister a, XmmRegister b) { EmitXmmRegisterOperand(a.LowBits(), b); } +void X86_64Assembler::ucomiss(XmmRegister a, XmmRegister b) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOptionalRex32(a, b); + EmitUint8(0x0F); + EmitUint8(0x2E); + EmitXmmRegisterOperand(a.LowBits(), b); +} + + +void X86_64Assembler::ucomisd(XmmRegister a, XmmRegister b) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(a, b); + EmitUint8(0x0F); + EmitUint8(0x2E); + EmitXmmRegisterOperand(a.LowBits(), b); +} + void X86_64Assembler::sqrtsd(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index ab1bc9e97..929ed3174 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -344,6 +344,8 @@ class X86_64Assembler FINAL : public Assembler { void comiss(XmmRegister a, XmmRegister b); void comisd(XmmRegister a, XmmRegister b); + void ucomiss(XmmRegister a, XmmRegister b); + void ucomisd(XmmRegister a, XmmRegister b); void sqrtsd(XmmRegister dst, XmmRegister src); void sqrtss(XmmRegister dst, XmmRegister src); diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index 14a98b935..c8e923c9d 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -660,6 +660,14 @@ TEST_F(AssemblerX86_64Test, Comisd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::comisd, "comisd %{reg2}, %{reg1}"), "comisd"); } +TEST_F(AssemblerX86_64Test, Ucomiss) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::ucomiss, "ucomiss %{reg2}, %{reg1}"), "ucomiss"); +} + +TEST_F(AssemblerX86_64Test, Ucomisd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::ucomisd, "ucomisd %{reg2}, %{reg1}"), "ucomisd"); +} + TEST_F(AssemblerX86_64Test, Sqrtss) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::sqrtss, "sqrtss %{reg2}, %{reg1}"), "sqrtss"); } diff --git a/compiler/utils/x86_64/constants_x86_64.h b/compiler/utils/x86_64/constants_x86_64.h index 2a5b43da4..0c782d46c 100644 --- a/compiler/utils/x86_64/constants_x86_64.h +++ b/compiler/utils/x86_64/constants_x86_64.h @@ -105,7 +105,8 @@ enum Condition { kZero = kEqual, kNotZero = kNotEqual, kNegative = kSign, - kPositive = kNotSign + kPositive = kNotSign, + kUnordered = kParityEven }; diff --git a/test/432-optimizing-cmp/expected.txt b/test/432-optimizing-cmp/expected.txt new file mode 100644 index 000000000..e69de29bb diff --git a/test/432-optimizing-cmp/info.txt b/test/432-optimizing-cmp/info.txt new file mode 100644 index 000000000..fad6cee72 --- /dev/null +++ b/test/432-optimizing-cmp/info.txt @@ -0,0 +1 @@ +Tests for compare operations. diff --git a/test/432-optimizing-cmp/smali/cmp.smali b/test/432-optimizing-cmp/smali/cmp.smali new file mode 100644 index 000000000..470d940e3 --- /dev/null +++ b/test/432-optimizing-cmp/smali/cmp.smali @@ -0,0 +1,33 @@ +.class public LTestCmp; + +.super Ljava/lang/Object; + +.method public static $opt$CmpLong(JJ)I + .registers 5 + cmp-long v0, v1, v3 + return v0 +.end method + +.method public static $opt$CmpGtFloat(FF)I + .registers 3 + cmpg-float v0, v1, v2 + return v0 +.end method + +.method public static $opt$CmpLtFloat(FF)I + .registers 3 + cmpl-float v0, v1, v2 + return v0 +.end method + +.method public static $opt$CmpGtDouble(DD)I + .registers 5 + cmpg-double v0, v1, v3 + return v0 +.end method + +.method public static $opt$CmpLtDouble(DD)I + .registers 5 + cmpl-double v0, v1, v3 + return v0 +.end method diff --git a/test/432-optimizing-cmp/src/Main.java b/test/432-optimizing-cmp/src/Main.java new file mode 100644 index 000000000..f45b94db5 --- /dev/null +++ b/test/432-optimizing-cmp/src/Main.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; + +public class Main { + + public static void main(String[] args) throws Exception { + cmpLong(); + cmpFloat(); + cmpDouble(); + } + + private static void cmpLong() throws Exception { + expectLt(3.1F, 5.1F); + expectGt(5.1F, 3.1F); + expectLt(Long.MIN_VALUE, Long.MAX_VALUE); + expectGt(Long.MAX_VALUE, Long.MIN_VALUE); + + expectEquals(0, smaliCmpLong(0L, 0L)); + expectEquals(0, smaliCmpLong(1L, 1L)); + expectEquals(-1, smaliCmpLong(1L, 2L)); + expectEquals(1, smaliCmpLong(2L, 1L)); + expectEquals(-1, smaliCmpLong(Long.MIN_VALUE, Long.MAX_VALUE)); + expectEquals(1, smaliCmpLong(Long.MAX_VALUE, Long.MIN_VALUE)); + expectEquals(0, smaliCmpLong(Long.MIN_VALUE, Long.MIN_VALUE)); + expectEquals(0, smaliCmpLong(Long.MAX_VALUE, Long.MAX_VALUE)); + } + + private static void cmpFloat() throws Exception { + expectLt(3.1F, 5.1F); + expectGt(5.1F, 3.1F); + expectLt(Float.MIN_VALUE, Float.MAX_VALUE); + expectGt(Float.MAX_VALUE, Float.MIN_VALUE); + expectFalse(3.1F, Float.NaN); + expectFalse(Float.NaN, 3.1F); + + expectEquals(0, smaliCmpGtFloat(0F, 0F)); + expectEquals(0, smaliCmpGtFloat(1F, 1F)); + expectEquals(-1, smaliCmpGtFloat(1.1F, 2.1F)); + expectEquals(1, smaliCmpGtFloat(2.1F, 1.1F)); + expectEquals(-1, smaliCmpGtFloat(Float.MIN_VALUE, Float.MAX_VALUE)); + expectEquals(1, smaliCmpGtFloat(Float.MAX_VALUE, Float.MIN_VALUE)); + expectEquals(0, smaliCmpGtFloat(Float.MIN_VALUE, Float.MIN_VALUE)); + expectEquals(0, smaliCmpGtFloat(Float.MAX_VALUE, Float.MAX_VALUE)); + expectEquals(1, smaliCmpGtFloat(5F, Float.NaN)); + expectEquals(1, smaliCmpGtFloat(Float.NaN, 5F)); + + expectEquals(0, smaliCmpLtFloat(0F, 0F)); + expectEquals(0, smaliCmpLtFloat(1F, 1F)); + expectEquals(-1, smaliCmpLtFloat(1.1F, 2.1F)); + expectEquals(1, smaliCmpLtFloat(2.1F, 1.1F)); + expectEquals(-1, smaliCmpLtFloat(Float.MIN_VALUE, Float.MAX_VALUE)); + expectEquals(1, smaliCmpLtFloat(Float.MAX_VALUE, Float.MIN_VALUE)); + expectEquals(0, smaliCmpLtFloat(Float.MIN_VALUE, Float.MIN_VALUE)); + expectEquals(0, smaliCmpLtFloat(Float.MAX_VALUE, Float.MAX_VALUE)); + expectEquals(-1, smaliCmpLtFloat(5F, Float.NaN)); + expectEquals(-1, smaliCmpLtFloat(Float.NaN, 5F)); + } + + private static void cmpDouble() throws Exception { + expectLt(3.1D, 5.1D); + expectGt(5.1D, 3.1D); + expectLt(Double.MIN_VALUE, Double.MAX_VALUE); + expectGt(Double.MAX_VALUE, Double.MIN_VALUE); + expectFalse(3.1D, Double.NaN); + expectFalse(Double.NaN, 3.1D); + + expectEquals(0, smaliCmpGtDouble(0D, 0D)); + expectEquals(0, smaliCmpGtDouble(1D, 1D)); + expectEquals(-1, smaliCmpGtDouble(1.1D, 2.1D)); + expectEquals(1, smaliCmpGtDouble(2.1D, 1.1D)); + expectEquals(-1, smaliCmpGtDouble(Double.MIN_VALUE, Double.MAX_VALUE)); + expectEquals(1, smaliCmpGtDouble(Double.MAX_VALUE, Double.MIN_VALUE)); + expectEquals(0, smaliCmpGtDouble(Double.MIN_VALUE, Double.MIN_VALUE)); + expectEquals(0, smaliCmpGtDouble(Double.MAX_VALUE, Double.MAX_VALUE)); + expectEquals(1, smaliCmpGtDouble(5D, Double.NaN)); + expectEquals(1, smaliCmpGtDouble(Double.NaN, 5D)); + + expectEquals(0, smaliCmpLtDouble(0D, 0D)); + expectEquals(0, smaliCmpLtDouble(1D, 1D)); + expectEquals(-1, smaliCmpLtDouble(1.1D, 2.1D)); + expectEquals(1, smaliCmpLtDouble(2.1D, 1.1D)); + expectEquals(-1, smaliCmpLtDouble(Double.MIN_VALUE, Double.MAX_VALUE)); + expectEquals(1, smaliCmpLtDouble(Double.MAX_VALUE, Double.MIN_VALUE)); + expectEquals(0, smaliCmpLtDouble(Double.MIN_VALUE, Double.MIN_VALUE)); + expectEquals(0, smaliCmpLtDouble(Double.MAX_VALUE, Double.MAX_VALUE)); + expectEquals(-1, smaliCmpLtDouble(5D, Double.NaN)); + expectEquals(-1, smaliCmpLtDouble(Float.NaN, 5D)); + } + + static boolean $opt$lt(long a, long b) { + return a < b; + } + + static boolean $opt$lt(float a, float b) { + return a < b; + } + + static boolean $opt$lt(double a, double b) { + return a < b; + } + + static boolean $opt$gt(long a, long b) { + return a > b; + } + + static boolean $opt$gt(float a, float b) { + return a > b; + } + + static boolean $opt$gt(double a, double b) { + return a > b; + } + + // Wrappers around methods located in file cmp.smali. + + private static int smaliCmpLong(long a, long b) throws Exception { + Class c = Class.forName("TestCmp"); + Method m = c.getMethod("$opt$CmpLong", long.class, long.class); + int result = (Integer)m.invoke(null, a, b); + return result; + } + + private static int smaliCmpGtFloat(float a, float b) throws Exception { + Class c = Class.forName("TestCmp"); + Method m = c.getMethod("$opt$CmpGtFloat", float.class, float.class); + int result = (Integer)m.invoke(null, a, b); + return result; + } + + private static int smaliCmpLtFloat(float a, float b) throws Exception { + Class c = Class.forName("TestCmp"); + Method m = c.getMethod("$opt$CmpLtFloat", float.class, float.class); + int result = (Integer)m.invoke(null, a, b); + return result; + } + + private static int smaliCmpGtDouble(double a, double b) throws Exception { + Class c = Class.forName("TestCmp"); + Method m = c.getMethod("$opt$CmpGtDouble", double.class, double.class); + int result = (Integer)m.invoke(null, a, b); + return result; + } + + private static int smaliCmpLtDouble(double a, double b) throws Exception { + Class c = Class.forName("TestCmp"); + Method m = c.getMethod("$opt$CmpLtDouble", double.class, double.class); + int result = (Integer)m.invoke(null, a, b); + return result; + } + + public static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void expectLt(long a, long b) { + if (!$opt$lt(a, b)) { + throw new Error("Expected: " + a + " < " + b); + } + } + + public static void expectGt(long a, long b) { + if (!$opt$gt(a, b)) { + throw new Error("Expected: " + a + " > " + b); + } + } + + public static void expectLt(float a, float b) { + if (!$opt$lt(a, b)) { + throw new Error("Expected: " + a + " < " + b); + } + } + + public static void expectGt(float a, float b) { + if (!$opt$gt(a, b)) { + throw new Error("Expected: " + a + " > " + b); + } + } + + public static void expectFalse(float a, float b) { + if ($opt$lt(a, b)) { + throw new Error("Not expecting: " + a + " < " + b); + } + if ($opt$gt(a, b)) { + throw new Error("Not expecting: " + a + " > " + b); + } + } + + public static void expectLt(double a, double b) { + if (!$opt$lt(a, b)) { + throw new Error("Expected: " + a + " < " + b); + } + } + + public static void expectGt(double a, double b) { + if (!$opt$gt(a, b)) { + throw new Error("Expected: " + a + " > " + b); + } + } + + public static void expectFalse(double a, double b) { + if ($opt$lt(a, b)) { + throw new Error("Not expecting: " + a + " < " + b); + } + if ($opt$gt(a, b)) { + throw new Error("Not expecting: " + a + " > " + b); + } + } + +} + diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 47d186a47..d9699bd24 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -331,6 +331,7 @@ TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS := \ 428-optimizing-arith-rem \ 430-live-register-slow-path \ 431-optimizing-arith-shifts \ + 432-optimizing-cmp \ 701-easy-div-rem \ 800-smali \ -- 2.11.0