From dff1f2812ecdaea89978c5351f0c70cdabbc0821 Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Wed, 5 Nov 2014 14:15:05 +0000 Subject: [PATCH] Support int-to-long conversions in the optimizing compiler. - Add support for the int-to-float Dex instruction in the optimizing compiler. - Add a HTypeConversion node type for control-flow graphs. - Generate x86, x86-64 and ARM (but not ARM64) code for int-to-float HTypeConversion nodes. - Add a 64-bit "Move doubleword to quadword with sign-extension" (MOVSXD) instruction to the x86-64 assembler. - Add related tests to test/422-type-conversion. Change-Id: Ieb8ec5380f9c411857119c79aa8d0728fd10f780 --- compiler/optimizing/builder.cc | 13 ++++ compiler/optimizing/builder.h | 4 ++ compiler/optimizing/code_generator_arm.cc | 88 ++++++++++++++++++++++++++++ compiler/optimizing/code_generator_arm64.cc | 1 + compiler/optimizing/code_generator_x86.cc | 85 +++++++++++++++++++++++++++ compiler/optimizing/code_generator_x86_64.cc | 86 +++++++++++++++++++++++++++ compiler/optimizing/nodes.h | 23 ++++++++ compiler/utils/x86_64/assembler_x86_64.cc | 16 +++++ compiler/utils/x86_64/assembler_x86_64.h | 3 + test/422-type-conversion/expected.txt | 0 test/422-type-conversion/info.txt | 1 + test/422-type-conversion/src/Main.java | 45 ++++++++++++++ test/Android.run-test.mk | 1 + 13 files changed, 366 insertions(+) create mode 100644 test/422-type-conversion/expected.txt create mode 100644 test/422-type-conversion/info.txt create mode 100644 test/422-type-conversion/src/Main.java diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index d168fc80f..4ce23d725 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -273,6 +273,14 @@ void HGraphBuilder::Unop_12x(const Instruction& instruction, Primitive::Type typ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); } +void HGraphBuilder::Conversion_12x(const Instruction& instruction, + Primitive::Type input_type, + Primitive::Type result_type) { + HInstruction* first = LoadLocal(instruction.VRegB(), input_type); + current_block_->AddInstruction(new (arena_) HTypeConversion(result_type, first)); + UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); +} + template void HGraphBuilder::Binop_23x(const Instruction& instruction, Primitive::Type type) { HInstruction* first = LoadLocal(instruction.VRegB(), type); @@ -823,6 +831,11 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::INT_TO_LONG: { + Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimLong); + break; + } + case Instruction::ADD_INT: { Binop_23x(instruction, Primitive::kPrimInt); break; diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index eea762f83..f25415bc3 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -116,6 +116,10 @@ class HGraphBuilder : public ValueObject { template void If_21t(const Instruction& instruction, uint32_t dex_offset); template void If_22t(const Instruction& instruction, uint32_t dex_offset); + void Conversion_12x(const Instruction& instruction, + Primitive::Type input_type, + Primitive::Type result_type); + void BuildReturn(const Instruction& instruction, Primitive::Type type); // Builds an instance field access node and returns whether the instruction is supported. diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 6e6d64cbf..39e564a7c 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -1223,6 +1223,94 @@ void InstructionCodeGeneratorARM::VisitNeg(HNeg* neg) { } } +void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(conversion, LocationSummary::kNoCall); + Primitive::Type result_type = conversion->GetResultType(); + Primitive::Type input_type = conversion->GetInputType(); + switch (result_type) { + case Primitive::kPrimLong: + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + // int-to-long conversion. + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type << " to " + << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } + break; + + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } +} + +void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversion) { + LocationSummary* locations = conversion->GetLocations(); + Location out = locations->Out(); + Location in = locations->InAt(0); + Primitive::Type result_type = conversion->GetResultType(); + Primitive::Type input_type = conversion->GetInputType(); + switch (result_type) { + case Primitive::kPrimLong: + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + // int-to-long conversion. + DCHECK(out.IsRegisterPair()); + DCHECK(in.IsRegister()); + __ Mov(out.AsRegisterPairLow(), in.As()); + // Sign extension. + __ Asr(out.AsRegisterPairHigh(), + out.AsRegisterPairLow(), + 31); + break; + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type << " to " + << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } + break; + + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } +} + void LocationsBuilderARM::VisitAdd(HAdd* add) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 90d7c3597..1c5db9e15 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -545,6 +545,7 @@ InstructionCodeGeneratorARM64::InstructionCodeGeneratorARM64(HGraph* graph, M(ParallelMove) \ M(StaticFieldGet) \ M(StaticFieldSet) \ + M(TypeConversion) \ #define UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name) name##UnimplementedInstructionBreakCode diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 1e37909be..8132e8179 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1126,6 +1126,91 @@ void InstructionCodeGeneratorX86::VisitNeg(HNeg* neg) { } } +void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(conversion, LocationSummary::kNoCall); + Primitive::Type result_type = conversion->GetResultType(); + Primitive::Type input_type = conversion->GetInputType(); + switch (result_type) { + case Primitive::kPrimLong: + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + // int-to-long conversion. + locations->SetInAt(0, Location::RegisterLocation(EAX)); + locations->SetOut(Location::RegisterPairLocation(EAX, EDX)); + break; + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type << " to " + << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } + break; + + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } +} + +void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversion) { + LocationSummary* locations = conversion->GetLocations(); + Location out = locations->Out(); + Location in = locations->InAt(0); + Primitive::Type result_type = conversion->GetResultType(); + Primitive::Type input_type = conversion->GetInputType(); + switch (result_type) { + case Primitive::kPrimLong: + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + // int-to-long conversion. + DCHECK_EQ(out.AsRegisterPairLow(), EAX); + DCHECK_EQ(out.AsRegisterPairHigh(), EDX); + DCHECK_EQ(in.As(), EAX); + __ cdq(); + break; + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type << " to " + << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } + break; + + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } +} + void LocationsBuilderX86::VisitAdd(HAdd* add) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 40eec9b15..d9eb17c0e 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1104,6 +1104,92 @@ void InstructionCodeGeneratorX86_64::VisitNeg(HNeg* neg) { } } +void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(conversion, LocationSummary::kNoCall); + Primitive::Type result_type = conversion->GetResultType(); + Primitive::Type input_type = conversion->GetInputType(); + switch (result_type) { + case Primitive::kPrimLong: + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + // int-to-long conversion. + // TODO: We would benefit from a (to-be-implemented) + // Location::RegisterOrStackSlot requirement for this input. + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + break; + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type << " to " + << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } + break; + + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } +} + +void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conversion) { + LocationSummary* locations = conversion->GetLocations(); + Location out = locations->Out(); + Location in = locations->InAt(0); + Primitive::Type result_type = conversion->GetResultType(); + Primitive::Type input_type = conversion->GetInputType(); + switch (result_type) { + case Primitive::kPrimLong: + switch (input_type) { + DCHECK(out.IsRegister()); + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + // int-to-long conversion. + DCHECK(in.IsRegister()); + __ movsxd(out.As(), in.As()); + break; + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type << " to " + << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } + break; + + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } +} + void LocationsBuilderX86_64::VisitAdd(HAdd* add) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 79638b354..d7069ca31 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -516,6 +516,7 @@ class HBasicBlock : public ArenaObject { M(Sub, BinaryOperation) \ M(SuspendCheck, Instruction) \ M(Temporary, Instruction) \ + M(TypeConversion, Instruction) \ #define FOR_EACH_INSTRUCTION(M) \ FOR_EACH_CONCRETE_INSTRUCTION(M) \ @@ -1761,6 +1762,28 @@ class HNot : public HUnaryOperation { DISALLOW_COPY_AND_ASSIGN(HNot); }; +class HTypeConversion : public HExpression<1> { + public: + // Instantiate a type conversion of `input` to `result_type`. + HTypeConversion(Primitive::Type result_type, HInstruction* input) + : HExpression(result_type, SideEffects::None()) { + SetRawInputAt(0, input); + DCHECK_NE(input->GetType(), result_type); + } + + HInstruction* GetInput() const { return InputAt(0); } + Primitive::Type GetInputType() const { return GetInput()->GetType(); } + Primitive::Type GetResultType() const { return GetType(); } + + bool CanBeMoved() const OVERRIDE { return true; } + bool InstructionDataEquals(HInstruction* /* other */) const OVERRIDE { return true; } + + DECLARE_INSTRUCTION(TypeConversion); + + private: + DISALLOW_COPY_AND_ASSIGN(HTypeConversion); +}; + class HPhi : public HInstruction { public: HPhi(ArenaAllocator* arena, uint32_t reg_number, size_t number_of_inputs, Primitive::Type type) diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index 2e951ddae..5b706584b 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -351,6 +351,22 @@ void X86_64Assembler::movss(XmmRegister dst, XmmRegister src) { } +void X86_64Assembler::movsxd(CpuRegister dst, CpuRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitRex64(dst); + EmitUint8(0x63); + EmitRegisterOperand(dst.LowBits(), src.LowBits()); +} + + +void X86_64Assembler::movsxd(CpuRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitRex64(dst); + EmitUint8(0x63); + EmitOperand(dst.LowBits(), src); +} + + void X86_64Assembler::movd(XmmRegister dst, CpuRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 5b16f0891..ed80e44ea 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -300,6 +300,9 @@ class X86_64Assembler FINAL : public Assembler { void movss(const Address& dst, XmmRegister src); void movss(XmmRegister dst, XmmRegister src); + void movsxd(CpuRegister dst, CpuRegister src); + void movsxd(CpuRegister dst, const Address& src); + void movd(XmmRegister dst, CpuRegister src); void movd(CpuRegister dst, XmmRegister src); diff --git a/test/422-type-conversion/expected.txt b/test/422-type-conversion/expected.txt new file mode 100644 index 000000000..e69de29bb diff --git a/test/422-type-conversion/info.txt b/test/422-type-conversion/info.txt new file mode 100644 index 000000000..e734f3213 --- /dev/null +++ b/test/422-type-conversion/info.txt @@ -0,0 +1 @@ +Tests for type conversions. diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java new file mode 100644 index 000000000..997491997 --- /dev/null +++ b/test/422-type-conversion/src/Main.java @@ -0,0 +1,45 @@ +/* + * 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. + */ + +// Note that $opt$ is a marker for the optimizing compiler to ensure +// it does compile the method. +public class Main { + + public static void assertEquals(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void main(String[] args) { + intToLong(); + } + + private static void intToLong() { + assertEquals(1L, $opt$IntToLong(1)); + assertEquals(0L, $opt$IntToLong(0)); + assertEquals(-1L, $opt$IntToLong(-1)); + assertEquals(51L, $opt$IntToLong(51)); + assertEquals(-51L, $opt$IntToLong(-51)); + assertEquals(2147483647L, $opt$IntToLong(2147483647)); // (2^31) - 1 + assertEquals(-2147483647L, $opt$IntToLong(-2147483647)); // -(2^31) - 1 + assertEquals(-2147483648L, $opt$IntToLong(-2147483648)); // -(2^31) + } + + static long $opt$IntToLong(int a){ + return a; + } +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 0a1e3e111..a317f8cf3 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -406,6 +406,7 @@ TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS := \ 418-const-string \ 419-long-parameter \ 420-const-class \ + 422-type-conversion \ 700-LoadArgRegs \ 701-easy-div-rem \ 702-LargeBranchOffset \ -- 2.11.0